All pastes #819606 Raw Edit

Removed job references from queu

public diff v1 · immutable
·published 2007-12-17 02:21 UTC
rendered paste body
Index: libhb/hb.c===================================================================--- libhb/hb.c	(revision 1129)+++ libhb/hb.c	(working copy)@@ -964,6 +964,7 @@     p.hours     = -1;     p.minutes   = -1;     p.seconds   = -1;+    p.sequence_id = 0; #undef p     hb_unlock( h->state_lock ); @@ -1203,6 +1204,12 @@         h->state.param.working.job_cur =             h->job_count_permanent - hb_list_count( h->jobs );         h->state.param.working.job_count = h->job_count_permanent;+        +        // Set which job is being worked on+        if (h->current_job)+            h->state.param.working.sequence_id = h->current_job->sequence_id;+        else+            h->state.param.working.sequence_id = 0;     }     hb_unlock( h->state_lock );     hb_unlock( h->pause_lock );Index: libhb/hb.h===================================================================--- libhb/hb.h	(revision 1129)+++ libhb/hb.h	(working copy)@@ -83,7 +83,6 @@ /* Handling jobs */ 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 * ); void          hb_rem( hb_handle_t *, hb_job_t * ); Index: libhb/common.h===================================================================--- libhb/common.h	(revision 1129)+++ libhb/common.h	(working copy)@@ -462,6 +462,7 @@             int   hours;             int   minutes;             int   seconds;+            int   sequence_id;         } working;          structIndex: macosx/HBQueueController.h===================================================================--- macosx/HBQueueController.h	(revision 1129)+++ macosx/HBQueueController.h	(working copy)@@ -9,10 +9,19 @@ #include "hb.h"  @class HBController;+@class HBJob;+@class HBJobGroup;  #define HB_QUEUE_DRAGGING 0             // <--- NOT COMPLETELY FUNCTIONAL YET #define HB_OUTLINE_METRIC_CONTROLS 1    // for tweaking the outline cell spacings +// hb_job_t contains a sequence_id field. The high word is a unique job group id.+// The low word contains the "sequence id" which is a value starting at 0 and+// incremented for each pass in the job group. Use the function below to create and+// interpret a sequence_id field.+int MakeJobID(int jobGroupID, int sequenceNum);+bool IsFirstPass(int jobID);+ typedef enum _HBQueueJobGroupStatus {     HBStatusNone          = 0,@@ -52,17 +61,66 @@ @end  //------------------------------------------------------------------------------------+// HBJob is the UI's equivalent to libhb's hb_job_t struct. It is used mainly for+// drawing the job's description. HBJob are referred to in the UI as 'passes'.+//------------------------------------------------------------------------------------  @interface HBJob : NSObject {-    hb_job_t                     *hbJob;+    HBJobGroup                   *jobGroup;+    +    // The following fields match up with similar fields found in hb_job_t and it's+    // various substructures.+@public+    // from hb_job_s+    int                         sequence_id;        // This is how we xref to the jobs inside libhb++    int                         chapter_start;+    int                         chapter_end;+    int                         chapter_markers;+    int                         crop[4];+    int                         deinterlace;+    int                         width;+    int                         height;+    int                         keep_ratio;+    int                         grayscale;+    int                         pixel_ratio;+    int                         pixel_aspect_width;+    int                         pixel_aspect_height;+    int                         vcodec;+    float                       vquality;+    int                         vbitrate;+    int                         vrate;+    int                         vrate_base;+    int                         pass;+    int                         h264_level;+    int                         crf;+    NSString                    *x264opts;++    int                         audio_mixdowns[8];+    int                         acodec;+    int                         abitrate;+    int                         arate;+    int                         subtitle;++    int                         mux;+    NSString                    *file;++    // from hb_title_s+    NSString                    *titleName;+    int                         titleIndex;+    int                         titleWidth;+    int                         titleHeight;++    // from hb_subtitle_s+    NSString                    *subtitleLang; }  + (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+- (HBJobGroup *)       jobGroup;+- (void)               setJobGroup: (HBJobGroup *)aJobGroup;+- (NSMutableAttributedString *) attributedDescriptionWithIcon: (BOOL)withIcon                               withTitle: (BOOL)withTitle                            withPassName: (BOOL)withPassName                          withFormatInfo: (BOOL)withFormatInfo@@ -76,6 +134,11 @@ @end  //------------------------------------------------------------------------------------+// HBJobGroup is what's referred to in the UI as an 'encode'. A job group contains+// multiple HBJobs, one for each 'pass' of the encode. Whereas libhb keeps a simple+// list of jobs in it's queue, the queue controller presents them to the user as a+// series of encodes and passes (HBJObGroups and HBJobs).+//------------------------------------------------------------------------------------  @interface HBJobGroup : NSObject {@@ -85,7 +148,6 @@     float                        fLastDescriptionHeight;     float                        fLastDescriptionWidth;     HBQueueJobGroupStatus        fStatus;-    NSString                     *fPath; }  // Creating a job group@@ -94,23 +156,20 @@ // 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;+- (unsigned int)       indexOfJob: (HBJob *)aJob; - (NSEnumerator *)     jobEnumerator; - (void)               setStatus: (HBQueueJobGroupStatus)status; - (HBQueueJobGroupStatus)  status;-- (void)               setPath: (NSString *)path; - (NSString *)         path;+- (NSString *)         name;  // Creating a description - (void)               setNeedsDescription: (BOOL)flag;-- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle;-- (float)              heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle;+- (NSMutableAttributedString *) attributedDescription;+- (float)              heightOfDescriptionForWidth:(float)width; - (float)              lastDescriptionHeight;  @end@@ -122,15 +181,16 @@     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+    HBJobGroup                   *fCurrentJobGroup;     // the HJobGroup currently being processed by hblib+    HBJob                        *fCurrentJob;          // the HJob (pass) currently being processed by hblib+    int                          fCurrentJobID;         // this is how we track when hbib has started processing a different job. This is the job's sequence_id.     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 #if HB_QUEUE_DRAGGING     NSArray                      *fDraggedNodes; #endif-    NSMutableArray               *fCompleted;           // HBJobGroups that have been completed. These also appear in fJobGroups.+    NSMutableArray               *fCompleted;           // HBJobGroups that libhb has finihsed with, whether successfully encoded or canceled by the user. 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     @@ -177,6 +237,9 @@ - (void)hblibStateChanged: (hb_state_t &)state; - (void)hblibWillStop; +- (HBJobGroup *) currentJobGroup;+- (HBJob *) currentJob;+ - (IBAction)showQueueWindow: (id)sender; - (IBAction)removeSelectedJobGroups: (id)sender; - (IBAction)revealSelectedJobGroups: (id)sender;Index: macosx/Controller.mm===================================================================--- macosx/Controller.mm	(revision 1129)+++ macosx/Controller.mm	(working copy)@@ -172,15 +172,14 @@     // Warn if encoding a movie     hb_state_t s;     hb_get_state( fHandle, &s );-    hb_job_t * job = hb_current_job( fHandle );-    if ( job && ( s.state != HB_STATE_IDLE ) )+    HBJobGroup * jobGroup = [fQueueController currentJobGroup];+    if ( jobGroup && ( s.state != HB_STATE_IDLE ) )     {-        hb_job_t * job = hb_current_job( fHandle );         int result = NSRunCriticalAlertPanel(                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),                 NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil),                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil,-                job ? [NSString stringWithUTF8String:job->title->name] : @"A movie" );+                jobGroup ? [jobGroup name] : @"A movie" );                  if (result == NSAlertDefaultReturn)         {@@ -655,23 +654,11 @@             // 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.+            // Delete all remaining jobs of this encode.             hb_job_t * job;-            while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) )+            while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) )                 hb_rem( fHandle, job ); -            // Start processing back up if jobs still left in queue-            if (hb_count(fHandle) > 0)-            {-                hb_start(fHandle);-                fEncodeState = 1;-                // Validate the toolbar (hack). The toolbar will usually get autovalidated-                // before we had the chance to restart the queue, hence it will now be in-                // the wrong state.-                [toolbar validateVisibleItems];-                break;-            }-             [fStatusField setStringValue: _( @"Done." )];             [fRipIndicator setIndeterminate: NO];             [fRipIndicator setDoubleValue: 0.0];@@ -1658,10 +1645,15 @@     hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );     hb_job_t * job = title->job; -    // Assign a sequence number, starting at zero, to each job added so they can-    // be lumped together in the UI.-    job->sequence_id = -1;-+    // Assign a unique job group ID for all passes of the same encode. This is how the+    // UI lumps together jobs to form encodes. libhb does not use this id.+    static int jobGroupID = 0;+    jobGroupID++;+    +    // A sequence number, starting at zero, is also used to identifiy to each pass.+    // This is used by the UI to determine if a pass if the first pass of an encode.+    int sequenceNum = -1;+         [self prepareJob];      /* Destination file */@@ -1697,7 +1689,7 @@         /*         * Add the pre-scan job         */-        job->sequence_id++; // for job grouping+        job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);         hb_add( fHandle, job );          job->x264opts = x264opts_tmp;@@ -1722,11 +1714,11 @@         job->select_subtitle = NULL;                  job->pass = 1;-        job->sequence_id++; // for job grouping+        job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);         hb_add( fHandle, job );          job->pass = 2;-        job->sequence_id++; // for job grouping+        job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);          job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */           strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);@@ -1739,7 +1731,7 @@     {         job->indepth_scan = 0;         job->pass = 0;-        job->sequence_id++; // for job grouping+        job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);         hb_add( fHandle, job );     } 	@@ -1881,7 +1873,8 @@ }  //-------------------------------------------------------------------------------------// Cancels the current job and proceeds with the next one in the queue.+// Cancels and deletes the current job and stops libhb from processing the remaining+// encodes. //------------------------------------------------------------------------------------ - (void) doCancelCurrentJob {@@ -1906,11 +1899,11 @@ {     if (!fHandle) return;     -    hb_job_t * job = hb_current_job(fHandle);-    if (!job) return;+    HBJobGroup * jobGroup = [fQueueController currentJobGroup];+    if (!jobGroup) return; -    NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),-            [NSString stringWithUTF8String:job->title->name]];+    NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil),+            [jobGroup name]];          // Which window to attach the sheet to?     NSWindow * docWindow;@@ -1922,32 +1915,19 @@     NSBeginCriticalAlertSheet(             alertTitle,             NSLocalizedString(@"Keep Encoding", nil),-            NSLocalizedString(@"Delete All", nil),+            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]);+            NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));          // 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];-    }+        [self doCancelCurrentJob];  // <- this also stops libhb }  Index: macosx/HandBrake.xcodeproj/project.pbxproj===================================================================--- macosx/HandBrake.xcodeproj/project.pbxproj	(revision 1129)+++ macosx/HandBrake.xcodeproj/project.pbxproj	(working copy)@@ -165,6 +165,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 */; };+		E3FC10910D1611EC00470E7B /* EncodeCanceled.png in Resources */ = {isa = PBXBuildFile; fileRef = E3FC10900D1611EC00470E7B /* 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 */; };@@ -343,6 +344,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>"; };+		E3FC10900D1611EC00470E7B /* 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; };@@ -599,6 +601,7 @@ 				E3C844F20CA6B3F90013B683 /* RevealPressed.png */, 				E3C844F40CA6B3F90013B683 /* RevealHighlight.png */, 				E3C844F30CA6B3F90013B683 /* RevealHighlightPressed.png */,+				E3FC10900D1611EC00470E7B /* EncodeCanceled.png */, 				E3C845860CA6E9080013B683 /* EncodeComplete.png */, 				E3997A290CAB58BC00287239 /* EncodeWorking0.png */, 				E3997A280CAB58BC00287239 /* EncodeWorking1.png */,@@ -823,6 +826,7 @@ 				E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */, 				E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */, 				E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */,+				E3FC10910D1611EC00470E7B /* EncodeCanceled.png in Resources */, 			); 			runOnlyForDeploymentPostprocessing = 0; 		};Index: macosx/HBQueueController.mm===================================================================--- macosx/HBQueueController.mm	(revision 1129)+++ macosx/HBQueueController.mm	(working copy)@@ -13,7 +13,20 @@ // Pasteboard type for or drag operations #define HBQueuePboardType            @"HBQueuePboardType" +//------------------------------------------------------------------------------------+// Job ID Utilities+//------------------------------------------------------------------------------------ +int MakeJobID(int jobGroupID, int sequenceNum)+{+    return jobGroupID<<16 | sequenceNum;+}++bool IsFirstPass(int jobID)+{+    return LoWord(jobID) == 0;+}+ //------------------------------------------------------------------------------------ // NSMutableAttributedString (HBAdditions) //------------------------------------------------------------------------------------@@ -100,7 +113,7 @@     int index = 0;     while( ( job = hb_job( h, index++ ) ) )     {-        if (job->sequence_id == 0)+        if (IsFirstPass(job->sequence_id))             count++;     }     return count;@@ -119,7 +132,7 @@     int index = 0;     while( ( job = hb_job( h, index++ ) ) )     {-        if (job->sequence_id == 0)+        if (IsFirstPass(job->sequence_id))         {             if (count == i)                 return job;@@ -145,7 +158,7 @@         {             // Delete this job plus the following ones in the sequence             hb_rem( h, job );-            while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )+            while( ( j = hb_job( h, index ) ) && ( !IsFirstPass(j->sequence_id ) ) )                 hb_rem( h, j );             return;         }@@ -189,23 +202,80 @@ {     if (self = [super init])     {-        // job is not owned by HBJob. It does not get dealloacted when HBJob is released.-        hbJob = job;+        sequence_id = job->sequence_id;++        chapter_start = job->chapter_start;+        chapter_end = job->chapter_end;+        chapter_markers = job->chapter_markers;+        memcpy(crop, job->crop, sizeof(crop));+        deinterlace = job->deinterlace;+        width = job->width;+        height = job->height;+        keep_ratio = job->keep_ratio;+        grayscale = job->grayscale;+        pixel_ratio = job->pixel_ratio;+        pixel_aspect_width = job->pixel_aspect_width;+        pixel_aspect_height = job->pixel_aspect_height;+        vcodec = job->vcodec;+        vquality = job->vquality;+        vbitrate = job->vbitrate;+        vrate = job->vrate;+        vrate_base = job->vrate_base;+        pass = job->pass;+        h264_level = job->h264_level;+        crf = job->crf;+        if (job->x264opts)+            x264opts = [[NSString stringWithUTF8String:job->x264opts] retain];+        memcpy(audio_mixdowns, job->audio_mixdowns, sizeof(audio_mixdowns));+        acodec = job->acodec;+        abitrate = job->abitrate;+        arate = job->arate;+        subtitle = job->subtitle;+        mux = job->mux;+        if (job->file)+            file = [[NSString stringWithUTF8String:job->file] retain];+        if (job->title->name)+            titleName = [[NSString stringWithUTF8String:job->title->name] retain];+        titleIndex = job->title->index;+        titleWidth = job->title->width;+        titleHeight = job->title->height;+        if (job->subtitle >= 0)+        {+            hb_subtitle_t * aSubtitle = (hb_subtitle_t *) hb_list_item(job->title->list_subtitle, 0);+            if (aSubtitle)+                subtitleLang = [[NSString stringWithUTF8String:aSubtitle->lang] retain];+        }+     }-    return self; +    return self; } -- (hb_job_t*) job+- (void) dealloc {-    return hbJob;+    // jobGroup is a weak reference and does not need to be deleted+    [x264opts release];+    [file release];+    [titleName release];+    [subtitleLang release];+    [super dealloc]; } +- (HBJobGroup *) jobGroup+{+    return jobGroup;+}++- (void) setJobGroup: (HBJobGroup *)aJobGroup+{+    // This is a weak reference. We don't retain or release it.+    jobGroup = aJobGroup;+}+ //------------------------------------------------------------------------------------ // Generate string to display in UI. //------------------------------------------------------------------------------------ -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle-                               withIcon: (BOOL)withIcon+- (NSMutableAttributedString *) attributedDescriptionWithIcon: (BOOL)withIcon                               withTitle: (BOOL)withTitle                            withPassName: (BOOL)withPassName                          withFormatInfo: (BOOL)withFormatInfo@@ -219,8 +289,6 @@ {     NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];     -    hb_title_t * title = hbJob->title;-         // Attributes     static NSMutableParagraphStyle * ps = NULL;     if (!ps)@@ -271,15 +339,15 @@              // 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];+        [finalString appendString:titleName 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];+        NSString * chapterString = (chapter_start == chapter_end) ?+                [NSString stringWithFormat:@"Chapter %d", chapter_start] :+                [NSString stringWithFormat:@"Chapters %d through %d", chapter_start, chapter_end]; -        BOOL hasIndepthScan = (hbJob->pass == -1);+        BOOL hasIndepthScan = (pass == -1);         int numVideoPasses = 0;          // To determine number of video passes, we need to skip past the subtitle scan.@@ -287,25 +355,24 @@         {             // 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);+            HBJob * nextjob = nil;+            unsigned int index = [jobGroup indexOfJob:self];+            if (index != NSNotFound)+                nextjob = [jobGroup jobAtIndex:index+1];             if (nextjob)    // Overly cautious in case there is no next job!                 numVideoPasses = MIN( 2, nextjob->pass + 1 );         }         else-            numVideoPasses = MIN( 2, hbJob->pass + 1 );+            numVideoPasses = MIN( 2, pass + 1 );          if (hasIndepthScan && numVideoPasses == 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, Single Video Pass)", title->index, chapterString];+            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, Single Video Pass)", titleIndex, chapterString];         else if (hasIndepthScan && numVideoPasses > 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, %d Video Passes)", title->index, chapterString, numVideoPasses];+            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Deep Scan, %d Video Passes)", titleIndex, chapterString, numVideoPasses];         else if (numVideoPasses == 1)-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Single Video Pass)", title->index, chapterString];+            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, Single Video Pass)", titleIndex, chapterString];         else-            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, %d Video Passes)", title->index, chapterString, numVideoPasses];+            summaryInfo = [NSString stringWithFormat: @"  (Title %d, %@, %d Video Passes)", titleIndex, chapterString, numVideoPasses];          [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttribute];         @@ -322,7 +389,7 @@         if (withIcon)         {             NSString * imageName;-            switch (hbJob->pass)+            switch (pass)             {                 case -1: imageName = @"JobPassSubtitleSmall"; break;                 case  0: imageName = @"JobPassFirstSmall"; break;@@ -348,11 +415,11 @@         }              NSString * jobPassName;-        if (hbJob->pass == -1)+        if (pass == -1)             jobPassName = NSLocalizedString (@"Deep Scan", nil);         else         {-            int passNum = MAX( 1, hbJob->pass );+            int passNum = MAX( 1, pass );             if (passNum == 0)                 jobPassName = NSLocalizedString (@"1st Pass", nil);             else if (passNum == 1)@@ -371,14 +438,14 @@     {         // 2097152         // Video Codec settings (Encoder in the gui)-        if (hbJob->vcodec == HB_VCODEC_FFMPEG)+        if (vcodec == HB_VCODEC_FFMPEG)             jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG-        else if (hbJob->vcodec == HB_VCODEC_XVID)+        else if (vcodec == HB_VCODEC_XVID)             jobVideoCodec = @"XviD"; // HB_VCODEC_XVID-        else if (hbJob->vcodec == HB_VCODEC_X264)+        else if (vcodec == HB_VCODEC_X264)         {             // Deterimine for sure how we are now setting iPod uuid atom-            if (hbJob->h264_level) // We are encoding for iPod+            if (h264_level) // We are encoding for iPod                 jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264                 else                 jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264@@ -391,13 +458,13 @@     NSString * jobAudioCodec = nil;     if (withFormatInfo || withAudioInfo)     {-        if (hbJob->acodec == 256)+        if (acodec == 256)             jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC-        else if (hbJob->acodec == 512)+        else if (acodec == 512)             jobAudioCodec = @"MP3"; // HB_ACODEC_LAME-        else if (hbJob->acodec == 1024)+        else if (acodec == 1024)             jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS-        else if (hbJob->acodec == 2048)+        else if (acodec == 2048)             jobAudioCodec = @"AC3"; // HB_ACODEC_AC3     }     if (jobAudioCodec == nil)@@ -408,18 +475,18 @@     {         NSString * jobFormatInfo;         // Muxer settings (File Format in the gui)-        if (hbJob->mux == 65536 || hbJob->mux == 131072 || hbJob->mux == 1048576)+        if (mux == 65536 || mux == 131072 || mux == 1048576)             jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD-        else if (hbJob->mux == 262144)+        else if (mux == 262144)             jobFormatInfo = @"AVI"; // HB_MUX_AVI-        else if (hbJob->mux == 524288)+        else if (mux == 524288)             jobFormatInfo = @"OGM"; // HB_MUX_OGM-        else if (hbJob->mux == 2097152)+        else if (mux == 2097152)             jobFormatInfo = @"MKV"; // HB_MUX_MKV         else             jobFormatInfo = @"unknown";                 -        if (hbJob->chapter_markers == 1)+        if (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];@@ -431,7 +498,7 @@     if (withDestination)     {         [finalString appendString: @"Destination: " withAttributes:detailBoldAttribute];-        [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->file]] withAttributes:detailAttribute];+        [finalString appendString:[NSString stringWithFormat:@"%@\n", file] withAttributes:detailAttribute];     }  @@ -440,22 +507,22 @@         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)+        if (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];+            int croppedWidth = titleWidth - crop[2] - crop[3];+            int displayparwidth = croppedWidth * pixel_aspect_width / pixel_aspect_height;+            int displayparheight = titleHeight - crop[0] - crop[1];+            jobPictureInfo = [NSString stringWithFormat:@"%dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, width, displayparheight];         }         else-            jobPictureInfo = [NSString stringWithFormat:@"%dx%d", hbJob->width, hbJob->height];-        if (hbJob->keep_ratio == 1)+            jobPictureInfo = [NSString stringWithFormat:@"%dx%d", width, height];+        if (keep_ratio == 1)             jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"];         -        if (hbJob->grayscale == 1)+        if (grayscale == 1)             jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"];         -        if (hbJob->deinterlace == 1)+        if (deinterlace == 1)             jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"];         if (withIcon)   // implies indent the info             [finalString appendString: @"\t" withAttributes:detailBoldAttribute];@@ -468,25 +535,25 @@         NSString * jobVideoQuality;         NSString * jobVideoDetail;         -        if (hbJob->vquality <= 0 || hbJob->vquality >= 1)-            jobVideoQuality = [NSString stringWithFormat:@"%d kbps", hbJob->vbitrate];+        if (vquality <= 0 || vquality >= 1)+            jobVideoQuality = [NSString stringWithFormat:@"%d kbps", vbitrate];         else         {             NSNumber * vidQuality;-            vidQuality = [NSNumber numberWithInt:hbJob->vquality * 100];+            vidQuality = [NSNumber numberWithInt:vquality * 100];             // this is screwed up kind of. Needs to be formatted properly.-            if (hbJob->crf == 1)+            if (crf == 1)                 jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality];                         else                 jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality];         }         -        if (hbJob->vrate_base == 1126125)+        if (vrate_base == 1126125)         {             // NTSC FILM 23.976             jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];         }-        else if (hbJob->vrate_base == 900900)+        else if (vrate_base == 900900)         {             // NTSC 29.97             jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];@@ -494,7 +561,7 @@         else         {             // Everything else-            jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base];+            jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, vrate / vrate_base];         }         if (withIcon)   // implies indent the info             [finalString appendString: @"\t" withAttributes:detailBoldAttribute];@@ -504,12 +571,12 @@          if (withx264Info)     {-        if (hbJob->vcodec == HB_VCODEC_X264 && hbJob->x264opts)+        if (vcodec == HB_VCODEC_X264 && 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];+            [finalString appendString:[NSString stringWithFormat:@"%@\n", x264opts] withAttributes:detailAttribute];         }     } @@ -519,7 +586,7 @@         if ([jobAudioCodec isEqualToString: @"AC3"])             jobAudioInfo = [NSString stringWithFormat:@"%@, Pass-Through", jobAudioCodec];         else-            jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->arate];+            jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, abitrate, 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.@@ -527,15 +594,15 @@         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)+            if (audio_mixdowns[ai] == HB_AMIXDOWN_MONO)                 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)+            if (audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)                 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo", ai + 1]];-            if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)+            if (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)+            if (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)+            if (audio_mixdowns[ai] == HB_AMIXDOWN_6CH)                 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet", ai + 1]];         }         if (withIcon)   // implies indent the info@@ -546,25 +613,24 @@          if (withSubtitleInfo)     {-        // hbJob->subtitle can == -1 in two cases:+        // subtitle can == -1 in two cases:         // autoselect: when pass == -1         // none: when pass != -1-        if ((hbJob->subtitle == -1) && (hbJob->pass == -1))+        if ((subtitle == -1) && (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)+        else if (subtitle >= 0)         {-            hb_subtitle_t * subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, 0 );-            if (subtitle)+            if (subtitleLang)             {                 if (withIcon)   // implies indent the info                     [finalString appendString: @"\t" withAttributes:detailBoldAttribute];                 [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute];-                [finalString appendString: [NSString stringWithCString: subtitle->lang] withAttributes:detailAttribute];+                [finalString appendString: subtitleLang   withAttributes:detailAttribute];             }         }     }@@ -606,7 +672,6 @@ - (void) dealloc {     [fJobs release];-    [fPath release];     [super dealloc]; } @@ -617,17 +682,13 @@  - (void) addJob: (HBJob *)aJob {+    [aJob setJobGroup:self];     [fJobs addObject: aJob];     [self setNeedsDescription: YES];     fLastDescriptionHeight = 0;     fLastDescriptionWidth = 0; } -- (void) removeAllJobs-{-    [fJobs removeAllObjects];-}- - (HBJob *) jobAtIndex: (unsigned)index {     return [fJobs objectAtIndex: index];@@ -648,7 +709,7 @@     fNeedsDescription = flag; } -- (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle+- (void) updateDescription {     fNeedsDescription = NO; @@ -662,8 +723,7 @@          HBJob * job = [self jobAtIndex:0];     -    [fDescription appendAttributedString: [job attributedDescriptionWithHBHandle: handle-                             withIcon: NO+    [fDescription appendAttributedString: [job attributedDescriptionWithIcon: NO                             withTitle: YES                          withPassName: NO                        withFormatInfo: YES@@ -679,11 +739,10 @@     NSEnumerator * e = [self jobEnumerator];     while ( (job = [e nextObject]) )     {-        int pass = [job job]->pass;+        int pass = job->pass;         [fDescription appendAttributedString:carriageReturn];         [fDescription appendAttributedString:-            [job attributedDescriptionWithHBHandle: handle-                                 withIcon: YES+            [job attributedDescriptionWithIcon: YES                                 withTitle: NO                              withPassName: YES                            withFormatInfo: NO@@ -697,21 +756,21 @@      } -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle+- (NSMutableAttributedString *) attributedDescription {     if (fNeedsDescription)-        [self updateDescriptionWithHBHandle: handle];+        [self updateDescription];     return fDescription; } -- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle+- (float) heightOfDescriptionForWidth:(float)width {     // 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];+        [self updateDescription];      // Calculate the height         NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];@@ -748,16 +807,16 @@     return self->fStatus; } -- (void) setPath: (NSString *)path+- (NSString *) name {-    [path retain];-    [fPath release];-    fPath = path;+    HBJob * firstJob = [self jobAtIndex:0];+    return firstJob ? firstJob->titleName : nil; }  - (NSString *) path {-    return fPath;+    HBJob * firstJob = [self jobAtIndex:0];+    return firstJob ? firstJob->file : nil; }  @end@@ -837,6 +896,24 @@ }  //------------------------------------------------------------------------------------+// Returns the HBJobGroup that is currently being encoded; nil if no encoding is+// occurring.+//------------------------------------------------------------------------------------+- (HBJobGroup *) currentJobGroup;+{+    return fCurrentJobGroup;+}++//------------------------------------------------------------------------------------+// Returns the HBJob (pass) that is currently being encoded; nil if no encoding is+// occurring.+//------------------------------------------------------------------------------------+- (HBJob *) currentJob+{+    return fCurrentJob;+}++//------------------------------------------------------------------------------------ // Displays and brings the queue window to the front //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender@@ -845,6 +922,7 @@     [fQueueWindow makeKeyAndOrderFront: self];     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; }+ //------------------------------------------------------------------------------------ // Show or hide the current job pane (fCurrentJobPane). //------------------------------------------------------------------------------------@@ -897,7 +975,14 @@ //------------------------------------------------------------------------------------ - (void)rebuildJobGroups {-    // Currently, job groups are rdered like this:+    // This method is called every time we detect that hblib has changed its job list.+    // It releases the previous job group list and rebuilds it by reading the current+    // list of jobs from libhb. libhb does not implement job groups/encodes itself. It+    // just maintains a simply list of jobs. The queue controller however, presents+    // these jobs to the user organized into encodes where all jobs associated with+    // the same encode are grouped into a logical job group/encode.+         +    // Currently, job groups are ordered like this:     // Completed job groups     // Current job group     // Pending job groups@@ -908,7 +993,7 @@     // Add all the completed job groups     [fJobGroups addObjectsFromArray: fCompleted]; -    // Add all the completed job groups+    // Add the current job group     if (fCurrentJobGroup)         [fJobGroups addObject: fCurrentJobGroup]; @@ -921,7 +1006,7 @@     hb_job_t * nextJob = hb_group( fHandle, 0 );     while( nextJob )     {-        if (nextJob->sequence_id == 0)+        if (IsFirstPass(nextJob->sequence_id))         {             // Encountered a new group. Add the current one to fJobGroups and then start a new one.             if ([aJobGroup count] > 0)@@ -932,7 +1017,6 @@             }         }         [aJobGroup addJob: [HBJob jobWithJob:nextJob]];-        [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]];         nextJob = hb_next_job (fHandle, nextJob);     }     if ([aJobGroup count] > 0)@@ -943,68 +1027,70 @@ }  //-------------------------------------------------------------------------------------// Adds aJobGroup to the list of completed job groups, marking its status as-// HBStatusComplete.+// Adds aJobGroup to the list of completed job groups. The completed list is+// maintained by the queue, since libhb deletes all its jobs after they are complete. //------------------------------------------------------------------------------------ - (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.+    // Put the group in the completed list for permanent storage.     [fCompleted addObject: aJobGroup]; } -- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob+//------------------------------------------------------------------------------------+// Sets fCurrentJobGroup to a new job group.+//------------------------------------------------------------------------------------+- (void) setCurrentJobGroup: (HBJobGroup *)aJobGroup {-    HBJobGroup * aJobGroup = nil;-    -    // Try to find this new group in our existing job groups.-    if (aJob)+    if (aJobGroup)+        [aJobGroup setStatus: HBStatusWorking];++    [aJobGroup retain];+    [fCurrentJobGroup release];+    fCurrentJobGroup = aJobGroup;+}++//------------------------------------------------------------------------------------+// Locates and returns a HBJob whose sequence_id matches a specified value.+//------------------------------------------------------------------------------------+- (HBJob *) findJobWithID: (int)aJobID+{+    HBJobGroup * aJobGroup;+    NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+    while ( (aJobGroup = [groupEnum nextObject]) )     {-        BOOL found = NO;-        NSEnumerator * groupEnum = [fJobGroups objectEnumerator];-        while ( !found && (aJobGroup = [groupEnum nextObject]) )+        HBJob * job;+        NSEnumerator * jobEnum = [aJobGroup jobEnumerator];+        while ( (job = [jobEnum nextObject]) )         {-            HBJob * j;-            NSEnumerator * jobEnum = [aJobGroup jobEnumerator];-            while ( !found && (j = [jobEnum nextObject]) )-            {-                if ([j job] == aJob)-                    found = YES;-            }+            if (job->sequence_id == aJobID)+                return job;         }-        -        // Or create the job group.-        if (!aJobGroup)-        {-            aJobGroup = [HBJobGroup jobGroup];-            [aJobGroup addJob: [HBJob jobWithJob: aJob]];-            [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]];-            while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) )-                [aJobGroup addJob: [HBJob jobWithJob: aJob]];+    }+    return nil;+} -            [aJobGroup updateDescriptionWithHBHandle: fHandle];-        }-        -        [aJobGroup setStatus: HBStatusWorking];+//------------------------------------------------------------------------------------+// Locates and returns a libhb job whose sequence_id matches a specified value.+//------------------------------------------------------------------------------------+- (hb_job_t *) findLibhbJobWithID: (int)aJobID+{+    hb_job_t * job;+    int index = 0;+    while( ( job = hb_job( fHandle, index++ ) ) )+    {+        if (job->sequence_id == aJobID)+            return job;     }-    -    [aJobGroup retain];-    [fCurrentJobGroup release];-    fCurrentJobGroup = aJobGroup;+    return nil; }  #pragma mark - #pragma mark UI Updating  //-------------------------------------------------------------------------------------// Saves the state of the items that are currently expanded. Calling restoreOutlineViewState-// will restore the state of all items to match what was saved by saveOutlineViewState.+// Saves the state of the items that are currently expanded and selected. Calling+// restoreOutlineViewState will restore the state of all items to match what was saved+// by saveOutlineViewState. Nested calls to saveOutlineViewState are not supported. //------------------------------------------------------------------------------------ - (void) saveOutlineViewState {@@ -1013,29 +1099,18 @@     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.+    // This code stores the sequence_id of the first job of each job group into an+    // index set. This is sufficient to identify each group uniquely.          HBJobGroup * aJobGroup;     NSEnumerator * e = [fJobGroups objectEnumerator];     while ( (aJobGroup = [e nextObject]) )     {         if ([fOutlineView isItemExpanded: aJobGroup])-        {-            if ([aJobGroup status] == HBStatusComplete)-                [fSavedExpandedItems addIndex: (unsigned int)aJobGroup];-            else-                [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]];-        }+            [fSavedExpandedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id];     }     -    // 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.+    // Save the selection also.      if (!fSavedSelectedItems)         fSavedSelectedItems = [[NSMutableIndexSet alloc] init];@@ -1047,10 +1122,7 @@     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]];    +        [fSavedSelectedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id];         row = [selectedRows indexGreaterThanIndex: row];     } @@ -1068,40 +1140,24 @@         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];-            }+            HBJob * job = [aJobGroup jobAtIndex:0];+            if (job && [fSavedExpandedItems containsIndex: job->sequence_id])+                [fOutlineView expandItem: aJobGroup];         }     }          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;+        NSEnumerator * e = [fJobGroups objectEnumerator];+        int i = 0;+        while ( (aJobGroup = [e nextObject]) )         {-            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];-            }+            HBJob * job = [aJobGroup jobAtIndex:0];+            if (job && [fSavedSelectedItems containsIndex: job->sequence_id])+                [rowsToSelect addIndex: i];+            i++;         }         if ([rowsToSelect count] == 0)             [fOutlineView deselectAll: nil];@@ -1158,7 +1214,7 @@ //------------------------------------------------------------------------------------ // Generate string to display in UI. //-------------------------------------------------------------------------------------- (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s+- (NSString *) progressStatusStringForJob: (HBJob *)job state: (hb_state_t *)s {     if (s->state == HB_STATE_WORKING)     {@@ -1198,7 +1254,7 @@ //------------------------------------------------------------------------------------ // Generate string to display in UI. //-------------------------------------------------------------------------------------- (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s+- (NSString *) progressTimeRemainingStringForJob: (HBJob *)job state: (hb_state_t *)s {     if (s->state == HB_STATE_WORKING)     {@@ -1270,7 +1326,7 @@ //------------------------------------------------------------------------------------ // Refresh progress bar (fProgressTextField) from current state. //-------------------------------------------------------------------------------------- (void) updateProgressTextForJob: (hb_job_t *)job state: (hb_state_t *)s+- (void) updateProgressTextForJob: (HBJob *)job state: (hb_state_t *)s {     NSString * statusMsg = [self progressStatusStringForJob:job state:s];     NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:s];@@ -1335,17 +1391,13 @@ //------------------------------------------------------------------------------------ - (void)updateCurrentJobDescription {-    hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil;--    if (job)+    if (fCurrentJob)     {-        HBJob * currentJob = [HBJob jobWithJob: job];-        switch (job->pass)+        switch (fCurrentJob->pass)         {             case -1:  // Subtitle scan                 [fJobDescTextField setAttributedStringValue:-                    [currentJob attributedDescriptionWithHBHandle:fHandle-                                 withIcon: NO+                    [fCurrentJob attributedDescriptionWithIcon: NO                                 withTitle: YES                              withPassName: YES                            withFormatInfo: NO@@ -1359,8 +1411,7 @@                              case 1:  // video 1st pass                 [fJobDescTextField setAttributedStringValue:-                    [currentJob attributedDescriptionWithHBHandle:fHandle-                                 withIcon: NO+                    [fCurrentJob attributedDescriptionWithIcon: NO                                 withTitle: YES                              withPassName: YES                            withFormatInfo: NO@@ -1375,8 +1426,7 @@             case 0:  // single pass             case 2:  // video 2nd pass + audio                 [fJobDescTextField setAttributedStringValue:-                    [currentJob attributedDescriptionWithHBHandle:fHandle-                                 withIcon: NO+                    [fCurrentJob attributedDescriptionWithIcon: NO                                 withTitle: YES                              withPassName: YES                            withFormatInfo: NO@@ -1390,8 +1440,7 @@                          default: // unknown                 [fJobDescTextField setAttributedStringValue:-                    [currentJob attributedDescriptionWithHBHandle:fHandle-                                 withIcon: NO+                    [fCurrentJob attributedDescriptionWithIcon: NO                                 withTitle: YES                              withPassName: YES                            withFormatInfo: NO@@ -1414,10 +1463,9 @@ //------------------------------------------------------------------------------------ - (void)updateCurrentJobProgress {-    hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil;     hb_state_t s;     hb_get_state2( fHandle, &s );-    [self updateProgressTextForJob: job state: &s];+    [self updateProgressTextForJob: fCurrentJob state: &s];     [self updateProgressBarWithState:&s]; } @@ -1462,8 +1510,10 @@                     [self cancelCurrentJob: sender];                     break;                 case HBStatusPending:-                    hb_job_t * job = [[jobGroup jobAtIndex: 0] job];-                    hb_rem_group( fHandle, job );+                    HBJob * job = [jobGroup jobAtIndex: 0];+                    hb_job_t * libhbJob = [self findLibhbJobWithID:job->sequence_id];+                    if (libhbJob)+                        hb_rem_group( fHandle, libhbJob );                     break;                 case HBStatusNone:                     break;@@ -1548,18 +1598,52 @@ //------------------------------------------------------------------------------------ // Notifies HBQueueController that hblib's current job has changed //-------------------------------------------------------------------------------------- (void)currentJobGroupChanged: (hb_job_t *) currentJob+- (void)currentJobChanged: (HBJob *) currentJob {-    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];+    [currentJob retain];+    [fCurrentJob release];+    fCurrentJob = currentJob;++    // Check to see if this is also a change in Job Group+    +    HBJobGroup * theJobGroup = [currentJob jobGroup];+    if ((theJobGroup == nil) || (theJobGroup != fCurrentJobGroup))     // no more job groups or start of a new group+    {+        // Previous job has completed+        if (fCurrentJobGroup)+        {+            // Update the status of the job that just finished. If the user canceled,+            // the status will have already been set to canceled by hblibWillStop. So+            // all other cases are assumed to be a successful encode. BTW, libhb+            // doesn't currently report errors back to the GUI.+            if ([fCurrentJobGroup status] != HBStatusCanceled)+                [fCurrentJobGroup setStatus:HBStatusComplete];++            [self addCompletedJobGroup: fCurrentJobGroup];+        }+        +        // Set the new group+        [self setCurrentJobGroup: theJobGroup];+        +        // Update the UI+        [self updateCurrentJobDescription];+        [self updateCurrentJobProgress];+        [self showCurrentJobPane: fCurrentJobGroup != nil];+        if (fCurrentJobGroup)+            [self startAnimatingCurrentJobGroupInQueue];+        else+            [self stopAnimatingCurrentJobGroupInQueue];++        [self hblibJobListChanged];+    }+    +    else    // start a new job/pass in the same group+    {+        // Update the UI+        [self updateCurrentJobDescription];+        [self updateCurrentJobProgress];+    }+ }  //------------------------------------------------------------------------------------@@ -1590,32 +1674,26 @@ //------------------------------------------------------------------------------------ - (void)hblibStateChanged: (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))-    {-        hb_job_t * currentJob = hb_current_job(fHandle);-        if (!currentJob || currentJob->sequence_id == 0)     // start of a new group-        {-            [self currentJobGroupChanged: currentJob];-            [self hblibJobListChanged];-        }-        else-        {-            [self updateCurrentJobDescription];-            [self updateCurrentJobProgress];-        }--       fLastKnownCurrentJob = currentJob;-    }-     switch( state.state )     {         case HB_STATE_WORKING:         {-            [self updateCurrentJobProgress];-            [self startAnimatingCurrentJobGroupInQueue];+            //NSLog(@"job = %x; job_cur = %d; job_count = %d", state.param.working.sequence_id, state.param.working.job_cur, state.param.working.job_count);+            // 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 (fCurrentJobID != state.param.working.sequence_id)+            {+                fCurrentJobID = state.param.working.sequence_id;+                HBJob * currentJob = [self findJobWithID:fCurrentJobID];+                [self currentJobChanged: currentJob];+            }++            if (fCurrentJob)+            {+                [self updateCurrentJobProgress];+                [self startAnimatingCurrentJobGroupInQueue];+            }             break;         } @@ -1637,6 +1715,9 @@             // 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             // completed.++            [self currentJobChanged: nil];+            fCurrentJobID = 0;         }      }@@ -1984,7 +2065,7 @@         // Column width is NOT what is ultimately used         width -= 47;    // 26 pixels for disclosure triangle, 20 for icon, 1 for intercell spacing         -        float height = [item heightOfDescriptionForWidth: width withHBHandle: fHandle];+        float height = [item heightOfDescriptionForWidth: width];         return height;     }     else@@ -1997,11 +2078,14 @@ 	// using the image portion of the cell so we could switch back to a regular NSTextFieldCell. 	     if ([[tableColumn identifier] isEqualToString:@"desc"])-        return [item attributedDescriptionWithHBHandle: fHandle];+        return [item attributedDescription];     else if ([[tableColumn identifier] isEqualToString:@"icon"])     {         switch ([(HBJobGroup*)item status])         {+            case HBStatusCanceled:+                return [NSImage imageNamed:@"EncodeCanceled"];+                break;             case HBStatusComplete:                 return [NSImage imageNamed:@"EncodeComplete"];                 break;