All pastes #771556 Raw Edit

travistex_jobrefcnt_macosx.diff

public diff v1 · immutable
·published 2007-11-13 01:25 UTC
rendered paste body
Index: macosx/HBQueueController.h===================================================================--- macosx/HBQueueController.h	(revision 1055)+++ macosx/HBQueueController.h	(working copy)@@ -9,41 +9,40 @@ #include "hb.h"  @class HBController;+@class HBJobGroup; -#define HB_QUEUE_DRAGGING 0             // <--- NOT COMPLETELY FUNCTIONAL YET+#define HB_QUEUE_DRAGGING 1             // <--- NOT COMPLETELY FUNCTIONAL YET #define HB_OUTLINE_METRIC_CONTROLS 1    // for tweaking the outline cell spacings -typedef enum _HBQueueJobGroupStatus-{-    HBStatusNone          = 0,-    HBStatusPending       = 1,-    HBStatusWorking       = 2,-    HBStatusComplete      = 3,-    HBStatusCanceled      = 4-} HBQueueJobGroupStatus;- //-------------------------------------------------------------------------------------// As usual, we need to subclass NSOutlineView to handle a few special cases:+// HBQueueOutlineView+//------------------------------------------------------------------------------------+// We need to subclass NSOutlineView to handle a few special cases: //-// (1) variable row heights during live resizes-// HBQueueOutlineView exists solely to get around a bug in variable row height outline-// views in which row heights get messed up during live resizes. See this discussion:+// (1) Variable row heights during live resizes:+// There's a bug/issue in Tiger and Leopard where variable row height outline view row+// heights get messed up (or not recalculated) during live resizes. See this+// discussion: // http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00871.html // However, the recommeneded fix (override drawRect:) does not work. Instead, this // subclass implements viewDidEndLiveResize in order to recalculate all row heights. //-// (2) prevent expanding of items during drags+// (2) Prevent expanding of items during drags: // During dragging operations, we don't want outline items to expand, since a queue // doesn't really have children items. //-// (3) generate a drag image that incorporates more than just the first column+// (3) Generate a drag image that incorporates more than just the first column: // By default, NSTableView only drags an image of the first column. Change this to // drag an image of the queue's icon and desc columns.+//+// (4) Add suppport for contextual menu+// A delegate method is added that provides support for a per-column/row contextual+// menu.  @interface HBQueueOutlineView : NSOutlineView { #if HB_QUEUE_DRAGGING-BOOL                        fIsDragging;+    BOOL                         fIsDragging; #endif } #if HB_QUEUE_DRAGGING@@ -51,86 +50,38 @@ #endif @end -//--------------------------------------------------------------------------------------@interface HBJob : NSObject-{-    hb_job_t                     *hbJob;-}--+ (HBJob*)             jobWithJob: (hb_job_t *) job;-- (id)                 initWithJob: (hb_job_t *) job;-- (hb_job_t *)         job;-- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle-                               withIcon: (BOOL)withIcon-                              withTitle: (BOOL)withTitle-                           withPassName: (BOOL)withPassName-                         withFormatInfo: (BOOL)withFormatInfo-                        withDestination: (BOOL)withDestination-                        withPictureInfo: (BOOL)withPictureInfo-                          withVideoInfo: (BOOL)withVideoInfo-                           withx264Info: (BOOL)withx264Info-                          withAudioInfo: (BOOL)withAudioInfo-                       withSubtitleInfo: (BOOL)withSubtitleInfo;-+@interface NSObject(HBQueueOutlineViewDelegate)+- (NSMenu *) outlineView:(NSOutlineView *)outlineView menuForTableColumn:(NSTableColumn *)tableColumn item:(id)item; @end  //--------------------------------------------------------------------------------------@interface HBJobGroup : NSObject-{-    NSMutableArray               *fJobs;   // array of HBJob-    NSMutableAttributedString    *fDescription;-    BOOL                         fNeedsDescription;-    float                        fLastDescriptionHeight;-    float                        fLastDescriptionWidth;-    HBQueueJobGroupStatus        fStatus;-    NSString                     *fPath;-}--// Creating a job group-+ (HBJobGroup *)       jobGroup;--// Adding jobs-- (void)               addJob: (HBJob *)aJob;--// Removing jobs-- (void)               removeAllJobs;--// Querying a job group-- (unsigned int)       count;-- (HBJob *)            jobAtIndex: (unsigned)index;-- (unsigned)           indexOfJob: (HBJob *)aJob;-- (NSEnumerator *)     jobEnumerator;-- (void)               setStatus: (HBQueueJobGroupStatus)status;-- (HBQueueJobGroupStatus)  status;-- (void)               setPath: (NSString *)path;-- (NSString *)         path;--// Creating a description-- (void)               setNeedsDescription: (BOOL)flag;-- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle;-- (float)              heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle;-- (float)              lastDescriptionHeight;--@end-+// HBQueueController //------------------------------------------------------------------------------------  @interface HBQueueController : NSObject {     hb_handle_t                  *fHandle;              // reference to hblib     HBController                 *fHBController;        // reference to HBController-    NSMutableArray               *fJobGroups;           // hblib's job list organized in a hierarchy of HBJobGroup and HBJob-    HBJobGroup                   *fCurrentJobGroup;     // the HJobGroup current being processed by hblib+    NSMutableArray               *fJobGroups;           // List of all job groups tracked by the controller. Ordered the way they are to be presented in the UI.+    HBJobGroup                   *fCurrentJobGroup;     // HBJobGroup current being processed by hblib. This also appear in fJobGroups.++    unsigned int                 fPendingCount;         // Number of various kinds of job groups in fJobGroups.+    unsigned int                 fCompletedCount;       // Don't access these directly as they may not always be up-to-date.+    unsigned int                 fCanceledCount;        // Use the accessor functions instead.+    unsigned int                 fWorkingCount;+    BOOL                         fJobGroupCountsNeedUpdating;+    +    NSAlert                      *fStopQueueAlert;      // Alert asking user to confirm stoping of the queue. nil when no alert is displayed.+    BOOL                         fStopQueueAlertDisplayed;+    BOOL                         fStopQueueRequested;   // YES when hb_stop has been called. Set back to NO once libhb has performed the stop.+     BOOL                         fCurrentJobPaneShown;  // NO when fCurrentJobPane has been shifted out of view (see showCurrentJobPane)-    hb_job_t                     *fLastKnownCurrentJob; // this is how we track when hbib has started processing a different job-    NSMutableIndexSet            *fSavedExpandedItems;  // used by save/restoreOutlineViewState to preserve which items are expanded-    NSMutableIndexSet            *fSavedSelectedItems;  // used by save/restoreOutlineViewState to preserve which items are selected+    hb_job_t                     *fLastKnownLibHBJob;   // this is how we track when hbib has started processing a different job+    NSMutableArray               *fSavedExpandedItems;  // used by save/restoreOutlineViewState to preserve which items are expanded+    NSArray                      *fSavedSelectedItems;  // used by save/restoreOutlineViewState to preserve which items are selected #if HB_QUEUE_DRAGGING     NSArray                      *fDraggedNodes; #endif-    NSMutableArray               *fCompleted;           // HBJobGroups that have been completed. These also appear in fJobGroups.     NSTimer                      *fAnimationTimer;      // animates the icon of the current job in the queue outline view     int                          fAnimationIndex;       // used to generate name of image used to animate the current job in the queue outline view     @@ -171,19 +122,50 @@      } +// Initialization and setup - (void)setHandle: (hb_handle_t *)handle; - (void)setHBController: (HBController *)controller;-- (void)hblibJobListChanged;-- (void)hblibStateChanged: (hb_state_t &)state;-- (void)hblibWillStop; +// Notification from libhb+- (void)libhbStateChanged: (hb_state_t &)state;++// Adding and removing items in the queue+- (void) addJobGroup: (HBJobGroup *) aJobGroup;+- (void) removeJobGroup: (HBJobGroup *) aJobGroup;++// Getting the currently processing job group+- (HBJobGroup *) currentJobGroup;++// Getting queue statistics+- (unsigned int) pendingCount;+- (unsigned int) completedCount;+- (unsigned int) canceledCount;+- (unsigned int) workingCount;++- (BOOL) canStart;+- (BOOL) canStop;+- (BOOL) canPause;+- (BOOL) canResume;++// Starting and stopping the queue+- (IBAction) startQueue: (id)sender;+- (IBAction) stopQueue: (id)sender;+- (void)     stopQueue;+- (IBAction) pauseQueue: (id)sender;+- (IBAction) resumeQueue: (id)sender;++// IB actions - (IBAction)showQueueWindow: (id)sender; - (IBAction)removeSelectedJobGroups: (id)sender; - (IBAction)revealSelectedJobGroups: (id)sender; - (IBAction)cancelCurrentJob: (id)sender;-- (IBAction)toggleStartCancel: (id)sender;-- (IBAction)togglePauseResume: (id)sender; +// Getting UI strings+- (NSString *) stateStringForState: (hb_state_t *)s;+- (NSString *) rateStringForState: (hb_state_t *)s;+- (NSString *) timeRemainingStringForState: (hb_state_t *)s;+- (NSString *) statusStringForState: (hb_state_t *)s;+ #if HB_OUTLINE_METRIC_CONTROLS - (IBAction)imageSpacingChanged: (id)sender; - (IBAction)indentChanged: (id)sender;Index: macosx/Controller.mm===================================================================--- macosx/Controller.mm	(revision 1055)+++ macosx/Controller.mm	(working copy)@@ -8,6 +8,8 @@ #include "a52dec/a52.h" #import "HBOutputPanelController.h" #import "HBPreferencesController.h"+#import "HBJobGroup.h"+#import "HBJob.h" /* Added to integrate scanning into HBController */ #include <IOKit/IOKitLib.h> #include <IOKit/storage/IOMedia.h>@@ -156,7 +158,7 @@                  if (result == NSAlertDefaultReturn)         {-            [self doCancelCurrentJob];+            [fQueueController stopQueue];             return NSTerminateNow;         }         else@@ -164,11 +166,11 @@     }          // Warn if items still in the queue-    else if ( hb_count( fHandle ) > 0 )+    else if ( [fQueueController pendingCount] > 0 )     {         int result = NSRunCriticalAlertPanel(                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),-                NSLocalizedString(@"One or more encodes are queued for encoding. Do you want to quit anyway?", nil),+                NSLocalizedString(@"One or more encodes are queued. Do you want to quit anyway?", nil),                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);                  if ( result == NSAlertDefaultReturn )@@ -542,20 +544,9 @@         case HB_STATE_WORKING:         {             float progress_total;-            NSMutableString * string;-			/* Currently, p.job_cur and p.job_count get screwed up when adding-				jobs during encoding, if they cannot be fixed in libhb, will implement a-				nasty but working cocoa solution */-			/* Update text field */-			string = [NSMutableString stringWithFormat: _( @"Encoding: task %d of %d, %.2f %%" ), p.job_cur, p.job_count, 100.0 * p.progress];-            -			if( p.seconds > -1 )-            {-                [string appendFormat:-                    _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),-                    p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];-            }-            [fStatusField setStringValue: string];++			/* Update status field */+            [fStatusField setStringValue: [fQueueController statusStringForState: &s]]; 			             /* Update slider */ 			progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;@@ -585,7 +576,7 @@ 			             // Has current job changed? That means the queue has probably changed as 			// well so update it-            [fQueueController hblibStateChanged: s];+            [fQueueController libhbStateChanged: s];                          break;         }@@ -597,8 +588,7 @@             NSMutableString * string; 			             /* Update text field */-            string = [NSMutableString stringWithFormat:-                _( @"Muxing..." )];+            string = [NSMutableString stringWithFormat: _( @"Muxing..." )];             [fStatusField setStringValue: string]; 			             /* Update slider */@@ -609,7 +599,7 @@             [self UpdateDockIcon: 1.0]; 			 			// Pass along the info to HBQueueController-            [fQueueController hblibStateChanged: s];+            [fQueueController libhbStateChanged: s]; 			             break;         }@@ -619,134 +609,54 @@ 		    [fStatusField setStringValue: _( @"Paused" )];              			// Pass along the info to HBQueueController-            [fQueueController hblibStateChanged: s];+            [fQueueController libhbStateChanged: s];              break; 			         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 );+			// Pass along the info to HBQueueController. It may pass another job into+            // libhb.+            [fQueueController libhbStateChanged: s]; -            // Start processing back up if jobs still left in queue-            if (hb_count(fHandle) > 0)+            // If HBQueueController didn't start anything else up, then we are really done.+            hb_get_state2( fHandle, &s );+            if ((s.state == HB_STATE_WORKDONE) || (s.state == HB_STATE_IDLE))             {-                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.+                [fStatusField setStringValue: _( @"Done." )];+                [fRipIndicator setIndeterminate: NO];+                [fRipIndicator setDoubleValue: 0.0];                 [toolbar validateVisibleItems];-                break;-            } -            [fStatusField setStringValue: _( @"Done." )];-            [fRipIndicator setIndeterminate: NO];-            [fRipIndicator setDoubleValue: 0.0];-            [toolbar validateVisibleItems];+                // Restore dock icon+                [self UpdateDockIcon: -1.0]; -            /* Restore dock icon */-            [self UpdateDockIcon: -1.0];--            if (fRipIndicatorShown)-            {-                NSRect frame = [fWindow frame];-                if (frame.size.width <= 591)-				    frame.size.width = 591;-                frame.size.height += -36;-                frame.origin.y -= -36;-                [fWindow setFrame:frame display:YES animate:YES];-				fRipIndicatorShown = NO;-			}-			-			// Pass along the info to HBQueueController-            [fQueueController hblibStateChanged: s];-			-            /* Check to see if the encode state has not been cancelled-				to determine if we should check for encode done notifications */-			if (fEncodeState != 2) 			{-				/* If Growl Notification or Window and Growl has been selected */-				if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || -					[[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+                if (fRipIndicatorShown)                 {-					/*Growl Notification*/-					[self showGrowlDoneNotification: NULL];+                    NSRect frame = [fWindow frame];+                    if (frame.size.width <= 591)+                        frame.size.width = 591;+                    frame.size.height += -36;+                    frame.origin.y -= -36;+                    [fWindow setFrame:frame display:YES animate:YES];+                    fRipIndicatorShown = NO;                 }-                /* If Alert Window or Window and Growl has been selected */-				if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || -					[[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])-                {-					/*On Screen Notification*/-					int status;-					NSBeep();-					status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);-					[NSApp requestUserAttention:NSCriticalRequest];-					if ( status == NSAlertDefaultReturn ) -					{-						[self enableUI: YES];-					}-                }-				else-				{-					[self enableUI: YES];-				}-			           /* If sleep has been selected */ -            if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) -                { -               /* Sleep */ -               NSDictionary* errorDict; -               NSAppleEventDescriptor* returnDescriptor = NULL; -               NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: -                        @"tell application \"Finder\" to sleep"]; -               returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; -               [scriptObject release]; -               [self enableUI: YES]; -                } -            /* If Shutdown has been selected */ -            if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) -                { -               /* Shut Down */ -               NSDictionary* errorDict; -               NSAppleEventDescriptor* returnDescriptor = NULL; -               NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: -                        @"tell application \"Finder\" to shut down"]; -               returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; -               [scriptObject release]; -               [self enableUI: YES]; -                }-			-						// MetaX insertion via AppleScript-			if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)-			{-			NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]];-			[myScript executeAndReturnError: nil];-			[myScript release];-			}-			-			-			}-			else-			{-				[self enableUI: YES];-			}++            }             break;         }     } 	     /* Lets show the queue status here in the main window */-	int queue_count = hb_count( fHandle );-	if( queue_count )+	int queue_count = [fQueueController pendingCount];+	if( queue_count == 1) 	{-		[fQueueStatus setStringValue: [NSString stringWithFormat:-			@"%d pass%s in the queue",-						 queue_count, ( queue_count > 1 ) ? "es" : ""]];+		[fQueueStatus setStringValue: _( @"1 encode queued") ]; 	}+    else if (queue_count > 1)+    {+		[fQueueStatus setStringValue: [NSString stringWithFormat: _( @"%d encodes queued" ), queue_count]];+    } 	else 	{ 		[fQueueStatus setStringValue: @""];@@ -918,7 +828,7 @@             if ([ident isEqualToString: StartEncodingIdentifier])             {                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];-                if (hb_count(fHandle) > 0)+                if ([fQueueController pendingCount] > 0)                     [toolbarItem setLabel: @"Start Queue"];                 else                     [toolbarItem setLabel: @"Start"];@@ -1019,9 +929,9 @@  -(IBAction)showGrowlDoneNotification:(id)sender {-  [GrowlApplicationBridge -            notifyWithTitle:@"Put down that cocktail..." -                description:@"your HandBrake encode is done!" +    [GrowlApplicationBridge +            notifyWithTitle:@"HandBrake has finished its encoding tasks." +                description:@"More information is available in the Queue window."             notificationName:SERVICE_NAME                    iconData:nil                     priority:0 @@ -1029,6 +939,7 @@                clickContext:nil]; } + #pragma mark - #pragma mark Get New Source @@ -1258,7 +1169,6 @@     hb_title_t * title = (hb_title_t *) hb_list_item( list,             [fSrcTitlePopUp indexOfSelectedItem] );     hb_job_t * job = title->job;-    //int i;      /* Chapter selection */     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;@@ -1476,55 +1386,17 @@  } ---/* addToQueue: puts up an alert before ultimately calling doAddToQueue-*/-- (IBAction) addToQueue: (id) sender-{-	/* 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;-	}--    /* 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];-    }-}--/* 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];-}- - (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; +    // Create a job group for adding the individual job passes. The job group will+    // then be added to the queue.+    HBJobGroup * jobGroup = [HBJobGroup jobGroup];+    hb_job_t * job_copy;+         // Assign a sequence number, starting at zero, to each job added so they can     // be lumped together in the UI.     job->sequence_id = -1;@@ -1565,8 +1437,11 @@         * Add the pre-scan job         */         job->sequence_id++; // for job grouping-        hb_add( fHandle, job );-+ +        job_copy = hb_job_copy(job);      // make a copy for jobWithLibhbJob to use +        [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]];     // add it to the queue+        hb_job_dereference (job_copy);    // delete our reference+                         job->x264opts = x264opts_tmp;     }     else@@ -1590,8 +1465,11 @@                  job->pass = 1;         job->sequence_id++; // for job grouping-        hb_add( fHandle, job ); +        job_copy = hb_job_copy(job);      // make a copy for jobWithLibhbJob to use +        [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]];     // add it to the queue+        hb_job_dereference (job_copy);    // delete our reference+         job->pass = 2;         job->sequence_id++; // for job grouping @@ -1600,42 +1478,91 @@          job->select_subtitle = subtitle_tmp; -        hb_add( fHandle, job );+        job_copy = hb_job_copy(job);      // make a copy for jobWithLibhbJob to use +        [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]];     // add it to the queue+        hb_job_dereference (job_copy);    // delete our reference     }     else     {         job->indepth_scan = 0;         job->pass = 0;         job->sequence_id++; // for job grouping-        hb_add( fHandle, job );++        job_copy = hb_job_copy(job);      // make a copy for jobWithLibhbJob to use +        [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]];     // add it to the queue+        hb_job_dereference (job_copy);    // delete our reference     } 	     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; 	[[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; 	-    // Notify the queue-	[fQueueController hblibJobListChanged];+    // Add job group to the queue+    [fQueueController addJobGroup:jobGroup]; } -/* Rip: puts up an alert before ultimately calling doRip++/* addToQueue: puts up an alert before ultimately calling doAddToQueue */+- (IBAction) addToQueue: (id) sender+{+	/* 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;+	}++    /* 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];+    }+}++/* 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];+}++/* Rip: puts up an alert before ultimately calling HBQueueController startQueue:.+*/ - (IBAction) Rip: (id) sender {     /* Rip or Cancel ? */     hb_state_t s;     hb_get_state2( fHandle, &s ); -    if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)+    // If processing jobs, then stop the queue+    +    if(s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED) 	{-        [self Cancel: sender];+        [fQueueController stopQueue: fWindow];         return;     }     -    // If there are jobs in the queue, then this is a rip the queue+    // If there are jobs in the queue, then start the queue     -    if (hb_count( fHandle ) > 0)+    if ([fQueueController pendingCount] > 0)     {-        [self doRip];+        [fQueueController startQueue: sender];         return;     } @@ -1658,20 +1585,20 @@             _( @"Do you want to overwrite %@?" ),             [fDstFile2Field stringValue]] );             -        // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.+        // overWriteAlertDone: will be called when the alert is dismissed. It will call startQueue.     }     else     {         /* if there are no jobs in the queue, then add this one to the queue and rip          otherwise, just rip the queue */-        if( hb_count( fHandle ) == 0)+        if( [fQueueController pendingCount] == 0)         {             [self doAddToQueue];         }          NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];-        [self doRip];+        [fQueueController startQueue: sender];     } } @@ -1685,155 +1612,117 @@     {         /* if there are no jobs in the queue, then add this one to the queue and rip          otherwise, just rip the queue */-        if( hb_count( fHandle ) == 0 )+        if( [fQueueController pendingCount] == 0 )         {             [self doAddToQueue];         }          NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];-        [self doRip];+        [fQueueController startQueue: nil];     } } -- (void) remindUserOfSleepOrShutdown+//------------------------------------------------------------------------------------+// Calls HBQueueController stopQueue: to request stopping of the queue. Cancel:+// returns immediately after making the request.+//------------------------------------------------------------------------------------+- (IBAction)Cancel: (id)sender {-       if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])-       {-               /*Warn that computer will sleep after encoding*/-               int reminduser;-               NSBeep();-               reminduser = NSRunAlertPanel(@"The computer will sleep after encoding is done.",@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);-               [NSApp requestUserAttention:NSCriticalRequest];-               if ( reminduser == NSAlertAlternateReturn ) -               {-                       [self showPreferencesWindow:NULL];-               }-       } -       else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])-       {-               /*Warn that computer will shut down after encoding*/-               int reminduser;-               NSBeep();-               reminduser = NSRunAlertPanel(@"The computer will shut down after encoding is done.",@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);-               [NSApp requestUserAttention:NSCriticalRequest];-               if ( reminduser == NSAlertAlternateReturn ) -               {-                       [self showPreferencesWindow:NULL];-               }-       }-+    [fQueueController stopQueue: fWindow]; } --- (void) doRip-{-    /* Let libhb do the job */-    hb_start( fHandle );-	/*set the fEncodeState State */-	fEncodeState = 1;-}---- //-------------------------------------------------------------------------------------// Removes all jobs from the queue. Does not cancel the current processing job.+// Calls HBQueueController pauseQueue: or resumeQueue: depending on the current state+// of libhb. //-------------------------------------------------------------------------------------- (void) doDeleteQueuedJobs+- (IBAction) Pause: (id) sender {-    hb_job_t * job;-    while( ( job = hb_job( fHandle, 0 ) ) )-        hb_rem( fHandle, job );+    hb_state_t s;+    hb_get_state2( fHandle, &s );++    if( s.state == HB_STATE_PAUSED )+        [fQueueController resumeQueue:sender];+    else+        [fQueueController pauseQueue:sender]; }  //-------------------------------------------------------------------------------------// Cancels the current job and proceeds with the next one in the queue.+// Displays alert reminder user that their computer will be put to sleep or shutdown+// after the current set of encodes completes. //-------------------------------------------------------------------------------------- (void) doCancelCurrentJob+- (void) remindUserOfSleepOrShutdown {-    // 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.-     -    [fQueueController hblibWillStop];-    hb_stop( fHandle );-    fEncodeState = 2;   // don't alert at end of processing since this was a cancel-    +    if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])+    {+        /*Warn that computer will sleep after encoding*/+        int reminduser;+        NSBeep();+        reminduser = NSRunAlertPanel(@"Your computer will sleep after encoding has completed.",+            @"You have selected to sleep the computer after encoding. To turn off sleeping, use HandBrake preferences.",+            nil, @"Preferences...", nil);+        if ( reminduser == NSAlertAlternateReturn ) +            [self showPreferencesWindow:NULL];+    } +    else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])+    {+        /*Warn that computer will shut down after encoding*/+        int reminduser;+        NSBeep();+        reminduser = NSRunAlertPanel(@"Your computer will shut down after encoding has completed.",+            @"You have selected to shut down the computer after encoding. To turn off shut down, use HandBrake preferences.",+            nil, @"Preferences...", nil);+        if ( reminduser == NSAlertAlternateReturn ) +            [self showPreferencesWindow:NULL];+    } }  //-------------------------------------------------------------------------------------// 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.+// Performs actions required after encodes have been completed. //-------------------------------------------------------------------------------------- (IBAction)Cancel: (id)sender+- (void) performPostProcessingTasks {-    if (!fHandle) return;+    // If Growl Notification or Window and Growl has been selected+    if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || +        [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+    {+        [self showGrowlDoneNotification: NULL];+    }     -    hb_job_t * job = hb_current_job(fHandle);-    if (!job) return;--    NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),-            [NSString stringWithUTF8String:job->title->name]];+    // If Alert Window or Window and Growl has been selected+    if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || +        [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+    {+        NSBeep();+        NSRunAlertPanel(@"HandBrake has finished its encoding tasks.", @"More information is available in the Queue window.", nil, nil, nil);+    }     -    // 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(@"Delete All", nil),-            NSLocalizedString(@"Stop Encoding", 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]);+    // If sleep has been selected+    if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) +    { +        NSDictionary* errorDict; +        NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: @"tell application \"Finder\" to sleep"]; +        [scriptObject executeAndReturnError: &errorDict]; +        [scriptObject release]; +    }     -    // 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 == NSAlertOtherReturn)-        [self doCancelCurrentJob];-    else if (returnCode == NSAlertAlternateReturn)-    {-        [self doDeleteQueuedJobs];-        [self doCancelCurrentJob];+    // If Shutdown has been selected+    if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) +    { +        NSDictionary* errorDict; +        NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: @"tell application \"Finder\" to shut down"]; +        [scriptObject executeAndReturnError: &errorDict]; +        [scriptObject release];      }-} ------ (IBAction) Pause: (id) sender-{-    hb_state_t s;-    hb_get_state2( fHandle, &s );--    if( s.state == HB_STATE_PAUSED )+    // MetaX insertion via AppleScript+    if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)     {-        hb_resume( fHandle );+        NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]];+        [myScript executeAndReturnError: nil];+        [myScript release];     }-    else-    {-        hb_pause( fHandle );-    }+ }  #pragma mark -Index: macosx/HandBrake.xcodeproj/project.pbxproj===================================================================--- macosx/HandBrake.xcodeproj/project.pbxproj	(revision 1055)+++ macosx/HandBrake.xcodeproj/project.pbxproj	(working copy)@@ -145,6 +145,10 @@ 		E37167A90C92FAA50072B384 /* JobPassUnknownSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = E37167A70C92FAA50072B384 /* JobPassUnknownSmall.png */; }; 		E37172670C977D340072B384 /* HBImageAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E37172650C977D340072B384 /* HBImageAndTextCell.m */; }; 		E37172680C977D340072B384 /* HBImageAndTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = E37172660C977D340072B384 /* HBImageAndTextCell.h */; };+		E3724BEA0CD2280800010454 /* HBJob.h in Headers */ = {isa = PBXBuildFile; fileRef = E3724BE90CD2280800010454 /* HBJob.h */; };+		E3724BF90CD22A8400010454 /* HBJob.m in Sources */ = {isa = PBXBuildFile; fileRef = E3724BF80CD22A8400010454 /* HBJob.m */; };+		E37274450CD4113000CFE13E /* HBJobGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = E37274440CD4113000CFE13E /* HBJobGroup.h */; };+		E372744D0CD412E100CFE13E /* HBJobGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = E372744C0CD412E100CFE13E /* HBJobGroup.m */; }; 		E37C89410C83988F00C1B919 /* DeleteHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893D0C83988F00C1B919 /* DeleteHighlight.png */; }; 		E37C89420C83988F00C1B919 /* Delete.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893E0C83988F00C1B919 /* Delete.png */; }; 		E37C89430C83988F00C1B919 /* JobSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893F0C83988F00C1B919 /* JobSmall.png */; };@@ -163,6 +167,7 @@ 		E3C844F80CA6B3F90013B683 /* RevealHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F40CA6B3F90013B683 /* RevealHighlight.png */; }; 		E3C844F90CA6B3F90013B683 /* Reveal.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F50CA6B3F90013B683 /* Reveal.png */; }; 		E3C845870CA6E9080013B683 /* EncodeComplete.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C845860CA6E9080013B683 /* EncodeComplete.png */; };+		E3EFC1320CD750FE0068DF25 /* EncodeCanceled.png in Resources */ = {isa = PBXBuildFile; fileRef = E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */; }; 		EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; 		EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; 		FC8519500C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; };@@ -321,6 +326,10 @@ 		E37167A70C92FAA50072B384 /* JobPassUnknownSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = JobPassUnknownSmall.png; sourceTree = "<group>"; }; 		E37172650C977D340072B384 /* HBImageAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = HBImageAndTextCell.m; sourceTree = "<group>"; }; 		E37172660C977D340072B384 /* HBImageAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HBImageAndTextCell.h; sourceTree = "<group>"; };+		E3724BE90CD2280800010454 /* HBJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBJob.h; sourceTree = "<group>"; };+		E3724BF80CD22A8400010454 /* HBJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBJob.m; sourceTree = "<group>"; };+		E37274440CD4113000CFE13E /* HBJobGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBJobGroup.h; sourceTree = "<group>"; };+		E372744C0CD412E100CFE13E /* HBJobGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBJobGroup.m; sourceTree = "<group>"; }; 		E37C893D0C83988F00C1B919 /* DeleteHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DeleteHighlight.png; sourceTree = "<group>"; }; 		E37C893E0C83988F00C1B919 /* Delete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Delete.png; sourceTree = "<group>"; }; 		E37C893F0C83988F00C1B919 /* JobSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = JobSmall.png; sourceTree = "<group>"; };@@ -339,6 +348,7 @@ 		E3C844F40CA6B3F90013B683 /* RevealHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealHighlight.png; sourceTree = "<group>"; }; 		E3C844F50CA6B3F90013B683 /* Reveal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Reveal.png; sourceTree = "<group>"; }; 		E3C845860CA6E9080013B683 /* EncodeComplete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeComplete.png; sourceTree = "<group>"; };+		E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeCanceled.png; sourceTree = "<group>"; }; 		EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; }; 		FC85194C0C59A02C0073812C /* denoise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = denoise.c; path = ../libhb/denoise.c; sourceTree = SOURCE_ROOT; }; 		FC85194D0C59A02C0073812C /* deinterlace.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deinterlace.c; path = ../libhb/deinterlace.c; sourceTree = SOURCE_ROOT; };@@ -544,6 +554,10 @@ 				A9AC41DD0C918DB500DDF9B8 /* HBAdvancedController.m */, 				E37172660C977D340072B384 /* HBImageAndTextCell.h */, 				E37172650C977D340072B384 /* HBImageAndTextCell.m */,+				E3724BE90CD2280800010454 /* HBJob.h */,+				E3724BF80CD22A8400010454 /* HBJob.m */,+				E37274440CD4113000CFE13E /* HBJobGroup.h */,+				E372744C0CD412E100CFE13E /* HBJobGroup.m */, 			); 			name = "HandBrake Sources"; 			sourceTree = "<group>";@@ -600,6 +614,7 @@ 				E3997A260CAB58BC00287239 /* EncodeWorking3.png */, 				E3997A270CAB58BC00287239 /* EncodeWorking4.png */, 				E3997A2B0CAB58BC00287239 /* EncodeWorking5.png */,+				E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */, 			); 			path = icons; 			sourceTree = "<group>";@@ -637,6 +652,8 @@ 				E37C89480C83989F00C1B919 /* HBQueueController.h in Headers */, 				A9AC41E00C918DB500DDF9B8 /* HBAdvancedController.h in Headers */, 				E37172680C977D340072B384 /* HBImageAndTextCell.h in Headers */,+				E3724BEA0CD2280800010454 /* HBJob.h in Headers */,+				E37274450CD4113000CFE13E /* HBJobGroup.h in Headers */, 			); 			runOnlyForDeploymentPostprocessing = 0; 		};@@ -738,9 +755,11 @@ 		29B97313FDCFA39411CA2CEA /* Project object */ = { 			isa = PBXProject; 			buildConfigurationList = 4D4E7BC4087804870051572B /* Build configuration list for PBXProject "HandBrake" */;+			compatibilityVersion = "Xcode 2.4"; 			hasScannedForEncodings = 1; 			mainGroup = 29B97314FDCFA39411CA2CEA /* HandBrake */; 			projectDirPath = "";+			projectRoot = ""; 			targets = ( 				4DD93F8D082036E8008E1322 /* HandBrake */, 				4D1EA2D90993B01000FDC1A2 /* Instant HandBrake */,@@ -814,6 +833,7 @@ 				E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */, 				E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */, 				E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */,+				E3EFC1320CD750FE0068DF25 /* EncodeCanceled.png in Resources */, 			); 			runOnlyForDeploymentPostprocessing = 0; 		};@@ -945,6 +965,8 @@ 				E37C89470C83989F00C1B919 /* HBQueueController.mm in Sources */, 				A9AC41DF0C918DB500DDF9B8 /* HBAdvancedController.m in Sources */, 				E37172670C977D340072B384 /* HBImageAndTextCell.m in Sources */,+				E3724BF90CD22A8400010454 /* HBJob.m in Sources */,+				E372744D0CD412E100CFE13E /* HBJobGroup.m in Sources */, 			); 			runOnlyForDeploymentPostprocessing = 0; 		};@@ -1551,7 +1573,7 @@ 				GCC_ENABLE_FIX_AND_CONTINUE = YES; 				GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 				GCC_MODEL_TUNING = G5;-				GCC_OPTIMIZATION_LEVEL = s;+				GCC_OPTIMIZATION_LEVEL = 0; 				HEADER_SEARCH_PATHS = ../libhb; 				INSTALL_PATH = /usr/local/bin; 				LIBRARY_SEARCH_PATHS = "";Index: macosx/Controller.h===================================================================--- macosx/Controller.h	(revision 1055)+++ macosx/Controller.h	(working copy)@@ -174,9 +174,6 @@ 	     hb_handle_t                  * fHandle; 	hb_title_t                   * fTitle;-    /* integer to set to determine the previous state-		of encode 0==idle, 1==encoding, 2==cancelled*/-    int                            fEncodeState; 	int                            currentScanCount; 	int                            currentSuccessfulScanCount;     BOOL                           SuccessfulScan;@@ -210,8 +207,6 @@ - (IBAction) subtitleSelectionChanged: (id) sender;  - (IBAction) browseFile: (id) sender;-- (void)     browseFileDone: (NSSavePanel *) sheet-    returnCode: (int) returnCode contextInfo: (void *) contextInfo;  - (IBAction) videoMatrixChanged: (id) sender; - (IBAction) qualitySliderChanged: (id) sender;@@ -219,26 +214,12 @@ - (IBAction) showPicturePanel: (id) sender; - (IBAction) calculatePictureSizing: (id) sender; - (IBAction) openMainWindow: (id) sender;--- (IBAction) addToQueue: (id) sender;-- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet-    returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void)     doAddToQueue;- - (IBAction) showQueueWindow:(id)sender;+- (IBAction) showPreferencesWindow:(id)sender; -- (IBAction)showPreferencesWindow:(id)sender;-+- (IBAction) addToQueue: (id) sender; - (IBAction) Rip: (id) sender;-- (void)     overWriteAlertDone: (NSWindow *) sheet-    returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void)     updateAlertDone: (NSWindow *) sheet-    returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void)     doRip;- - (IBAction) Cancel: (id) sender;-- (void)     doCancelCurrentJob;- - (IBAction) Pause: (id) sender;  - (IBAction) calculateBitrate: (id) sender;@@ -307,5 +288,6 @@ - (void)setupToolbar;  - (void) remindUserOfSleepOrShutdown;+- (void) performPostProcessingTasks; @end Index: macosx/HBQueueController.mm===================================================================--- macosx/HBQueueController.mm	(revision 1055)+++ macosx/HBQueueController.mm	(working copy)@@ -4,36 +4,26 @@     Homepage: <http://handbrake.m0k.org/>.     It may be used under the terms of the GNU General Public License. */ -#include "HBQueueController.h"-#include "Controller.h"+#import "HBQueueController.h"+#import "Controller.h" #import "HBImageAndTextCell.h"+#import "HBJob.h"+#import "HBJobGroup.h"  #define HB_ROW_HEIGHT_TITLE_ONLY           17.0  // Pasteboard type for or drag operations #define HBQueuePboardType            @"HBQueuePboardType" +// Button return codes passed to didEndSelector method of beginStopQueueAlertModalForWindow:::.+enum {+   HBKeepEncodingButtonReturn  = NSAlertFirstButtonReturn,+   HBStopEncodingButtonReturn  = NSAlertSecondButtonReturn+}; -//-------------------------------------------------------------------------------------// NSMutableAttributedString (HBAdditions)-//------------------------------------------------------------------------------------ -@interface NSMutableAttributedString (HBAdditions)-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary;-@end--@implementation NSMutableAttributedString (HBAdditions)-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary-{-    NSAttributedString * s = [[[NSAttributedString alloc]-        initWithString: aString-        attributes: aDictionary] autorelease];-    [self appendAttributedString: s];-}-@end- //-------------------------------------------------------------------------------------#pragma mark -+// HBQueueOutlineView //------------------------------------------------------------------------------------  @implementation HBQueueOutlineView@@ -79,703 +69,57 @@ } #endif -@end--//-------------------------------------------------------------------------------------#pragma mark --#pragma mark Job group functions-//-------------------------------------------------------------------------------------// These could be part of hblib if we think hblib should have knowledge of groups.-// For now, I see groups as a metaphor that HBQueueController provides.--/**- * 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)    +- (NSMenu *) menuForEvent:(NSEvent*)theEvent {-    hb_job_t * job;-    int count = 0;-    int index = 0;-    while( ( job = hb_job( h, index++ ) ) )-    {-        if (job->sequence_id == 0)-            count++;-    }-    return count;-}+    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];+    int row = [self rowAtPoint:pt];+    int column = [self columnAtPoint:pt]; -/**- * Returns handle to the first job in the i-th group within the job list.- * @param h Handle to hb_handle_t.- * @param i Index of group.- * @returns Handle to hb_job_t of desired job.- */-static hb_job_t * hb_group(hb_handle_t * h, int i)    -{-    hb_job_t * job;-    int count = 0;-    int index = 0;-    while( ( job = hb_job( h, index++ ) ) )-    {-        if (job->sequence_id == 0)-        {-            if (count == i)-                return job;-            count++;-        }-    }-    return NULL;-}+    // Select the row being clicked. By default, right-clicks and control-clicks don't+    // select the clicked on row. But we want that behavior. This code could be done+    // in an override of rightMouseDown but that would not catch the case of a+    // control-click. We can catch both those cases here in menuForEent. -/**- * Removes a groups of jobs from the job list.- * @param h Handle to hb_handle_t.- * @param job Handle to the first job in the group.- */-static void hb_rem_group( hb_handle_t * h, hb_job_t * job )-{-    // Find job in list-    hb_job_t * j;-    int index = 0;-    while( ( j = hb_job( h, index ) ) )-    {-        if (j == job)-        {-            // Delete this job plus the following ones in the sequence-            hb_rem( h, job );-            while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )-                hb_rem( h, j );-            return;-        }-        else-            index++;-    }-}--/**- * Returns handle to the next job after the given job.- * @param h Handle to hb_handle_t.- * @param job Handle to the a job in the group.- * @returns Handle to hb_job_t of desired job or NULL if no such job.- */-static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job )-{-    hb_job_t * j = NULL;-    int index = 0;-    while( ( j = hb_job( h, index++ ) ) )-    {-        if (j == job)-            return hb_job( h, index );-    }-    return NULL;-}--#pragma mark ---//-------------------------------------------------------------------------------------// HBJob-//--------------------------------------------------------------------------------------@implementation HBJob--+ (HBJob*) jobWithJob: (hb_job_t *) job-{-    return [[[HBJob alloc] initWithJob:job] autorelease];-}--- (id) initWithJob: (hb_job_t *) job-{-    if (self = [super init])-    {-        // job is not owned by HBJob. It does not get dealloacted when HBJob is released.-        hbJob = job;-    }-    return self; -}--- (hb_job_t*) job-{-    return hbJob;-}--//-------------------------------------------------------------------------------------// Generate string to display in UI.-//--------------------------------------------------------------------------------------- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle-                               withIcon: (BOOL)withIcon-                              withTitle: (BOOL)withTitle-                           withPassName: (BOOL)withPassName-                         withFormatInfo: (BOOL)withFormatInfo-                        withDestination: (BOOL)withDestination-                        withPictureInfo: (BOOL)withPictureInfo-                          withVideoInfo: (BOOL)withVideoInfo-                           withx264Info: (BOOL)withx264Info-                          withAudioInfo: (BOOL)withAudioInfo-                       withSubtitleInfo: (BOOL)withSubtitleInfo--{-    NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];+    if (row == -1)+        return [super menuForEvent:theEvent];+    else if (![self isRowSelected:row])+       [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];     -    hb_title_t * title = hbJob->title;+    // Ask delegate for a menu     -    // Attributes-    static NSMutableParagraphStyle * ps = NULL;-    if (!ps)+    if (column >= 0 && row >= 0)     {-        ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];-        [ps setHeadIndent: 40.0];-        [ps setParagraphSpacing: 1.0];-        [ps setTabStops:[NSArray array]];    // clear all tabs-        [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];+        id del = [self delegate];+        NSTableColumn *col = [[self tableColumns] objectAtIndex:column];+        if ([del respondsToSelector:@selector(outlineView:menuForTableColumn:item:)])+            return [del outlineView:self menuForTableColumn:col item:[self itemAtRow:row]];     }--    static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:-                [NSFont systemFontOfSize:10.0], NSFontAttributeName,-                ps, NSParagraphStyleAttributeName,-                nil] retain];-    static NSDictionary* detailBoldAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:-                [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName,-                ps, NSParagraphStyleAttributeName,-                nil] retain];-    static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:-                [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,-                ps, NSParagraphStyleAttributeName,-                nil] retain];-    static NSDictionary* shortHeightAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:-                [NSFont systemFontOfSize:2.0], NSFontAttributeName,-                nil] retain];--    // Title with summary-    if (withTitle)-    {-        if (withIcon)-        {-            NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: @"JobSmall"]] autorelease];-            NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease];--            NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys:-                            [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName,-                            imageAttachment, NSAttachmentAttributeName,-                            ps, NSParagraphStyleAttributeName,-                            nil];--            NSAttributedString * imageAsString = [[[NSAttributedString alloc]-                    initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter]-                    attributes: imageAttributes] autorelease];--            [finalString appendAttributedString:imageAsString];-        }-    -        // Note: use title->name instead of title->dvd since name is just the chosen-        // folder, instead of dvd which is the full path-        [finalString appendString:[NSString stringWithUTF8String:title->name] withAttributes:titleAttribute];-        -        NSString * summaryInfo;-    -        NSString * chapterString = (hbJob->chapter_start == hbJob->chapter_end) ?-                [NSString stringWithFormat:@"Chapter %d", hbJob->chapter_start] :-                [NSString stringWithFormat:@"Chapters %d through %d", hbJob->chapter_start, hbJob->chapter_end];--        BOOL hasIndepthScan = (hbJob->pass == -1);-        int numVideoPasses = 0;--        // To determine number of video passes, we need to skip past the subtitle scan.-        if (hasIndepthScan)-        {-            // When job is the one currently being processed, then the next in its group-            // is the the first job in the queue.-            hb_job_t * nextjob;-            if (hbJob == hb_current_job(handle))-                nextjob = hb_job(handle, 0);-            else-                nextjob = hb_next_job(handle, hbJob);-            if (nextjob)    // Overly cautious in case there is no next job!-                numVideoPasses = MIN( 2, nextjob->pass + 1 );-        }-        else-            numVideoPasses = MIN( 2, hbJob->pass + 1 );--        if (hasIndepthScan && numVideoPasses == 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, Single Video Pass)", title->index, chapterString];-        else if (hasIndepthScan && numVideoPasses > 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, %d Video Passes)", title->index, chapterString, numVideoPasses];-        else if (numVideoPasses == 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Single Video Pass)", title->index, chapterString];-        else-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, %d Video Passes)", title->index, chapterString, numVideoPasses];--        [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttribute];-        -        // Insert a short-in-height line to put some white space after the title-        [finalString appendString:@"\n" withAttributes:shortHeightAttribute];-    }-    -    // End of title stuff-    --    // Pass Name-    if (withPassName)-    {-        if (withIcon)-        {-            NSString * imageName;-            switch (hbJob->pass)-            {-                case -1: imageName = @"JobPassSubtitleSmall"; break;-                case  0: imageName = @"JobPassFirstSmall"; break;-                case  1: imageName = @"JobPassFirstSmall"; break;-                case  2: imageName = @"JobPassSecondSmall"; break;-                default: imageName = @"JobPassUnknownSmall"; break;-            }--            NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: imageName]] autorelease];-            NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease];--            NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys:-                            [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName,-                            imageAttachment, NSAttachmentAttributeName,-                            ps, NSParagraphStyleAttributeName,-                            nil];--            NSAttributedString * imageAsString = [[[NSAttributedString alloc]-                    initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter]-                    attributes: imageAttributes] autorelease];--            [finalString appendAttributedString:imageAsString];-        }-    -        NSString * jobPassName;-        if (hbJob->pass == -1)-            jobPassName = NSLocalizedString (@"Deep Scan", nil);-        else-        {-            int passNum = MAX( 1, hbJob->pass );-            if (passNum == 0)-                jobPassName = NSLocalizedString (@"1st Pass", nil);-            else if (passNum == 1)-                jobPassName = NSLocalizedString (@"1st Pass", nil);-            else if (passNum == 2)-                jobPassName = NSLocalizedString (@"2nd Pass", nil);-            else-                jobPassName = [NSString stringWithFormat: NSLocalizedString(@"Pass %d", nil), passNum];-        }-        [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPassName] withAttributes:detailBoldAttribute];-    }--    // Video Codec needed by FormatInfo and withVideoInfo-    NSString * jobVideoCodec = nil;-    if (withFormatInfo || withVideoInfo)-    {-        // 2097152-        // Video Codec settings (Encoder in the gui)-        if (hbJob->vcodec == HB_VCODEC_FFMPEG)-            jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG-        else if (hbJob->vcodec == HB_VCODEC_XVID)-            jobVideoCodec = @"XviD"; // HB_VCODEC_XVID-        else if (hbJob->vcodec == HB_VCODEC_X264)-        {-            // Deterimine for sure how we are now setting iPod uuid atom-            if (hbJob->h264_level) // We are encoding for iPod-                jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264    -            else-                jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264-        }-    }-    if (jobVideoCodec == nil)-        jobVideoCodec = @"unknown";-    -    // Audio Codec needed by FormatInfo and AudioInfo-    NSString * jobAudioCodec = nil;-    if (withFormatInfo || withAudioInfo)-    {-        if (hbJob->acodec == 256)-            jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC-        else if (hbJob->acodec == 512)-            jobAudioCodec = @"MP3"; // HB_ACODEC_LAME-        else if (hbJob->acodec == 1024)-            jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS-        else if (hbJob->acodec == 2048)-            jobAudioCodec = @"AC3"; // HB_ACODEC_AC3-    }-    if (jobAudioCodec == nil)-        jobAudioCodec = @"unknown";---    if (withFormatInfo)-    {-        NSString * jobFormatInfo;-        // Muxer settings (File Format in the gui)-        if (hbJob->mux == 65536 || hbJob->mux == 131072 || hbJob->mux == 1048576)-            jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD-        else if (hbJob->mux == 262144)-            jobFormatInfo = @"AVI"; // HB_MUX_AVI-        else if (hbJob->mux == 524288)-            jobFormatInfo = @"OGM"; // HB_MUX_OGM-        else if (hbJob->mux == 2097152)-            jobFormatInfo = @"MKV"; // HB_MUX_MKV-        else-            jobFormatInfo = @"unknown";-                -        if (hbJob->chapter_markers == 1)-            jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio, Chapter Markers\n", jobFormatInfo, jobVideoCodec, jobAudioCodec];-        else-            jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio\n", jobFormatInfo, jobVideoCodec, jobAudioCodec];-            -        [finalString appendString: @"Format: " withAttributes:detailBoldAttribute];-        [finalString appendString: jobFormatInfo withAttributes:detailAttribute];-    }--    if (withDestination)-    {-        [finalString appendString: @"Destination: " withAttributes:detailBoldAttribute];-        [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->file]] withAttributes:detailAttribute];-    }---    if (withPictureInfo)-    {-        NSString * jobPictureInfo;-        // integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height,-        // maxWidth, maxHeight-        if (hbJob->pixel_ratio == 1)-        {-            int titlewidth = title->width - hbJob->crop[2] - hbJob->crop[3];-            int displayparwidth = titlewidth * hbJob->pixel_aspect_width / hbJob->pixel_aspect_height;-            int displayparheight = title->height - hbJob->crop[0] - hbJob->crop[1];-            jobPictureInfo = [NSString stringWithFormat:@"%dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, hbJob->width, displayparheight];-        }-        else-            jobPictureInfo = [NSString stringWithFormat:@"%dx%d", hbJob->width, hbJob->height];-        if (hbJob->keep_ratio == 1)-            jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"];-        -        if (hbJob->grayscale == 1)-            jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"];-        -        if (hbJob->deinterlace == 1)-            jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"];-        if (withIcon)   // implies indent the info-            [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-        [finalString appendString: @"Picture: " withAttributes:detailBoldAttribute];-        [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPictureInfo] withAttributes:detailAttribute];-    }-    -    if (withVideoInfo)-    {-        NSString * jobVideoQuality;-        NSString * jobVideoDetail;-        -        if (hbJob->vquality <= 0 || hbJob->vquality >= 1)-            jobVideoQuality = [NSString stringWithFormat:@"%d kbps", hbJob->vbitrate];-        else-        {-            NSNumber * vidQuality;-            vidQuality = [NSNumber numberWithInt:hbJob->vquality * 100];-            // this is screwed up kind of. Needs to be formatted properly.-            if (hbJob->crf == 1)-                jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality];            -            else-                jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality];-        }-        -        if (hbJob->vrate_base == 1126125)-        {-            // NTSC FILM 23.976-            jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];-        }-        else if (hbJob->vrate_base == 900900)-        {-            // NTSC 29.97-            jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];-        }-        else-        {-            // Everything else-            jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base];-        }-        if (withIcon)   // implies indent the info-            [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-        [finalString appendString: @"Video: " withAttributes:detailBoldAttribute];-        [finalString appendString:[NSString stringWithFormat:@"%@\n", jobVideoDetail] withAttributes:detailAttribute];-    }-    -    if (withx264Info)-    {-        if (hbJob->vcodec == HB_VCODEC_X264 && hbJob->x264opts)-        {-            if (withIcon)   // implies indent the info-                [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-            [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttribute];-            [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->x264opts]] withAttributes:detailAttribute];-        }-    }--    if (withAudioInfo)-    {-        NSString * jobAudioInfo;-        if ([jobAudioCodec isEqualToString: @"AC3"])-            jobAudioInfo = [NSString stringWithFormat:@"%@, Pass-Through", jobAudioCodec];-        else-            jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->arate];-        -        // we now get the audio mixdown info for each of the two gui audio tracks-        // lets do it the long way here to get a handle on things.-        // Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i]-        int ai; // counter for each audios [] , macgui only allows for two audio tracks currently-        for( ai = 0; ai < 2; ai++ )-        {-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_MONO)-                jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)-                jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)-                jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII)-                jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_6CH)-                jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet", ai + 1]];-        }-        if (withIcon)   // implies indent the info-            [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-        [finalString appendString: @"Audio: " withAttributes:detailBoldAttribute];-        [finalString appendString:[NSString stringWithFormat:@"%@\n", jobAudioInfo] withAttributes:detailAttribute];-    }-    -    if (withSubtitleInfo)-    {-        // hbJob->subtitle can == -1 in two cases:-        // autoselect: when pass == -1-        // none: when pass != -1-        if ((hbJob->subtitle == -1) && (hbJob->pass == -1))-        {-            if (withIcon)   // implies indent the info-                [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-            [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute];-            [finalString appendString: @"Autoselect " withAttributes:detailAttribute];-        }-        else if (hbJob->subtitle >= 0)-        {-            hb_subtitle_t * subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, 0 );-            if (subtitle)-            {-                if (withIcon)   // implies indent the info-                    [finalString appendString: @"\t" withAttributes:detailBoldAttribute];-                [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute];-                [finalString appendString: [NSString stringWithCString: subtitle->lang] withAttributes:detailAttribute];-            }-        }-    }-    -    -    if ([[finalString string] hasSuffix: @"\n"])-        [finalString deleteCharactersInRange: NSMakeRange([[finalString string] length]-1, 1)];-    -    return finalString;+    return nil; }  @end  #pragma mark - -//-------------------------------------------------------------------------------------// HBJobGroup-//------------------------------------------------------------------------------------+@interface HBQueueController (LibhbInteractions) -@implementation HBJobGroup+- (void) libhbCurrentJobChanged: (hb_job_t *) currentJob;+- (void) libhbAddJobGroup: (HBJobGroup *) aJobGroup;+- (BOOL) libhbAddNextJobGroup; -+ (HBJobGroup *) jobGroup;-{-    return [[[HBJobGroup alloc] init] autorelease];-}--- (id) init-{-    if (self = [super init])-    {-        fJobs = [[NSMutableArray arrayWithCapacity:0] retain];-        fDescription = [[NSMutableAttributedString alloc] initWithString: @""];-        [self setNeedsDescription: NO];-        fStatus = HBStatusNone;-    }-    return self; -}--- (void) dealloc-{-    [fJobs release];-    [fPath release];-    [super dealloc];-}--- (unsigned int) count-{-    return [fJobs count];-}--- (void) addJob: (HBJob *)aJob-{-    [fJobs addObject: aJob];-    [self setNeedsDescription: YES];-    fLastDescriptionHeight = 0;-    fLastDescriptionWidth = 0;-}--- (void) removeAllJobs-{-    [fJobs removeAllObjects];-}--- (HBJob *) jobAtIndex: (unsigned)index-{-    return [fJobs objectAtIndex: index];-}--- (unsigned) indexOfJob: (HBJob *)aJob;-{-    return [fJobs indexOfObject: aJob];-}--- (NSEnumerator *) jobEnumerator-{-    return [fJobs objectEnumerator];-}--- (void) setNeedsDescription: (BOOL)flag-{-    fNeedsDescription = flag;-}--- (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle-{-    fNeedsDescription = NO;--    [fDescription deleteCharactersInRange: NSMakeRange(0, [fDescription length])]; --    if ([self count] == 0)-    {-        NSAssert(NO, @" jobgroup with no jobs");-        return;-    }-    -    HBJob * job = [self jobAtIndex:0];-    -    [fDescription appendAttributedString: [job attributedDescriptionWithHBHandle: handle-                             withIcon: NO-                            withTitle: YES-                         withPassName: NO-                       withFormatInfo: YES-                      withDestination: YES-                      withPictureInfo: NO-                        withVideoInfo: NO-                         withx264Info: NO-                        withAudioInfo: NO-                     withSubtitleInfo: NO]];--    static NSAttributedString * carriageReturn = [[NSAttributedString alloc] initWithString:@"\n"];-    -    NSEnumerator * e = [self jobEnumerator];-    while ( (job = [e nextObject]) )-    {-        int pass = [job job]->pass;-        [fDescription appendAttributedString:carriageReturn];-        [fDescription appendAttributedString:-            [job attributedDescriptionWithHBHandle: handle-                                 withIcon: YES-                                withTitle: NO-                             withPassName: YES-                           withFormatInfo: NO-                          withDestination: NO-                          withPictureInfo: pass != -1-                            withVideoInfo: pass != -1-                             withx264Info: pass != -1-                            withAudioInfo: pass == 0 || pass == 2-                         withSubtitleInfo: YES]];-    }-    -}--- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle-{-    if (fNeedsDescription)-        [self updateDescriptionWithHBHandle: handle];-    return fDescription;-}--- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle-{-    // Try to return the cached value if no changes have happened since the last time-    if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription)-        return fLastDescriptionHeight;-    -    if (fNeedsDescription)-        [self updateDescriptionWithHBHandle: handle];--    // Calculate the height    -    NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];-    fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom-    fLastDescriptionWidth = width;-    return fLastDescriptionHeight;--/* supposedly another way to do this, in case boundingRectWithSize isn't working-    NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)];-    [[tmpView textStorage] setAttributedString:aString];-    [tmpView setHorizontallyResizable:NO];-    [tmpView setVerticallyResizable:YES];-//    [[tmpView textContainer] setHeightTracksTextView: YES];-//    [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)];-    [tmpView sizeToFit];-    float height = [tmpView frame].size.height;-    [tmpView release];-    return height;-*/-}--- (float) lastDescriptionHeight-{-    return fLastDescriptionHeight;-}--- (void) setStatus: (HBQueueJobGroupStatus)status-{-    self->fStatus = status;-}--- (HBQueueJobGroupStatus) status-{-    return self->fStatus;-}--- (void) setPath: (NSString *)path-{-    [path retain];-    [fPath release];-    fPath = path;-}--- (NSString *) path-{-    return fPath;-}- @end - #pragma mark - -@interface HBQueueController (Private)-- (void)updateQueueUI;-@end+//------------------------------------------------------------------------------------+// HBQueueController+//------------------------------------------------------------------------------------  // Toolbar identifiers static NSString*    HBQueueToolbar                            = @"HBQueueToolbar1"; static NSString*    HBQueueStartCancelToolbarIdentifier       = @"HBQueueStartCancelToolbarIdentifier"; static NSString*    HBQueuePauseResumeToolbarIdentifier       = @"HBQueuePauseResumeToolbarIdentifier"; -#pragma mark -- @implementation HBQueueController  //------------------------------------------------------------------------------------@@ -793,11 +137,13 @@             nil]];          fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];-        fCompleted = [[NSMutableArray arrayWithCapacity:0] retain];-+                 BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow;         NSAssert(loadSucceeded, @"Could not open Queue nib");         NSAssert(fQueueWindow, @"fQueueWindow not found in Queue nib");+        +        // Register for HBJobGroup status changes+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobGroupStatusNotification:) name:HBJobGroupStatusNotification object:nil];     }     return self;  }@@ -811,12 +157,14 @@     if ([fQueueWindow delegate] == self)         [fQueueWindow setDelegate:nil];     +    [[NSNotificationCenter defaultCenter] removeObserver:self];+     [fJobGroups release];-    [fCompleted release];     [fCurrentJobGroup release];     [fSavedExpandedItems release];     [fSavedSelectedItems release];-+    [fStopQueueAlert release];+         [super dealloc]; } @@ -841,7 +189,6 @@ //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender {-    [self updateQueueUI];     [fQueueWindow makeKeyAndOrderFront: self];     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; }@@ -892,222 +239,123 @@     fCurrentJobPaneShown = showPane; } +#pragma mark -+#pragma mark Queue Counts+ //-------------------------------------------------------------------------------------// Rebuilds the contents of fJobGroups which is a hierarchy of HBJobGroup and HBJobs.+// Sets a flag indicating that the values for fPendingCount, fCompletedCount,+// fCanceledCount, and fWorkingCount need to be recalculated. //-------------------------------------------------------------------------------------- (void)rebuildJobGroups+- (void) setJobGroupCountsNeedUpdating: (BOOL)flag {-    // Currently, job groups are rdered like this:-    // Completed job groups-    // Current job group-    // Pending job groups-    -    [fJobGroups autorelease];-    fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];+    fJobGroupCountsNeedUpdating = flag;+} -    // Add all the completed job groups-    [fJobGroups addObjectsFromArray: fCompleted];+//------------------------------------------------------------------------------------+// Recalculates and stores new values in fPendingCount, fCompletedCount,+// fCanceledCount, and fWorkingCount.+//------------------------------------------------------------------------------------+- (void) recalculateJobGroupCounts+{+    fPendingCount = 0;+    fCompletedCount = 0;+    fCanceledCount = 0;+    fWorkingCount = 0; -    // Add all the completed job groups-    if (fCurrentJobGroup)-        [fJobGroups addObject: fCurrentJobGroup];--    // Add all the pending job groups. These come from hblib-    HBJobGroup * aJobGroup = [HBJobGroup jobGroup];--    // If hblib is currently processing something, hb_group will skip over that group.-    // And that's exactly what we want -- fJobGroups contains only pending job groups.--    hb_job_t * nextJob = hb_group( fHandle, 0 );-    while( nextJob )+    NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+    HBJobGroup * aJobGroup;+    while ( (aJobGroup = [groupEnum nextObject]) )     {-        if (nextJob->sequence_id == 0)+        switch ([aJobGroup status])         {-            // Encountered a new group. Add the current one to fJobGroups and then start a new one.-            if ([aJobGroup count] > 0)-            {-                [aJobGroup setStatus: HBStatusPending];-                [fJobGroups addObject: aJobGroup];-                aJobGroup = [HBJobGroup jobGroup];-            }+            case HBStatusNone:+                // We don't track these.+                break;+            case HBStatusPending:+                fPendingCount++;+                break;+            case HBStatusCompleted:+                fCompletedCount++;+                break;+            case HBStatusCanceled:+                fCanceledCount++;+                break;+            case HBStatusWorking:+                fWorkingCount++;+                break;         }-        [aJobGroup addJob: [HBJob jobWithJob:nextJob]];-        [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]];-        nextJob = hb_next_job (fHandle, nextJob);     }-    if ([aJobGroup count] > 0)-    {-        [aJobGroup setStatus: HBStatusPending];-        [fJobGroups addObject:aJobGroup];-    }+    fJobGroupCountsNeedUpdating = NO; }  //-------------------------------------------------------------------------------------// Adds aJobGroup to the list of completed job groups, marking its status as-// HBStatusComplete.+// Returns the number of job groups whose status is HBStatusPending. //-------------------------------------------------------------------------------------- (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup+- (unsigned int) pendingCount {-    // Once hblib has completed its work, the hb_job_t objects will be freed, so we-    // can't keep a reference to them.-    [aJobGroup removeAllJobs];-    -    [aJobGroup setStatus: HBStatusComplete];-    -    // Put the group in the completed list for permanent storage, and also rebuild-    // the master job group list which contains completed and pending groups.-    [fCompleted addObject: aJobGroup];+    if (fJobGroupCountsNeedUpdating)+        [self recalculateJobGroupCounts];+    return fPendingCount; } -- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob+//------------------------------------------------------------------------------------+// Returns the number of job groups whose status is HBStatusComplete.+//------------------------------------------------------------------------------------+- (unsigned int) completedCount {-    HBJobGroup * aJobGroup = nil;-    -    // Try to find this new group in our existing job groups.-    if (aJob)-    {-        BOOL found = NO;-        NSEnumerator * groupEnum = [fJobGroups objectEnumerator];-        while ( !found && (aJobGroup = [groupEnum nextObject]) )-        {-            HBJob * j;-            NSEnumerator * jobEnum = [aJobGroup jobEnumerator];-            while ( !found && (j = [jobEnum nextObject]) )-            {-                if ([j job] == aJob)-                    found = YES;-            }-        }-        -        // Or create the job group.-        if (!aJobGroup)-        {-            aJobGroup = [HBJobGroup jobGroup];-            [aJobGroup addJob: [HBJob jobWithJob: aJob]];-            [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]];-            while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) )-                [aJobGroup addJob: [HBJob jobWithJob: aJob]];+    if (fJobGroupCountsNeedUpdating)+        [self recalculateJobGroupCounts];+    return fCompletedCount;+} -            [aJobGroup updateDescriptionWithHBHandle: fHandle];-        }-        -        [aJobGroup setStatus: HBStatusWorking];-    }-    -    [aJobGroup retain];-    [fCurrentJobGroup release];-    fCurrentJobGroup = aJobGroup;+//------------------------------------------------------------------------------------+// Returns the number of job groups whose status is HBStatusCanceled.+//------------------------------------------------------------------------------------+- (unsigned int) canceledCount+{+    if (fJobGroupCountsNeedUpdating)+        [self recalculateJobGroupCounts];+    return fCanceledCount; } +//------------------------------------------------------------------------------------+// Returns the number of job groups whose status is HBStatusWorking.+//------------------------------------------------------------------------------------+- (unsigned int) workingCount+{+    if (fJobGroupCountsNeedUpdating)+        [self recalculateJobGroupCounts];+    return fWorkingCount;+}+ #pragma mark - #pragma mark UI Updating  //-------------------------------------------------------------------------------------// Saves the state of the items that are currently expanded. Calling restoreOutlineViewState-// will restore the state of all items to match what was saved by saveOutlineViewState.+// Marks the icon region of a job group in the queue view as needing display. //-------------------------------------------------------------------------------------- (void) saveOutlineViewState+- (void) updateJobGroupIconInQueue:(HBJobGroup*)aJobGroup {-    if (!fSavedExpandedItems)-        fSavedExpandedItems = [[NSMutableIndexSet alloc] init];-    else-        [fSavedExpandedItems removeAllIndexes];-    -    // NB: This code is stuffing the address of each job into an index set. While it-    // works 99.9% of the time, it's not the ideal solution. We need unique ids in-    // each job, possibly using the existing sequence_id field. Could use the high-    // word as a unique encode id and the low word the sequence number.-    -    HBJobGroup * aJobGroup;-    NSEnumerator * e = [fJobGroups objectEnumerator];-    while ( (aJobGroup = [e nextObject]) )+    int row = [fOutlineView rowForItem: aJobGroup];+    int col = [fOutlineView columnWithIdentifier: @"icon"];+    if (row != -1 && col != -1)     {-        if ([fOutlineView isItemExpanded: aJobGroup])-        {-            if ([aJobGroup status] == HBStatusComplete)-                [fSavedExpandedItems addIndex: (unsigned int)aJobGroup];-            else-                [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]];-        }+        NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];+        [fOutlineView setNeedsDisplayInRect: frame];     }-    -    // Save the selection also. This is really UGLY code. Since I have to rebuild the-    // entire outline hierachy every time hblib changes its job list, there's no easy-    // way for me to remember the selection state other than saving off the first-    // hb_job_t item in each selected group. This is done by saving the object's-    // address. This could go away if I'd save a unique id in each job object.--    if (!fSavedSelectedItems)-        fSavedSelectedItems = [[NSMutableIndexSet alloc] init];-    else-        [fSavedSelectedItems removeAllIndexes];--    NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];-    int row = [selectedRows firstIndex];-    while (row != NSNotFound)-    {-        aJobGroup = [fOutlineView itemAtRow: row];-        if ([aJobGroup status] == HBStatusComplete)-            [fSavedSelectedItems addIndex: (unsigned int)aJobGroup];-        else-            [fSavedSelectedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]];    -        row = [selectedRows indexGreaterThanIndex: row];-    }- }  //-------------------------------------------------------------------------------------// Restores the expanded state of items in the outline view to match those saved by a-// previous call to saveOutlineViewState.+// Marks the entire region of a job group in the queue view as needing display. //-------------------------------------------------------------------------------------- (void) restoreOutlineViewState+- (void) updateJobGroupInQueue:(HBJobGroup*)aJobGroup {-    if (fSavedExpandedItems)+    int row = [fOutlineView rowForItem: aJobGroup];+    if (row != -1)     {-        HBJobGroup * aJobGroup;-        NSEnumerator * e = [fJobGroups objectEnumerator];-        while ( (aJobGroup = [e nextObject]) )-        {-            if ([aJobGroup status] == HBStatusComplete)-            {-                if ([fSavedExpandedItems containsIndex: (unsigned int)aJobGroup])-                    [fOutlineView expandItem: aJobGroup];-            }-            else-            {-                hb_job_t * j = [[aJobGroup jobAtIndex:0] job];-                if ([fSavedExpandedItems containsIndex: (unsigned int)j])-                    [fOutlineView expandItem: aJobGroup];-            }-        }+        NSRect frame = [fOutlineView rectOfRow:row];+        [fOutlineView setNeedsDisplayInRect: frame];     }-    -    if (fSavedSelectedItems)-    {-        // Ugh. Have to cycle through each row looking for the previously selected job.-        // See the explanation in saveOutlineViewState about the logic here.-                -        NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease];-        for (int i = 0; i < [fOutlineView numberOfRows]; i++)-        {-            HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i];-            // Test to see if the group or the group's first job is a match-            if ([aJobGroup status] == HBStatusComplete)-            {-                if ([fSavedSelectedItems containsIndex: (unsigned int)aJobGroup])-                    [rowsToSelect addIndex: i];-            }-            else-            {-                if ([fSavedSelectedItems containsIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]])-                    [rowsToSelect addIndex: i];-            }-        }-        if ([rowsToSelect count] == 0)-            [fOutlineView deselectAll: nil];-        else-            [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO];-    } }  //------------------------------------------------------------------------------------@@ -1116,14 +364,11 @@ //------------------------------------------------------------------------------------ - (void) animateCurrentJobGroupInQueue:(NSTimer*)theTimer {-    int row = [fOutlineView rowForItem: fCurrentJobGroup];-    int col = [fOutlineView columnWithIdentifier: @"icon"];-    if (row != -1 && col != -1)+    if (fCurrentJobGroup)     {         fAnimationIndex++;         fAnimationIndex %= 6;   // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.-        NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];-        [fOutlineView setNeedsDisplayInRect: frame];+        [self updateJobGroupIconInQueue: fCurrentJobGroup];     } } @@ -1156,31 +401,28 @@ }  //-------------------------------------------------------------------------------------// Generate string to display in UI.+// Returns a string expressing the state of the current job.+// Examples:+//  Encoding movie+//  Muxing+//  Done //-------------------------------------------------------------------------------------- (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s+- (NSString *) stateStringForState: (hb_state_t *)s {     if (s->state == HB_STATE_WORKING)     {-        NSString * msg;-        if (job->pass == -1)-            msg = NSLocalizedString( @"Deep Scan", nil );+        hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil;++        if (!job)+            return @"";  // no current job, even though state says working. Means there's a transition from one job to another going on.+        else if (job->pass == -1)+            return NSLocalizedString( @"Deep Scan", nil );         else if (job->pass == 1)-            msg = NSLocalizedString( @"Analyzing video", nil );+            return NSLocalizedString( @"Analyzing video", nil );         else if ((job->pass == 0) ||  (job->pass == 2))-            msg = NSLocalizedString( @"Encoding movie", nil );+            return NSLocalizedString( @"Encoding movie", nil );         else             return @""; // unknown condition!-            -        if( s->param.working.seconds > -1 )-        {-            return [NSString stringWithFormat:-                NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ),-                msg, s->param.working.rate_cur, s->param.working.rate_avg];-        }-        else-            return msg;-     }      else if (s->state == HB_STATE_MUXING)@@ -1196,12 +438,38 @@ }  //-------------------------------------------------------------------------------------// Generate string to display in UI.+// Returns a string expressing the rate of the current job.+// Example:+//  27.55 fps, avg 22.32 fps //-------------------------------------------------------------------------------------- (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s+- (NSString *) rateStringForState: (hb_state_t *)s {     if (s->state == HB_STATE_WORKING)     {+        if( s->param.working.seconds > -1 )+        {+            return [NSString stringWithFormat:+                NSLocalizedString( @"%.2f fps, avg %.2f fps", nil ),+                s->param.working.rate_cur, s->param.working.rate_avg];+        }+        else+            return @"";+    }++    return @"";+}++//------------------------------------------------------------------------------------+// Returns a string representation of the time remaining as specified in a hb_state_t.+// Examples:+//      2 hours 15 minutes remaining+//      1 minute 15 seconds remaining+//      15 seconds remaining+//------------------------------------------------------------------------------------+- (NSString *) timeRemainingStringForState: (hb_state_t *)s+{+    if (s->state == HB_STATE_WORKING)+    {         #define p s->param.working         if (p.seconds < 0)             return @"";@@ -1268,15 +536,34 @@ }  //------------------------------------------------------------------------------------+// Returns a string expressing the status of the current job being processed.+// Examples:+//      Encoding movie (27.55 fps, avg 22.32 fps) - 1 hour 15 minutes remaining+//      Muxing+//      Paused+//      Done+//------------------------------------------------------------------------------------+- (NSString *) statusStringForState: (hb_state_t *)s+{+    NSString * actionStr = [self stateStringForState:s];+    NSString * rateStr = [self rateStringForState:s];+    NSString * timeStr = [self timeRemainingStringForState:s];+    +    NSMutableString * str = [NSMutableString stringWithCapacity:0];+    [str setString:actionStr];+    if ([rateStr length] > 0)+        [str appendFormat:@" (%@)", rateStr];+    if ([timeStr length] > 0)+        [str appendFormat:@" - %@", timeStr];+    return str;+}++//------------------------------------------------------------------------------------ // Refresh progress bar (fProgressTextField) from current state. //-------------------------------------------------------------------------------------- (void) updateProgressTextForJob: (hb_job_t *)job state: (hb_state_t *)s+- (void) updateProgressTextWithState: (hb_state_t *)s {-    NSString * statusMsg = [self progressStatusStringForJob:job state:s];-    NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:s];-    if ([timeMsg length] > 0)-        statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg];-    [fProgressTextField setStringValue:statusMsg];+    [fProgressTextField setStringValue:[self statusStringForState: s]]; }  //------------------------------------------------------------------------------------@@ -1318,13 +605,21 @@ - (void)updateQueueCountField {     NSString * msg;-    int jobCount;-    -    jobCount = fHandle ? hb_group_count(fHandle) : 0;-    if (jobCount == 1)-        msg = NSLocalizedString(@"1 pending encode", nil);+    int jobCount = [fJobGroups count];+    int pendingCount = [self pendingCount];+    if (jobCount == 0)+        msg = NSLocalizedString(@"0 encodes", nil);+    else if ((jobCount == 1) && (pendingCount == 0))+        msg = NSLocalizedString(@"1 encode", nil);+    else if (jobCount == pendingCount)+    {+        if (jobCount == 1)+            msg = NSLocalizedString(@"1 pending encode", nil);+        else+            msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), pendingCount];+    }     else-        msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];+        msg = [NSString stringWithFormat:NSLocalizedString(@"%d encodes (%d pending)", nil), jobCount, pendingCount];      [fQueueCountField setStringValue:msg]; }@@ -1339,7 +634,7 @@      if (job)     {-        HBJob * currentJob = [HBJob jobWithJob: job];+        HBJob * currentJob = [HBJob jobWithLibhbJob: job];         switch (job->pass)         {             case -1:  // Subtitle scan@@ -1401,6 +696,7 @@                              withx264Info: YES                             withAudioInfo: YES                          withSubtitleInfo: YES]];+                break;         }     }     @@ -1414,21 +710,94 @@ //------------------------------------------------------------------------------------ - (void)updateCurrentJobProgress {-    hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil;     hb_state_t s;     hb_get_state2( fHandle, &s );-    [self updateProgressTextForJob: job state: &s];+    [self updateProgressTextWithState:&s];     [self updateProgressBarWithState:&s]; }  //-------------------------------------------------------------------------------------// Refresh the UI in the queue pane. Should be called whenever the content of HB's job-// list has changed so that HBQueueController can sync up.+// Saves the state of the items that are currently expanded and selected. Calling+// restoreOutlineViewState will restore the outline view to the saved state. //-------------------------------------------------------------------------------------- (void)updateQueueUI+- (void) saveOutlineViewState {+    // Save the expanded job groups+    if (!fSavedExpandedItems)+        fSavedExpandedItems = [[NSMutableArray arrayWithCapacity:0] retain];+    else+        [fSavedExpandedItems removeAllObjects];+    +    HBJobGroup * aJobGroup;+    NSEnumerator * e = [fJobGroups objectEnumerator];+    while ( (aJobGroup = [e nextObject]) )+    {+        if ([fOutlineView isItemExpanded: aJobGroup])+            [fSavedExpandedItems addObject:aJobGroup];+    }+    +    // Save the selected job groups+    [fSavedSelectedItems release];+    NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];+    fSavedSelectedItems = [[fJobGroups objectsAtIndexes:selectedRows] retain];+}++//------------------------------------------------------------------------------------+// Restores the expanded state of items in the outline view to match those saved by a+// previous call to saveOutlineViewState.+//------------------------------------------------------------------------------------+- (void) restoreOutlineViewState+{+    // Restore the expanded job groups+    if (fSavedExpandedItems)+    {+        HBJobGroup * aJobGroup;+        NSEnumerator * e = [fJobGroups objectEnumerator];+        while ( (aJobGroup = [e nextObject]) )+        {+            if ([fSavedExpandedItems containsObject: aJobGroup])+                [fOutlineView expandItem: aJobGroup];+        }+        [fSavedExpandedItems release];+        fSavedExpandedItems = nil;+    }+    +    // Restore the selected job groups+    if (fSavedSelectedItems)+    {+        NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease];+        for (int i = 0; i < [fOutlineView numberOfRows]; i++)+        {+            HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i];+            if ([fSavedSelectedItems containsObject: aJobGroup])+                [rowsToSelect addIndex: i];+        }+        if ([rowsToSelect count] == 0)+            [fOutlineView deselectAll: nil];+        else+            [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO];+        [fSavedSelectedItems release];+        fSavedSelectedItems = nil;+    }+}++//------------------------------------------------------------------------------------+// Notifies HBQueuecontroller that the contents of fJobGroups is about to be modified.+// HBQueuecontroller remembers the state of the UI (selection and expanded items).+//------------------------------------------------------------------------------------+- (void) beginEditingJobGroupsArray+{     [self saveOutlineViewState];-    [self rebuildJobGroups];+}++//------------------------------------------------------------------------------------+// Notifies HBQueuecontroller that modifications to fJobGroups as indicated by a prior+// call to beginEditingJobGroupsArray have been completed. HBQueuecontroller reloads+// the queue view and restores the state of the UI (selection and expanded items).+//------------------------------------------------------------------------------------+- (void) endEditingJobGroupsArray+{+    [self setJobGroupCountsNeedUpdating:YES];     [fOutlineView noteNumberOfRowsChanged];     [fOutlineView reloadData];     [self restoreOutlineViewState];    @@ -1436,15 +805,223 @@ }  #pragma mark -++//------------------------------------------------------------------------------------+// Displays a modal sheet window asking the user if they want to stop processing of+// the queue. The didEndSelector method is invoked after the modal session has ended+// and is passed a button return code and the caller specified contextInfo. The button+// return values passed to didEndSelector are:+//   HBKeepEncodingButtonReturn+//   HBStopEncodingButtonReturn+// The didEndSelector method MUST set fStopQueueAlertDisplayed to NO. To dismiss the+// alert prior to the user taking action, call endStopQueueAlert.+//------------------------------------------------------------------------------------+- (void) beginStopQueueAlertModalForWindow:(NSWindow *)docWindow+            didEndSelector:(SEL)didEndSelector+            contextInfo:(void *)contextInfo+{+    NSAssert (didEndSelector != nil, @"A didEndSelector must be specified in beginStopQueueAlertModalForWindow");+    +    // Create fStopQueueAlert the first time around. Subsequently, reuse the alert.+    if (!fStopQueueAlert)+    {+        fStopQueueAlert = [[NSAlert alloc] init];+        [fStopQueueAlert setAlertStyle: NSCriticalAlertStyle];+        [fStopQueueAlert addButtonWithTitle: NSLocalizedString(@"Keep Encoding", nil)];+        [fStopQueueAlert addButtonWithTitle: NSLocalizedString(@"Stop Encoding", nil)];+        [fStopQueueAlert setInformativeText: NSLocalizedString(@"Your partially encoded movie may not be usable if you stop encoding.", nil)];+    }++    [fStopQueueAlert setMessageText: [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil), [fCurrentJobGroup name]]];+    +    if (!fStopQueueAlertDisplayed)+    {+        [fStopQueueAlert beginSheetModalForWindow:docWindow modalDelegate:self didEndSelector:didEndSelector contextInfo:contextInfo];+        fStopQueueAlertDisplayed = YES;+    }+    else+    {+        NSBeep();+        [[fStopQueueAlert window] makeKeyAndOrderFront: nil];+    }+        +}++//------------------------------------------------------------------------------------+// If the alert created by a previous call to beginStopQueueAlertModalForWindow::: is+// currently displayed, it is dismissed. Use this method to abort the alert prior to+// the user taking any action.+//------------------------------------------------------------------------------------+- (void) endStopQueueAlert+{+    if (fStopQueueAlertDisplayed)+    {+        [NSApp endSheet:[fStopQueueAlert window] returnCode: HBKeepEncodingButtonReturn];+        fStopQueueAlertDisplayed = NO;+    }+}++#pragma mark - #pragma mark Actions  //------------------------------------------------------------------------------------+// Starts processing of job groups in the pending list. If processing has already been+// started, nothing happens.+//------------------------------------------------------------------------------------+- (IBAction) startQueue: (id)sender+{+    // hb_start will begin processing. We will get messages from libhb as its state+    // changes.+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    if ((s.state == HB_STATE_IDLE) || (s.state == HB_STATE_WORKDONE))+    {+        if ([self libhbAddNextJobGroup])+        {+            fStopQueueRequested = NO;+            hb_start (fHandle);+        }+    }+}++//------------------------------------------------------------------------------------+// Advices the queue that it should stop processing jobs. If the queue is already+// stopped, nothing happens. Otherwise, the request is scheduled and control is+// returned immediately to the caller. Some time after stopQueue is called, the+// current job group will be canceled (it's state set to HBStatusCanceled) and the+// queue will stop processing any further jobs.+//------------------------------------------------------------------------------------+- (void) stopQueue+{+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING) || (s.state == HB_STATE_PAUSED))+    {+        // Set a flag so that we know this was a request to cancel the current job.+        fStopQueueRequested = YES;+        // Tell libhb to stop. We'll receive a HB_STATE_WORKDONE message when is has+        // finished stopping.+        hb_stop (fHandle);+    }+}++//------------------------------------------------------------------------------------+// Prompts the user to verify that they want the queue to be stopped. If acknowledged,+// stopQueue will be called to cancel the current job group and stop the queue. To+// stop the queue without asking the user, use stopQueue.+//------------------------------------------------------------------------------------+- (IBAction) stopQueue: (id)sender+{+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING) || (s.state == HB_STATE_PAUSED))+    {+        if (fCurrentJobGroup)+        {+            // Which window to attach the sheet to?+            NSWindow * docWindow;+            if ([sender isKindOfClass:[NSWindow class]])+                 docWindow = sender;+           else if ([sender respondsToSelector: @selector(window)])+                docWindow = [sender window];+            else+                docWindow = fQueueWindow;+            +            [self beginStopQueueAlertModalForWindow: docWindow didEndSelector:@selector(didEndStopQueueAlert:returnCode:contextInfo:) contextInfo:nil];+            +            // didEndStopQueueAlert:returnCode:contextInfo: will be called when the dialog is dismissed++        }+        +        // If there wasn't a current job group (which really would be a programmatic+        // error), just call hb_stop directly.+        else+            hb_stop (fHandle);+    }+}++- (void) didEndStopQueueAlert:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo+{+    if (returnCode == HBStopEncodingButtonReturn)+        [self stopQueue];+    fStopQueueAlertDisplayed = NO;+}+++//------------------------------------------------------------------------------------+// Pauses the queue if it is currently active.+//------------------------------------------------------------------------------------+- (IBAction) pauseQueue: (id)sender+{+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))+        hb_pause (fHandle);+}+++//------------------------------------------------------------------------------------+// Resumes the queue if it had previously been paused.+//------------------------------------------------------------------------------------+- (IBAction) resumeQueue: (id)sender+{+    hb_state_t s;+    hb_get_state2(fHandle, &s);+    if (s.state == HB_STATE_PAUSED)+        hb_resume (fHandle);+}+++//------------------------------------------------------------------------------------ // Deletes the selected jobs from HB and the queue UI //-------------------------------------------------------------------------------------- (IBAction)removeSelectedJobGroups: (id)sender+- (IBAction) removeSelectedJobGroups: (id)sender {     if (!fHandle) return;     +    NSIndexSet * selectedRows = [[fOutlineView selectedRowIndexes] retain];+    NSArray * groupsToDelete = [fJobGroups objectsAtIndexes:selectedRows];+    +    // Send a message to cancel processing of the current job, if it is being deleted.+    // The cancel will not happen immediately. didEndStopQueueAndRemoveSelected::::+    // will be called after the user responds to the alert. selectedRows is passed as+    // context info so that didEndStopQueueAndRemoveSelected: knows what to delete.+    if ([groupsToDelete containsObject:fCurrentJobGroup])+    {+        [self beginStopQueueAlertModalForWindow: fQueueWindow didEndSelector:@selector(didEndStopQueueAndRemoveSelected:returnCode:contextInfo:) contextInfo:selectedRows];+    }+    else+    {+        [self beginEditingJobGroupsArray];+        [fJobGroups removeObjectsAtIndexes: selectedRows];+        [selectedRows release];+        [self endEditingJobGroupsArray];+    }+}++- (void) didEndStopQueueAndRemoveSelected:(NSAlert *)alert returnCode: (int)returnCode contextInfo: (void *)contextInfo+{+    if (returnCode == HBStopEncodingButtonReturn)+    {+        [self stopQueue];++        NSIndexSet * selectedRows = (NSIndexSet*)contextInfo;+        [self beginEditingJobGroupsArray];+        [fJobGroups removeObjectsAtIndexes: selectedRows];+        [selectedRows release];+        [self endEditingJobGroupsArray];+    }+    fStopQueueAlertDisplayed = NO;+}+++//------------------------------------------------------------------------------------+// Reveals the file icons in the Finder of the selected job groups.+//------------------------------------------------------------------------------------+- (IBAction) revealSelectedJobGroups: (id)sender+{+    if (!fHandle) return;+         NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];     int row = [selectedRows firstIndex];     if (row != NSNotFound)@@ -1452,34 +1029,18 @@         while (row != NSNotFound)         {             HBJobGroup * jobGroup = [fOutlineView itemAtRow: row];-            switch ([jobGroup status])-            {-                case HBStatusComplete:-                case HBStatusCanceled:-                    [fCompleted removeObject: jobGroup];-                    break;-                case HBStatusWorking:-                    [self cancelCurrentJob: sender];-                    break;-                case HBStatusPending:-                    hb_job_t * job = [[jobGroup jobAtIndex: 0] job];-                    hb_rem_group( fHandle, job );-                    break;-                case HBStatusNone:-                    break;-            }+            if ([[jobGroup path] length])+                [[NSWorkspace sharedWorkspace] selectFile:[jobGroup path] inFileViewerRootedAtPath:nil];                      row = [selectedRows indexGreaterThanIndex: row];         }--        [self hblibJobListChanged];     }  }  //-------------------------------------------------------------------------------------// Reveals the file icons in the Finder of the selected job groups.+// Chnages the status of any selected anc canceled job groups to pending. //-------------------------------------------------------------------------------------- (IBAction)revealSelectedJobGroups: (id)sender+- (IBAction) uncancelSelectedJobGroups: (id)sender {     if (!fHandle) return;     @@ -1490,9 +1051,8 @@         while (row != NSNotFound)         {             HBJobGroup * jobGroup = [fOutlineView itemAtRow: row];-            if ([[jobGroup path] length])-                [[NSWorkspace sharedWorkspace] selectFile:[jobGroup path] inFileViewerRootedAtPath:nil];-        +            if ([jobGroup status] == HBStatusCanceled)+                [jobGroup setStatus: HBStatusPending];             row = [selectedRows indexGreaterThanIndex: row];         }     } @@ -1504,110 +1064,159 @@ // the alert. Later, when the user acknowledges the alert, HBController will call // hblib to cancel the job. //-------------------------------------------------------------------------------------- (IBAction)cancelCurrentJob: (id)sender+// ************ is this necessary? YES, currently however cancelCurrentJob is the same+// as stopQueue. It cancels and stops teh whole queue. At one point, canceling the+// current job did not stop the rest of the queue.+- (IBAction) cancelCurrentJob: (id)sender {-    [fHBController Cancel:sender];+    [self stopQueue: sender]; } +#pragma mark -+ //-------------------------------------------------------------------------------------// Starts or cancels the processing of jobs depending on the current state+// Queues a job group. The job group's status is set to HBStatusPending. //-------------------------------------------------------------------------------------- (IBAction)toggleStartCancel: (id)sender+- (void) addJobGroup: (HBJobGroup *) aJobGroup {-    if (!fHandle) return;+    NSAssert(![fJobGroups containsObject:aJobGroup], @"Duplicate job group");+    [aJobGroup setStatus:HBStatusPending];     -    hb_state_t s;-    hb_get_state2 (fHandle, &s);--    if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))-        [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window--    else if (hb_group_count(fHandle) > 0)-        [fHBController doRip];+    [self beginEditingJobGroupsArray];+    [fJobGroups addObject:aJobGroup];+    [self endEditingJobGroupsArray]; }  //-------------------------------------------------------------------------------------// Toggles the pause/resume state of hblib+// Removes a job from the queue. //-------------------------------------------------------------------------------------- (IBAction)togglePauseResume: (id)sender+- (void) removeJobGroup: (HBJobGroup *) aJobGroup {-    if (!fHandle) return;+    // Removal of the current job being processed is akin to stopping the queue.+    if (aJobGroup == fCurrentJobGroup)+        [self stopQueue];+   +    [self beginEditingJobGroupsArray];+    [fJobGroups removeObject: aJobGroup];+    [self endEditingJobGroupsArray];     -    hb_state_t s;-    hb_get_state2 (fHandle, &s);+} -    if (s.state == HB_STATE_PAUSED)-        hb_resume (fHandle);-    else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))-        hb_pause (fHandle);+//------------------------------------------------------------------------------------+// Returns the job group currently being processed.+//------------------------------------------------------------------------------------+- (HBJobGroup *) currentJobGroup+{+    return fCurrentJobGroup; } -#pragma mark --#pragma mark Synchronizing with hblib - //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's current job has changed+// Returns the job group located at index in the list of job groups with status. If+// index is greater than or equal to the number of job groups with the specified+// status, nil is returned. //-------------------------------------------------------------------------------------- (void)currentJobGroupChanged: (hb_job_t *) currentJob+- (HBJobGroup *) jobGroupWithStatus: (HBQueueJobGroupStatus)status atIndex: (unsigned int)index {-    if (fCurrentJobGroup && [fCurrentJobGroup status] != HBStatusCanceled)-        [self addCompletedJobGroup: fCurrentJobGroup];-    [self setCurrentJobGroupFromJob: currentJob];-    [self updateCurrentJobDescription];-    [self updateCurrentJobProgress];-    [self showCurrentJobPane: fCurrentJobGroup != nil];-    if (fCurrentJobGroup)-        [self startAnimatingCurrentJobGroupInQueue];-    else-        [self stopAnimatingCurrentJobGroupInQueue];+    unsigned int count = 0;+    NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+    HBJobGroup * aJobGroup = nil;+    while ( (aJobGroup = [groupEnum nextObject]) )+    {+        if ([aJobGroup status] == status)+        {+            if (count == index)+                return aJobGroup;+            count++;+       }+    }+    return aJobGroup; } +#pragma mark -+#pragma mark Debugging++#if HB_OUTLINE_METRIC_CONTROLS+static float spacingWidth = 3.0;+- (IBAction)imageSpacingChanged: (id)sender;+{+    spacingWidth = [sender floatValue];+    [fOutlineView setNeedsDisplay: YES];+}+- (IBAction)indentChanged: (id)sender+{+    [fOutlineView setIndentationPerLevel: [sender floatValue]];+    [fOutlineView setNeedsDisplay: YES];+}+#endif++#pragma mark -+ //-------------------------------------------------------------------------------------// Notifies HBQueueController that hb_stop is about to be called. This signals us that-// the current job is going to be canceled and deleted. This is somewhat of a hack to-// let HBQueueController know when a job group has been cancelled. Otherwise, we'd-// have no way of knowing if a job was canceled or completed sucessfully.+// Receives notification whenever an HBJobGroup's status is changed. //-------------------------------------------------------------------------------------- (void)hblibWillStop+- (void) jobGroupStatusNotification:(NSNotification *)notification {-    if (fCurrentJobGroup)-        [fCurrentJobGroup setStatus: HBStatusCanceled];+    [self setJobGroupCountsNeedUpdating: YES];+//    HBQueueJobGroupStatus oldStatus = (HBQueueJobGroupStatus) [[[notification userInfo] objectForKey:@"HBOldJobGroupStatus"] intValue];+    HBJobGroup * jobGroup = [notification object];+    if (jobGroup)+        [self updateJobGroupInQueue:jobGroup]; } +#pragma mark -+ //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's job list has been modified+// Sets a new fCurrentJobGroup by locating the HBJobGroup associated with a libhb+// hb_job_t. //-------------------------------------------------------------------------------------- (void)hblibJobListChanged+- (void) setCurrentJobGroupFromLibhbJob: (hb_job_t *)aJob {-    // This message is received from HBController after it has added a job group to-    // hblib's job list. It is also received from self when a job group is deleted by-    // the user.-    [self updateQueueUI];+    HBJobGroup * aJobGroup = nil;+    +    // Try to find this new group in our existing job groups.+    if (aJob)+    {+        BOOL found = NO;+        NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+        while ( !found && (aJobGroup = [groupEnum nextObject]) )+        {+            HBJob * j;+            NSEnumerator * jobEnum = [aJobGroup jobEnumerator];+            while ( !found && (j = [jobEnum nextObject]) )+            {+                if ([j libhbJob] == aJob)+                    found = YES;+            }+        }+        +        NSAssert (aJobGroup != nil, @"HBQueueController could not find current libhb job.");+        +        [aJobGroup setStatus: HBStatusWorking];+    }+    +    [aJobGroup retain];+    [fCurrentJobGroup release];+    fCurrentJobGroup = aJobGroup; }  //------------------------------------------------------------------------------------ // Notifies HBQueueController that hblib's state has changed //-------------------------------------------------------------------------------------- (void)hblibStateChanged: (hb_state_t &)state+- (void)libhbStateChanged: (hb_state_t &)state {     // First check to see if hblib has moved on to another job. We get no direct     // message when this happens, so we have to detect it ourself. The new job could     // be either just the next job in the current group, or the start of a new group.-    if (fLastKnownCurrentJob != hb_current_job(fHandle))+    // When state is HB_STATE_WORKDONE, this detection is delayed until it is known+    // whether a new job group is going to be started up.+    if (state.state != HB_STATE_WORKDONE)     {         hb_job_t * currentJob = hb_current_job(fHandle);-        if (!currentJob || currentJob->sequence_id == 0)     // start of a new group+        if (fLastKnownLibHBJob != currentJob)         {-            [self currentJobGroupChanged: currentJob];-            [self hblibJobListChanged];+            [self libhbCurrentJobChanged: currentJob];+            fLastKnownLibHBJob = currentJob;         }-        else-        {-            [self updateCurrentJobDescription];-            [self updateCurrentJobProgress];-        }--       fLastKnownCurrentJob = currentJob;     }      switch( state.state )@@ -1635,27 +1244,46 @@         case HB_STATE_WORKDONE:         {             // HB_STATE_WORKDONE means that hblib has finished processing all the jobs-            // in *its* queue. This message is NOT sent as each individual job is+            // in *its* queue. This message is NOT sent as each individual job/pass is             // completed.-        }+ +            // HB_STATE_WORKDONE also happpens as a result of someone calling hb_stop.+            // In this latter case, hb_stop does not clear out the remaining+            // passes/jobs in the queue. We'll do that here.+            +            // Close the "stop queue" alert if the user had caused it to be invoked.+            [self endStopQueueAlert]; -    }+            // Delete all remaining items in libhb's queue.+            hb_job_t * job;+            while( ( job = hb_job(fHandle, 0) ) )+                hb_rem( fHandle, job ); -}+            // Start processing the next job group in our queue -#if HB_OUTLINE_METRIC_CONTROLS-static float spacingWidth = 3.0;-- (IBAction)imageSpacingChanged: (id)sender;-{-    spacingWidth = [sender floatValue];-    [fOutlineView setNeedsDisplay: YES];+            BOOL restartQueue = !fStopQueueRequested && ([self pendingCount] > 0);+            if (restartQueue && [self libhbAddNextJobGroup])+            {+                fStopQueueRequested = NO;+                hb_start(fHandle);+            }+            else+            {                +                [self libhbCurrentJobChanged: nil];+                fLastKnownLibHBJob = nil;+                +                // Notify user. Doing this via performSelectorOnMainThread so that+                // HBController gets a chance to clean up from state HB_STATE_WORKDONE+                // before a possibly modal notification happens in performPostProcessingTasks.+                if (!fStopQueueRequested)+                    [fHBController performSelectorOnMainThread:@selector(performPostProcessingTasks) withObject:nil waitUntilDone:NO];+ +                fStopQueueRequested = NO;+            }+            break;+        }+    } }-- (IBAction)indentChanged: (id)sender-{-    [fOutlineView setIndentationPerLevel: [sender floatValue]];-    [fOutlineView setNeedsDisplay: YES];-}-#endif   #pragma mark -@@ -1702,13 +1330,13 @@         [toolbarItem setLabel: @"Start"];         [toolbarItem setPaletteLabel: @"Start/Cancel"];         -        // Set up a reasonable tooltip, and image+        // Set up a reasonable tooltip, and image. These will change dynamically by validateToolbarItem.         [toolbarItem setToolTip: @"Start Encoding"];         [toolbarItem setImage: [NSImage imageNamed: @"Play"]];-        -        // Tell the item what message to send when it is clicked ++        // Set a default target/action. These will change dynamically by validateToolbarItem.         [toolbarItem setTarget: self];-        [toolbarItem setAction: @selector(toggleStartCancel:)];+        [toolbarItem setAction: @selector(startQueue:)];     }          if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])@@ -1722,10 +1350,10 @@         // Set up a reasonable tooltip, and image         [toolbarItem setToolTip: @"Pause Encoding"];         [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];-        -        // Tell the item what message to send when it is clicked ++        // Set a default target/action. These will change dynamically by validateToolbarItem.         [toolbarItem setTarget: self];-        [toolbarItem setAction: @selector(togglePauseResume:)];+        [toolbarItem setAction: @selector(pauseQueue:)];     }          return toolbarItem;@@ -1764,6 +1392,34 @@         nil]; } +- (BOOL) canStart+{+    if (!fCurrentJobGroup && ([self pendingCount] > 0))+        return YES;+    else+        return NO;+}++- (BOOL) canStop+{+    return (fCurrentJobGroup != nil);+}++- (BOOL) canPause+{+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    return fCurrentJobGroup && ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING));+}++- (BOOL) canResume+{+    hb_state_t s;+    hb_get_state2 (fHandle, &s);+    return (s.state == HB_STATE_PAUSED);+}++ //------------------------------------------------------------------------------------ // validateToolbarItem: //------------------------------------------------------------------------------------@@ -1776,25 +1432,96 @@      BOOL enable = NO; -    hb_state_t s;-    hb_get_state2 (fHandle, &s);+    if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])+    {+        if ([self canStop])+        {+            enable = YES;+            [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];+            [toolbarItem setLabel: @"Stop"];+            [toolbarItem setToolTip: @"Stop Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(stopQueue:)];+        }+        +        else if ([self canStart])+        {+            enable = YES;+            [toolbarItem setImage:[NSImage imageNamed: @"Play"]];+            [toolbarItem setLabel: @"Start"];+            [toolbarItem setToolTip: @"Start Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(startQueue:)];+        }+        +        else+        {+            enable = NO;+            [toolbarItem setImage:[NSImage imageNamed: @"Play"]];+            [toolbarItem setLabel: @"Start"];+            [toolbarItem setToolTip: @"Start Encoding"];+        }+       +    }+    +    if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])+    {+        if ([self canResume])+        {+            enable = YES;+            [toolbarItem setImage:[NSImage imageNamed: @"Play"]];+            [toolbarItem setLabel: @"Resume"];+            [toolbarItem setToolTip: @"Resume Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(resumeQueue:)];+        }+        +        else if ([self canPause])+        {+            enable = YES;+            [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];+            [toolbarItem setLabel: @"Pause"];+            [toolbarItem setToolTip: @"Pause Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(pauseQueue:)];+        }+        +        else+        {+            enable = NO;+            [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];+            [toolbarItem setLabel: @"Pause"];+            [toolbarItem setToolTip: @"Pause Encoding"];+        }+    }+    +    return enable; +++    +    +     if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])     {-        if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))+        if (fCurrentJobGroup)         {             enable = YES;             [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];             [toolbarItem setLabel: @"Stop"];             [toolbarItem setToolTip: @"Stop Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(stopQueue:)];         } -        else if (hb_count(fHandle) > 0)+        else if ([self pendingCount] > 0)         {             enable = YES;             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];             [toolbarItem setLabel: @"Start"];             [toolbarItem setToolTip: @"Start Encoding"];+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(startQueue:)];         }          else@@ -1808,13 +1535,18 @@          if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])     {+        hb_state_t s;+        hb_get_state2 (fHandle, &s);+         if (s.state == HB_STATE_PAUSED)         {             enable = YES;             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];             [toolbarItem setLabel: @"Resume"];             [toolbarItem setToolTip: @"Resume Encoding"];-       }+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(resumeQueue:)];+      }                  else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))         {@@ -1822,7 +1554,9 @@             [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];             [toolbarItem setLabel: @"Pause"];             [toolbarItem setToolTip: @"Pause Encoding"];-        }+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(pauseQueue:)];+       }         else         {             enable = NO;@@ -1982,8 +1716,12 @@                  float width = [[outlineView tableColumnWithIdentifier: @"desc"] width];         // Column width is NOT what is ultimately used-        width -= 47;    // 26 pixels for disclosure triangle, 20 for icon, 1 for intercell spacing-        +        long sysVers;+        Gestalt( gestaltSystemVersion, &sysVers );+        if (sysVers < 0x1050)+            width -= 47;    // 26 pixels for disclosure triangle, 20 for icon, 1 for intercell spacing+        else        +            width -= 1;     // 1 for intercell spacing         float height = [item heightOfDescriptionForWidth: width withHBHandle: fHandle];         return height;     }@@ -2002,12 +1740,15 @@     {         switch ([(HBJobGroup*)item status])         {-            case HBStatusComplete:+            case HBStatusCompleted:                 return [NSImage imageNamed:@"EncodeComplete"];                 break;             case HBStatusWorking:                 return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];                 break;+            case HBStatusCanceled:+                return [NSImage imageNamed:@"EncodeCanceled"];+                break;             default:                 return [NSImage imageNamed:@"JobSmall"];                 break;@@ -2038,7 +1779,7 @@     {         [cell setEnabled: YES];         BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);-        if ([(HBJobGroup*)item status] == HBStatusComplete)+        if ([(HBJobGroup*)item status] == HBStatusCompleted)         {             [cell setAction: @selector(revealSelectedJobGroups:)];             if (highlighted)@@ -2077,7 +1818,7 @@ #pragma mark NSOutlineView delegate (dragging related)  //-------------------------------------------------------------------------------------// NSTableView delegate+// NSOutlineView delegate //------------------------------------------------------------------------------------  #if HB_QUEUE_DRAGGING@@ -2093,13 +1834,14 @@ 	} 	     // Don't retain since this is just holding temporaral drag information, and it is-    //only used during a drag!  We could put this in the pboard actually.+    // only used during a drag! We could put this in the pboard actually.     fDraggedNodes = items; 	     // Provide data for our custom type, and simple NSStrings.     [pboard declareTypes:[NSArray arrayWithObjects: HBQueuePboardType, nil] owner:self]; -    // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.+    // the actual data doesn't matter since DragDropSimplePboardType drags aren't+    // recognized by anyone but us.     [pboard setData:[NSData data] forType:HBQueuePboardType];       return YES;@@ -2122,7 +1864,7 @@ 	}  	// Prevent dragging into the completed or current job.-	int firstPendingIndex = [fCompleted count];+	int firstPendingIndex = [fJobGroups count] - [self pendingCount]; 	if (fCurrentJobGroup) 		firstPendingIndex++; 	index = MAX (index, firstPendingIndex);@@ -2145,14 +1887,125 @@     }      // Rearrange the data and view-    [self saveOutlineViewState];+    [self beginEditingJobGroupsArray];     [self moveObjectsInArray:fJobGroups fromIndexes:moveItems toIndex: index];-    [fOutlineView reloadData];-    [self restoreOutlineViewState];+    [self endEditingJobGroupsArray];              return YES; } #endif +#pragma mark -+#pragma mark HBQueueOutlineViewDelegate +//------------------------------------------------------------------------------------+// HBQueueOutlineViewDelegate+//------------------------------------------------------------------------------------++- (NSMenu *) outlineView:(NSOutlineView *)outlineView menuForTableColumn:(NSTableColumn *)tableColumn item:(id)item+{+    NSIndexSet * selectedRows = [outlineView selectedRowIndexes];+    NSArray * selectedGroups = [fJobGroups objectsAtIndexes:selectedRows];+    HBJobGroup * jobGroup = (HBJobGroup*) item;++    NSMenu * menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];+    +    int numSelected = [[fOutlineView selectedRowIndexes] count];+    if (numSelected == 0)+        return nil;+    +    // If current job group is selected, add a "Stop Encoding" menu item+    if ([selectedGroups containsObject:fCurrentJobGroup])+        [menu addItemWithTitle:[NSString stringWithFormat:@"Stop Encoding  %@", [fCurrentJobGroup name]] action:@selector(cancelCurrentJob:) keyEquivalent:@""];+    +    // Add a "Remove" menu item+    if (numSelected == 1)+        [menu addItemWithTitle:[NSString stringWithFormat:@"Remove %@", [jobGroup name]] action:@selector(removeSelectedJobGroups:) keyEquivalent:@""];+    else+        [menu addItemWithTitle:[NSString stringWithFormat:@"Remove %d Items", numSelected] action:@selector(removeSelectedJobGroups:) keyEquivalent:@""];+    +    // Add a "Show in Finder" menu item+    [menu addItemWithTitle:@"Show in Finder" action:@selector(revealSelectedJobGroups:) keyEquivalent:@""];+    +    // Add a "Try Again" menu item+    if ((numSelected == 1) && ([jobGroup status] == HBStatusCanceled))+        [menu addItemWithTitle:@"Try Again" action:@selector(uncancelSelectedJobGroups:) keyEquivalent:@""];++    return menu;+}+ @end+++#pragma mark -++@implementation HBQueueController (LibhbInteractions)++//------------------------------------------------------------------------------------+// Notifies HBQueueController that hblib's current job has changed+//------------------------------------------------------------------------------------+- (void)libhbCurrentJobChanged: (hb_job_t *) currentJob+{+    if (!currentJob || currentJob->sequence_id == 0)     // start of a new group+    {+        // Here's our only chance to change the current job group's status after it is+        // finished.+        if (fCurrentJobGroup)+        {+            // If the user canceled, set status to canceled, else complete.+            if (fStopQueueRequested)+                [fCurrentJobGroup setStatus: HBStatusCanceled];+            else+                [fCurrentJobGroup setStatus:HBStatusCompleted];+        }+        [self setCurrentJobGroupFromLibhbJob: currentJob];+        [self updateCurrentJobDescription];+        [self updateCurrentJobProgress];+        [self updateQueueCountField];+        [self showCurrentJobPane: fCurrentJobGroup != nil];+        if (fCurrentJobGroup)+            [self startAnimatingCurrentJobGroupInQueue];+        else+            [self stopAnimatingCurrentJobGroupInQueue];+    }+    else+    {+        [self updateCurrentJobDescription];+        [self updateCurrentJobProgress];+    }+}++//------------------------------------------------------------------------------------+// Adds the individual jobs in a job group to libhb's list of jobs to process.+//------------------------------------------------------------------------------------+- (void) libhbAddJobGroup: (HBJobGroup *) aJobGroup+{+    NSEnumerator * jobEnum = [aJobGroup jobEnumerator];+    HBJob * aJob;+    while ( (aJob = [jobEnum nextObject]) )+    {+        // Note that hb_add generates a copy of the libhb job.+        hb_job_t * job_copy = hb_add (fHandle, [aJob libhbJob]);+        [aJob setLibhbJob: job_copy];+    }+}++//------------------------------------------------------------------------------------+// Adds the individual jobs in the first pending job group to libhb's list of jobs to+// process. Return YES if a job group was added, NO if no pending job groups exist.+//------------------------------------------------------------------------------------+- (BOOL) libhbAddNextJobGroup+{+    if ([self pendingCount] > 0)+    {+        HBJobGroup * aJobGroup = [self jobGroupWithStatus: HBStatusPending atIndex: 0];+        [self libhbAddJobGroup: aJobGroup];+        return YES;+    }+    else+        return NO;+}++@end    // HBQueueController (LibhbInteractions)++