OSDN Git Service

MacGui: Fix a crash in calculateBitrate:
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.mm
index 9e06c49..4e30273 100644 (file)
@@ -51,23 +51,6 @@ static NSString *        AddToQueueIdentifier               = @"Add to Queue Ite
 static NSString *        ShowActivityIdentifier             = @"Debug Output Item Identifier";
 static NSString *        ChooseSourceIdentifier             = @"Choose Source Item Identifier";
 
-/**
- * Returns the number of jobs groups in the queue.
- * @param h Handle to hb_handle_t.
- * @return Number of job groups.
- */
-static int hb_group_count(hb_handle_t * h)    
-{
-       hb_job_t * job;
-       int count = 0;
-       int index = 0;
-       while( ( job = hb_job( h, index++ ) ) )
-       {
-               if (job->sequence_id == 0)
-                       count++;
-       }
-       return count;
-}
 
 /*******************************
  * HBController implementation *
@@ -80,9 +63,10 @@ static int hb_group_count(hb_handle_t * h)
     [HBPreferencesController registerUserDefaults];
     fHandle = NULL;
     outputPanel = [[HBOutputPanelController alloc] init];
-    fPictureController = [[PictureController alloc] init];
+    fPictureController = [[PictureController alloc] initWithDelegate:self];
     fQueueController = [[HBQueueController alloc] init];
     fAdvancedOptions = [[HBAdvancedController alloc] init];
+    fPreferencesController = [[HBPreferencesController alloc] init];
     return self;
 }
 
@@ -100,15 +84,16 @@ static int hb_group_count(hb_handle_t * h)
     /* Init others controllers */
     [fPictureController SetHandle: fHandle];
     [fQueueController   setHandle: fHandle];
+    [fQueueController   setHBController: self];
        
     fChapterTitlesDelegate = [[ChapterTitles alloc] init];
     [fChapterTable setDataSource:fChapterTitlesDelegate];
 
-     /* Call UpdateUI every 2/10 sec */
+     /* Call UpdateUI every 1/2 sec */
     [[NSRunLoop currentRunLoop] addTimer: [NSTimer
-        scheduledTimerWithTimeInterval: 0.2 target: self
-        selector: @selector( updateUI: ) userInfo: NULL repeats: FALSE]
-        forMode: NSModalPanelRunLoopMode];
+        scheduledTimerWithTimeInterval: 0.5 target: self
+        selector: @selector( updateUI: ) userInfo: NULL repeats: YES]
+        forMode: NSEventTrackingRunLoopMode];
 
     if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
     {
@@ -432,7 +417,7 @@ static int hb_group_count(hb_handle_t * h)
 {
     return [NSArray arrayWithObjects:  StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
         ChooseSourceIdentifier, ShowQueueIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
-        NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier,
+        NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
         NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
 }
 
@@ -450,9 +435,9 @@ static int hb_group_count(hb_handle_t * h)
             if ([ident isEqualToString: StartEncodingIdentifier])
             {
                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
-                [toolbarItem setLabel: @"Cancel"];
-                [toolbarItem setPaletteLabel: @"Cancel"];
-                [toolbarItem setToolTip: @"Cancel Encoding"];
+                [toolbarItem setLabel: @"Stop"];
+                [toolbarItem setPaletteLabel: @"Stop"];
+                [toolbarItem setToolTip: @"Stop Encoding"];
                 return YES;
             }
             if ([ident isEqualToString: PauseEncodingIdentifier])
@@ -489,7 +474,10 @@ static int hb_group_count(hb_handle_t * h)
             if ([ident isEqualToString: StartEncodingIdentifier])
             {
                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
-                [toolbarItem setLabel: @"Start"];
+                if (hb_count(fHandle) > 0)
+                    [toolbarItem setLabel: @"Start Queue"];
+                else
+                    [toolbarItem setLabel: @"Start"];
                 [toolbarItem setPaletteLabel: @"Start Encoding"];
                 [toolbarItem setToolTip: @"Start Encoding"];
                 return YES;
@@ -521,15 +509,18 @@ static int hb_group_count(hb_handle_t * h)
     
     if (fHandle)
     {
-
-        if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:))
-            return SuccessfulScan;
-            
+        if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
+            return SuccessfulScan && [fWindow attachedSheet] == nil;
+        
         if (action == @selector(showScanPanel:))
         {
             if (s.state == HB_STATE_SCANNING)
                 return NO;
+            else
+                return [fWindow attachedSheet] == nil;
         }
+        if (action == @selector(selectDefaultPreset:))
+            return [tableView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
         if (action == @selector(Pause:))
         {
             if (s.state == HB_STATE_WORKING)
@@ -558,12 +549,11 @@ static int hb_group_count(hb_handle_t * h)
             {
                 if(![[menuItem title] isEqualToString:@"Start Encoding"])
                     [menuItem setTitle:@"Start Encoding"];
-                return YES;
+                return [fWindow attachedSheet] == nil;
             }
             else
                 return NO;
         }
-
     
     return YES;
 }
@@ -574,13 +564,14 @@ static int hb_group_count(hb_handle_t * h)
 #define SERVICE_NAME @"Encode Done"
 - (NSDictionary *)registrationDictionaryForGrowl 
 { 
-NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
-[NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
-[NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
-nil]; 
+    NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
+    [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
+    [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
+    nil]; 
 
-return registrationDictionary; 
+    return registrationDictionary; 
 } 
+
 - (void) TranslateStrings
 {
     [fSrcTitleField     setStringValue: _( @"Title:" )];
@@ -685,8 +676,8 @@ return registrationDictionary;
 - (void) updateUI: (NSTimer *) timer
 {
 
-hb_list_t  * list;
-list = hb_get_titles( fHandle );       
+    hb_list_t  * list;
+    list = hb_get_titles( fHandle );   
     /* check to see if there has been a new scan done
        this bypasses the constraints of HB_STATE_WORKING
        not allowing setting a newly scanned source */
@@ -703,8 +694,6 @@ list = hb_get_titles( fHandle );
                [self showNewScan: NULL];
        }
        
-    BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
-       
     hb_state_t s;
     hb_get_state( fHandle, &s );
        
@@ -764,7 +753,7 @@ list = hb_get_titles( fHandle );
             [fRipIndicator setDoubleValue: 100.0 * progress_total];
                        
             // If progress bar hasn't been revealed at the bottom of the window, do
-            // that now. This code used to be in _Rip. I moved it to here to handle
+            // that now. This code used to be in doRip. I moved it to here to handle
             // the case where hb_start is called by HBQueueController and not from
             // HBController.
             if (!fRipIndicatorShown)
@@ -828,6 +817,27 @@ list = hb_get_titles( fHandle );
                        
         case HB_STATE_WORKDONE:
         {
+            // HB_STATE_WORKDONE happpens as a result of hblib finishing all its jobs
+            // or someone calling hb_stop. In the latter case, hb_stop does not clear
+            // out the remaining passes/jobs in the queue. We'll do that here.
+                        
+            // Delete all remaining scans of this job, ie, delete whole encodes.
+            hb_job_t * job;
+            while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) )
+                hb_rem( fHandle, job );
+
+            // Start processing back up if jobs still left in queue
+            if (hb_count(fHandle) > 0)
+            {
+                hb_start(fHandle);
+                fEncodeState = 1;
+                // Validate the toolbar (hack). The toolbar will usually get autovalidated
+                // before we had the chance to restart the queue, hence it will now be in
+                // the wrong state.
+                [toolbar validateVisibleItems];
+                break;
+            }
+
             [fStatusField setStringValue: _( @"Done." )];
             [fRipIndicator setIndeterminate: NO];
             [fRipIndicator setDoubleValue: 0.0];
@@ -836,21 +846,6 @@ list = hb_get_titles( fHandle );
             /* Restore dock icon */
             [self UpdateDockIcon: -1.0];
 
-            if (jobGroups)
-            {
-                // Delete all remaining scans of this job
-                hb_job_t * job;
-                while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) )
-                    hb_rem( fHandle, job );
-            }
-
-            // Start processing back up if jobs still left in queue
-            if (hb_count(fHandle) > 0)
-            {
-                hb_start(fHandle);
-                break;
-            }
-                       
             if (fRipIndicatorShown)
             {
                 NSRect frame = [fWindow frame];
@@ -884,7 +879,7 @@ list = hb_get_titles( fHandle );
                                        /*On Screen Notification*/
                                        int status;
                                        NSBeep();
-                                       status = NSRunAlertPanel(@"Put down that cocktail...",@"your HandBrake encode is done!", @"OK", nil, nil);
+                                       status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);
                                        [NSApp requestUserAttention:NSCriticalRequest];
                                        if ( status == NSAlertDefaultReturn ) 
                                        {
@@ -939,22 +934,17 @@ list = hb_get_titles( fHandle );
     }
        
     /* Lets show the queue status here in the main window */
-       int queue_count = jobGroups ? hb_group_count( fHandle ) : hb_count( fHandle );
+       int queue_count = hb_count( fHandle );
        if( queue_count )
        {
                [fQueueStatus setStringValue: [NSString stringWithFormat:
-                       @"%d task%s in the queue",
-                                                queue_count, ( queue_count > 1 ) ? "s" : ""]];
+                       @"%d pass%s in the queue",
+                                                queue_count, ( queue_count > 1 ) ? "es" : ""]];
        }
        else
        {
                [fQueueStatus setStringValue: @""];
        }
-       
-    [[NSRunLoop currentRunLoop] addTimer: [NSTimer
-        scheduledTimerWithTimeInterval: 0.5 target: self
-                                                         selector: @selector( updateUI: ) userInfo: NULL repeats: FALSE]
-                                                                forMode: NSModalPanelRunLoopMode];
 }
 
 - (IBAction) showNewScan:(id)sender
@@ -1288,7 +1278,7 @@ list = hb_get_titles( fHandle );
     }
 
     [self qualitySliderChanged: sender];
-    [self calculateBitrate:     sender];
+    [self calculateBitrate: sender];
        [self customSettingUsed: sender];
 }
 
@@ -1317,7 +1307,6 @@ list = hb_get_titles( fHandle );
     if( returnCode == NSOKButton )
     {
         [fDstFile2Field setStringValue: [sheet filename]];
-               
     }
 }
 
@@ -1326,10 +1315,11 @@ list = hb_get_titles( fHandle );
        hb_list_t  * list  = hb_get_titles( fHandle );
     hb_title_t * title = (hb_title_t *) hb_list_item( list,
             [fSrcTitlePopUp indexOfSelectedItem] );
-    
     [fPictureController showPanelInWindow:fWindow forTitle:title];
-    
-       [self calculatePictureSizing: sender];
+}
+
+- (void)pictureSettingsDidChange {
+       [self calculatePictureSizing: NULL];
 }
 
 - (void) PrepareJob
@@ -1541,115 +1531,142 @@ list = hb_get_titles( fHandle );
 
 
 
+/* addToQueue: puts up an alert before ultimately calling doAddToQueue
+*/
 - (IBAction) addToQueue: (id) sender
 {
-/* We get the destination directory from the destingation field here */
+       /* We get the destination directory from the destination field here */
        NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
        /* We check for a valid destination here */
        if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
        {
                NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
+        return;
        }
-       else
-       {
-               
-               hb_list_t  * list  = hb_get_titles( fHandle );
-               hb_title_t * title = (hb_title_t *) hb_list_item( list,
-                                                                                                                 [fSrcTitlePopUp indexOfSelectedItem] );
-               hb_job_t * job = title->job;
-               
-        // Assign a sequence number, starting at zero, to each job added so they can
-        // be lumped together in the UI.
-        job->sequence_id = -1;
-               
-               [self PrepareJob];
-               
-               /* Destination file */
-               job->file = [[fDstFile2Field stringValue] UTF8String];
 
-                if( [fSubForcedCheck state] == NSOnState )
-                {
-                    job->subtitle_force = 1;
-                } else {
-                    job->subtitle_force = 0;
-                }
+    /* We check for duplicate name here */
+       if( [[NSFileManager defaultManager] fileExistsAtPath:
+            [fDstFile2Field stringValue]] )
+    {
+        NSBeginCriticalAlertSheet( _( @"File already exists" ),
+            _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
+            @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
+            NULL, NULL, [NSString stringWithFormat:
+            _( @"Do you want to overwrite %@?" ),
+            [fDstFile2Field stringValue]] );
+        // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
+    }
+    else
+    {
+        [self doAddToQueue];
+    }
+}
 
-                /*
-                 * subtitle of -1 is a scan
-                 */
-                if( job->subtitle == -1 )
-                {
-                    char *x264opts_tmp;
-
-                    /*
-                     * When subtitle scan is enabled do a fast pre-scan job
-                     * which will determine which subtitles to enable, if any.
-                     */
-                    job->pass = -1;
-                    x264opts_tmp = job->x264opts;
-                    job->subtitle = -1;
-
-                    job->x264opts = NULL;
-                    
-                    job->subtitle_scan = 1;  
-
-                    job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
-                    *(job->select_subtitle) = NULL;
-                    
-                    /*
-                     * Add the pre-scan job
-                     */
-                                       job->sequence_id++; // for job grouping
-                    hb_add( fHandle, job );
-
-                    job->x264opts = x264opts_tmp;
-                } else {
-                    job->select_subtitle = NULL;
-                }
+/* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
+   the user if they want to overwrite an exiting movie file.
+*/
+- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
+    returnCode: (int) returnCode contextInfo: (void *) contextInfo
+{
+    if( returnCode == NSAlertAlternateReturn )
+        [self doAddToQueue];
+}
 
-                /* No subtitle were selected, so reset the subtitle to -1 (which before
-                 * this point meant we were scanning
-                 */
-                if( job->subtitle == -2 )
-                {
-                    job->subtitle = -1;
-                }
+- (void) doAddToQueue
+{
+    hb_list_t  * list  = hb_get_titles( fHandle );
+    hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
+    hb_job_t * job = title->job;
 
-               if( [fVidTwoPassCheck state] == NSOnState )
-               {
-            hb_subtitle_t **subtitle_tmp = job->select_subtitle;
-            job->select_subtitle = NULL;
-            job->subtitle_scan = 0;
+    // Assign a sequence number, starting at zero, to each job added so they can
+    // be lumped together in the UI.
+    job->sequence_id = -1;
 
-                       job->pass = 1;
-                       job->sequence_id++; // for job grouping
-                       hb_add( fHandle, job );
-            
-                       job->pass = 2;
-                       job->sequence_id++; // for job grouping
-                       
-                       job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
-                       strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
+    [self PrepareJob];
 
-            job->select_subtitle = subtitle_tmp;
+    /* Destination file */
+    job->file = [[fDstFile2Field stringValue] UTF8String];
 
-                       hb_add( fHandle, job );
-               }
-               else
-               {
-                        job->subtitle_scan = 0;
-                       job->pass = 0;
-                       job->sequence_id++; // for job grouping
-                       hb_add( fHandle, job );
-               }
+    if( [fSubForcedCheck state] == NSOnState )
+        job->subtitle_force = 1;
+    else
+        job->subtitle_force = 0;
+
+    /*
+    * subtitle of -1 is a scan
+    */
+    if( job->subtitle == -1 )
+    {
+        char *x264opts_tmp;
+
+        /*
+        * When subtitle scan is enabled do a fast pre-scan job
+        * which will determine which subtitles to enable, if any.
+        */
+        job->pass = -1;
+        x264opts_tmp = job->x264opts;
+        job->subtitle = -1;
+
+        job->x264opts = NULL;
+
+        job->indepth_scan = 1;  
+
+        job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
+        *(job->select_subtitle) = NULL;
+
+        /*
+        * Add the pre-scan job
+        */
+        job->sequence_id++; // for job grouping
+        hb_add( fHandle, job );
+
+        job->x264opts = x264opts_tmp;
+    }
+    else
+        job->select_subtitle = NULL;
+
+    /* No subtitle were selected, so reset the subtitle to -1 (which before
+    * this point meant we were scanning
+    */
+    if( job->subtitle == -2 )
+        job->subtitle = -1;
+
+    if( [fVidTwoPassCheck state] == NSOnState )
+    {
+        hb_subtitle_t **subtitle_tmp = job->select_subtitle;
+        job->indepth_scan = 0;
+
+        job->pass = 1;
+        job->sequence_id++; // for job grouping
+        hb_add( fHandle, job );
+
+        job->pass = 2;
+        job->sequence_id++; // for job grouping
+
+        job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
+        strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
+
+        job->select_subtitle = subtitle_tmp;
+
+        hb_add( fHandle, job );
+    }
+    else
+    {
+        job->indepth_scan = 0;
+        job->pass = 0;
+        job->sequence_id++; // for job grouping
+        hb_add( fHandle, job );
+    }
        
+    NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
        [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
        /* Lets try to update stuff, taken from remove in the queue controller */
        [fQueueController performSelectorOnMainThread: @selector( updateQueueUI )
         withObject: NULL waitUntilDone: NO];
-       }
 }
 
+/* Rip: puts up an alert before ultimately calling doRip
+*/
 - (IBAction) Rip: (id) sender
 {
     /* Rip or Cancel ? */
@@ -1661,17 +1678,26 @@ list = hb_get_titles( fHandle );
         [self Cancel: sender];
         return;
     }
-       /* if there is no job in the queue, then add it to the queue and rip 
-       otherwise, there are already jobs in queue, so just rip the queue */
-       int count = hb_count( fHandle );
-       if( count < 1 )
-        {
-               [self addToQueue: sender];
-               }
     
-           /* We check for duplicate name here */
-       if( [[NSFileManager defaultManager] fileExistsAtPath:
-            [fDstFile2Field stringValue]] )
+    // If there are jobs in the queue, then this is a rip the queue
+    
+    if (hb_count( fHandle ) > 0)
+    {
+        [self doRip];
+        return;
+    }
+
+    // Before adding jobs to the queue, check for a valid destination.
+
+    NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
+    if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
+    {
+        NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
+        return;
+    }
+
+    /* We check for duplicate name here */
+    if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
     {
         NSBeginCriticalAlertSheet( _( @"File already exists" ),
             _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
@@ -1679,31 +1705,40 @@ list = hb_get_titles( fHandle );
             NULL, NULL, [NSString stringWithFormat:
             _( @"Do you want to overwrite %@?" ),
             [fDstFile2Field stringValue]] );
-        return;
+            
+        // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
     }
-       /* We get the destination directory from the destination field here */
-       NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
-       /* We check for a valid destination here */
-       if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
-       {
-               NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
-       }
-       else
-       {
-       [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
-               [self _Rip];
-       }
-       
-
-
-}
+    else
+    {
+        /* We add a the encode to the queue if there is nothing in it and then start our encode.
+           This should be reviewed by travistex to verify queue sync, etc.*/
+        if( hb_count( fHandle ) == 0)
+        {
+            [self doAddToQueue]; 
+        }
+        [self doRip];
+    }
+} 
 
+/* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
+   want to overwrite an exiting movie file.
+*/
 - (void) overWriteAlertDone: (NSWindow *) sheet
     returnCode: (int) returnCode contextInfo: (void *) contextInfo
 {
     if( returnCode == NSAlertAlternateReturn )
     {
-        [self _Rip];
+        /* if there is no job in the queue, then add it to the queue and rip 
+        otherwise, there are already jobs in queue, so just rip the queue */
+        int count = hb_count( fHandle );
+        if( count == 0 )
+        {
+            [self doAddToQueue];
+        }
+
+        NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
+        [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
+        [self doRip];
     }
 }
 
@@ -1723,7 +1758,7 @@ list = hb_get_titles( fHandle );
     [NSApp terminate: self];
 }
 
-- (void) _Rip
+- (void) doRip
 {
     /* Let libhb do the job */
     hb_start( fHandle );
@@ -1731,25 +1766,82 @@ list = hb_get_titles( fHandle );
        fEncodeState = 1;
 }
 
-- (IBAction) Cancel: (id) sender
+
+
+
+
+//------------------------------------------------------------------------------------
+// Cancels the current job and proceeds with the next one in the queue.
+//------------------------------------------------------------------------------------
+- (void) doCancelCurrentJob
 {
-    NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
-        _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
-        @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
-        _( @"Encoding won't be recoverable." ) );
+    // Stop the current job. hb_stop will only cancel the current pass and then set
+    // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
+    // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
+    // remaining passes of the job and then start the queue back up if there are any
+    // remaining jobs.
+     
+    hb_stop( fHandle );
+    fEncodeState = 2;   // don't alert at end of processing since this was a cancel
+    
 }
 
-- (void) _Cancel: (NSWindow *) sheet
-    returnCode: (int) returnCode contextInfo: (void *) contextInfo
+//------------------------------------------------------------------------------------
+// Displays an alert asking user if the want to cancel encoding of current job.
+// Cancel: returns immediately after posting the alert. Later, when the user
+// acknowledges the alert, doCancelCurrentJob is called.
+//------------------------------------------------------------------------------------
+- (IBAction)Cancel: (id)sender
 {
-    if( returnCode == NSAlertAlternateReturn )
+    if (!fHandle) return;
+    
+    hb_job_t * job = hb_current_job(fHandle);
+    if (!job) return;
+
+    // If command key is down, don't prompt
+    BOOL hasCmdKeyMask = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0;
+    if (hasCmdKeyMask)
+        [self doCancelCurrentJob];
+    else
     {
-        hb_stop( fHandle );
-               /*set the fEncodeState State */
-            fEncodeState = 2;
+        NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),
+                [NSString stringWithUTF8String:job->title->name]];
+        
+        // Which window to attach the sheet to?
+        NSWindow * docWindow;
+        if ([sender respondsToSelector: @selector(window)])
+            docWindow = [sender window];
+        else
+            docWindow = fWindow;
+            
+        NSBeginCriticalAlertSheet(
+                alertTitle,
+                NSLocalizedString(@"Keep Encoding", nil), NSLocalizedString(@"Stop Encoding", nil), nil, docWindow, self,
+                nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
+                NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil),
+                [NSString stringWithUTF8String:job->title->name]);
+        
+        // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
+
+        // N.B.: didDimissCancelCurrentJob:returnCode:contextInfo: is designated as the dismiss
+        // selector to prevent a crash. As a dismiss selector, the alert window will
+        // have already be dismissed. If we don't do it this way, the dismissing of
+        // the alert window will cause the table view to be redrawn at a point where
+        // current job has been deleted by hblib but we don't know about it yet. This
+        // is a prime example of wy we need to NOT be relying on hb_current_job!!!!
     }
 }
 
+- (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
+{
+    if (returnCode == NSAlertAlternateReturn)
+        [self doCancelCurrentJob];
+}
+
+
+
+
+
 - (IBAction) Pause: (id) sender
 {
     hb_state_t s;
@@ -1813,7 +1905,6 @@ list = hb_get_titles( fHandle );
        /* Pixel Ratio Setting */
        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
     {
-
                job->pixel_ratio = 1 ;
        }
        else
@@ -1900,7 +1991,6 @@ list = hb_get_titles( fHandle );
     }
 
                
-       
        hb_list_t  * list  = hb_get_titles( fHandle );
     hb_title_t * title = (hb_title_t *)
         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
@@ -2007,8 +2097,7 @@ list = hb_get_titles( fHandle );
                 _( @"AVC/H.264 Video / MP3 Audio" )];
                        [fDstCodecsPopUp addItemWithTitle:
                 _( @"AVC/H.264 Video / Vorbis Audio" )];
-            /* We disable the create chapters checkbox here since we are NOT .mp4 
-                       and make sure it is unchecked*/
+            /* We enable the create chapters checkbox here since */
                        [fCreateChapterMarkers setEnabled: YES];
                        break;
     }
@@ -2500,12 +2589,12 @@ list = hb_get_titles( fHandle );
 {
        if ([fSubPopUp indexOfSelectedItem] == 0)
        {
-       [fSubForcedCheck setState: NSOffState];
-       [fSubForcedCheck setEnabled: NO];       
+        [fSubForcedCheck setState: NSOffState];
+        [fSubForcedCheck setEnabled: NO];      
        }
        else
        {
-       [fSubForcedCheck setEnabled: YES];      
+        [fSubForcedCheck setEnabled: YES];     
        }
        
 }
@@ -2655,7 +2744,7 @@ list = hb_get_titles( fHandle );
 
 - (IBAction) calculateBitrate: (id) sender
 {
-    if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
+    if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
     {
         return;
     }
@@ -2669,8 +2758,6 @@ list = hb_get_titles( fHandle );
 
     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
             [fVidTargetSizeField intValue] )];
-                       
-                       
 }
 
 /* Method to determine if we should change the UI
@@ -2687,8 +2774,6 @@ the user is using "Custom" settings by determining the sender*/
                [fPresetSelectedDisplay setStringValue: @"Custom"];
                
                curUserPresetChosenNum = nil;
-
-               
        }
 
 }
@@ -2751,40 +2836,40 @@ the user is using "Custom" settings by determining the sender*/
 - (IBAction) showAddPresetPanel: (id) sender
 {
     /* Deselect the currently selected Preset if there is one*/
-               [tableView deselectRow:[tableView selectedRow]];
+    [tableView deselectRow:[tableView selectedRow]];
 
-       /* Populate the preset picture settings popup here */
-       [fPresetNewPicSettingsPopUp removeAllItems];
-       [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
-       [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
-       [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
-       [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];      
+    /* Populate the preset picture settings popup here */
+    [fPresetNewPicSettingsPopUp removeAllItems];
+    [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
+    [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
+    [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
+    [fPresetNewPicSettingsPopUp selectItemAtIndex: 0]; 
        
-               /* Erase info from the input fields fPresetNewDesc*/
+    /* Erase info from the input fields fPresetNewDesc*/
        [fPresetNewName setStringValue: @""];
        [fPresetNewDesc setStringValue: @""];
        /* Show the panel */
-       [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow
-        modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
-    [NSApp runModalForWindow: fAddPresetPanel];
-    [NSApp endSheet: fAddPresetPanel];
-    [fAddPresetPanel orderOut: self];
+       [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
 }
 
 - (IBAction) closeAddPresetPanel: (id) sender
 {
-       [NSApp stopModal];
+    [NSApp endSheet: fAddPresetPanel];
+    [fAddPresetPanel orderOut: self];
 }
 
-
 - (IBAction)addUserPreset:(id)sender
 {
-    /* Here we create a custom user preset */
-    [UserPresets addObject:[self createPreset]];
-    
-       /* We stop the modal window for the new preset */
-       [NSApp stopModal];
-    [self addPreset];
+    if (![[fPresetNewName stringValue] length])
+            NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
+    else
+    {
+        /* Here we create a custom user preset */
+        [UserPresets addObject:[self createPreset]];
+        [self addPreset];
+        
+        [self closeAddPresetPanel:NULL];
+    }
 }
 - (void)addPreset
 {
@@ -4434,7 +4519,6 @@ id theRecord, theValue;
     [self savePreset];
 }
 
-
 - (void)savePreset
 {
     [UserPresets writeToFile:UserPresetsFile atomically:YES];
@@ -4443,18 +4527,6 @@ id theRecord, theValue;
 
 }
 
-
-
-- (void) controlTextDidBeginEditing: (NSNotification *) notification
-{
-    [self calculateBitrate: NULL];
-}
-
-- (void) controlTextDidEndEditing: (NSNotification *) notification
-{
-    [self calculateBitrate: NULL];
-}
-
 - (void) controlTextDidChange: (NSNotification *) notification
 {
     [self calculateBitrate: NULL];
@@ -4486,14 +4558,15 @@ id theRecord, theValue;
 }
 
 /**
- * Creates preferences controller, shows preferences window modally, and
- * releases the controller after user has closed the window.
+ * Shows preferences window modally.
  */
-- (IBAction)showPreferencesWindow:(id)sender
+- (IBAction) showPreferencesWindow: (id) sender
 {
-    HBPreferencesController *controller = [[HBPreferencesController alloc] init];
-    [controller runModal:nil];
-    [controller release];
+    NSWindow * window = [fPreferencesController window];
+    if (![window isVisible])
+        [window center];
+
+    [window makeKeyAndOrderFront: nil];
 }
 
 /**