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