TweetFollow Us on Twitter

Horse Feathers

Volume Number: 20 (2004)
Issue Number: 7
Column Tag: Programming

QuickTime Toolkit

by Tim Monroe

Horse Feathers

Introduction

In the previous article ("Tickle Me" in MacTech, June 2004), we took a look at using the Tcl scripting language and the Tk graphical user interface toolkit to build a QuickTime application. We saw how to use the QuickTimeTcl extension to add to a basic Tcl/Tk application the ability to open and display QuickTime movies. Figure 1 shows a movie displayed by our sample application, called TickLeez.


Figure 1: A movie window displayed by TickLeez (Macintosh)

In this article, I want to continue investigating QuickTimeTcl. In particular, we'll see how to configure our application to react to keyboard and operating system events. We'll also see how to implement movie editing. By the end of this article, TickLeez will incorporate all the standard movie playback and editing behaviors.

Event Bindings

Last time, we saw how to create the menu bar and menus for the TickLeez application. Later in this article, we'll learn how to adjust the state of menu items according to the current edited state of the frontmost movie window. Right now, we need to create some Tk bindings for a handful of events.

An event binding attaches a Tcl command to a specific event related to a movie window or to some other event, such as a keyboard event. The -accelerator options we specified when creating the menu items affect only the appearance of the menu items. To enable the keyboard shortcuts, we need to call the bind command, for instance like this:

bind .$winName <$appData(modKey)-n> {newDoc}

Tk also allows us to create bindings for activate and deactivate events for a window and for mouse-enter and mouse-exit events. Our complete set of event bindings is established in the setupBindings procedure, shown in Listing 1.

Listing 1: Setting up key bindings

setupBindings
proc setupBindings {winName} {
   global appData
   bind .$winName <$appData(modKey)-n> {newDoc}
   bind .$winName <$appData(modKey)-o> {openDoc}
   bind .$winName <$appData(modKey)-q> {exit}
      
   if {[isMovieWindow $winName]} {
      # window state-change bindings
      bind .$winName <Activate> {adjustMenus}
      bind .$winName <Deactivate> {adjustMenus}
      
      # accelerator key bindings
      bind .$winName <$appData(modKey)-w> {closeDoc}
      bind .$winName <$appData(modKey)-s> {saveDoc}
      bind .$winName <$appData(modKey)-S> {saveAsDoc}
      
      bind .$winName <$appData(modKey)-z> {undoDoc}
      bind .$winName <$appData(modKey)-x> {editDoc cut}
      bind .$winName <$appData(modKey)-c> {editDoc copy}
      bind .$winName <$appData(modKey)-v> {editDoc paste}
      bind .$winName <$appData(modKey)-a> {selectDoc all}
      bind .$winName <$appData(modKey)-b> {selectDoc none}
      
      bind .$winName <$appData(modKey)-KeyPress-1> \
                                                {puts "TO BE PROVIDED"}
      
      # miscellaneous bindings
      bind .$winName <Delete> {editDoc cut}
      bind .$winName <KeyPress> "handleKey $winName %K"
      bind .$winName <Leave> ".$winName configure \
                                                -cursor arrow"
   }
}

Note the Leave binding: it's mainly intended for QuickTime VR movies, where the cursor is not automatically changed to the normal arrow cursor when it moves outside of the movie rectangle. (Other media types that alter the cursor, such as Flash, do not have this problem, so one could reasonably consider the VR behavior to be a bug.) Keep in mind that this workaround is not perfect: the cursor does not change to an arrow until the cursor hotspot has completely exited the movie window. This means that it will remain as the current VR cursor if moved into the window's title bar. Addressing that issue is left as an exercise for the reader.

Movie Playback

Once we've loaded a movie into a movie window (using the configure command), QuickTimeTcl takes care of all the nitty-gritty details of passing user or system events to the movie and making sure it reacts appropriately to those events. Mouse events on the movie controller bar work fine. Moreover, QuickTimeTcl takes care of tasking the movie often enough to ensure uninterrupted playback of linear movies or smooth user interaction with nonlinear movies like QuickTime VR movies or wired sprite movies.

Handling Keyboard Events

QuickTimeTcl, however, does not appear to provide built-in support for the standard methods of controlling movie playback using the keyboard. With a linear movie, for instance, pressing the space bar should toggle the current play state of the movie. And with a QuickTime VR movie, pressing the left arrow key should pan the movie to the left.

As you've probably guessed, we can support these behaviors by creating the appropriate event bindings. The setupBindings function considered just above binds key presses to the handleKey function, like so:

bind .$winName <KeyPress> "handleKey $winName %K"

The "%K" keyword is replaced with the keysym, which is a special Tk designator for a key on the keyboard. For instance, the keysym for the space bar is "space", and the keysym for the Shift key is "Shift_L".

Our implementation of handleKey, shown in Listing 2, first determines whether the specified movie window is a QuickTime VR movie or not. (The command ispanoramic is slightly misnamed, as it also returns 1 for a QuickTime VR object movie.) For a QuickTime VR movie, we use the movie widget commands pan, tilt, and fieldofview to adjust the view parameters. For linear movies, we use the commands step, play, and stop to adjust the movie time and play rate. We also adjust the movie's volume when we receive an up arrow or down arrow key.

Listing 2: Handling key presses

handleKey
proc handleKey {winName key} {
   global appData
    
   set movie $appData($winName,movie)
   if {[$movie ispanoramic]} {
      # handle QTVR movie controller key bindings 
      # (and, yes, "ispanoramic" is true for object movies too)
      switch $key {
         "Right" {$movie pan [expr [$movie pan] - 10]}
         "Left" {$movie pan [expr [$movie pan] + 10]}
         "Up" {$movie tilt [expr [$movie tilt] + 10]}
         "Down" {$movie tilt [expr [$movie tilt] - 10]}
         "Shift_L" {$movie fieldofview [expr [$movie \
                              fieldofview] - 5]}
         "Control_L" {$movie fieldofview [expr [$movie \
                              fieldofview] + 5]}
      }
   } else {
      # handle standard movie controller key bindings
      switch $key {
         "Right" {$movie step 1}
         "Left" {$movie step -1}
         "Up" {$movie configure -volume [min 255 \
                  [expr 64 + [$movie cget -volume]]]}
         "Down" {$movie configure -volume [max 0 \
                  [expr -64 + [$movie cget -volume]]]}
         "space" {if {[$movie rate] == 0} {$movie play} 
                                                else {$movie stop}}
      }
   }
}

Installing a Movie Controller Callback Function

At this point, TickLeez can open and display QuickTime movies, and it can handle all the standard means of user interaction with the movie. Before we move on to consider how to support movie editing, I should mention that QuickTimeTcl provides a way to observe and possibly override actions associated with the movie; that is to say, it provides a wrapper for QuickTime's movie controller action filter function.

We installed this movie controller callback function when we configured our movie widget, by passing a procedure name to the -mccommand argument:

.$winName.mov configure -mccommand controllerProc \
               -mcedit 1 -resizable 1

The specified procedure is passed the name of the movie widget, a string representing the action, and a possibly empty list of parameters associated with that action. For linear movies, these actions include activate, customButtonClick, deactivate, key, goToTime, mouseDown, play, setSelectionBegin, setSelectionDuration, and setVolume. For QuickTime VR movies, these actions also include pan, tilt, fieldofview, and triggerhotspot. Listing 3 shows the basic form of a movie controller callback function.

Listing 3: Handling movie controller actions

controllerProc
proc controllerProc {w what {par {}}} {
   if {![isMovieWindow [string range $w 1 end]]} {
      return
   }
   if {$what == "key"} {
      puts "got key event in controller action proc"
   }
   
}

Normally the action is processed by the movie controller after our callback procedure returns. To suppress an action, we can execute this line of code:

return -code 3

Movie Editing

QuickTimeTcl provides a number of movie widget commands for performing the standard movie editing operations (cut, copy, paste, and so forth). It also provides support for multilevel undo, that is, the ability to undo more than one previous editing operation. None of the QuickTime-savvy applications that we've built hitherto supports this very nice feature, except for the MooVeez application we built in an earlier article, where the multilevel undo is provided by the Cocoa application framework. (See "The Cocoanuts" in MacTech, December 2002.) In this section, we'll see how to implement editing in TickLeez and how to adjust the File and Edit menus in accordance with the edited state of the frontmost movie window.

Handling Editing Operations

We've already seen how to invoke an edit command when the appropriate menu item is selected. For instance, in the setupMenus procedure (Listing 4 in the previous article), we saw this line of code for setting up the Clear menu item:

$m add command -label "Clear" -command {editDoc clear}

So, when the user selects the Clear menu item, the editDoc procedure is called with the "clear" parameter. Listing 4 shows our implementation of the editDoc procedure.

Listing 4: Editing a movie

editDoc
proc editDoc {op} {
   global appData
   set winName [topMovieWindow]
   $appData($winName,movie) $op
   if {$op != "copy"} {
      set n [incr appData($winName,undoLevel)]
      set appData($winName,undoOp,$n) $op
      setWindowDirty $winName 1 
   }
   adjustSize $winName
   adjustMenus
}

The key step here is the line of code that sends the specified operation string $op to the frontmost movie widget as a command. The movie widget supports these editing commands: cut, copy, paste, clear, add, and undo. Notice that we also increment the widget's undo level and store the operation string itself in the movie window's global data array. In a moment, we'll see how these items are used to support multilevel undo.

The editDoc function also calls the setWindowDirty function (shown in Listing 5) to mark the movie window as having been changed. (Note that QuickTimeTcl provides a built-in procedure haschanged that we could use instead of reading this stored value; however, I prefer to keep track of changes to the movie myself.)

Listing 5: Marking a movie window as changed

setWindowDirty
proc setWindowDirty {winName state} {
   global appData
   set appData($winName,dirty) $state
   if {[string match "mac*" $appData(os)]} {
      wm attributes .$winName -modified $state
   }
}

Notice that, on Macintosh computers, we also set the modified attribute of the movie window; on Mac OS X, this has the effect of drawing a dot inside the window's close button, as shown in Figure 2.


Figure 2: A modified movie window

Undoing Editing Operations

Whenever an editing operation is performed, QuickTimeTcl adds information about the state of the movie just prior to the edit to an edit stack. The number of the topmost item on the stack is called the undo level. When a movie is first opened, its undo level is 0. Subsequent editing operations increment the undo level.

QuickTimeTcl allows us to undo one or more edits by executing the undo command. This command requires one parameter, which specifies the undo level we want to revert to. In TickLeez, we shall always revert to the previous edit, so we'll decrement the undo level by one and pass that number to undo. This is how we implement multilevel undo. Listing 6 shows our definition of the undoDoc function.

Listing 6: Undoing an edit

undoDoc
proc undoDoc {} {
   global appData
   set winName [topMovieWindow]
   if {$appData($winName,undoLevel) > 0} {
      incr appData($winName,undoLevel) -1
      $appData($winName,movie) undo \
                                       $appData($winName,undoLevel)
   }
   
   if {$appData($winName,undoLevel) == 0} {
      setWindowDirty $winName 0 
   }
   
   adjustMenus
}

As you can see, if the user undoes all edits (so that the undo level reaches 0 once again), we call setWindowDirty with the parameter 0 to mark the window as not dirty.

Selecting All or None of a Movie

The Edit menu in TickLeez contains the Select All and Select None menu items. In previous articles, we've seen how to handle those items quite easily. For instance, we can handle the Select All item by setting the beginning of the movie selection to movie time 0 and the duration of the movie selection to the duration of the entire movie.

QuickTimeTcl provides the select command that we can use to set the movie selection to a specified range, but it does not provide a command that returns the duration of a movie directly. Instead, QuickTimeTcl provides the gettime command, which returns an array of four movie-related times: the current time, the length of the movie, the movie time scale, and the movie poster time. For a specific movie window, we can retrieve that array like this:

array set timeArr [$appData($winName,movie) gettime]

Once we've successfully done that, we can read the movie duration by looking at the $timeArr(-movieduration) element in the array; and we can read the current movie time by looking at the $timeArr(-movietime) element. Then we can handle the two selection menu items using the selectDoc function, defined in Listing 7.

Listing 7: Selecting all or none of a movie

selectDoc
proc selectDoc {op} {
   global appData
   set winName [topMovieWindow]
   
   array set timeArr [$appData($winName,movie) gettime]
   switch -- ${op} {
       all {$appData($winName,movie) select \
               0 $timeArr(-movieduration)}
       none {$appData($winName,movie) select \
               $timeArr(-movietime) 0}
   }
}

Enabling and Disabling Menu Items

As you know, we need to adjust some of the items in the File and Edit menus based on the current state of the frontmost movie window (if one exists). For instance, if a movie has been edited, we need to enable the Save menu item in the File menu so that the user can save those changes, if desired. Listing 8 shows our implementation of the adjustMenus function, which is called by many of the editing functions we've encountered recently. We handle the Save menu item by looking at the dirty field of the stored movie window data, like this:

if {$appData($winName,dirty) == 1} {
   $bar.file entryconfigure "Save" -state active}

Here, we use Tk's entryconfigure command to set the state of a menu item.

We can determine whether other menu items should be enabled or disabled by executing the controllerinfo command. Like the gettime command we just considered, controllerinfo fills an array with a set of entries, in this case with entries that indicate the current state of the movie controller.

array set infoArr [$appData($winName,movie) controllerinfo]

For instance, if the $infoArr(-cutavailable) item is 1, then we want to enable the Cut menu item.

Listing 8: Adjusting the menus and menu items

adjustMenus
proc adjustMenus {} {
   global appData
   
   set winName [topMovieWindow]
   set bar .${winName}mbar
   if {[string equal $appData(os) "windows"]} {
      if {[string equal $winName ""]} {
         return
      }
   }
   # set the default state
   $bar.file entryconfigure "New" -state active
   $bar.file entryconfigure "Open..." -state active
   $bar.file entryconfigure "Close" -state disabled
   $bar.file entryconfigure "Save" -state disabled
   $bar.file entryconfigure "Save As..." -state disabled
   
   $bar.edit entryconfigure "Undo" -state disabled
   $bar.edit entryconfigure "Cut" -state disabled
   $bar.edit entryconfigure "Copy" -state disabled
   $bar.edit entryconfigure "Paste" -state disabled
   $bar.edit entryconfigure "Clear" -state disabled
   $bar.edit entryconfigure "Select All" -state disabled
   $bar.edit entryconfigure "Select None" -state disabled
   
   $bar.movie entryconfigure "Hide Controller Bar" \
            -state disabled
   if {[string equal $appData(os) "windows"]} {
      $bar.help entryconfigure "About TickLeez" -state active
   } else {
      $bar.apple entryconfigure "About TickLeez" \
            -state active
   }
   
   if {[isMovieWindow $winName]} {
      $bar.file entryconfigure "Close" -state active
      $bar.file entryconfigure "Save As..." -state active
      $bar.edit entryconfigure "Select All" -state active
      $bar.edit entryconfigure "Select None" -state active
   
      if {$appData($winName,dirty) == 1} {
         $bar.file entryconfigure "Save" -state active}
      
      array set infoArr [$appData($winName,movie) \
                                                         controllerinfo]
      if {$appData($winName,undoLevel) > 0} {
         $bar.edit entryconfigure "Undo" -state active}
      if {$infoArr(-cutavailable) == 1} {
         $bar.edit entryconfigure "Cut" -state active}
      if {$infoArr(-copyavailable) == 1} {
         $bar.edit entryconfigure "Copy" -state active}
      if {$infoArr(-pasteavailable) == 1} {
         $bar.edit entryconfigure "Paste" -state active}
      if {$infoArr(-clearavailable) == 1} {
         $bar.edit entryconfigure "Clear" -state active}
      if {$appData($winName,undoLevel) > 0} {
         set op \ $appData($winName,undoOp,$appData($winName,undoLevel))
         $bar.edit entryconfigure "Undo*" -label \
                                    "Undo [string totitle $op]"
      }
      
      $bar.movie entryconfigure "Hide Controller Bar" \
            -state active
   }
}

File Manipulation

Our final task before wrapping up TickLeez for delivery to end users is to handle the document-related operations -- that is saving an edited movie, saving a movie into a new file, closing a movie window, and ultimately quitting the application itself. As we've seen in previous articles, we need to track the state of a movie and prompt the user to save or discard changes to it when the movie window is to be closed or the application is to be quit.

Closing a Movie Window

When the user selects the Close menu item in the File menu (or types the appropriate keyboard shortcut), TickLeez executes the closeDoc procedure, which is defined in Listing 9.

Listing 9: Handling the Close menu item

closeDoc
proc closeDoc {} {
   attemptClose [topMovieWindow] closing
}

As you can see, closeDoc finds the name of the frontmost movie window and passes that name as an argument to the attemptClose function, along with the action name "closing". The attemptClose function needs to look at the dirty state of the window and, if necessary, display a dialog box asking the user to save or discard the changes to the movie. Listing 10 shows our definition of attemptClose.

Listing 10: Prompting the user to save a changed movie

attemptClose
proc attemptClose {winName action} {
   global appData
   set closeWindow 1
   if {$appData($winName,dirty) == 1} {
      set movieName [file tail $appData($winName,fileName)]
   
      set answer [tk_messageBox \
         -parent .$winName \
         -title "Save changes before $action" \
         -message "Do you want to save the changes you made \
                     to the document \u201C$movieName\u201D \
                     before $action?" \
         -type yesnocancel \
         -icon warning]
   
      switch $answer {
         yes         {set closeWindow [saveDoc]}
         cancel   {set closeWindow 0}
         no          {set closeWindow 1}
      }
   }
   if {$closeWindow == 1} {disposeDoc $winName}
   return $closeWindow
}

We display the Save Changes dialog box by calling the tk_messageBox function, this time with the yesnocancel type. Figure 3 shows the Macintosh dialog box, and Figure 4 shows the Windows dialog box.


Figure 3: The Save Changes dialog box of TickLeez (Macintosh)


Figure 4: The Save Changes dialog box of TickLeez (Windows)

Notice in Listing 10 that if the user selects Yes, we call the saveDoc procedure (defined later) and then set the closeWindow variable to the value it returns. We set the closeWindow variable to 1 if the user selects No and to 0 if the user selects Cancel. If closeWindow is 1, we call the disposeDoc function, defined in Listing 11.

Listing 11: Disposing a movie window and its associated data

disposeDoc
proc disposeDoc {winName} {
   global appData
   
   if {![isMovieWindow $winName]} {return}
   destroy .$winName
   array unset appData $winName,*
   adjustMenus
}

The key step in disposeDoc is to call the Tk function destroy, which removes the specified window from the screen and dispose of any memory it occupies. We also call the array unset command to remove from the appData associative array any elements associated with the movie window.

Saving a Changed Movie

As we've seen, TickLeez calls the saveDoc function when the user selects the Save menu item (or types the appropriate keyboard equivalent). In addition, the attemptClose method (Listing 10) calls the saveDoc function if the user elects to save the changes to a window that is about to close. It's easy to implement saveDoc, using the save command provided by QuickTimeTcl, as shown in Listing 12.

Listing 12: Saving a movie

saveDoc
proc saveDoc {} {
   global appData
   set winName [topMovieWindow]
   # if the movie file is in our temporary directory, alert the user
   if {[string compare \
      -length [string length $appData(tempDir)] \
      $appData($winName,fileName) $appData(tempDir)] == 0} {
      tk_messageBox \
         -parent .$winName \
         -title "Warning" \
         -message "Cannot save this movie into the current \
            folder.\nPlease choose \u201CSave As...\u201D to \
            select a different folder." \
         -type ok \
         -icon warning
      return    0
   }
   
   $appData($winName,movie) save
   
   setWindowDirty $winName 0 
   adjustMenus
   return 1
}

Recall that QuickTimeTcl requires a filename for each open movie and that we create a temporary file when the user selects the New command in the File menu. If the file we are saving is located in the designated temporary directory, we want to display the alert sheet shown in Figure 5 to force the user to save the file elsewhere.


Figure 5: The bad directory alert sheet

Saving a Movie into a New File

When the user selects the Save As menu item in the File menu, we need to elicit a location for the new movie file and then save the movie into that file. In TickLeez, we'll execute this line of code:

set newFile [tk_getSaveFile]

The tk_getSaveFile function displays a dialog box like the one shown in Figure 6. It returns as its result the full pathname of the specified file, or an empty string if the user did not specify a file.


Figure 6: The file-saving dialog box displayed by Tk

If the user does specify a file, we can write the movie into that file by calling the QuickTimeTcl command flatten, as shown in Listing 13

Listing 13: Saving a movie into a new file

saveAsDoc
proc saveAsDoc {} {
   global appData
   set winName [topMovieWindow]
   set newFile [tk_getSaveFile]
   if {$newFile != ""} {
      $appData($winName,movie) flatten $newFile
      $appData($winName,movie) configure -file $newFile
      
      wm title .$winName [file tail $newFile]
      
      set appData($winName,dirty) 0   
      set appData($winName,fileName) $newFile   
      set appData($winName,undoLevel) 0
      
      setWindowDirty $winName 0 
   }
   
   adjustMenus
}

As you know, the standard behavior of a Save As operation is for the new movie to replace the existing movie in the existing movie window. Accordingly, saveAsDoc calls the QuickTimeTcl command configure with the full pathname of the new movie file. Then it resets the window title and several fields of the global data array associated with the movie.

Quitting the Application

When the user decides to quit TickLeez, we need to perform the standard quitting-time operations (such as making sure the user saves or discards any unsaved changes to the movie windows). When the user selects the Quit menu item, Tcl calls its own exit function, which performs any Tcl-specific cleanup tasks. We can make sure that our own cleanup tasks are performed prior to that by renaming the exit function to some other name, like this:

rename exit __exit

Then we can define our own exit function, as shown in Listing 14.

Listing 14: Quitting the application

exit
proc exit {} {
   quitApp
}

The TickLeez function quitApp is shown in Listing 15. After we've inspected all movie windows and made sure that all changes have been saved, we remove the temporary directory we created earlier. Then we call __exit to let Tcl perform its cleanup tasks.

Listing 15: Handling the Quit menu item

quitApp
proc quitApp {} {
   global appData
   set cancelled 0
   
   while {([topMovieWindow] != "") && ($cancelled == 0)} {
      set cancelled [expr ![attemptClose [topMovieWindow] \
               quitting]]
   }
   if {$cancelled == 0} {
      # remove the temp directory if it exists
      if {[file exists $appData(tempDir)]} {
         file delete -force $appData(tempDir)
      }
      __exit
   }
}

Conclusion

This brings us to the end of our investigation of Tcl/Tk and QuickTimeTcl as a delivery vehicle for QuickTime applications. In just under four hundred lines of script (not counting the blank lines and comments in the file TickLeez.tcl), we've managed to construct a multi-window QuickTime playback and editing application that runs on both Macintosh and Windows computers. TickLeez exhibits all the standard user-interaction and document-related behaviors that we've come to expect of a QuickTime application, and it does so with a minimum of platform-specific code. I think that QuickTimeTcl provides a very nice wrapper for the underlying QuickTime APIs. Better still, its source code is freely available and can be easily modified to add capabilities that it does not currently provide. Neither Tcl/Tk nor QuickTimeTcl is perfect, but all three packages are under active development and promise to get even better as time goes by.

Acknowledgements

Thanks are due once again to Jim Ingham and to Mats Bengtsson for reviewing a draft of this article and for providing invaluable feedback.


Tim Monroe is a member of the QuickTime engineering team at Apple. You can contact him at monroe@mactech.com. The views expressed here are not necessarily shared by his employer.

 
AAPL
$100.58
Apple Inc.
+0.01
MSFT
$45.22
Microsoft Corpora
+0.27
GOOG
$583.37
Google Inc.
-1.12

MacTech Search:
Community Search:

Software Updates via MacUpdate

Pages 5.2.2 - Apple's word processo...
Apple Pages is a powerful word processor that gives you everything you need to create documents that look beautiful. And read beautifully. It lets you work seamlessly between Mac and iOS devices. And... Read more
Quicken 2015 2.0.1 - Complete personal f...
The new Quicken 2015 helps you manage all your personal finances in one place, so you can see where you're spending and where you can save. Quicken automatically categorizes your financial... Read more
CleanMyMac 2.2.7 - Delete files that was...
CleanMyMac makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac 2 lets you safely and intelligently scan and clean your entire system, delete large, unused... Read more
MacFamilyTree 7.2.4 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more
Videobox 4.1.1 - Download Flash video th...
Videobox allows you to quickly and easily download Flash video from most all of the popular video sites on the internet. Videobox will convert the video into a native Quicktime format so it's ready... Read more
Web Snapper 3.3.5 - Capture entire Web p...
Web Snapper lets you capture Web pages exactly as they appear in your browser. You can send them to a file as images or vector-based, multi-page PDFs. It captures the whole Web page - eliminating... Read more
Picasa 3.9.138 - Organize, edit, and sha...
Picasa and Picasa Web Albums allows you to organize, edit, and upload your photos to the Web from your computer in quick, simple steps. Arrange your photos into folders and albums and erase their... Read more
Tidy Up 3.0.15.0 - Find duplicate files...
Tidy Up is a complete duplicate finder and disk-tidiness utility. With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator, extension, time... Read more
Parallels Desktop 10.0 - Run Windows app...
Parallels Desktop is simply the world's bestselling, top-rated, and most trusted solution for running Windows applications on your Mac. With Parallels Desktop for Mac, you can seamlessly run both... Read more
Apple Final Cut Pro X 10.1.3 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more

Latest Forum Discussions

See All

DotEmu Summer Sale Cuts Several Games’ P...
DotEmu Summer Sale Cuts Several Games’ Prices to $0.99 Posted by Ellis Spice on August 21st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
King Announces its Newest “Saga” – Diamo...
King Announces its Newest “Saga” – Diamond Digger Saga Posted by Jessica Fisher on August 21st, 2014 [ permalink ] Diamond Digger Saga, by King Digital Entertainment, promises to be another addictive, adorable game in the “Saga” | Read more »
Spacetime Studios’ Greg Mueller Tells Al...
| Read more »
TourStar Review
TourStar Review By Rob Thomas on August 21st, 2014 Our Rating: :: BORING TOURINGUniversal App - Designed for iPhone and iPad When The Ramones sang “Touring, touring/It’s never boring,” they clearly weren’t referring to TourStar.... | Read more »
Track Your Pregnancy with Glow Nurture
Track Your Pregnancy with Glow Nurture Posted by Jessica Fisher on August 21st, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Outcast Odyssey – An Interview with the...
At this year’s San Diego Comic-Con, fans got the chance to meet and mingle with several of the artists behind Magic Pixel Games and Namco Bandai’s upcoming card battler, Outcast Odyssey. Considering many of these artists have worked on comics in the... | Read more »
BoxPop Review
BoxPop Review By Jennifer Allen on August 21st, 2014 Our Rating: :: UNTAXING PUZZLESUniversal App - Designed for iPhone and iPad Move from tile to tile to clear the board in this slightly too simple and easy puzzle game.   | Read more »
Breakdown Buddy Review
Breakdown Buddy Review By Jennifer Allen on August 21st, 2014 Our Rating: :: FLAWED CONCEPTiPhone App - Designed for the iPhone, compatible with the iPad There’s a good idea here, but Breakdown Buddy isn’t as useful as it tries to... | Read more »
jamstik, the Guitar You Can Take Everywh...
jamstik, the Guitar You Can Take Everywhere, is Now Available for Purchase Posted by Jessica Fisher on August 21st, 2014 [ permalink ] Zivix has announced that their guitar-in-your-pocket, | Read more »
Star Walk 2 - Guide to the Sky Day and N...
Star Walk 2 - Guide to the Sky Day and Night 1.0.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0.0 (iTunes) Description: The next generation of the best-selling Star Walk, winner of Apple Design Award, used by... | Read more »

Price Scanner via MacPrices.net

21-inch iMacs on sale for up to $150 off MSRP
B&H Photo has 21″ iMacs on sale for up to $150 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 2.7GHz iMac: $... Read more
27-inch 3.2GHz iMac on sale for $1698, save $...
Abt has the 27″ 3.2GHz iMac on sale for $1698 including free shipping. Their price is $101 off MSRP. Read more
Mac Backup Guru 2.0 Drive Backup/Cloneing Uti...
Mac Backup Guru developer MacDaddy has released Mac Backup Guru 2.0, offering new and enhanced advanced features, such as bootable backups, synchronised volumes and folders, and a Snapshot mode that... Read more
Operate GE’s New Free-Standing KItchen Range...
Think you accidentally left the oven on? Switch it off while on the go. The new free-standing Profile™ Series gas and electric ranges are GE’s second cooking appliances, following their wall oven, to... Read more
Parallels Announces Parallels Desktop 10 for...
The no. 1-selling software for running Windows applications on a Mac becomes an even easier choice for millions of consumers and IT professionals worldwide with the launch of the most powerful... Read more
Apple now offering certified refurbished 2014...
 The Apple Store is now offering Apple Certified Refurbished 2014 MacBook Airs for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Best Buy’s College Student Deals: $100 off Ma...
Take an additional $100 off all MacBooks and iMacs, $50 off iPad Airs and iPad minis, at Best Buy Online with their College Students Deals Savings, valid through September 6th. Anyone with a valid .... Read more
MacBook Airs on sale for $100 off MSRP, free...
B&H Photo has three 2014 MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for Laptops... Read more
Razer Taipan Mouse For Gamers And Non-Gamers...
If you’re a serious gamer on either Mac or Windows PCs, a serious gaming mouse is a necessity for first-tier performance. However, even if like me you’re not much of a gamer, there’s still a strong... Read more
15-inch 2.2GHz MacBook Pro on sale for $1899,...
Adorama has the new 15″ 2.2GHz Retina MacBook Pro on sale for $1899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** As more and more people discover Apple , they visit our stores seeking ways to incorporate our products into their lives. It's your job, as a Store Read more
Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.