Index: libhb/hb.c===================================================================--- libhb/hb.c (revision 1043)+++ libhb/hb.c (working copy)@@ -692,11 +692,14 @@ } /**- * Adds a job to the job list.+ * Allocates and returns a copy of the job. The copy will have a reference count of 1.+ * The caller is responsible for calling hb_job_dereference when the copy is no longer+ * needed. * @param h Handle to hb_handle_t. * @param job Handle to hb_job_t.+ * @returns Handle to hb_job_t of the copy. */-void hb_add( hb_handle_t * h, hb_job_t * job )+hb_job_t * hb_job_copy( hb_job_t * job ) { hb_job_t * job_copy; hb_title_t * title, * title_copy;@@ -903,8 +906,8 @@ title_copy->job = job_copy; job_copy->title = title_copy; job_copy->file = strdup( job->file );- job_copy->h = h;- job_copy->pause = h->pause_lock;+ job_copy->h = NULL;+ job_copy->pause = NULL; /* Copy the job filter list */ if( job->filters )@@ -919,10 +922,32 @@ } }+ job_copy->reference_count = 1;++ return( job_copy );+}++/**+ * Adds a copy of a job to the job list.+ * @param h Handle to hb_handle_t.+ * @param job Handle to hb_job_t.+ * @returns Handle to hb_job_t of the copied job.+ */+hb_job_t * hb_add( hb_handle_t * h, hb_job_t * job )+{+ hb_job_t * job_copy;++ job_copy = hb_job_copy ( job );++ job_copy->h = h;+ job_copy->pause = h->pause_lock;+ /* Add the job to the list */ hb_list_add( h->jobs, job_copy ); h->job_count = hb_count(h); h->job_count_permanent++;++ return( job_copy ); } /**@@ -939,6 +964,7 @@ h->job_count_permanent--; /* XXX free everything XXX */+ hb_job_dereference( job ); } /**@@ -1207,3 +1233,31 @@ hb_unlock( h->state_lock ); hb_unlock( h->pause_lock ); }++/*+ * Register an interest in referencing this job, it won't be deleted+ * until this party deregisters its interest in it.+ */+void hb_job_reference( const hb_job_t * job_copy )+{+ hb_job_t * job = (hb_job_t * )job_copy;++ job->reference_count++;+}++void hb_job_dereference( const hb_job_t * job_copy )+{+ hb_job_t * job = (hb_job_t * )job_copy;++ job->reference_count--;++ if( job->reference_count <= 0 )+ {+ hb_log( "Freeing job 0x%x", job );++// travistex has added this call to hb_title_close because it was removed from do_job.+ hb_title_close( &job->title );++ free( job );+ }+}Index: libhb/hb.h===================================================================--- libhb/hb.h (revision 1043)+++ libhb/hb.h (working copy)@@ -84,7 +84,8 @@ int hb_count( hb_handle_t * ); hb_job_t * hb_job( hb_handle_t *, int ); hb_job_t * hb_current_job( hb_handle_t * h );-void hb_add( hb_handle_t *, hb_job_t * );+hb_job_t * hb_job_copy( hb_job_t * job );+hb_job_t * hb_add( hb_handle_t *, hb_job_t * ); void hb_rem( hb_handle_t *, hb_job_t * ); void hb_start( hb_handle_t * );@@ -105,6 +106,9 @@ Aborts all current jobs if any, frees memory. */ void hb_close( hb_handle_t ** );+void hb_job_reference( const hb_job_t * job_copy );+void hb_job_dereference( const hb_job_t * job_copy );+ #ifdef __cplusplus } #endifIndex: libhb/work.c===================================================================--- libhb/work.c (revision 1043)+++ libhb/work.c (working copy)@@ -702,8 +702,11 @@ hb_buffer_pool_free();- hb_title_close( &job->title );- free( job );+// travistex is requesting that all code to dealloc a job be moved to hb_job_dereference+// so that all data in the job can continued to be referenced until the job is really+// deallocated. I don't know if any of the other code above falls in this same category.+// hb_title_close( &job->title );+ hb_job_dereference( job ); } /**Index: libhb/common.h===================================================================--- libhb/common.h (revision 1043)+++ libhb/common.h (working copy)@@ -105,6 +105,12 @@ *****************************************************************************/ struct hb_job_s {+ /*+ * Number of folks interested in this job, only free when equal to+ * 0.+ */+ int reference_count;+ /* ID assigned by UI so it can groups job passes together */ int sequence_id;Index: macosx/HBQueueController.h===================================================================--- macosx/HBQueueController.h (revision 1043)+++ macosx/HBQueueController.h (working copy)@@ -9,30 +9,25 @@ #include "hb.h" @class HBController;+@class HBJobGroup;-#define HB_QUEUE_DRAGGING 0 // <--- NOT COMPLETELY FUNCTIONAL YET+#define HB_QUEUE_DRAGGING 1 // <--- NOT COMPLETELY FUNCTIONAL YET #define HB_OUTLINE_METRIC_CONTROLS 1 // for tweaking the outline cell spacings-typedef enum _HBQueueJobGroupStatus-{- HBStatusNone = 0,- HBStatusPending = 1,- HBStatusWorking = 2,- HBStatusComplete = 3,- HBStatusCanceled = 4-} HBQueueJobGroupStatus;- //-------------------------------------------------------------------------------------// As usual, we need to subclass NSOutlineView to handle a few special cases:+// HBQueueOutlineView+//------------------------------------------------------------------------------------+// We need to subclass NSOutlineView to handle a few special cases: //-// (1) variable row heights during live resizes-// HBQueueOutlineView exists solely to get around a bug in variable row height outline-// views in which row heights get messed up during live resizes. See this discussion:+// (1) Variable row heights during live resizes+// There's a bug/issue in Tiger and Leopard where variable row height outline view row+// heights get messed up (or not recalculated) during live resizes. See this+// discussion: // http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00871.html // However, the recommeneded fix (override drawRect:) does not work. Instead, this // subclass implements viewDidEndLiveResize in order to recalculate all row heights. //-// (2) prevent expanding of items during drags+// (2) Prevent expanding of items during drags // During dragging operations, we don't want outline items to expand, since a queue // doesn't really have children items. //@@ -43,7 +38,7 @@ @interface HBQueueOutlineView : NSOutlineView { #if HB_QUEUE_DRAGGING-BOOL fIsDragging;+ BOOL fIsDragging; #endif } #if HB_QUEUE_DRAGGING@@ -52,85 +47,23 @@ @end //--------------------------------------------------------------------------------------@interface HBJob : NSObject-{- hb_job_t *hbJob;-}--+ (HBJob*) jobWithJob: (hb_job_t *) job;-- (id) initWithJob: (hb_job_t *) job;-- (hb_job_t *) job;-- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle- withIcon: (BOOL)withIcon- withTitle: (BOOL)withTitle- withPassName: (BOOL)withPassName- withFormatInfo: (BOOL)withFormatInfo- withDestination: (BOOL)withDestination- withPictureInfo: (BOOL)withPictureInfo- withVideoInfo: (BOOL)withVideoInfo- withx264Info: (BOOL)withx264Info- withAudioInfo: (BOOL)withAudioInfo- withSubtitleInfo: (BOOL)withSubtitleInfo;--@end-+// HBQueueController //-------------------------------------------------------------------------------------@interface HBJobGroup : NSObject-{- NSMutableArray *fJobs; // array of HBJob- NSMutableAttributedString *fDescription;- BOOL fNeedsDescription;- float fLastDescriptionHeight;- float fLastDescriptionWidth;- HBQueueJobGroupStatus fStatus;- NSString *fPath;-}--// Creating a job group-+ (HBJobGroup *) jobGroup;--// Adding jobs-- (void) addJob: (HBJob *)aJob;--// Removing jobs-- (void) removeAllJobs;--// Querying a job group-- (unsigned int) count;-- (HBJob *) jobAtIndex: (unsigned)index;-- (unsigned) indexOfJob: (HBJob *)aJob;-- (NSEnumerator *) jobEnumerator;-- (void) setStatus: (HBQueueJobGroupStatus)status;-- (HBQueueJobGroupStatus) status;-- (void) setPath: (NSString *)path;-- (NSString *) path;--// Creating a description-- (void) setNeedsDescription: (BOOL)flag;-- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle;-- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle;-- (float) lastDescriptionHeight;--@end--//------------------------------------------------------------------------------------- @interface HBQueueController : NSObject { hb_handle_t *fHandle; // reference to hblib HBController *fHBController; // reference to HBController- NSMutableArray *fJobGroups; // hblib's job list organized in a hierarchy of HBJobGroup and HBJob- HBJobGroup *fCurrentJobGroup; // the HJobGroup current being processed by hblib+ NSMutableArray *fJobGroups; // List of all job groups tracked by the controller. Ordered the way they are to be presented in the UI.+ HBJobGroup *fCurrentJobGroup; // HBJobGroup current being processed by hblib. This also appear in fJobGroups.+ BOOL fStopQueueRequested; // YES when hb_stop has been called. Set back to NO once libhb has performed the stop. BOOL fCurrentJobPaneShown; // NO when fCurrentJobPane has been shifted out of view (see showCurrentJobPane)- hb_job_t *fLastKnownCurrentJob; // this is how we track when hbib has started processing a different job- NSMutableIndexSet *fSavedExpandedItems; // used by save/restoreOutlineViewState to preserve which items are expanded- NSMutableIndexSet *fSavedSelectedItems; // used by save/restoreOutlineViewState to preserve which items are selected+ hb_job_t *fLastKnownLibHBJob; // this is how we track when hbib has started processing a different job+ NSMutableArray *fSavedExpandedItems; // used by save/restoreOutlineViewState to preserve which items are expanded+ NSArray *fSavedSelectedItems; // used by save/restoreOutlineViewState to preserve which items are selected #if HB_QUEUE_DRAGGING NSArray *fDraggedNodes; #endif- NSMutableArray *fCompleted; // HBJobGroups that have been completed. These also appear in fJobGroups. NSTimer *fAnimationTimer; // animates the icon of the current job in the queue outline view int fAnimationIndex; // used to generate name of image used to animate the current job in the queue outline view@@ -173,16 +106,26 @@ - (void)setHandle: (hb_handle_t *)handle; - (void)setHBController: (HBController *)controller;-- (void)hblibJobListChanged;-- (void)hblibStateChanged: (hb_state_t &)state;-- (void)hblibWillStop;+- (void)libhbStateChanged: (hb_state_t &)state;+- (void) addJobGroup: (HBJobGroup *) aJobGroup;+- (void) removeJobGroup: (HBJobGroup *) aJobGroup;++- (unsigned int) pendingCount;+- (unsigned int) completedCount;+- (unsigned int) canceledCount;+- (unsigned int) workingCount;++- (IBAction) startQueue: (id)sender;+- (IBAction) stopQueue: (id)sender;+- (void) stopQueue;+- (IBAction) pauseQueue: (id)sender;+- (IBAction) resumeQueue: (id)sender;+ - (IBAction)showQueueWindow: (id)sender; - (IBAction)removeSelectedJobGroups: (id)sender; - (IBAction)revealSelectedJobGroups: (id)sender; - (IBAction)cancelCurrentJob: (id)sender;-- (IBAction)toggleStartCancel: (id)sender;-- (IBAction)togglePauseResume: (id)sender; #if HB_OUTLINE_METRIC_CONTROLS - (IBAction)imageSpacingChanged: (id)sender;Index: macosx/Controller.mm===================================================================--- macosx/Controller.mm (revision 1043)+++ macosx/Controller.mm (working copy)@@ -8,6 +8,8 @@ #include "a52dec/a52.h" #import "HBOutputPanelController.h" #import "HBPreferencesController.h"+#import "HBJobGroup.h"+#import "HBJob.h" /* Added to integrate scanning into HBController */ #include <IOKit/IOKitLib.h> #include <IOKit/storage/IOMedia.h>@@ -156,7 +158,7 @@ if (result == NSAlertDefaultReturn) {- [self doCancelCurrentJob];+ [fQueueController stopQueue]; return NSTerminateNow; } else@@ -584,7 +586,7 @@ // Has current job changed? That means the queue has probably changed as // well so update it- [fQueueController hblibStateChanged: s];+ [fQueueController libhbStateChanged: s]; break; }@@ -608,7 +610,7 @@ [self UpdateDockIcon: 1.0]; // Pass along the info to HBQueueController- [fQueueController hblibStateChanged: s];+ [fQueueController libhbStateChanged: s]; break; }@@ -618,122 +620,40 @@ [fStatusField setStringValue: _( @"Paused" )]; // Pass along the info to HBQueueController- [fQueueController hblibStateChanged: s];+ [fQueueController libhbStateChanged: s]; break; case HB_STATE_WORKDONE: {- // HB_STATE_WORKDONE happpens as a result of hblib finishing all its jobs- // or someone calling hb_stop. In the latter case, hb_stop does not clear- // out the remaining passes/jobs in the queue. We'll do that here.- - // Delete all remaining scans of this job, ie, delete whole encodes.- hb_job_t * job;- while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) )- hb_rem( fHandle, job );+ // Pass along the info to HBQueueController. It may pass another job into+ // libhb.+ [fQueueController libhbStateChanged: s];- // Start processing back up if jobs still left in queue- if (hb_count(fHandle) > 0)+ // If HBQueueController didn't start anything else up, then we are really done.+ hb_get_state2( fHandle, &s );+ if ((s.state == HB_STATE_WORKDONE) || (s.state == HB_STATE_IDLE)) {- hb_start(fHandle);- fEncodeState = 1;- // Validate the toolbar (hack). The toolbar will usually get autovalidated- // before we had the chance to restart the queue, hence it will now be in- // the wrong state.+ [fStatusField setStringValue: _( @"Done." )];+ [fRipIndicator setIndeterminate: NO];+ [fRipIndicator setDoubleValue: 0.0]; [toolbar validateVisibleItems];- break;- }- [fStatusField setStringValue: _( @"Done." )];- [fRipIndicator setIndeterminate: NO];- [fRipIndicator setDoubleValue: 0.0];- [toolbar validateVisibleItems];+ // Restore dock icon+ [self UpdateDockIcon: -1.0];- /* Restore dock icon */- [self UpdateDockIcon: -1.0];-- if (fRipIndicatorShown)- {- NSRect frame = [fWindow frame];- if (frame.size.width <= 591)- frame.size.width = 591;- frame.size.height += -36;- frame.origin.y -= -36;- [fWindow setFrame:frame display:YES animate:YES];- fRipIndicatorShown = NO;- }- - // Pass along the info to HBQueueController- [fQueueController hblibStateChanged: s];- - /* Check to see if the encode state has not been cancelled- to determine if we should check for encode done notifications */- if (fEncodeState != 2) {- /* If Growl Notification or Window and Growl has been selected */- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || - [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+ if (fRipIndicatorShown) {- /*Growl Notification*/- [self showGrowlDoneNotification: NULL];+ NSRect frame = [fWindow frame];+ if (frame.size.width <= 591)+ frame.size.width = 591;+ frame.size.height += -36;+ frame.origin.y -= -36;+ [fWindow setFrame:frame display:YES animate:YES];+ fRipIndicatorShown = NO; }- /* If Alert Window or Window and Growl has been selected */- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || - [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])- {- /*On Screen Notification*/- int status;- NSBeep();- status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);- [NSApp requestUserAttention:NSCriticalRequest];- if ( status == NSAlertDefaultReturn ) - {- [self enableUI: YES];- }- }- else- {- [self enableUI: YES];- }- /* If sleep has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) - { - /* Sleep */ - NSDictionary* errorDict; - NSAppleEventDescriptor* returnDescriptor = NULL; - NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: - @"tell application \"Finder\" to sleep"]; - returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; - [scriptObject release]; - [self enableUI: YES]; - } - /* If Shutdown has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) - { - /* Shut Down */ - NSDictionary* errorDict; - NSAppleEventDescriptor* returnDescriptor = NULL; - NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: - @"tell application \"Finder\" to shut down"]; - returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; - [scriptObject release]; - [self enableUI: YES]; - }- - // MetaX insertion via AppleScript- if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)- {- NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]];- [myScript executeAndReturnError: nil];- [myScript release];- }- - - }- else- {- [self enableUI: YES];- }++ } break; } }@@ -917,7 +837,7 @@ if ([ident isEqualToString: StartEncodingIdentifier]) { [toolbarItem setImage: [NSImage imageNamed: @"Play"]];- if (hb_count(fHandle) > 0)+ if ([fQueueController pendingCount] > 0) [toolbarItem setLabel: @"Start Queue"]; else [toolbarItem setLabel: @"Start"];@@ -1018,9 +938,9 @@ -(IBAction)showGrowlDoneNotification:(id)sender {- [GrowlApplicationBridge - notifyWithTitle:@"Put down that cocktail..." - description:@"your HandBrake encode is done!" + [GrowlApplicationBridge + notifyWithTitle:@"HandBrake has finished its encoding tasks." + description:@"More information is available in the Queue window." notificationName:SERVICE_NAME iconData:nil priority:0 @@ -1028,6 +948,7 @@ clickContext:nil]; }+ #pragma mark - #pragma mark Get New Source@@ -1257,7 +1178,6 @@ hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); hb_job_t * job = title->job;- //int i; /* Chapter selection */ job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;@@ -1458,55 +1378,17 @@ }---/* addToQueue: puts up an alert before ultimately calling doAddToQueue-*/-- (IBAction) addToQueue: (id) sender-{- /* We get the destination directory from the destination field here */- NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];- /* We check for a valid destination here */- if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) - {- NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);- return;- }-- /* We check for duplicate name here */- if( [[NSFileManager defaultManager] fileExistsAtPath:- [fDstFile2Field stringValue]] )- {- NSBeginCriticalAlertSheet( _( @"File already exists" ),- _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,- @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),- NULL, NULL, [NSString stringWithFormat:- _( @"Do you want to overwrite %@?" ),- [fDstFile2Field stringValue]] );- // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.- }- else- {- [self doAddToQueue];- }-}--/* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks- the user if they want to overwrite an exiting movie file.-*/-- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet- returnCode: (int) returnCode contextInfo: (void *) contextInfo-{- if( returnCode == NSAlertAlternateReturn )- [self doAddToQueue];-}- - (void) doAddToQueue { hb_list_t * list = hb_get_titles( fHandle ); hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); hb_job_t * job = title->job;+ // Create a job group for adding the individual job passes. The job group will+ // then be added to the queue.+ HBJobGroup * jobGroup = [HBJobGroup jobGroup];+ hb_job_t * job_copy;+ // Assign a sequence number, starting at zero, to each job added so they can // be lumped together in the UI. job->sequence_id = -1;@@ -1547,8 +1429,11 @@ * Add the pre-scan job */ job->sequence_id++; // for job grouping- hb_add( fHandle, job );-++ job_copy = hb_job_copy(job); // make a copy for jobWithLibhbJob to use + [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]]; // add it to the queue+ hb_job_dereference (job_copy); // delete our reference+ job->x264opts = x264opts_tmp; } else@@ -1572,8 +1457,11 @@ job->pass = 1; job->sequence_id++; // for job grouping- hb_add( fHandle, job );+ job_copy = hb_job_copy(job); // make a copy for jobWithLibhbJob to use + [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]]; // add it to the queue+ hb_job_dereference (job_copy); // delete our reference+ job->pass = 2; job->sequence_id++; // for job grouping@@ -1582,42 +1470,91 @@ job->select_subtitle = subtitle_tmp;- hb_add( fHandle, job );+ job_copy = hb_job_copy(job); // make a copy for jobWithLibhbJob to use + [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]]; // add it to the queue+ hb_job_dereference (job_copy); // delete our reference } else { job->indepth_scan = 0; job->pass = 0; job->sequence_id++; // for job grouping- hb_add( fHandle, job );++ job_copy = hb_job_copy(job); // make a copy for jobWithLibhbJob to use + [jobGroup addJob:[HBJob jobWithLibhbJob:job_copy]]; // add it to the queue+ hb_job_dereference (job_copy); // delete our reference } NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];- // Notify the queue- [fQueueController hblibJobListChanged];+ // Add job group to the queue+ [fQueueController addJobGroup:jobGroup]; }-/* Rip: puts up an alert before ultimately calling doRip++/* addToQueue: puts up an alert before ultimately calling doAddToQueue */+- (IBAction) addToQueue: (id) sender+{+ /* We get the destination directory from the destination field here */+ NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];+ /* We check for a valid destination here */+ if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) + {+ NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);+ return;+ }++ /* We check for duplicate name here */+ if( [[NSFileManager defaultManager] fileExistsAtPath:+ [fDstFile2Field stringValue]] )+ {+ NSBeginCriticalAlertSheet( _( @"File already exists" ),+ _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,+ @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),+ NULL, NULL, [NSString stringWithFormat:+ _( @"Do you want to overwrite %@?" ),+ [fDstFile2Field stringValue]] );+ // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.+ }+ else+ {+ [self doAddToQueue];+ }+}++/* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks+ the user if they want to overwrite an exiting movie file.+*/+- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet+ returnCode: (int) returnCode contextInfo: (void *) contextInfo+{+ if( returnCode == NSAlertAlternateReturn )+ [self doAddToQueue];+}++/* Rip: puts up an alert before ultimately calling HBQueueController startQueue:.+*/ - (IBAction) Rip: (id) sender { /* Rip or Cancel ? */ hb_state_t s; hb_get_state2( fHandle, &s );- if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)+ // If processing jobs, then stop the queue++ if(s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED) {- [self Cancel: sender];+ [fQueueController stopQueue: fWindow]; return; }- // If there are jobs in the queue, then this is a rip the queue+ // If there are jobs in the queue, then start the queue- if (hb_count( fHandle ) > 0)+ if ([fQueueController pendingCount] > 0) {- [self doRip];+ [fQueueController startQueue: sender]; return; }@@ -1640,20 +1577,20 @@ _( @"Do you want to overwrite %@?" ), [fDstFile2Field stringValue]] );- // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.+ // overWriteAlertDone: will be called when the alert is dismissed. It will call startQueue. } else { /* if there are no jobs in the queue, then add this one to the queue and rip otherwise, just rip the queue */- if( hb_count( fHandle ) == 0)+ if( [fQueueController pendingCount] == 0) { [self doAddToQueue]; } NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];- [self doRip];+ [fQueueController startQueue: sender]; } }@@ -1667,155 +1604,117 @@ { /* if there are no jobs in the queue, then add this one to the queue and rip otherwise, just rip the queue */- if( hb_count( fHandle ) == 0 )+ if( [fQueueController pendingCount] == 0 ) { [self doAddToQueue]; } NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];- [self doRip];+ [fQueueController startQueue: nil]; } }-- (void) remindUserOfSleepOrShutdown+//------------------------------------------------------------------------------------+// Calls HBQueueController stopQueue: to request stopping of the queue. Cancel:+// returns immediately after making the request.+//------------------------------------------------------------------------------------+- (IBAction)Cancel: (id)sender {- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])- {- /*Warn that computer will sleep after encoding*/- int reminduser;- NSBeep();- reminduser = NSRunAlertPanel(@"The computer will sleep after encoding is done.",@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);- [NSApp requestUserAttention:NSCriticalRequest];- if ( reminduser == NSAlertAlternateReturn ) - {- [self showPreferencesWindow:NULL];- }- } - else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])- {- /*Warn that computer will shut down after encoding*/- int reminduser;- NSBeep();- reminduser = NSRunAlertPanel(@"The computer will shut down after encoding is done.",@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);- [NSApp requestUserAttention:NSCriticalRequest];- if ( reminduser == NSAlertAlternateReturn ) - {- [self showPreferencesWindow:NULL];- }- }-+ [fQueueController stopQueue: fWindow]; }--- (void) doRip-{- /* Let libhb do the job */- hb_start( fHandle );- /*set the fEncodeState State */- fEncodeState = 1;-}---- //-------------------------------------------------------------------------------------// Removes all jobs from the queue. Does not cancel the current processing job.+// Calls HBQueueController pauseQueue: or resumeQueue: depending on the current state+// of libhb. //-------------------------------------------------------------------------------------- (void) doDeleteQueuedJobs+- (IBAction) Pause: (id) sender {- hb_job_t * job;- while( ( job = hb_job( fHandle, 0 ) ) )- hb_rem( fHandle, job );+ hb_state_t s;+ hb_get_state2( fHandle, &s );++ if( s.state == HB_STATE_PAUSED )+ [fQueueController resumeQueue:sender];+ else+ [fQueueController pauseQueue:sender]; } //-------------------------------------------------------------------------------------// Cancels the current job and proceeds with the next one in the queue.+// Displays alert reminder user that their computer will be put to sleep or shutdown+// after the current set of encodes completes. //-------------------------------------------------------------------------------------- (void) doCancelCurrentJob+- (void) remindUserOfSleepOrShutdown {- // Stop the current job. hb_stop will only cancel the current pass and then set- // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we- // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the- // remaining passes of the job and then start the queue back up if there are any- // remaining jobs.- - [fQueueController hblibWillStop];- hb_stop( fHandle );- fEncodeState = 2; // don't alert at end of processing since this was a cancel- + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])+ {+ /*Warn that computer will sleep after encoding*/+ int reminduser;+ NSBeep();+ reminduser = NSRunAlertPanel(@"Your computer will sleep after encoding has completed.",+ @"You have selected to sleep the computer after encoding. To turn off sleeping, use HandBrake preferences.",+ nil, @"Preferences...", nil);+ if ( reminduser == NSAlertAlternateReturn ) + [self showPreferencesWindow:NULL];+ } + else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])+ {+ /*Warn that computer will shut down after encoding*/+ int reminduser;+ NSBeep();+ reminduser = NSRunAlertPanel(@"Your computer will shut down after encoding has completed.",+ @"You have selected to shut down the computer after encoding. To turn off shut down, use HandBrake preferences.",+ nil, @"Preferences...", nil);+ if ( reminduser == NSAlertAlternateReturn ) + [self showPreferencesWindow:NULL];+ } } //-------------------------------------------------------------------------------------// Displays an alert asking user if the want to cancel encoding of current job.-// Cancel: returns immediately after posting the alert. Later, when the user-// acknowledges the alert, doCancelCurrentJob is called.+// Performs actions required after encodes have been completed. //-------------------------------------------------------------------------------------- (IBAction)Cancel: (id)sender+- (void) performPostProcessingTasks {- if (!fHandle) return;+ // If Growl Notification or Window and Growl has been selected+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+ {+ [self showGrowlDoneNotification: NULL];+ }- hb_job_t * job = hb_current_job(fHandle);- if (!job) return;-- NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),- [NSString stringWithUTF8String:job->title->name]];+ // If Alert Window or Window and Growl has been selected+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])+ {+ NSBeep();+ NSRunAlertPanel(@"HandBrake has finished its encoding tasks.", @"More information is available in the Queue window.", nil, nil, nil);+ }- // Which window to attach the sheet to?- NSWindow * docWindow;- if ([sender respondsToSelector: @selector(window)])- docWindow = [sender window];- else- docWindow = fWindow;- - NSBeginCriticalAlertSheet(- alertTitle,- NSLocalizedString(@"Keep Encoding", nil),- NSLocalizedString(@"Delete All", nil),- NSLocalizedString(@"Stop Encoding", nil),- docWindow, self,- nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,- NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil),- [NSString stringWithUTF8String:job->title->name]);+ // If sleep has been selected+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) + { + NSDictionary* errorDict; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: @"tell application \"Finder\" to sleep"]; + [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; + }- // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed-- // N.B.: didDimissCancelCurrentJob:returnCode:contextInfo: is designated as the dismiss- // selector to prevent a crash. As a dismiss selector, the alert window will- // have already be dismissed. If we don't do it this way, the dismissing of- // the alert window will cause the table view to be redrawn at a point where- // current job has been deleted by hblib but we don't know about it yet. This- // is a prime example of wy we need to NOT be relying on hb_current_job!!!!-}--- (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo-{- if (returnCode == NSAlertOtherReturn)- [self doCancelCurrentJob];- else if (returnCode == NSAlertAlternateReturn)- {- [self doDeleteQueuedJobs];- [self doCancelCurrentJob];+ // If Shutdown has been selected+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) + { + NSDictionary* errorDict; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: @"tell application \"Finder\" to shut down"]; + [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; }-}------ (IBAction) Pause: (id) sender-{- hb_state_t s;- hb_get_state2( fHandle, &s );-- if( s.state == HB_STATE_PAUSED )+ // MetaX insertion via AppleScript+ if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES) {- hb_resume( fHandle );+ NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]];+ [myScript executeAndReturnError: nil];+ [myScript release]; }- else- {- hb_pause( fHandle );- }+ } #pragma mark -Index: macosx/HandBrake.xcodeproj/project.pbxproj===================================================================--- macosx/HandBrake.xcodeproj/project.pbxproj (revision 1043)+++ macosx/HandBrake.xcodeproj/project.pbxproj (working copy)@@ -145,6 +145,10 @@ E37167A90C92FAA50072B384 /* JobPassUnknownSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = E37167A70C92FAA50072B384 /* JobPassUnknownSmall.png */; }; E37172670C977D340072B384 /* HBImageAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E37172650C977D340072B384 /* HBImageAndTextCell.m */; }; E37172680C977D340072B384 /* HBImageAndTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = E37172660C977D340072B384 /* HBImageAndTextCell.h */; };+ E3724BEA0CD2280800010454 /* HBJob.h in Headers */ = {isa = PBXBuildFile; fileRef = E3724BE90CD2280800010454 /* HBJob.h */; };+ E3724BF90CD22A8400010454 /* HBJob.m in Sources */ = {isa = PBXBuildFile; fileRef = E3724BF80CD22A8400010454 /* HBJob.m */; };+ E37274450CD4113000CFE13E /* HBJobGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = E37274440CD4113000CFE13E /* HBJobGroup.h */; };+ E372744D0CD412E100CFE13E /* HBJobGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = E372744C0CD412E100CFE13E /* HBJobGroup.m */; }; E37C89410C83988F00C1B919 /* DeleteHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893D0C83988F00C1B919 /* DeleteHighlight.png */; }; E37C89420C83988F00C1B919 /* Delete.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893E0C83988F00C1B919 /* Delete.png */; }; E37C89430C83988F00C1B919 /* JobSmall.png in Resources */ = {isa = PBXBuildFile; fileRef = E37C893F0C83988F00C1B919 /* JobSmall.png */; };@@ -163,6 +167,7 @@ E3C844F80CA6B3F90013B683 /* RevealHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F40CA6B3F90013B683 /* RevealHighlight.png */; }; E3C844F90CA6B3F90013B683 /* Reveal.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F50CA6B3F90013B683 /* Reveal.png */; }; E3C845870CA6E9080013B683 /* EncodeComplete.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C845860CA6E9080013B683 /* EncodeComplete.png */; };+ E3EFC1320CD750FE0068DF25 /* EncodeCanceled.png in Resources */ = {isa = PBXBuildFile; fileRef = E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */; }; EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; FC8519500C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; };@@ -215,7 +220,7 @@ /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 0D096DFF0B707D1200A845D4 /* libhb.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libhb.dylib; sourceTree = BUILT_PRODUCTS_DIR; };- 0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; };+ 0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0DF377970B7BF99A00115CB0 /* fakexcode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = fakexcode.cpp; path = ../test/fakexcode.cpp; sourceTree = SOURCE_ROOT; }; 0DFA5C7A0B8DD1E90020BC09 /* HandBrake.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = HandBrake.icns; sourceTree = "<group>"; }; 0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = declpcm.c; path = ../libhb/declpcm.c; sourceTree = SOURCE_ROOT; };@@ -321,6 +326,10 @@ E37167A70C92FAA50072B384 /* JobPassUnknownSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = JobPassUnknownSmall.png; sourceTree = "<group>"; }; E37172650C977D340072B384 /* HBImageAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = HBImageAndTextCell.m; sourceTree = "<group>"; }; E37172660C977D340072B384 /* HBImageAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HBImageAndTextCell.h; sourceTree = "<group>"; };+ E3724BE90CD2280800010454 /* HBJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBJob.h; sourceTree = "<group>"; };+ E3724BF80CD22A8400010454 /* HBJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBJob.m; sourceTree = "<group>"; };+ E37274440CD4113000CFE13E /* HBJobGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBJobGroup.h; sourceTree = "<group>"; };+ E372744C0CD412E100CFE13E /* HBJobGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBJobGroup.m; sourceTree = "<group>"; }; E37C893D0C83988F00C1B919 /* DeleteHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DeleteHighlight.png; sourceTree = "<group>"; }; E37C893E0C83988F00C1B919 /* Delete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Delete.png; sourceTree = "<group>"; }; E37C893F0C83988F00C1B919 /* JobSmall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = JobSmall.png; sourceTree = "<group>"; };@@ -339,6 +348,7 @@ E3C844F40CA6B3F90013B683 /* RevealHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealHighlight.png; sourceTree = "<group>"; }; E3C844F50CA6B3F90013B683 /* Reveal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Reveal.png; sourceTree = "<group>"; }; E3C845860CA6E9080013B683 /* EncodeComplete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeComplete.png; sourceTree = "<group>"; };+ E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeCanceled.png; sourceTree = "<group>"; }; EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; }; FC85194C0C59A02C0073812C /* denoise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = denoise.c; path = ../libhb/denoise.c; sourceTree = SOURCE_ROOT; }; FC85194D0C59A02C0073812C /* deinterlace.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deinterlace.c; path = ../libhb/deinterlace.c; sourceTree = SOURCE_ROOT; };@@ -544,6 +554,10 @@ A9AC41DD0C918DB500DDF9B8 /* HBAdvancedController.m */, E37172660C977D340072B384 /* HBImageAndTextCell.h */, E37172650C977D340072B384 /* HBImageAndTextCell.m */,+ E3724BE90CD2280800010454 /* HBJob.h */,+ E3724BF80CD22A8400010454 /* HBJob.m */,+ E37274440CD4113000CFE13E /* HBJobGroup.h */,+ E372744C0CD412E100CFE13E /* HBJobGroup.m */, ); name = "HandBrake Sources"; sourceTree = "<group>";@@ -600,6 +614,7 @@ E3997A260CAB58BC00287239 /* EncodeWorking3.png */, E3997A270CAB58BC00287239 /* EncodeWorking4.png */, E3997A2B0CAB58BC00287239 /* EncodeWorking5.png */,+ E3EFC1310CD750FE0068DF25 /* EncodeCanceled.png */, ); path = icons; sourceTree = "<group>";@@ -637,6 +652,8 @@ E37C89480C83989F00C1B919 /* HBQueueController.h in Headers */, A9AC41E00C918DB500DDF9B8 /* HBAdvancedController.h in Headers */, E37172680C977D340072B384 /* HBImageAndTextCell.h in Headers */,+ E3724BEA0CD2280800010454 /* HBJob.h in Headers */,+ E37274450CD4113000CFE13E /* HBJobGroup.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; };@@ -738,9 +755,11 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = 4D4E7BC4087804870051572B /* Build configuration list for PBXProject "HandBrake" */;+ compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* HandBrake */; projectDirPath = "";+ projectRoot = ""; targets = ( 4DD93F8D082036E8008E1322 /* HandBrake */, 4D1EA2D90993B01000FDC1A2 /* Instant HandBrake */,@@ -814,6 +833,7 @@ E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */, E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */, E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */,+ E3EFC1320CD750FE0068DF25 /* EncodeCanceled.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; };@@ -945,6 +965,8 @@ E37C89470C83989F00C1B919 /* HBQueueController.mm in Sources */, A9AC41DF0C918DB500DDF9B8 /* HBAdvancedController.m in Sources */, E37172670C977D340072B384 /* HBImageAndTextCell.m in Sources */,+ E3724BF90CD22A8400010454 /* HBJob.m in Sources */,+ E372744D0CD412E100CFE13E /* HBJobGroup.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };Index: macosx/Controller.h===================================================================--- macosx/Controller.h (revision 1043)+++ macosx/Controller.h (working copy)@@ -172,9 +172,6 @@ hb_handle_t * fHandle; hb_title_t * fTitle;- /* integer to set to determine the previous state- of encode 0==idle, 1==encoding, 2==cancelled*/- int fEncodeState; int currentScanCount; int currentSuccessfulScanCount; BOOL SuccessfulScan;@@ -208,8 +205,6 @@ - (IBAction) subtitleSelectionChanged: (id) sender; - (IBAction) browseFile: (id) sender;-- (void) browseFileDone: (NSSavePanel *) sheet- returnCode: (int) returnCode contextInfo: (void *) contextInfo; - (IBAction) videoMatrixChanged: (id) sender; - (IBAction) qualitySliderChanged: (id) sender;@@ -217,26 +212,12 @@ - (IBAction) showPicturePanel: (id) sender; - (IBAction) calculatePictureSizing: (id) sender; - (IBAction) openMainWindow: (id) sender;--- (IBAction) addToQueue: (id) sender;-- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet- returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void) doAddToQueue;- - (IBAction) showQueueWindow:(id)sender;+- (IBAction) showPreferencesWindow:(id)sender;-- (IBAction)showPreferencesWindow:(id)sender;-+- (IBAction) addToQueue: (id) sender; - (IBAction) Rip: (id) sender;-- (void) overWriteAlertDone: (NSWindow *) sheet- returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void) updateAlertDone: (NSWindow *) sheet- returnCode: (int) returnCode contextInfo: (void *) contextInfo;-- (void) doRip;- - (IBAction) Cancel: (id) sender;-- (void) doCancelCurrentJob;- - (IBAction) Pause: (id) sender; - (IBAction) calculateBitrate: (id) sender;@@ -305,5 +286,6 @@ - (void)setupToolbar; - (void) remindUserOfSleepOrShutdown;+- (void) performPostProcessingTasks; @endIndex: macosx/HBQueueController.mm===================================================================--- macosx/HBQueueController.mm (revision 1043)+++ macosx/HBQueueController.mm (working copy)@@ -4,9 +4,11 @@ Homepage: <http://handbrake.m0k.org/>. It may be used under the terms of the GNU General Public License. */-#include "HBQueueController.h"-#include "Controller.h"+#import "HBQueueController.h"+#import "Controller.h" #import "HBImageAndTextCell.h"+#import "HBJob.h"+#import "HBJobGroup.h" #define HB_ROW_HEIGHT_TITLE_ONLY 17.0@@ -15,27 +17,9 @@ //-------------------------------------------------------------------------------------// NSMutableAttributedString (HBAdditions)+// HBQueueOutlineView //-------------------------------------------------------------------------------------@interface NSMutableAttributedString (HBAdditions)-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary;-@end--@implementation NSMutableAttributedString (HBAdditions)-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary-{- NSAttributedString * s = [[[NSAttributedString alloc]- initWithString: aString- attributes: aDictionary] autorelease];- [self appendAttributedString: s];-}-@end--//-------------------------------------------------------------------------------------#pragma mark --//------------------------------------------------------------------------------------- @implementation HBQueueOutlineView - (void)viewDidEndLiveResize@@ -81,701 +65,27 @@ @end-//------------------------------------------------------------------------------------ #pragma mark --#pragma mark Job group functions-//-------------------------------------------------------------------------------------// These could be part of hblib if we think hblib should have knowledge of groups.-// For now, I see groups as a metaphor that HBQueueController provides.-/**- * Returns the number of jobs groups in the queue.- * @param h Handle to hb_handle_t.- * @return Number of job groups.- */-static int hb_group_count(hb_handle_t * h) -{- hb_job_t * job;- int count = 0;- int index = 0;- while( ( job = hb_job( h, index++ ) ) )- {- if (job->sequence_id == 0)- count++;- }- return count;-}+@interface HBQueueController (LibhbInteractions)-/**- * Returns handle to the first job in the i-th group within the job list.- * @param h Handle to hb_handle_t.- * @param i Index of group.- * @returns Handle to hb_job_t of desired job.- */-static hb_job_t * hb_group(hb_handle_t * h, int i) -{- hb_job_t * job;- int count = 0;- int index = 0;- while( ( job = hb_job( h, index++ ) ) )- {- if (job->sequence_id == 0)- {- if (count == i)- return job;- count++;- }- }- return NULL;-}+- (void) libhbCurrentJobChanged: (hb_job_t *) currentJob;+- (void) libhbAddJobGroup: (HBJobGroup *) aJobGroup;+- (BOOL) libhbAddNextJobGroup;-/**- * Removes a groups of jobs from the job list.- * @param h Handle to hb_handle_t.- * @param job Handle to the first job in the group.- */-static void hb_rem_group( hb_handle_t * h, hb_job_t * job )-{- // Find job in list- hb_job_t * j;- int index = 0;- while( ( j = hb_job( h, index ) ) )- {- if (j == job)- {- // Delete this job plus the following ones in the sequence- hb_rem( h, job );- while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )- hb_rem( h, j );- return;- }- else- index++;- }-}--/**- * Returns handle to the next job after the given job.- * @param h Handle to hb_handle_t.- * @param job Handle to the a job in the group.- * @returns Handle to hb_job_t of desired job or NULL if no such job.- */-static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job )-{- hb_job_t * j = NULL;- int index = 0;- while( ( j = hb_job( h, index++ ) ) )- {- if (j == job)- return hb_job( h, index );- }- return NULL;-}--#pragma mark ---//-------------------------------------------------------------------------------------// HBJob-//--------------------------------------------------------------------------------------@implementation HBJob--+ (HBJob*) jobWithJob: (hb_job_t *) job-{- return [[[HBJob alloc] initWithJob:job] autorelease];-}--- (id) initWithJob: (hb_job_t *) job-{- if (self = [super init])- {- // job is not owned by HBJob. It does not get dealloacted when HBJob is released.- hbJob = job;- }- return self; -}--- (hb_job_t*) job-{- return hbJob;-}--//-------------------------------------------------------------------------------------// Generate string to display in UI.-//--------------------------------------------------------------------------------------- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle- withIcon: (BOOL)withIcon- withTitle: (BOOL)withTitle- withPassName: (BOOL)withPassName- withFormatInfo: (BOOL)withFormatInfo- withDestination: (BOOL)withDestination- withPictureInfo: (BOOL)withPictureInfo- withVideoInfo: (BOOL)withVideoInfo- withx264Info: (BOOL)withx264Info- withAudioInfo: (BOOL)withAudioInfo- withSubtitleInfo: (BOOL)withSubtitleInfo--{- NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];- - hb_title_t * title = hbJob->title;- - // Attributes- static NSMutableParagraphStyle * ps = NULL;- if (!ps)- {- ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];- [ps setHeadIndent: 40.0];- [ps setParagraphSpacing: 1.0];- [ps setTabStops:[NSArray array]]; // clear all tabs- [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];- }-- static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:- [NSFont systemFontOfSize:10.0], NSFontAttributeName,- ps, NSParagraphStyleAttributeName,- nil] retain];- static NSDictionary* detailBoldAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:- [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName,- ps, NSParagraphStyleAttributeName,- nil] retain];- static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:- [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,- ps, NSParagraphStyleAttributeName,- nil] retain];- static NSDictionary* shortHeightAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:- [NSFont systemFontOfSize:2.0], NSFontAttributeName,- nil] retain];-- // Title with summary- if (withTitle)- {- if (withIcon)- {- NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: @"JobSmall"]] autorelease];- NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease];-- NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys:- [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName,- imageAttachment, NSAttachmentAttributeName,- ps, NSParagraphStyleAttributeName,- nil];-- NSAttributedString * imageAsString = [[[NSAttributedString alloc]- initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter]- attributes: imageAttributes] autorelease];-- [finalString appendAttributedString:imageAsString];- }- - // Note: use title->name instead of title->dvd since name is just the chosen- // folder, instead of dvd which is the full path- [finalString appendString:[NSString stringWithUTF8String:title->name] withAttributes:titleAttribute];- - NSString * summaryInfo;- - NSString * chapterString = (hbJob->chapter_start == hbJob->chapter_end) ?- [NSString stringWithFormat:@"Chapter %d", hbJob->chapter_start] :- [NSString stringWithFormat:@"Chapters %d through %d", hbJob->chapter_start, hbJob->chapter_end];-- BOOL hasIndepthScan = (hbJob->pass == -1);- int numVideoPasses = 0;-- // To determine number of video passes, we need to skip past the subtitle scan.- if (hasIndepthScan)- {- // When job is the one currently being processed, then the next in its group- // is the the first job in the queue.- hb_job_t * nextjob;- if (hbJob == hb_current_job(handle))- nextjob = hb_job(handle, 0);- else- nextjob = hb_next_job(handle, hbJob);- if (nextjob) // Overly cautious in case there is no next job!- numVideoPasses = MIN( 2, nextjob->pass + 1 );- }- else- numVideoPasses = MIN( 2, hbJob->pass + 1 );-- if (hasIndepthScan && numVideoPasses == 1)- summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, Single Video Pass)", title->index, chapterString];- else if (hasIndepthScan && numVideoPasses > 1)- summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, %d Video Passes)", title->index, chapterString, numVideoPasses];- else if (numVideoPasses == 1)- summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Single Video Pass)", title->index, chapterString];- else- summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, %d Video Passes)", title->index, chapterString, numVideoPasses];-- [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttribute];- - // Insert a short-in-height line to put some white space after the title- [finalString appendString:@"\n" withAttributes:shortHeightAttribute];- }- - // End of title stuff- -- // Pass Name- if (withPassName)- {- if (withIcon)- {- NSString * imageName;- switch (hbJob->pass)- {- case -1: imageName = @"JobPassSubtitleSmall"; break;- case 0: imageName = @"JobPassFirstSmall"; break;- case 1: imageName = @"JobPassFirstSmall"; break;- case 2: imageName = @"JobPassSecondSmall"; break;- default: imageName = @"JobPassUnknownSmall"; break;- }-- NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: imageName]] autorelease];- NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease];-- NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys:- [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName,- imageAttachment, NSAttachmentAttributeName,- ps, NSParagraphStyleAttributeName,- nil];-- NSAttributedString * imageAsString = [[[NSAttributedString alloc]- initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter]- attributes: imageAttributes] autorelease];-- [finalString appendAttributedString:imageAsString];- }- - NSString * jobPassName;- if (hbJob->pass == -1)- jobPassName = NSLocalizedString (@"Deep Scan", nil);- else- {- int passNum = MAX( 1, hbJob->pass );- if (passNum == 0)- jobPassName = NSLocalizedString (@"1st Pass", nil);- else if (passNum == 1)- jobPassName = NSLocalizedString (@"1st Pass", nil);- else if (passNum == 2)- jobPassName = NSLocalizedString (@"2nd Pass", nil);- else- jobPassName = [NSString stringWithFormat: NSLocalizedString(@"Pass %d", nil), passNum];- }- [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPassName] withAttributes:detailBoldAttribute];- }-- // Video Codec needed by FormatInfo and withVideoInfo- NSString * jobVideoCodec = nil;- if (withFormatInfo || withVideoInfo)- {- // 2097152- // Video Codec settings (Encoder in the gui)- if (hbJob->vcodec == HB_VCODEC_FFMPEG)- jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG- else if (hbJob->vcodec == HB_VCODEC_XVID)- jobVideoCodec = @"XviD"; // HB_VCODEC_XVID- else if (hbJob->vcodec == HB_VCODEC_X264)- {- // Deterimine for sure how we are now setting iPod uuid atom- if (hbJob->h264_level) // We are encoding for iPod- jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 - else- jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264- }- }- if (jobVideoCodec == nil)- jobVideoCodec = @"unknown";- - // Audio Codec needed by FormatInfo and AudioInfo- NSString * jobAudioCodec = nil;- if (withFormatInfo || withAudioInfo)- {- if (hbJob->acodec == 256)- jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC- else if (hbJob->acodec == 512)- jobAudioCodec = @"MP3"; // HB_ACODEC_LAME- else if (hbJob->acodec == 1024)- jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS- else if (hbJob->acodec == 2048)- jobAudioCodec = @"AC3"; // HB_ACODEC_AC3- }- if (jobAudioCodec == nil)- jobAudioCodec = @"unknown";--- if (withFormatInfo)- {- NSString * jobFormatInfo;- // Muxer settings (File Format in the gui)- if (hbJob->mux == 65536 || hbJob->mux == 131072 || hbJob->mux == 1048576)- jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD- else if (hbJob->mux == 262144)- jobFormatInfo = @"AVI"; // HB_MUX_AVI- else if (hbJob->mux == 524288)- jobFormatInfo = @"OGM"; // HB_MUX_OGM- else if (hbJob->mux == 2097152)- jobFormatInfo = @"MKV"; // HB_MUX_MKV- else- jobFormatInfo = @"unknown";- - if (hbJob->chapter_markers == 1)- jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio, Chapter Markers\n", jobFormatInfo, jobVideoCodec, jobAudioCodec];- else- jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio\n", jobFormatInfo, jobVideoCodec, jobAudioCodec];- - [finalString appendString: @"Format: " withAttributes:detailBoldAttribute];- [finalString appendString: jobFormatInfo withAttributes:detailAttribute];- }-- if (withDestination)- {- [finalString appendString: @"Destination: " withAttributes:detailBoldAttribute];- [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->file]] withAttributes:detailAttribute];- }--- if (withPictureInfo)- {- NSString * jobPictureInfo;- // integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height,- // maxWidth, maxHeight- if (hbJob->pixel_ratio == 1)- {- int titlewidth = title->width - hbJob->crop[2] - hbJob->crop[3];- int displayparwidth = titlewidth * hbJob->pixel_aspect_width / hbJob->pixel_aspect_height;- int displayparheight = title->height - hbJob->crop[0] - hbJob->crop[1];- jobPictureInfo = [NSString stringWithFormat:@"%dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, hbJob->width, displayparheight];- }- else- jobPictureInfo = [NSString stringWithFormat:@"%dx%d", hbJob->width, hbJob->height];- if (hbJob->keep_ratio == 1)- jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"];- - if (hbJob->grayscale == 1)- jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"];- - if (hbJob->deinterlace == 1)- jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"];- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"Picture: " withAttributes:detailBoldAttribute];- [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPictureInfo] withAttributes:detailAttribute];- }- - if (withVideoInfo)- {- NSString * jobVideoQuality;- NSString * jobVideoDetail;- - if (hbJob->vquality <= 0 || hbJob->vquality >= 1)- jobVideoQuality = [NSString stringWithFormat:@"%d kbps", hbJob->vbitrate];- else- {- NSNumber * vidQuality;- vidQuality = [NSNumber numberWithInt:hbJob->vquality * 100];- // this is screwed up kind of. Needs to be formatted properly.- if (hbJob->crf == 1)- jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality]; - else- jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality];- }- - if (hbJob->vrate_base == 1126125)- {- // NTSC FILM 23.976- jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];- }- else if (hbJob->vrate_base == 900900)- {- // NTSC 29.97- jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];- }- else- {- // Everything else- jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base];- }- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"Video: " withAttributes:detailBoldAttribute];- [finalString appendString:[NSString stringWithFormat:@"%@\n", jobVideoDetail] withAttributes:detailAttribute];- }- - if (withx264Info)- {- if (hbJob->vcodec == HB_VCODEC_X264 && hbJob->x264opts)- {- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttribute];- [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->x264opts]] withAttributes:detailAttribute];- }- }-- if (withAudioInfo)- {- NSString * jobAudioInfo;- if ([jobAudioCodec isEqualToString: @"AC3"])- jobAudioInfo = [NSString stringWithFormat:@"%@, Pass-Through", jobAudioCodec];- else- jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->arate];- - // we now get the audio mixdown info for each of the two gui audio tracks- // lets do it the long way here to get a handle on things.- // Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i]- int ai; // counter for each audios [] , macgui only allows for two audio tracks currently- for( ai = 0; ai < 2; ai++ )- {- if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_MONO)- jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono", ai + 1]];- if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)- jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo", ai + 1]];- if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)- jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround", ai + 1]];- if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII)- jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II", ai + 1]];- if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_6CH)- jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet", ai + 1]];- }- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"Audio: " withAttributes:detailBoldAttribute];- [finalString appendString:[NSString stringWithFormat:@"%@\n", jobAudioInfo] withAttributes:detailAttribute];- }- - if (withSubtitleInfo)- {- // hbJob->subtitle can == -1 in two cases:- // autoselect: when pass == -1- // none: when pass != -1- if ((hbJob->subtitle == -1) && (hbJob->pass == -1))- {- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute];- [finalString appendString: @"Autoselect " withAttributes:detailAttribute];- }- else if (hbJob->subtitle >= 0)- {- hb_subtitle_t * subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, 0 );- if (subtitle)- {- if (withIcon) // implies indent the info- [finalString appendString: @"\t" withAttributes:detailBoldAttribute];- [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute];- [finalString appendString: [NSString stringWithCString: subtitle->lang] withAttributes:detailAttribute];- }- }- }- - - if ([[finalString string] hasSuffix: @"\n"])- [finalString deleteCharactersInRange: NSMakeRange([[finalString string] length]-1, 1)];- - return finalString;-}- @end #pragma mark - //-------------------------------------------------------------------------------------// HBJobGroup+// HBQueueController //-------------------------------------------------------------------------------------@implementation HBJobGroup--+ (HBJobGroup *) jobGroup;-{- return [[[HBJobGroup alloc] init] autorelease];-}--- (id) init-{- if (self = [super init])- {- fJobs = [[NSMutableArray arrayWithCapacity:0] retain];- fDescription = [[NSMutableAttributedString alloc] initWithString: @""];- [self setNeedsDescription: NO];- fStatus = HBStatusNone;- }- return self; -}--- (void) dealloc-{- [fJobs release];- [fPath release];- [super dealloc];-}--- (unsigned int) count-{- return [fJobs count];-}--- (void) addJob: (HBJob *)aJob-{- [fJobs addObject: aJob];- [self setNeedsDescription: YES];- fLastDescriptionHeight = 0;- fLastDescriptionWidth = 0;-}--- (void) removeAllJobs-{- [fJobs removeAllObjects];-}--- (HBJob *) jobAtIndex: (unsigned)index-{- return [fJobs objectAtIndex: index];-}--- (unsigned) indexOfJob: (HBJob *)aJob;-{- return [fJobs indexOfObject: aJob];-}--- (NSEnumerator *) jobEnumerator-{- return [fJobs objectEnumerator];-}--- (void) setNeedsDescription: (BOOL)flag-{- fNeedsDescription = flag;-}--- (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle-{- fNeedsDescription = NO;-- [fDescription deleteCharactersInRange: NSMakeRange(0, [fDescription length])]; -- if ([self count] == 0)- {- NSAssert(NO, @" jobgroup with no jobs");- return;- }- - HBJob * job = [self jobAtIndex:0];- - [fDescription appendAttributedString: [job attributedDescriptionWithHBHandle: handle- withIcon: NO- withTitle: YES- withPassName: NO- withFormatInfo: YES- withDestination: YES- withPictureInfo: NO- withVideoInfo: NO- withx264Info: NO- withAudioInfo: NO- withSubtitleInfo: NO]];-- static NSAttributedString * carriageReturn = [[NSAttributedString alloc] initWithString:@"\n"];- - NSEnumerator * e = [self jobEnumerator];- while ( (job = [e nextObject]) )- {- int pass = [job job]->pass;- [fDescription appendAttributedString:carriageReturn];- [fDescription appendAttributedString:- [job attributedDescriptionWithHBHandle: handle- withIcon: YES- withTitle: NO- withPassName: YES- withFormatInfo: NO- withDestination: NO- withPictureInfo: pass != -1- withVideoInfo: pass != -1- withx264Info: pass != -1- withAudioInfo: pass == 0 || pass == 2- withSubtitleInfo: YES]];- }- -}--- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle-{- if (fNeedsDescription)- [self updateDescriptionWithHBHandle: handle];- return fDescription;-}--- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle-{- // Try to return the cached value if no changes have happened since the last time- if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription)- return fLastDescriptionHeight;- - if (fNeedsDescription)- [self updateDescriptionWithHBHandle: handle];-- // Calculate the height - NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];- fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom- fLastDescriptionWidth = width;- return fLastDescriptionHeight;--/* supposedly another way to do this, in case boundingRectWithSize isn't working- NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)];- [[tmpView textStorage] setAttributedString:aString];- [tmpView setHorizontallyResizable:NO];- [tmpView setVerticallyResizable:YES];-// [[tmpView textContainer] setHeightTracksTextView: YES];-// [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)];- [tmpView sizeToFit];- float height = [tmpView frame].size.height;- [tmpView release];- return height;-*/-}--- (float) lastDescriptionHeight-{- return fLastDescriptionHeight;-}--- (void) setStatus: (HBQueueJobGroupStatus)status-{- self->fStatus = status;-}--- (HBQueueJobGroupStatus) status-{- return self->fStatus;-}--- (void) setPath: (NSString *)path-{- [path retain];- [fPath release];- fPath = path;-}--- (NSString *) path-{- return fPath;-}--@end---#pragma mark ---@interface HBQueueController (Private)-- (void)updateQueueUI;-@end- // Toolbar identifiers static NSString* HBQueueToolbar = @"HBQueueToolbar1"; static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier"; static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier";-#pragma mark -- @implementation HBQueueController //------------------------------------------------------------------------------------@@ -793,8 +103,7 @@ nil]]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];- fCompleted = [[NSMutableArray arrayWithCapacity:0] retain];-+ BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow; NSAssert(loadSucceeded, @"Could not open Queue nib"); NSAssert(fQueueWindow, @"fQueueWindow not found in Queue nib");@@ -812,7 +121,6 @@ [fQueueWindow setDelegate:nil]; [fJobGroups release];- [fCompleted release]; [fCurrentJobGroup release]; [fSavedExpandedItems release]; [fSavedSelectedItems release];@@ -841,7 +149,6 @@ //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender {- [self updateQueueUI]; [fQueueWindow makeKeyAndOrderFront: self]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; }@@ -892,75 +199,8 @@ fCurrentJobPaneShown = showPane; }-//-------------------------------------------------------------------------------------// Rebuilds the contents of fJobGroups which is a hierarchy of HBJobGroup and HBJobs.-//-------------------------------------------------------------------------------------- (void)rebuildJobGroups+- (void) setCurrentJobGroupFromLibhbJob: (hb_job_t *)aJob {- // Currently, job groups are rdered like this:- // Completed job groups- // Current job group- // Pending job groups- - [fJobGroups autorelease];- fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];-- // Add all the completed job groups- [fJobGroups addObjectsFromArray: fCompleted];-- // Add all the completed job groups- if (fCurrentJobGroup)- [fJobGroups addObject: fCurrentJobGroup];-- // Add all the pending job groups. These come from hblib- HBJobGroup * aJobGroup = [HBJobGroup jobGroup];-- // If hblib is currently processing something, hb_group will skip over that group.- // And that's exactly what we want -- fJobGroups contains only pending job groups.-- hb_job_t * nextJob = hb_group( fHandle, 0 );- while( nextJob )- {- if (nextJob->sequence_id == 0)- {- // Encountered a new group. Add the current one to fJobGroups and then start a new one.- if ([aJobGroup count] > 0)- {- [aJobGroup setStatus: HBStatusPending];- [fJobGroups addObject: aJobGroup];- aJobGroup = [HBJobGroup jobGroup];- }- }- [aJobGroup addJob: [HBJob jobWithJob:nextJob]];- [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]];- nextJob = hb_next_job (fHandle, nextJob);- }- if ([aJobGroup count] > 0)- {- [aJobGroup setStatus: HBStatusPending];- [fJobGroups addObject:aJobGroup];- }-}--//-------------------------------------------------------------------------------------// Adds aJobGroup to the list of completed job groups, marking its status as-// HBStatusComplete.-//-------------------------------------------------------------------------------------- (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup-{- // Once hblib has completed its work, the hb_job_t objects will be freed, so we- // can't keep a reference to them.- [aJobGroup removeAllJobs];- - [aJobGroup setStatus: HBStatusComplete];- - // Put the group in the completed list for permanent storage, and also rebuild- // the master job group list which contains completed and pending groups.- [fCompleted addObject: aJobGroup];-}--- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob-{ HBJobGroup * aJobGroup = nil; // Try to find this new group in our existing job groups.@@ -974,7 +214,7 @@ NSEnumerator * jobEnum = [aJobGroup jobEnumerator]; while ( !found && (j = [jobEnum nextObject]) ) {- if ([j job] == aJob)+ if ([j libhbJob] == aJob) found = YES; } }@@ -982,13 +222,16 @@ // Or create the job group. if (!aJobGroup) {+ NSAssert (NO, @"HBQueueController could not find current libhb job.");+/* aJobGroup = [HBJobGroup jobGroup];- [aJobGroup addJob: [HBJob jobWithJob: aJob]];+ [aJobGroup addJob: [HBJob jobWithLibhbJob: aJob]]; [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]]; while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) )- [aJobGroup addJob: [HBJob jobWithJob: aJob]];+ [aJobGroup addJob: [HBJob jobWithLibhbJob: aJob]]; [aJobGroup updateDescriptionWithHBHandle: fHandle];+*/ } [aJobGroup setStatus: HBStatusWorking];@@ -1003,111 +246,30 @@ #pragma mark UI Updating //-------------------------------------------------------------------------------------// Saves the state of the items that are currently expanded. Calling restoreOutlineViewState-// will restore the state of all items to match what was saved by saveOutlineViewState.+// Marks the icon region of a job group in the queue view as needing display. //-------------------------------------------------------------------------------------- (void) saveOutlineViewState+- (void) updateJobGroupIconInQueue:(HBJobGroup*)aJobGroup {- if (!fSavedExpandedItems)- fSavedExpandedItems = [[NSMutableIndexSet alloc] init];- else- [fSavedExpandedItems removeAllIndexes];- - // NB: This code is stuffing the address of each job into an index set. While it- // works 99.9% of the time, it's not the ideal solution. We need unique ids in- // each job, possibly using the existing sequence_id field. Could use the high- // word as a unique encode id and the low word the sequence number.- - HBJobGroup * aJobGroup;- NSEnumerator * e = [fJobGroups objectEnumerator];- while ( (aJobGroup = [e nextObject]) )+ int row = [fOutlineView rowForItem: aJobGroup];+ int col = [fOutlineView columnWithIdentifier: @"icon"];+ if (row != -1 && col != -1) {- if ([fOutlineView isItemExpanded: aJobGroup])- {- if ([aJobGroup status] == HBStatusComplete)- [fSavedExpandedItems addIndex: (unsigned int)aJobGroup];- else- [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]];- }+ NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];+ [fOutlineView setNeedsDisplayInRect: frame]; }- - // Save the selection also. This is really UGLY code. Since I have to rebuild the- // entire outline hierachy every time hblib changes its job list, there's no easy- // way for me to remember the selection state other than saving off the first- // hb_job_t item in each selected group. This is done by saving the object's- // address. This could go away if I'd save a unique id in each job object.-- if (!fSavedSelectedItems)- fSavedSelectedItems = [[NSMutableIndexSet alloc] init];- else- [fSavedSelectedItems removeAllIndexes];-- NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];- int row = [selectedRows firstIndex];- while (row != NSNotFound)- {- aJobGroup = [fOutlineView itemAtRow: row];- if ([aJobGroup status] == HBStatusComplete)- [fSavedSelectedItems addIndex: (unsigned int)aJobGroup];- else- [fSavedSelectedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; - row = [selectedRows indexGreaterThanIndex: row];- }- } //-------------------------------------------------------------------------------------// Restores the expanded state of items in the outline view to match those saved by a-// previous call to saveOutlineViewState.+// Marks the entire region of a job group in the queue view as needing display. //-------------------------------------------------------------------------------------- (void) restoreOutlineViewState+- (void) updateJobGroupInQueue:(HBJobGroup*)aJobGroup {- if (fSavedExpandedItems)+ int row = [fOutlineView rowForItem: aJobGroup];+ if (row != -1) {- HBJobGroup * aJobGroup;- NSEnumerator * e = [fJobGroups objectEnumerator];- while ( (aJobGroup = [e nextObject]) )- {- if ([aJobGroup status] == HBStatusComplete)- {- if ([fSavedExpandedItems containsIndex: (unsigned int)aJobGroup])- [fOutlineView expandItem: aJobGroup];- }- else- {- hb_job_t * j = [[aJobGroup jobAtIndex:0] job];- if ([fSavedExpandedItems containsIndex: (unsigned int)j])- [fOutlineView expandItem: aJobGroup];- }- }+ NSRect frame = [fOutlineView rectOfRow:row];+ [fOutlineView setNeedsDisplayInRect: frame]; }- - if (fSavedSelectedItems)- {- // Ugh. Have to cycle through each row looking for the previously selected job.- // See the explanation in saveOutlineViewState about the logic here.- - NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease];- for (int i = 0; i < [fOutlineView numberOfRows]; i++)- {- HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i];- // Test to see if the group or the group's first job is a match- if ([aJobGroup status] == HBStatusComplete)- {- if ([fSavedSelectedItems containsIndex: (unsigned int)aJobGroup])- [rowsToSelect addIndex: i];- }- else- {- if ([fSavedSelectedItems containsIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]])- [rowsToSelect addIndex: i];- }- }- if ([rowsToSelect count] == 0)- [fOutlineView deselectAll: nil];- else- [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO];- } } //------------------------------------------------------------------------------------@@ -1116,14 +278,11 @@ //------------------------------------------------------------------------------------ - (void) animateCurrentJobGroupInQueue:(NSTimer*)theTimer {- int row = [fOutlineView rowForItem: fCurrentJobGroup];- int col = [fOutlineView columnWithIdentifier: @"icon"];- if (row != -1 && col != -1)+ if (fCurrentJobGroup) { fAnimationIndex++; fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.- NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];- [fOutlineView setNeedsDisplayInRect: frame];+ [self updateJobGroupIconInQueue: fCurrentJobGroup]; } }@@ -1163,7 +322,9 @@ if (s->state == HB_STATE_WORKING) { NSString * msg;- if (job->pass == -1)+ if (!job)+ msg = @""; // no current job, even though state says working. Means there's a transition from one job to another going on.+ else if (job->pass == -1) msg = NSLocalizedString( @"Deep Scan", nil ); else if (job->pass == 1) msg = NSLocalizedString( @"Analyzing video", nil );@@ -1180,7 +341,6 @@ } else return msg;- } else if (s->state == HB_STATE_MUXING)@@ -1318,13 +478,21 @@ - (void)updateQueueCountField { NSString * msg;- int jobCount;- - jobCount = fHandle ? hb_group_count(fHandle) : 0;- if (jobCount == 1)- msg = NSLocalizedString(@"1 pending encode", nil);+ int jobCount = [fJobGroups count];+ int pendingCount = [self pendingCount];+ if (jobCount == 0)+ msg = NSLocalizedString(@"0 encodes", nil);+ else if ((jobCount == 1) && (pendingCount == 0))+ msg = NSLocalizedString(@"1 encode", nil);+ else if (jobCount == pendingCount)+ {+ if (jobCount == 1)+ msg = NSLocalizedString(@"1 pending encode", nil);+ else+ msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), pendingCount];+ } else- msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];+ msg = [NSString stringWithFormat:NSLocalizedString(@"%d encodes (%d pending)", nil), jobCount, pendingCount]; [fQueueCountField setStringValue:msg]; }@@ -1339,7 +507,7 @@ if (job) {- HBJob * currentJob = [HBJob jobWithJob: job];+ HBJob * currentJob = [HBJob jobWithLibhbJob: job]; switch (job->pass) { case -1: // Subtitle scan@@ -1422,13 +590,86 @@ } //-------------------------------------------------------------------------------------// Refresh the UI in the queue pane. Should be called whenever the content of HB's job-// list has changed so that HBQueueController can sync up.+// Saves the state of the items that are currently expanded and selected. Calling+// restoreOutlineViewState will restore the outline view to the saved state. //-------------------------------------------------------------------------------------- (void)updateQueueUI+- (void) saveOutlineViewState {+ // Save the expanded job groups+ if (!fSavedExpandedItems)+ fSavedExpandedItems = [[NSMutableArray arrayWithCapacity:0] retain];+ else+ [fSavedExpandedItems removeAllObjects];++ HBJobGroup * aJobGroup;+ NSEnumerator * e = [fJobGroups objectEnumerator];+ while ( (aJobGroup = [e nextObject]) )+ {+ if ([fOutlineView isItemExpanded: aJobGroup])+ [fSavedExpandedItems addObject:aJobGroup];+ }++ // Save the selected job groups+ [fSavedSelectedItems release];+ NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];+ fSavedSelectedItems = [[fJobGroups objectsAtIndexes:selectedRows] retain];+}++//------------------------------------------------------------------------------------+// Restores the expanded state of items in the outline view to match those saved by a+// previous call to saveOutlineViewState.+//------------------------------------------------------------------------------------+- (void) restoreOutlineViewState+{+ // Restore the expanded job groups+ if (fSavedExpandedItems)+ {+ HBJobGroup * aJobGroup;+ NSEnumerator * e = [fJobGroups objectEnumerator];+ while ( (aJobGroup = [e nextObject]) )+ {+ if ([fSavedExpandedItems containsObject: aJobGroup])+ [fOutlineView expandItem: aJobGroup];+ }+ [fSavedExpandedItems release];+ fSavedExpandedItems = nil;+ }++ // Restore the selected job groups+ if (fSavedSelectedItems)+ {+ NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease];+ for (int i = 0; i < [fOutlineView numberOfRows]; i++)+ {+ HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i];+ if ([fSavedSelectedItems containsObject: aJobGroup])+ [rowsToSelect addIndex: i];+ }+ if ([rowsToSelect count] == 0)+ [fOutlineView deselectAll: nil];+ else+ [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO];+ [fSavedSelectedItems release];+ fSavedSelectedItems = nil;+ }+}++//------------------------------------------------------------------------------------+// Notifies HBQueuecontroller that the contents of fJobGroups is about to be modified.+// HBQueuecontroller remembers the state of the UI (selection and expanded items).+//------------------------------------------------------------------------------------+- (void) beginEditingJobGroupsArray+{ [self saveOutlineViewState];- [self rebuildJobGroups];+}++//------------------------------------------------------------------------------------+// Notifies HBQueuecontroller that modifications to fJobGroups as indicated by a prior+// call to beginEditingJobGroupsArray have been completed. HBQueuecontroller reloads+// the queue view and restores the state of the UI (selection and expanded items).+//------------------------------------------------------------------------------------+- (void) endEditingJobGroupsArray+{ [fOutlineView noteNumberOfRowsChanged]; [fOutlineView reloadData]; [self restoreOutlineViewState]; @@ -1436,46 +677,186 @@ } #pragma mark -++//------------------------------------------------------------------------------------+// Displays a modal sheet window asking the user if they want to cancel processing of+// the queue. If the didEndSelector method is specified, it is invoked after the modal+// session has ended and is passed a return code and caller specified in contextInfo.+//------------------------------------------------------------------------------------+- (void) beginStopQueueAlertModalForWindow:(NSWindow *)docWindow+ didEndSelector:(SEL)didEndSelector+ contextInfo:(void *)contextInfo+{+ NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil),+ [fCurrentJobGroup name]];++ NSBeginCriticalAlertSheet(+ alertTitle,+ NSLocalizedString(@"Keep Encoding", nil),+ nil, //NSLocalizedString(@"Delete All", nil),+ NSLocalizedString(@"Stop Encoding", nil),+ docWindow, self,+ nil, didEndSelector, contextInfo,+ NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil),+ [fCurrentJobGroup name]);+}++#pragma mark - #pragma mark Actions //------------------------------------------------------------------------------------+// Starts processing of job groups in the pending list. If processing has already been+// started, nothing happens.+//------------------------------------------------------------------------------------+- (IBAction) startQueue: (id)sender+{+ // hb_start will begin processing. We will get messages from libhb as its state+ // changes.+ hb_state_t s;+ hb_get_state2 (fHandle, &s);+ if ((s.state == HB_STATE_IDLE) || (s.state == HB_STATE_WORKDONE))+ {+ if ([self libhbAddNextJobGroup])+ {+ fStopQueueRequested = NO;+ hb_start (fHandle);+ }+ }+}++//------------------------------------------------------------------------------------+// Stops processing of job groups in the queue. The current job group will be canceled+// and its status set to HBStatusCanceled. This call returns immediately after+// scheduling the stop.+//------------------------------------------------------------------------------------+- (void) stopQueue+{+ hb_state_t s;+ hb_get_state2 (fHandle, &s);+ if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING) || (s.state == HB_STATE_PAUSED))+ {+ // Mark the current job group as canceled. Otherwise, we'd have no way of+ // knowing if the job group was canceled or completed sucessfully.+ // ************** This may not be needed anymore because there is now the+ // fStopQueueRequested flag which is used in libhbCurrentJobChanged to+ // determine if a job was canceled or not.+ if (fCurrentJobGroup)+ [fCurrentJobGroup setStatus: HBStatusCanceled];++ // Tell libhb to stop. We'll receive a HB_STATE_WORKDONE message when is has+ // finished stopping.+ fStopQueueRequested = YES;+ hb_stop (fHandle);+ }+}++//------------------------------------------------------------------------------------+// Prompts the user to verify that they want the queue to be stopped. If acknowledged,+// stopQueue will be called to cancel the current job group and stop the queue. To+// stop the queue without asking the user, use stopQueue.+//------------------------------------------------------------------------------------+- (IBAction) stopQueue: (id)sender+{+ hb_state_t s;+ hb_get_state2 (fHandle, &s);+ if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING) || (s.state == HB_STATE_PAUSED))+ {+ if (fCurrentJobGroup)+ {+ // Which window to attach the sheet to?+ NSWindow * docWindow;+ if ([sender isKindOfClass:[NSWindow class]])+ docWindow = sender;+ else if ([sender respondsToSelector: @selector(window)])+ docWindow = [sender window];+ else+ docWindow = fQueueWindow;++ [self beginStopQueueAlertModalForWindow: docWindow didEndSelector:@selector(didDimissStopQueueAlert:returnCode:contextInfo:) contextInfo:nil];++ // didDimissStopQueueAlert:returnCode:contextInfo: will be called when the dialog is dismissed++ }++ // If there wasn't a current job group (which really would be a programmatic+ // error), just call hb_stop directly.+ else+ hb_stop (fHandle);+ }+}++- (void) didDimissStopQueueAlert: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo+{+ if (returnCode == NSAlertOtherReturn)+ [self stopQueue];+}+++//------------------------------------------------------------------------------------+// Pauses the queue if it is currently active.+//------------------------------------------------------------------------------------+- (IBAction) pauseQueue: (id)sender+{+ hb_state_t s;+ hb_get_state2 (fHandle, &s);+ if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))+ hb_pause (fHandle);+}+++//------------------------------------------------------------------------------------+// Resumes the queue if it had previously been paused.+//------------------------------------------------------------------------------------+- (IBAction) resumeQueue: (id)sender+{+ hb_state_t s;+ hb_get_state2(fHandle, &s);+ if (s.state == HB_STATE_PAUSED)+ hb_resume (fHandle);+}+++//------------------------------------------------------------------------------------ // Deletes the selected jobs from HB and the queue UI //------------------------------------------------------------------------------------ - (IBAction)removeSelectedJobGroups: (id)sender { if (!fHandle) return;- NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];- int row = [selectedRows firstIndex];- if (row != NSNotFound)+ NSIndexSet * selectedRows = [[fOutlineView selectedRowIndexes] retain];+ NSArray * groupsToDelete = [fJobGroups objectsAtIndexes:selectedRows];++ // Send a message to cancel processing of the current job, if it is being deleted.+ // The cancel will not happen immediately. stopQueueAndRemoveSelected:::: will be+ // called after the user responds to the alert. selectedRows is passed as content+ // info so that stopQueueAndRemoveSelected: knows wht to delete.+ if ([groupsToDelete containsObject:fCurrentJobGroup]) {- while (row != NSNotFound)- {- HBJobGroup * jobGroup = [fOutlineView itemAtRow: row];- switch ([jobGroup status])- {- case HBStatusComplete:- case HBStatusCanceled:- [fCompleted removeObject: jobGroup];- break;- case HBStatusWorking:- [self cancelCurrentJob: sender];- break;- case HBStatusPending:- hb_job_t * job = [[jobGroup jobAtIndex: 0] job];- hb_rem_group( fHandle, job );- break;- case HBStatusNone:- break;- }- - row = [selectedRows indexGreaterThanIndex: row];- }+ [self beginStopQueueAlertModalForWindow: fQueueWindow didEndSelector:@selector(didDismissStopQueueAndRemoveSelected:returnCode:contextInfo:) contextInfo:selectedRows];+ return;+ }++ [self beginEditingJobGroupsArray];+ [fJobGroups removeObjectsAtIndexes: selectedRows];+ [selectedRows release];+ [self endEditingJobGroupsArray];+}- [self hblibJobListChanged];- } +- (void) didDismissStopQueueAndRemoveSelected: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo+{+ if (returnCode == NSAlertOtherReturn)+ {+ [self stopQueue];++ NSIndexSet * selectedRows = (NSIndexSet*)contextInfo;+ [self beginEditingJobGroupsArray];+ [fJobGroups removeObjectsAtIndexes: selectedRows];+ [selectedRows release];+ [self endEditingJobGroupsArray];+ } }+ //------------------------------------------------------------------------------------ // Reveals the file icons in the Finder of the selected job groups. //------------------------------------------------------------------------------------@@ -1504,110 +885,150 @@ // the alert. Later, when the user acknowledges the alert, HBController will call // hblib to cancel the job. //------------------------------------------------------------------------------------+// ************ is this necessary? - (IBAction)cancelCurrentJob: (id)sender {- [fHBController Cancel:sender];+ [self stopQueue: sender]; }+#pragma mark -+ //-------------------------------------------------------------------------------------// Starts or cancels the processing of jobs depending on the current state+// Queues a job group. The job group's status is set to HBStatusPending. //-------------------------------------------------------------------------------------- (IBAction)toggleStartCancel: (id)sender+- (void) addJobGroup: (HBJobGroup *) aJobGroup {- if (!fHandle) return;+ NSAssert(![fJobGroups containsObject:aJobGroup], @"Duplicate job group");+ [aJobGroup setStatus:HBStatusPending];- hb_state_t s;- hb_get_state2 (fHandle, &s);-- if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))- [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window-- else if (hb_group_count(fHandle) > 0)- [fHBController doRip];+ [self beginEditingJobGroupsArray];+ [fJobGroups addObject:aJobGroup];+ [self endEditingJobGroupsArray]; } //-------------------------------------------------------------------------------------// Toggles the pause/resume state of hblib+// Removes a job from the queue. //-------------------------------------------------------------------------------------- (IBAction)togglePauseResume: (id)sender+- (void) removeJobGroup: (HBJobGroup *) aJobGroup {- if (!fHandle) return;+ // Removal of the current job being processed is akin to stopping the queue.+ if (aJobGroup == fCurrentJobGroup)+ [self stopQueue];++ [self beginEditingJobGroupsArray];+ [fJobGroups removeObject: aJobGroup];+ [self endEditingJobGroupsArray];- hb_state_t s;- hb_get_state2 (fHandle, &s);+}- if (s.state == HB_STATE_PAUSED)- hb_resume (fHandle);- else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))- hb_pause (fHandle);+//------------------------------------------------------------------------------------+// Returns the number of job groups with a specified status.+//------------------------------------------------------------------------------------+- (unsigned int) jopGroupCountWithStatus: (HBQueueJobGroupStatus) status+{+ unsigned int count = 0;+ NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+ HBJobGroup * aJobGroup;+ while ( (aJobGroup = [groupEnum nextObject]) )+ {+ if ([aJobGroup status] == status)+ count++;+ }+ return count; }-#pragma mark --#pragma mark Synchronizing with hblib +//------------------------------------------------------------------------------------+// Returns the job group located at index in the list of job groups with status. If+// index is greater than or equal to the value returned by jopGroupCountWithStatus:,+// nil is retunred.+//------------------------------------------------------------------------------------+- (HBJobGroup *) jobGroupWithStatus: (HBQueueJobGroupStatus)status atIndex: (unsigned int)index+{+ unsigned int count = 0;+ NSEnumerator * groupEnum = [fJobGroups objectEnumerator];+ HBJobGroup * aJobGroup = nil;+ while ( (aJobGroup = [groupEnum nextObject]) )+ {+ if ([aJobGroup status] == status)+ {+ if (count == index)+ return aJobGroup;+ count++;+ }+ }+ return aJobGroup;+} //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's current job has changed+// Returns the number of job groups whose status is HBStatusPending. //-------------------------------------------------------------------------------------- (void)currentJobGroupChanged: (hb_job_t *) currentJob+- (unsigned int) pendingCount {- if (fCurrentJobGroup && [fCurrentJobGroup status] != HBStatusCanceled)- [self addCompletedJobGroup: fCurrentJobGroup];- [self setCurrentJobGroupFromJob: currentJob];- [self updateCurrentJobDescription];- [self updateCurrentJobProgress];- [self showCurrentJobPane: fCurrentJobGroup != nil];- if (fCurrentJobGroup)- [self startAnimatingCurrentJobGroupInQueue];- else- [self stopAnimatingCurrentJobGroupInQueue];+ // This value is calculated each time, not cached.+ return [self jopGroupCountWithStatus: HBStatusPending]; } //-------------------------------------------------------------------------------------// Notifies HBQueueController that hb_stop is about to be called. This signals us that-// the current job is going to be canceled and deleted. This is somewhat of a hack to-// let HBQueueController know when a job group has been cancelled. Otherwise, we'd-// have no way of knowing if a job was canceled or completed sucessfully.+// Returns the number of job groups whose status is HBStatusComplete. //-------------------------------------------------------------------------------------- (void)hblibWillStop+- (unsigned int) completedCount {- if (fCurrentJobGroup)- [fCurrentJobGroup setStatus: HBStatusCanceled];+ // This value is calculated each time, not cached.+ return [self jopGroupCountWithStatus: HBStatusComplete]; } //-------------------------------------------------------------------------------------// Notifies HBQueueController that hblib's job list has been modified+// Returns the number of job groups whose status is HBStatusCanceled. //-------------------------------------------------------------------------------------- (void)hblibJobListChanged+- (unsigned int) canceledCount {- // This message is received from HBController after it has added a job group to- // hblib's job list. It is also received from self when a job group is deleted by- // the user.- [self updateQueueUI];+ // This value is calculated each time, not cached.+ return [self jopGroupCountWithStatus: HBStatusCanceled]; } //------------------------------------------------------------------------------------+// Returns the number of job groups whose status is HBStatusWorking.+//------------------------------------------------------------------------------------+- (unsigned int) workingCount+{+ // This value is calculated each time, not cached.+ return [self jopGroupCountWithStatus: HBStatusWorking];+}++#if HB_OUTLINE_METRIC_CONTROLS+static float spacingWidth = 3.0;+- (IBAction)imageSpacingChanged: (id)sender;+{+ spacingWidth = [sender floatValue];+ [fOutlineView setNeedsDisplay: YES];+}+- (IBAction)indentChanged: (id)sender+{+ [fOutlineView setIndentationPerLevel: [sender floatValue]];+ [fOutlineView setNeedsDisplay: YES];+}+#endif++#pragma mark -++//------------------------------------------------------------------------------------ // Notifies HBQueueController that hblib's state has changed //-------------------------------------------------------------------------------------- (void)hblibStateChanged: (hb_state_t &)state+- (void)libhbStateChanged: (hb_state_t &)state { // First check to see if hblib has moved on to another job. We get no direct // message when this happens, so we have to detect it ourself. The new job could // be either just the next job in the current group, or the start of a new group.- if (fLastKnownCurrentJob != hb_current_job(fHandle))+ // When state is HB_STATE_WORKDONE, this detection is delayed until it is known+ // whether a new job group is going to be started up.+ if (state.state != HB_STATE_WORKDONE) { hb_job_t * currentJob = hb_current_job(fHandle);- if (!currentJob || currentJob->sequence_id == 0) // start of a new group+ if (fLastKnownLibHBJob != currentJob) {- [self currentJobGroupChanged: currentJob];- [self hblibJobListChanged];+ [self libhbCurrentJobChanged: currentJob];+ fLastKnownLibHBJob = currentJob; }- else- {- [self updateCurrentJobDescription];- [self updateCurrentJobProgress];- }-- fLastKnownCurrentJob = currentJob; } switch( state.state )@@ -1635,29 +1056,44 @@ case HB_STATE_WORKDONE: { // HB_STATE_WORKDONE means that hblib has finished processing all the jobs- // in *its* queue. This message is NOT sent as each individual job is+ // in *its* queue. This message is NOT sent as each individual job/pass is // completed.- }++ // HB_STATE_WORKDONE also happpens as a result of someone calling hb_stop.+ // In this latter case, hb_stop does not clear out the remaining+ // passes/jobs in the queue. We'll do that here.++ // Delete all remaining items in libhb's queue.+ hb_job_t * job;+ while( ( job = hb_job(fHandle, 0) ) )+ hb_rem( fHandle, job );- }+ // Start processing the next job group in our queue+ BOOL restartQueue = !fStopQueueRequested && ([self pendingCount] > 0);+ if (restartQueue && [self libhbAddNextJobGroup])+ {+ fStopQueueRequested = NO;+ hb_start(fHandle);+ }+ else+ { + [self libhbCurrentJobChanged: nil];+ fLastKnownLibHBJob = nil;++ // Notify user. Doing this via performSelectorOnMainThread so that+ // HBController gets a chance to clean up from state HB_STATE_WORKDONE+ // before a possibly modal notification happens in performPostProcessingTasks.+ if (!fStopQueueRequested)+ [fHBController performSelectorOnMainThread:@selector(performPostProcessingTasks) withObject:nil waitUntilDone:NO];++ fStopQueueRequested = NO;+ }+ }+ } }-#if HB_OUTLINE_METRIC_CONTROLS-static float spacingWidth = 3.0;-- (IBAction)imageSpacingChanged: (id)sender;-{- spacingWidth = [sender floatValue];- [fOutlineView setNeedsDisplay: YES];-}-- (IBAction)indentChanged: (id)sender-{- [fOutlineView setIndentationPerLevel: [sender floatValue]];- [fOutlineView setNeedsDisplay: YES];-}-#endif- #pragma mark - #pragma mark Toolbar@@ -1702,13 +1138,13 @@ [toolbarItem setLabel: @"Start"]; [toolbarItem setPaletteLabel: @"Start/Cancel"];- // Set up a reasonable tooltip, and image+ // Set up a reasonable tooltip, and image. These will change dynamically by validateToolbarItem. [toolbarItem setToolTip: @"Start Encoding"]; [toolbarItem setImage: [NSImage imageNamed: @"Play"]];- - // Tell the item what message to send when it is clicked ++ // Set a default target/action. These will change dynamically by validateToolbarItem. [toolbarItem setTarget: self];- [toolbarItem setAction: @selector(toggleStartCancel:)];+ [toolbarItem setAction: @selector(startQueue:)]; } if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])@@ -1722,10 +1158,10 @@ // Set up a reasonable tooltip, and image [toolbarItem setToolTip: @"Pause Encoding"]; [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];- - // Tell the item what message to send when it is clicked ++ // Set a default target/action. These will change dynamically by validateToolbarItem. [toolbarItem setTarget: self];- [toolbarItem setAction: @selector(togglePauseResume:)];+ [toolbarItem setAction: @selector(pauseQueue:)]; } return toolbarItem;@@ -1776,25 +1212,26 @@ BOOL enable = NO;- hb_state_t s;- hb_get_state2 (fHandle, &s);- if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier]) {- if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))+ if (fCurrentJobGroup) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Stop"]]; [toolbarItem setLabel: @"Stop"]; [toolbarItem setToolTip: @"Stop Encoding"];+ [toolbarItem setTarget: self];+ [toolbarItem setAction: @selector(stopQueue:)]; }- else if (hb_count(fHandle) > 0)+ else if ([self pendingCount] > 0) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; [toolbarItem setLabel: @"Start"]; [toolbarItem setToolTip: @"Start Encoding"];+ [toolbarItem setTarget: self];+ [toolbarItem setAction: @selector(startQueue:)]; } else@@ -1808,13 +1245,18 @@ if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier]) {+ hb_state_t s;+ hb_get_state2 (fHandle, &s);+ if (s.state == HB_STATE_PAUSED) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; [toolbarItem setLabel: @"Resume"]; [toolbarItem setToolTip: @"Resume Encoding"];- }+ [toolbarItem setTarget: self];+ [toolbarItem setAction: @selector(resumeQueue:)];+ } else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) {@@ -1822,7 +1264,9 @@ [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; [toolbarItem setLabel: @"Pause"]; [toolbarItem setToolTip: @"Pause Encoding"];- }+ [toolbarItem setTarget: self];+ [toolbarItem setAction: @selector(pauseQueue:)];+ } else { enable = NO;@@ -2008,6 +1452,9 @@ case HBStatusWorking: return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; break;+ case HBStatusCanceled:+ return [NSImage imageNamed:@"EncodeCanceled"];+ break; default: return [NSImage imageNamed:@"JobSmall"]; break;@@ -2093,13 +1540,14 @@ } // Don't retain since this is just holding temporaral drag information, and it is- //only used during a drag! We could put this in the pboard actually.+ // only used during a drag! We could put this in the pboard actually. fDraggedNodes = items; // Provide data for our custom type, and simple NSStrings. [pboard declareTypes:[NSArray arrayWithObjects: HBQueuePboardType, nil] owner:self];- // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.+ // the actual data doesn't matter since DragDropSimplePboardType drags aren't+ // recognized by anyone but us. [pboard setData:[NSData data] forType:HBQueuePboardType]; return YES;@@ -2122,7 +1570,7 @@ } // Prevent dragging into the completed or current job.- int firstPendingIndex = [fCompleted count];+ int firstPendingIndex = [self completedCount]; if (fCurrentJobGroup) firstPendingIndex++; index = MAX (index, firstPendingIndex);@@ -2145,10 +1593,9 @@ } // Rearrange the data and view- [self saveOutlineViewState];+ [self beginEditingJobGroupsArray]; [self moveObjectsInArray:fJobGroups fromIndexes:moveItems toIndex: index];- [fOutlineView reloadData];- [self restoreOutlineViewState];+ [self endEditingJobGroupsArray]; return YES; }@@ -2156,3 +1603,84 @@ @end+++#pragma mark -++@implementation HBQueueController (LibhbInteractions)++//------------------------------------------------------------------------------------+// Notifies HBQueueController that hblib's current job has changed+//------------------------------------------------------------------------------------+- (void)libhbCurrentJobChanged: (hb_job_t *) currentJob+{+ if (!currentJob || currentJob->sequence_id == 0) // start of a new group+ {+ // Here's our only chance to change the current job group's status after it is+ // finished.+ if (fCurrentJobGroup)+ {+ // If user canceled, reset the current job group back to pending so it+ // is in the pendng queue.+ if (fStopQueueRequested)+ {+ [fCurrentJobGroup setStatus: HBStatusPending];+ [self updateJobGroupInQueue: fCurrentJobGroup];+ }+ else+ {+ [fCurrentJobGroup setStatus:HBStatusComplete];+ [self updateJobGroupInQueue: fCurrentJobGroup];+ }+ }+ [self setCurrentJobGroupFromLibhbJob: currentJob];+ [self updateCurrentJobDescription];+ [self updateCurrentJobProgress];+ [self updateQueueCountField];+ [self showCurrentJobPane: fCurrentJobGroup != nil];+ if (fCurrentJobGroup)+ [self startAnimatingCurrentJobGroupInQueue];+ else+ [self stopAnimatingCurrentJobGroupInQueue];+ }+ else+ {+ [self updateCurrentJobDescription];+ [self updateCurrentJobProgress];+ }+}++//------------------------------------------------------------------------------------+// Adds the individual jobs in a job group to libhb's list of jobs to process.+//------------------------------------------------------------------------------------+- (void) libhbAddJobGroup: (HBJobGroup *) aJobGroup+{+ NSEnumerator * jobEnum = [aJobGroup jobEnumerator];+ HBJob * aJob;+ while ( (aJob = [jobEnum nextObject]) )+ {+ // Note that hb_add generates a copy of the libhb job.+ hb_job_t * job_copy = hb_add (fHandle, [aJob libhbJob]);+ [aJob setLibhbJob: job_copy];+ }+}++//------------------------------------------------------------------------------------+// Adds the individual jobs in the first pending job group to libhb's list of jobs to+// process. Return YES if a job group was added, NO if no pending job groups exist.+//------------------------------------------------------------------------------------+- (BOOL) libhbAddNextJobGroup+{+ if ([self pendingCount] > 0)+ {+ HBJobGroup * aJobGroup = [self jobGroupWithStatus: HBStatusPending atIndex: 0];+ [self libhbAddJobGroup: aJobGroup];+ return YES;+ }+ else+ return NO;+}++@end // HBQueueController (LibhbInteractions)++