All pastes #761699 Raw Edit

Unnamed

public diff v1 · immutable
·published 2007-11-05 02:13 UTC
rendered paste body
Index: libhb/hb.c===================================================================--- libhb/hb.c	(revision 1043)+++ libhb/hb.c	(working copy)@@ -692,11 +692,14 @@ }  /**- * Adds a job to the job list.+ * Allocates and returns a copy of the job. The copy will have a reference count of 1.+ * The caller is responsible for calling hb_job_dereference when the copy is no longer+ * needed.  * @param h Handle to hb_handle_t.  * @param job Handle to hb_job_t.+ * @returns Handle to hb_job_t of the copy.  */-void hb_add( hb_handle_t * h, hb_job_t * job )+hb_job_t * hb_job_copy( hb_job_t * job ) {     hb_job_t      * job_copy;     hb_title_t    * title,    * title_copy;@@ -903,8 +906,8 @@     title_copy->job = job_copy;     job_copy->title = title_copy;     job_copy->file  = strdup( job->file );-    job_copy->h     = h;-    job_copy->pause = h->pause_lock;+    job_copy->h     = NULL;+    job_copy->pause = NULL;      /* Copy the job filter list */     if( job->filters )@@ -919,10 +922,32 @@         }             }     +    job_copy->reference_count = 1;++    return( job_copy );+}++/**+ * Adds a copy of a job to the job list.+ * @param h Handle to hb_handle_t.+ * @param job Handle to hb_job_t.+ * @returns Handle to hb_job_t of the copied job.+ */+hb_job_t * hb_add( hb_handle_t * h, hb_job_t * job )+{+    hb_job_t      * job_copy;++    job_copy = hb_job_copy ( job );+        +    job_copy->h     = h;+    job_copy->pause = h->pause_lock;+     /* Add the job to the list */     hb_list_add( h->jobs, job_copy );     h->job_count = hb_count(h);     h->job_count_permanent++;++    return( job_copy ); }  /**@@ -939,6 +964,7 @@         h->job_count_permanent--;      /* XXX free everything XXX */+    hb_job_dereference( job ); }  /**@@ -1207,3 +1233,31 @@     hb_unlock( h->state_lock );     hb_unlock( h->pause_lock ); }++/*+ * Register an interest in referencing this job, it won't be deleted+ * until this party deregisters its interest in it.+ */+void hb_job_reference( const hb_job_t * job_copy )+{+    hb_job_t * job = (hb_job_t * )job_copy;++    job->reference_count++;+}++void hb_job_dereference( const hb_job_t * job_copy )+{+    hb_job_t * job = (hb_job_t * )job_copy;++    job->reference_count--;++    if( job->reference_count <= 0 )+    {+        hb_log( "Freeing job 0x%x", job );++// travistex has added this call to hb_title_close because it was removed from do_job.+        hb_title_close( &job->title );++        free( job );+    }+}Index: libhb/hb.h===================================================================--- libhb/hb.h	(revision 1043)+++ libhb/hb.h	(working copy)@@ -84,7 +84,8 @@ int           hb_count( hb_handle_t * ); hb_job_t *    hb_job( hb_handle_t *, int ); hb_job_t *    hb_current_job( hb_handle_t * h );-void          hb_add( hb_handle_t *, hb_job_t * );+hb_job_t *    hb_job_copy( hb_job_t * job );+hb_job_t *    hb_add( hb_handle_t *, hb_job_t * ); void          hb_rem( hb_handle_t *, hb_job_t * );  void          hb_start( hb_handle_t * );@@ -105,6 +106,9 @@    Aborts all current jobs if any, frees memory. */ void          hb_close( hb_handle_t ** ); +void hb_job_reference( const hb_job_t * job_copy );+void hb_job_dereference( const hb_job_t * job_copy );+ #ifdef __cplusplus } #endifIndex: libhb/work.c===================================================================--- libhb/work.c	(revision 1043)+++ libhb/work.c	(working copy)@@ -702,8 +702,11 @@      hb_buffer_pool_free(); -    hb_title_close( &job->title );-    free( job );+// travistex is requesting that all code to dealloc a job be moved to hb_job_dereference+// so that all data in the job can continued to be referenced until the job is really+// deallocated. I don't know if any of the other code above falls in this same category.+//    hb_title_close( &job->title );+    hb_job_dereference( job ); }  /**Index: libhb/common.h===================================================================--- libhb/common.h	(revision 1043)+++ libhb/common.h	(working copy)@@ -105,6 +105,12 @@  *****************************************************************************/ struct hb_job_s {+    /*+     * Number of folks interested in this job, only free when equal to+     * 0.+     */+    int reference_count;+     /* ID assigned by UI so it can groups job passes together */     int             sequence_id; 	Index: macosx/HBQueueController.h===================================================================--- macosx/HBQueueController.h	(revision 1043)+++ macosx/HBQueueController.h	(working copy)@@ -9,30 +9,25 @@ #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. //@@ -43,7 +38,7 @@ @interface HBQueueOutlineView : NSOutlineView { #if HB_QUEUE_DRAGGING-BOOL                        fIsDragging;+    BOOL                         fIsDragging; #endif } #if HB_QUEUE_DRAGGING@@ -52,85 +47,23 @@ @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;--@end-+// HBQueueController //------------------------------------------------------------------------------------ -@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--//------------------------------------------------------------------------------------- @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.+    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     @@ -173,16 +106,26 @@  - (void)setHandle: (hb_handle_t *)handle; - (void)setHBController: (HBController *)controller;-- (void)hblibJobListChanged;-- (void)hblibStateChanged: (hb_state_t &)state;-- (void)hblibWillStop;+- (void)libhbStateChanged: (hb_state_t &)state; +- (void) addJobGroup: (HBJobGroup *) aJobGroup;+- (void) removeJobGroup: (HBJobGroup *) aJobGroup;++- (unsigned int) pendingCount;+- (unsigned int) completedCount;+- (unsigned int) canceledCount;+- (unsigned int) workingCount;++- (IBAction) startQueue: (id)sender;+- (IBAction) stopQueue: (id)sender;+- (void)     stopQueue;+- (IBAction) pauseQueue: (id)sender;+- (IBAction) resumeQueue: (id)sender;+ - (IBAction)showQueueWindow: (id)sender; - (IBAction)removeSelectedJobGroups: (id)sender; - (IBAction)revealSelectedJobGroups: (id)sender; - (IBAction)cancelCurrentJob: (id)sender;-- (IBAction)toggleStartCancel: (id)sender;-- (IBAction)togglePauseResume: (id)sender;  #if HB_OUTLINE_METRIC_CONTROLS - (IBAction)imageSpacingChanged: (id)sender;Index: macosx/Controller.mm===================================================================--- macosx/Controller.mm	(revision 1043)+++ 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@@ -584,7 +586,7 @@ 			             // Has current job changed? That means the queue has probably changed as 			// well so update it-            [fQueueController hblibStateChanged: s];+            [fQueueController libhbStateChanged: s];                          break;         }@@ -608,7 +610,7 @@             [self UpdateDockIcon: 1.0]; 			 			// Pass along the info to HBQueueController-            [fQueueController hblibStateChanged: s];+            [fQueueController libhbStateChanged: s]; 			             break;         }@@ -618,122 +620,40 @@ 		    [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;         }     }@@ -917,7 +837,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"];@@ -1018,9 +938,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 @@ -1028,6 +948,7 @@                clickContext:nil]; } + #pragma mark - #pragma mark Get New Source @@ -1257,7 +1178,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;@@ -1458,55 +1378,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;@@ -1547,8 +1429,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@@ -1572,8 +1457,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 @@ -1582,42 +1470,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;     } @@ -1640,20 +1577,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];     } } @@ -1667,155 +1604,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 1043)+++ 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 */; };@@ -215,7 +220,7 @@ /* Begin PBXFileReference section */ 		089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 		0D096DFF0B707D1200A845D4 /* libhb.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libhb.dylib; sourceTree = BUILT_PRODUCTS_DIR; };-		0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; };+		0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; }; 		0DF377970B7BF99A00115CB0 /* fakexcode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = fakexcode.cpp; path = ../test/fakexcode.cpp; sourceTree = SOURCE_ROOT; }; 		0DFA5C7A0B8DD1E90020BC09 /* HandBrake.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = HandBrake.icns; sourceTree = "<group>"; }; 		0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = declpcm.c; path = ../libhb/declpcm.c; sourceTree = SOURCE_ROOT; };@@ -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; 		};Index: macosx/Controller.h===================================================================--- macosx/Controller.h	(revision 1043)+++ macosx/Controller.h	(working copy)@@ -172,9 +172,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;@@ -208,8 +205,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;@@ -217,26 +212,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;@@ -305,5 +286,6 @@ - (void)setupToolbar;  - (void) remindUserOfSleepOrShutdown;+- (void) performPostProcessingTasks; @end Index: macosx/HBQueueController.mm===================================================================--- macosx/HBQueueController.mm	(revision 1043)+++ macosx/HBQueueController.mm	(working copy)@@ -4,9 +4,11 @@     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 @@ -15,27 +17,9 @@   //-------------------------------------------------------------------------------------// NSMutableAttributedString (HBAdditions)+// HBQueueOutlineView //------------------------------------------------------------------------------------ -@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 --//------------------------------------------------------------------------------------- @implementation HBQueueOutlineView  - (void)viewDidEndLiveResize@@ -81,701 +65,27 @@  @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)    -{-    hb_job_t * job;-    int count = 0;-    int index = 0;-    while( ( job = hb_job( h, index++ ) ) )-    {-        if (job->sequence_id == 0)-            count++;-    }-    return count;-}+@interface HBQueueController (LibhbInteractions) -/**- * 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;-}+- (void) libhbCurrentJobChanged: (hb_job_t *) currentJob;+- (void) libhbAddJobGroup: (HBJobGroup *) aJobGroup;+- (BOOL) libhbAddNextJobGroup; -/**- * 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];-    -    hb_title_t * title = hbJob->title;-    -    // Attributes-    static NSMutableParagraphStyle * ps = NULL;-    if (!ps)-    {-        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]];-    }--    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;-}- @end  #pragma mark -  //-------------------------------------------------------------------------------------// HBJobGroup+// HBQueueController //------------------------------------------------------------------------------------ -@implementation HBJobGroup--+ (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- // Toolbar identifiers static NSString*    HBQueueToolbar                            = @"HBQueueToolbar1"; static NSString*    HBQueueStartCancelToolbarIdentifier       = @"HBQueueStartCancelToolbarIdentifier"; static NSString*    HBQueuePauseResumeToolbarIdentifier       = @"HBQueuePauseResumeToolbarIdentifier"; -#pragma mark -- @implementation HBQueueController  //------------------------------------------------------------------------------------@@ -793,8 +103,7 @@             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");@@ -812,7 +121,6 @@         [fQueueWindow setDelegate:nil];          [fJobGroups release];-    [fCompleted release];     [fCurrentJobGroup release];     [fSavedExpandedItems release];     [fSavedSelectedItems release];@@ -841,7 +149,6 @@ //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender {-    [self updateQueueUI];     [fQueueWindow makeKeyAndOrderFront: self];     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; }@@ -892,75 +199,8 @@     fCurrentJobPaneShown = showPane; } -//-------------------------------------------------------------------------------------// Rebuilds the contents of fJobGroups which is a hierarchy of HBJobGroup and HBJobs.-//-------------------------------------------------------------------------------------- (void)rebuildJobGroups+- (void) setCurrentJobGroupFromLibhbJob: (hb_job_t *)aJob {-    // Currently, job groups are rdered like this:-    // Completed job groups-    // Current job group-    // Pending job groups-    -    [fJobGroups autorelease];-    fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];--    // Add all the completed job groups-    [fJobGroups addObjectsFromArray: fCompleted];--    // 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 )-    {-        if (nextJob->sequence_id == 0)-        {-            // 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];-            }-        }-        [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];-    }-}--//-------------------------------------------------------------------------------------// Adds aJobGroup to the list of completed job groups, marking its status as-// HBStatusComplete.-//-------------------------------------------------------------------------------------- (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup-{-    // 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];-}--- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob-{     HBJobGroup * aJobGroup = nil;          // Try to find this new group in our existing job groups.@@ -974,7 +214,7 @@             NSEnumerator * jobEnum = [aJobGroup jobEnumerator];             while ( !found && (j = [jobEnum nextObject]) )             {-                if ([j job] == aJob)+                if ([j libhbJob] == aJob)                     found = YES;             }         }@@ -982,13 +222,16 @@         // Or create the job group.         if (!aJobGroup)         {+            NSAssert (NO, @"HBQueueController could not find current libhb job.");+/*             aJobGroup = [HBJobGroup jobGroup];-            [aJobGroup addJob: [HBJob jobWithJob: aJob]];+            [aJobGroup addJob: [HBJob jobWithLibhbJob: aJob]];             [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]];             while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) )-                [aJobGroup addJob: [HBJob jobWithJob: aJob]];+                [aJobGroup addJob: [HBJob jobWithLibhbJob: aJob]];              [aJobGroup updateDescriptionWithHBHandle: fHandle];+*/         }                  [aJobGroup setStatus: HBStatusWorking];@@ -1003,111 +246,30 @@ #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 +278,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];     } } @@ -1163,7 +322,9 @@     if (s->state == HB_STATE_WORKING)     {         NSString * msg;-        if (job->pass == -1)+        if (!job)+            msg = @"";  // 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)             msg = NSLocalizedString( @"Deep Scan", nil );         else if (job->pass == 1)             msg = NSLocalizedString( @"Analyzing video", nil );@@ -1180,7 +341,6 @@         }         else             return msg;-     }      else if (s->state == HB_STATE_MUXING)@@ -1318,13 +478,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 +507,7 @@      if (job)     {-        HBJob * currentJob = [HBJob jobWithJob: job];+        HBJob * currentJob = [HBJob jobWithLibhbJob: job];         switch (job->pass)         {             case -1:  // Subtitle scan@@ -1422,13 +590,86 @@ }  //-------------------------------------------------------------------------------------// 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+{     [fOutlineView noteNumberOfRowsChanged];     [fOutlineView reloadData];     [self restoreOutlineViewState];    @@ -1436,46 +677,186 @@ }  #pragma mark -++//------------------------------------------------------------------------------------+// Displays a modal sheet window asking the user if they want to cancel processing of+// the queue. If the didEndSelector method is specified, it is invoked after the modal+// session has ended and is passed a return code and caller specified in contextInfo.+//------------------------------------------------------------------------------------+- (void) beginStopQueueAlertModalForWindow:(NSWindow *)docWindow+            didEndSelector:(SEL)didEndSelector+            contextInfo:(void *)contextInfo+{+    NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),+            [fCurrentJobGroup name]];+    +    NSBeginCriticalAlertSheet(+            alertTitle,+            NSLocalizedString(@"Keep Encoding", nil),+            nil, //NSLocalizedString(@"Delete All", nil),+            NSLocalizedString(@"Stop Encoding", nil),+            docWindow, self,+            nil, didEndSelector, contextInfo,+            NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil),+            [fCurrentJobGroup name]);+}++#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);+        }+    }+}++//------------------------------------------------------------------------------------+// Stops processing of job groups in the queue. The current job group will be canceled+// and its status set to HBStatusCanceled. This call returns immediately after+// scheduling the stop.+//------------------------------------------------------------------------------------+- (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))+    {+        // Mark the current job group as canceled. Otherwise, we'd have no way of+        // knowing if the job group was canceled or completed sucessfully.+        //  ************** This may not be needed anymore because there is now the+        // fStopQueueRequested flag which is used in libhbCurrentJobChanged to+        // determine if a job was canceled or not.+        if (fCurrentJobGroup)+            [fCurrentJobGroup setStatus: HBStatusCanceled];+        +        // Tell libhb to stop. We'll receive a HB_STATE_WORKDONE message when is has+        // finished stopping.+        fStopQueueRequested = YES;+        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(didDimissStopQueueAlert:returnCode:contextInfo:) contextInfo:nil];+            +            // didDimissStopQueueAlert: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) didDimissStopQueueAlert: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo+{+    if (returnCode == NSAlertOtherReturn)+        [self stopQueue];+}+++//------------------------------------------------------------------------------------+// 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 {     if (!fHandle) return;     -    NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];-    int row = [selectedRows firstIndex];-    if (row != NSNotFound)+    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. stopQueueAndRemoveSelected:::: will be+    // called after the user responds to the alert. selectedRows is passed as content+    // info so that stopQueueAndRemoveSelected: knows wht to delete.+    if ([groupsToDelete containsObject:fCurrentJobGroup])     {-        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;-            }-        -            row = [selectedRows indexGreaterThanIndex: row];-        }+        [self beginStopQueueAlertModalForWindow: fQueueWindow didEndSelector:@selector(didDismissStopQueueAndRemoveSelected:returnCode:contextInfo:) contextInfo:selectedRows];+        return;+    }+    +    [self beginEditingJobGroupsArray];+    [fJobGroups removeObjectsAtIndexes: selectedRows];+    [selectedRows release];+    [self endEditingJobGroupsArray];+} -        [self hblibJobListChanged];-    } +- (void) didDismissStopQueueAndRemoveSelected: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo+{+    if (returnCode == NSAlertOtherReturn)+    {+        [self stopQueue];++        NSIndexSet * selectedRows = (NSIndexSet*)contextInfo;+        [self beginEditingJobGroupsArray];+        [fJobGroups removeObjectsAtIndexes: selectedRows];+        [selectedRows release];+        [self endEditingJobGroupsArray];+    } } + //------------------------------------------------------------------------------------ // Reveals the file icons in the Finder of the selected job groups. //------------------------------------------------------------------------------------@@ -1504,110 +885,150 @@ // the alert. Later, when the user acknowledges the alert, HBController will call // hblib to cancel the job. //------------------------------------------------------------------------------------+// ************ is this necessary?  - (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 number of job groups with a specified status.+//------------------------------------------------------------------------------------+- (unsigned int) jopGroupCountWithStatus: (HBQueueJobGroupStatus) status+{+    unsigned int count = 0;+    NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+    HBJobGroup * aJobGroup;+    while ( (aJobGroup = [groupEnum nextObject]) )+    {+        if ([aJobGroup status] == status)+            count++;+    }+    return count; } -#pragma mark --#pragma mark Synchronizing with hblib +//------------------------------------------------------------------------------------+// Returns the job group located at index in the list of job groups with status. If+// index is greater than or equal to the value returned by jopGroupCountWithStatus:,+// nil is retunred.+//------------------------------------------------------------------------------------+- (HBJobGroup *) jobGroupWithStatus: (HBQueueJobGroupStatus)status atIndex: (unsigned int)index+{+    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;+}  //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's current job has changed+// Returns the number of job groups whose status is HBStatusPending. //-------------------------------------------------------------------------------------- (void)currentJobGroupChanged: (hb_job_t *) currentJob+- (unsigned int) pendingCount {-    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];+    // This value is calculated each time, not cached.+    return [self jopGroupCountWithStatus: HBStatusPending]; }  //-------------------------------------------------------------------------------------// 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.+// Returns the number of job groups whose status is HBStatusComplete. //-------------------------------------------------------------------------------------- (void)hblibWillStop+- (unsigned int) completedCount {-    if (fCurrentJobGroup)-        [fCurrentJobGroup setStatus: HBStatusCanceled];+    // This value is calculated each time, not cached.+    return [self jopGroupCountWithStatus: HBStatusComplete]; }  //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's job list has been modified+// Returns the number of job groups whose status is HBStatusCanceled. //-------------------------------------------------------------------------------------- (void)hblibJobListChanged+- (unsigned int) canceledCount {-    // 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];+    // This value is calculated each time, not cached.+    return [self jopGroupCountWithStatus: HBStatusCanceled]; }  //------------------------------------------------------------------------------------+// Returns the number of job groups whose status is HBStatusWorking.+//------------------------------------------------------------------------------------+- (unsigned int) workingCount+{+    // This value is calculated each time, not cached.+    return [self jopGroupCountWithStatus: HBStatusWorking];+}++#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 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,29 +1056,44 @@         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.+            +            // 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 +            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;+            }+        }+    } } -#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 - #pragma mark Toolbar @@ -1702,13 +1138,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 +1158,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;@@ -1776,25 +1212,26 @@      BOOL enable = NO; -    hb_state_t s;-    hb_get_state2 (fHandle, &s);-     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 +1245,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 +1264,9 @@             [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];             [toolbarItem setLabel: @"Pause"];             [toolbarItem setToolTip: @"Pause Encoding"];-        }+            [toolbarItem setTarget: self];+            [toolbarItem setAction: @selector(pauseQueue:)];+       }         else         {             enable = NO;@@ -2008,6 +1452,9 @@             case HBStatusWorking:                 return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];                 break;+            case HBStatusCanceled:+                return [NSImage imageNamed:@"EncodeCanceled"];+                break;             default:                 return [NSImage imageNamed:@"JobSmall"];                 break;@@ -2093,13 +1540,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 +1570,7 @@ 	}  	// Prevent dragging into the completed or current job.-	int firstPendingIndex = [fCompleted count];+	int firstPendingIndex = [self completedCount]; 	if (fCurrentJobGroup) 		firstPendingIndex++; 	index = MAX (index, firstPendingIndex);@@ -2145,10 +1593,9 @@     }      // Rearrange the data and view-    [self saveOutlineViewState];+    [self beginEditingJobGroupsArray];     [self moveObjectsInArray:fJobGroups fromIndexes:moveItems toIndex: index];-    [fOutlineView reloadData];-    [self restoreOutlineViewState];+    [self endEditingJobGroupsArray];              return YES; }@@ -2156,3 +1603,84 @@   @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 user canceled, reset the current job group back to pending so it+            // is in the pendng queue.+            if (fStopQueueRequested)+            {+                [fCurrentJobGroup setStatus: HBStatusPending];+                [self updateJobGroupInQueue: fCurrentJobGroup];+            }+            else+            {+                [fCurrentJobGroup setStatus:HBStatusComplete];+                [self updateJobGroupInQueue: fCurrentJobGroup];+            }+        }+        [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)++