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;