static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier";
static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier";
-/**
- * Returns the number of jobs groups in the queue.
- * @param h Handle to hb_handle_t.
- * @return Number of job groups.
- */
-static int hb_group_count(hb_handle_t * h)
-{
- hb_job_t * job;
- int count = 0;
- int index = 0;
- while( ( job = hb_job( h, index++ ) ) )
- {
- if (job->sequence_id == 0)
- count++;
- }
- return count;
-}
/*******************************
* HBController implementation *
[HBPreferencesController registerUserDefaults];
fHandle = NULL;
outputPanel = [[HBOutputPanelController alloc] init];
- fPictureController = [[PictureController alloc] init];
+ fPictureController = [[PictureController alloc] initWithDelegate:self];
fQueueController = [[HBQueueController alloc] init];
fAdvancedOptions = [[HBAdvancedController alloc] init];
+ fPreferencesController = [[HBPreferencesController alloc] init];
return self;
}
/* Init others controllers */
[fPictureController SetHandle: fHandle];
[fQueueController setHandle: fHandle];
+ [fQueueController setHBController: self];
fChapterTitlesDelegate = [[ChapterTitles alloc] init];
[fChapterTable setDataSource:fChapterTitlesDelegate];
- /* Call UpdateUI every 2/10 sec */
+ /* Call UpdateUI every 1/2 sec */
[[NSRunLoop currentRunLoop] addTimer: [NSTimer
- scheduledTimerWithTimeInterval: 0.2 target: self
- selector: @selector( updateUI: ) userInfo: NULL repeats: FALSE]
- forMode: NSModalPanelRunLoopMode];
+ scheduledTimerWithTimeInterval: 0.5 target: self
+ selector: @selector( updateUI: ) userInfo: NULL repeats: YES]
+ forMode: NSEventTrackingRunLoopMode];
if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
{
withObject: NULL waitUntilDone: NO];
}
-- (NSApplicationTerminateReply) applicationShouldTerminate:
- (NSApplication *) app
+- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
{
+ // Warn if encoding a movie
hb_state_t s;
- hb_get_state2( fHandle, &s );
- if ( s.state == HB_STATE_WORKING )
+ hb_get_state( fHandle, &s );
+ hb_job_t * job = hb_current_job( fHandle );
+ if ( job && ( s.state != HB_STATE_IDLE ) )
{
- [self Cancel: NULL];
- return NSTerminateCancel;
- }
+ hb_job_t * job = hb_current_job( fHandle );
+ int result = NSRunCriticalAlertPanel(
+ NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
+ NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil),
+ NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil,
+ job ? [NSString stringWithUTF8String:job->title->name] : @"A movie" );
+
+ if (result == NSAlertDefaultReturn)
+ {
+ [self doCancelCurrentJob];
+ return NSTerminateNow;
+ }
+ else
+ return NSTerminateCancel;
+ }
+
+ // Warn if items still in the queue
+ else if ( hb_count( fHandle ) > 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(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
+
+ if ( result == NSAlertDefaultReturn )
+ return NSTerminateNow;
+ else
+ return NSTerminateCancel;
+ }
+
return NSTerminateNow;
}
[fWindow center];
[fWindow setExcludedFromWindowsMenu:YES];
[fAdvancedOptions setView:fAdvancedView];
- /* set the main menu bar so it doesnt auto enable the menu items
- so we can manually do it with setEnabled: This should be changed
- to use validateUserInterfaceItem: along with setAutoEnablesItems: YES
- in the next release */
- [fMenuBarFileMenu setAutoenablesItems: NO];
- [fMenuBarWindowMenu setAutoenablesItems: NO];
- [fMenuPauseEncode setEnabled: NO];
- [self TranslateStrings];
+
/* Initialize currentScanCount so HB can use it to
evaluate successive scans */
currentScanCount = 0;
[item setLabel: @"Toggle Presets"];
[item setPaletteLabel: @"Toggler Presets"];
[item setToolTip: @"Open/Close Preset Drawer"];
- [item setImage: [NSImage imageNamed: @"Drawer-List2"]];
+ [item setImage: [NSImage imageNamed: @"Drawer"]];
[item setTarget: self];
[item setAction: @selector(toggleDrawer:)];
[item setAutovalidates: NO];
[item setLabel: @"Show Queue"];
[item setPaletteLabel: @"Show Queue"];
[item setToolTip: @"Show Queue"];
- [item setImage: [NSImage imageNamed: @"Brushed Window"]];
+ [item setImage: [NSImage imageNamed: @"Queue"]];
[item setTarget: self];
[item setAction: @selector(showQueueWindow:)];
[item setAutovalidates: NO];
[item setLabel: @"Add to Queue"];
[item setPaletteLabel: @"Add to Queue"];
[item setToolTip: @"Add to Queue"];
- [item setImage: [NSImage imageNamed: @"Add"]];
+ [item setImage: [NSImage imageNamed: @"AddToQueue"]];
[item setTarget: self];
[item setAction: @selector(addToQueue:)];
}
[item setLabel: @"Activity Window"];
[item setPaletteLabel: @"Show Activity Window"];
[item setToolTip: @"Show Activity Window"];
- [item setImage: [NSImage imageNamed: @"Terminal"]];
+ [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
[item setTarget: self];
[item setAction: @selector(showDebugOutputPanel:)];
[item setAutovalidates: NO];
[item setLabel: @"Source"];
[item setPaletteLabel: @"Source"];
[item setToolTip: @"Choose Video Source"];
- [item setImage: [NSImage imageNamed: @"Disc"]];
+ [item setImage: [NSImage imageNamed: @"Source"]];
[item setTarget: self];
[item setAction: @selector(showScanPanel:)];
- [item setAutovalidates: NO];
}
else
{
{
return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
ChooseSourceIdentifier, ShowQueueIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
- NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier,
+ NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
}
if ([ident isEqualToString: StartEncodingIdentifier])
{
[toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
- [toolbarItem setLabel: @"Cancel"];
- [toolbarItem setPaletteLabel: @"Cancel"];
- [toolbarItem setToolTip: @"Cancel Encoding"];
+ [toolbarItem setLabel: @"Stop"];
+ [toolbarItem setPaletteLabel: @"Stop"];
+ [toolbarItem setToolTip: @"Stop Encoding"];
return YES;
}
if ([ident isEqualToString: PauseEncodingIdentifier])
if ([ident isEqualToString: StartEncodingIdentifier])
{
[toolbarItem setImage: [NSImage imageNamed: @"Play"]];
- [toolbarItem setLabel: @"Start"];
+ if (hb_count(fHandle) > 0)
+ [toolbarItem setLabel: @"Start Queue"];
+ else
+ [toolbarItem setLabel: @"Start"];
[toolbarItem setPaletteLabel: @"Start Encoding"];
[toolbarItem setToolTip: @"Start Encoding"];
return YES;
return NO;
}
+- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
+{
+ SEL action = [menuItem action];
+
+ hb_state_t s;
+ hb_get_state2( fHandle, &s );
+
+ if (fHandle)
+ {
+ if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
+ return SuccessfulScan && [fWindow attachedSheet] == nil;
+
+ if (action == @selector(showScanPanel:))
+ {
+ if (s.state == HB_STATE_SCANNING)
+ return NO;
+ else
+ return [fWindow attachedSheet] == nil;
+ }
+ if (action == @selector(selectDefaultPreset:))
+ return [tableView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
+ if (action == @selector(Pause:))
+ {
+ if (s.state == HB_STATE_WORKING)
+ {
+ if(![[menuItem title] isEqualToString:@"Pause Encoding"])
+ [menuItem setTitle:@"Pause Encoding"];
+ return YES;
+ }
+ else if (s.state == HB_STATE_PAUSED)
+ {
+ if(![[menuItem title] isEqualToString:@"Resume Encoding"])
+ [menuItem setTitle:@"Resume Encoding"];
+ return YES;
+ }
+ else
+ return NO;
+ }
+ if (action == @selector(Rip:))
+ if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
+ {
+ if(![[menuItem title] isEqualToString:@"Stop Encoding"])
+ [menuItem setTitle:@"Stop Encoding"];
+ return YES;
+ }
+ else if (SuccessfulScan)
+ {
+ if(![[menuItem title] isEqualToString:@"Start Encoding"])
+ [menuItem setTitle:@"Start Encoding"];
+ return [fWindow attachedSheet] == nil;
+ }
+ else
+ return NO;
+ }
+
+ return YES;
+}
+
+
// register a test notification and make
// it enabled by default
#define SERVICE_NAME @"Encode Done"
- (NSDictionary *)registrationDictionaryForGrowl
{
-NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
-[NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
-[NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
-nil];
+ NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
+ [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
+ nil];
-return registrationDictionary;
+ return registrationDictionary;
}
+
- (void) TranslateStrings
{
[fSrcTitleField setStringValue: _( @"Title:" )];
- (void) updateUI: (NSTimer *) timer
{
-hb_list_t * list;
-list = hb_get_titles( fHandle );
+ hb_list_t * list;
+ list = hb_get_titles( fHandle );
/* check to see if there has been a new scan done
this bypasses the constraints of HB_STATE_WORKING
not allowing setting a newly scanned source */
[self showNewScan: NULL];
}
- BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
-
hb_state_t s;
hb_get_state( fHandle, &s );
[fRipIndicator setDoubleValue: 100.0 * progress_total];
// If progress bar hasn't been revealed at the bottom of the window, do
- // that now. This code used to be in _Rip. I moved it to here to handle
+ // that now. This code used to be in doRip. I moved it to here to handle
// the case where hb_start is called by HBQueueController and not from
// HBController.
if (!fRipIndicatorShown)
frame.origin.y -= 36;
[fWindow setFrame:frame display:YES animate:YES];
fRipIndicatorShown = YES;
+ /* We check to see if we need to warn the user that the computer will go to sleep
+ or shut down when encoding is finished */
+ [self remindUserOfSleepOrShutdown];
}
/* Update dock icon */
[self UpdateDockIcon: progress_total];
- /* Main Menu controls */
- [fMenuPauseEncode setTitle: @"Pause Encode"];
- [fMenuStartEncode setTitle: @"Cancel Encode"];
-
// Has current job changed? That means the queue has probably changed as
// well so update it
if (fLastKnownCurrentJob != hb_current_job(fHandle))
case HB_STATE_PAUSED:
[fStatusField setStringValue: _( @"Paused" )];
- [fMenuPauseEncode setTitle: @"Resume Encode"];
-
// Pass along the info to HBQueueController
[fQueueController updateCurrentJobUI];
case HB_STATE_WORKDONE:
{
- [fStatusField setStringValue: _( @"Done." )];
- [fRipIndicator setIndeterminate: NO];
- [fRipIndicator setDoubleValue: 0.0];
- [toolbar validateVisibleItems];
-
- /* Main Menu Controls*/
- [fMenuPauseEncode setTitle: @"Pause Encode"];
- [fMenuPauseEncode setEnabled: NO];
- [fMenuStartEncode setTitle: @"Start Encode"];
- /* Restore dock icon */
- [self UpdateDockIcon: -1.0];
-
- if (jobGroups)
- {
- // Delete all remaining scans of this job
- hb_job_t * job;
- while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) )
- hb_rem( fHandle, job );
- }
+ // 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 );
// Start processing back up if jobs still left in queue
if (hb_count(fHandle) > 0)
{
hb_start(fHandle);
+ fEncodeState = 1;
+ // Validate the toolbar (hack). The toolbar will usually get autovalidated
+ // before we had the chance to restart the queue, hence it will now be in
+ // the wrong state.
+ [toolbar validateVisibleItems];
break;
}
-
+
+ [fStatusField setStringValue: _( @"Done." )];
+ [fRipIndicator setIndeterminate: NO];
+ [fRipIndicator setDoubleValue: 0.0];
+ [toolbar validateVisibleItems];
+
+ /* Restore dock icon */
+ [self UpdateDockIcon: -1.0];
+
if (fRipIndicatorShown)
{
NSRect frame = [fWindow frame];
/*On Screen Notification*/
int status;
NSBeep();
- status = NSRunAlertPanel(@"Put down that cocktail...",@"your HandBrake encode is done!", @"OK", nil, nil);
+ status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);
[NSApp requestUserAttention:NSCriticalRequest];
if ( status == NSAlertDefaultReturn )
{
}
/* Lets show the queue status here in the main window */
- int queue_count = jobGroups ? hb_group_count( fHandle ) : hb_count( fHandle );
+ int queue_count = hb_count( fHandle );
if( queue_count )
{
[fQueueStatus setStringValue: [NSString stringWithFormat:
- @"%d task%s in the queue",
- queue_count, ( queue_count > 1 ) ? "s" : ""]];
+ @"%d pass%s in the queue",
+ queue_count, ( queue_count > 1 ) ? "es" : ""]];
}
else
{
[fQueueStatus setStringValue: @""];
}
-
- [[NSRunLoop currentRunLoop] addTimer: [NSTimer
- scheduledTimerWithTimeInterval: 0.5 target: self
- selector: @selector( updateUI: ) userInfo: NULL repeats: FALSE]
- forMode: NSModalPanelRunLoopMode];
}
- (IBAction) showNewScan:(id)sender
{
/* We display a message if a valid dvd source was not chosen */
[fSrcDVD2Field setStringValue: @"No Valid Title Found"];
- [fMenuOpenSource setEnabled: YES];
SuccessfulScan = 0;
}
else
if this is the first successful scan since launch and whether
or not we should set all settings to the defaults */
- SuccessfulScan = 1;
currentSuccessfulScanCount++;
- [self enableUI: YES];
- /* Enable/Disable Menu Controls Accordingly */
- [fMenuOpenSource setEnabled: YES];
- [fMenuStartEncode setEnabled: YES];
- [fMenuAddToQueue setEnabled: YES];
- [fMenuPicturePanelShow setEnabled: YES];
- [fMenuQueuePanelShow setEnabled: YES];
[toolbar validateVisibleItems];
[fSrcTitlePopUp removeAllItems];
for( int i = 0; i < hb_list_count( list ); i++ )
{
title = (hb_title_t *) hb_list_item( list, i );
- /*Set DVD Name at top of window*/
- [fSrcDVD2Field setStringValue:[NSString stringWithUTF8String: title->name]];
-
- currentSource = [NSString stringWithUTF8String: title->dvd];
+ currentSource = [NSString stringWithUTF8String: title->dvd];
+
+ /* To get the source name as well as the default output name, first we check to see if
+ the selected directory is the VIDEO_TS Directory */
+ if ([[currentSource lastPathComponent] isEqualToString: @"VIDEO_TS"])
+ {
+ /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name */
+ sourceDisplayName = [NSString stringWithFormat:[[currentSource stringByDeletingLastPathComponent] lastPathComponent]];
+ }
+ else
+ {
+ /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
+ sourceDisplayName = [NSString stringWithFormat:[currentSource lastPathComponent]];
+ }
+ /*Set DVD Name at top of window*/
+ [fSrcDVD2Field setStringValue:[NSString stringWithFormat: @"%@", sourceDisplayName]];
/* Use the dvd name in the default output field here
May want to add code to remove blank spaces for some dvd names*/
if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
{
[fDstFile2Field setStringValue: [NSString stringWithFormat:
- @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[NSString
- stringWithUTF8String: title->name]]];
+ @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],sourceDisplayName]];
}
else
{
[fDstFile2Field setStringValue: [NSString stringWithFormat:
- @"%@/Desktop/%@.mp4", NSHomeDirectory(),[NSString
- stringWithUTF8String: title->name]]];
+ @"%@/Desktop/%@.mp4", NSHomeDirectory(),sourceDisplayName]];
}
[self titlePopUpChanged: NULL];
-
+ SuccessfulScan = 1;
[self enableUI: YES];
- /* we record the current source name here in case the next scan is unsuccessful,
- then we can replace the scan progress with the old name if necessary */
- sourceDisplayName = [NSString stringWithFormat:[fSrcDVD2Field stringValue]];
-
- /* if its the initial successful scan after awakeFromNib */
+ /* if its the initial successful scan after awakeFromNib */
if (currentSuccessfulScanCount == 1)
{
[self selectDefaultPreset: NULL];
- (IBAction) showScanPanel: (id) sender
{
- /* Enable/Disable Menu Controls Accordingly */
- [fMenuOpenSource setEnabled: NO];
- [fMenuStartEncode setEnabled: NO];
- [fMenuAddToQueue setEnabled: NO];
-
- [fMenuPicturePanelShow setEnabled: NO];
- [fMenuQueuePanelShow setEnabled: NO];
[self enableUI: NO];
[self browseSources:NULL];
}
else // User clicked Cancel in browse window
{
- /* use the outlets to the main menu bar to determine what to
- enable and disable */
- [fMenuOpenSource setEnabled: YES];
- [fMenuQueuePanelShow setEnabled: YES];
/* if we have a title loaded up */
if ([[fSrcDVD2Field stringValue] length] > 0)
{
- [fMenuAddToQueue setEnabled: YES];
- [fMenuStartEncode setEnabled: YES];
- [fMenuPicturePanelShow setEnabled: YES];
- [self enableUI: YES];
+ [self enableUI: YES];
}
}
}
}
[self qualitySliderChanged: sender];
- [self calculateBitrate: sender];
+ [self calculateBitrate: sender];
[self customSettingUsed: sender];
}
if( returnCode == NSOKButton )
{
[fDstFile2Field setStringValue: [sheet filename]];
-
}
}
- (IBAction) showPicturePanel: (id) sender
{
- /* Enable/Disable Menu Controls Accordingly */
- [fMenuOpenSource setEnabled: NO];
- [fMenuStartEncode setEnabled: NO];
- [fMenuAddToQueue setEnabled: NO];
-
- [fMenuPicturePanelShow setEnabled: NO];
- [fMenuQueuePanelShow setEnabled: NO];
-
hb_list_t * list = hb_get_titles( fHandle );
hb_title_t * title = (hb_title_t *) hb_list_item( list,
[fSrcTitlePopUp indexOfSelectedItem] );
-
[fPictureController showPanelInWindow:fWindow forTitle:title];
+}
- [fMenuOpenSource setEnabled: YES];
- [fMenuStartEncode setEnabled: YES];
- [fMenuAddToQueue setEnabled: YES];
-
- [fMenuPicturePanelShow setEnabled: YES];
- [fMenuQueuePanelShow setEnabled: YES];
-
- [self calculatePictureSizing: sender];
+- (void)pictureSettingsDidChange {
+ [self calculatePictureSizing: NULL];
}
- (void) PrepareJob
+/* addToQueue: puts up an alert before ultimately calling doAddToQueue
+*/
- (IBAction) addToQueue: (id) sender
{
-/* We get the destination directory from the destingation field here */
+ /* 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;
}
- else
- {
-
- 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;
-
- // Assign a sequence number, starting at zero, to each job added so they can
- // be lumped together in the UI.
- job->sequence_id = -1;
-
- [self PrepareJob];
-
- /* Destination file */
- job->file = [[fDstFile2Field stringValue] UTF8String];
- if( [fSubForcedCheck state] == NSOnState )
- {
- job->subtitle_force = 1;
- } else {
- job->subtitle_force = 0;
- }
+ /* 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];
+ }
+}
- /*
- * subtitle of -1 is a scan
- */
- if( job->subtitle == -1 )
- {
- char *x264opts_tmp;
-
- /*
- * When subtitle scan is enabled do a fast pre-scan job
- * which will determine which subtitles to enable, if any.
- */
- job->pass = -1;
- x264opts_tmp = job->x264opts;
- job->subtitle = -1;
-
- job->x264opts = NULL;
-
- job->subtitle_scan = 1;
-
- job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
- *(job->select_subtitle) = NULL;
-
- /*
- * Add the pre-scan job
- */
- job->sequence_id++; // for job grouping
- hb_add( fHandle, job );
-
- job->x264opts = x264opts_tmp;
- } else {
- job->select_subtitle = NULL;
- }
+/* 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];
+}
- /* No subtitle were selected, so reset the subtitle to -1 (which before
- * this point meant we were scanning
- */
- if( job->subtitle == -2 )
- {
- job->subtitle = -1;
- }
+- (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;
- if( [fVidTwoPassCheck state] == NSOnState )
- {
- hb_subtitle_t **subtitle_tmp = job->select_subtitle;
- job->select_subtitle = NULL;
- job->subtitle_scan = 0;
+ // Assign a sequence number, starting at zero, to each job added so they can
+ // be lumped together in the UI.
+ job->sequence_id = -1;
- job->pass = 1;
- job->sequence_id++; // for job grouping
- hb_add( fHandle, job );
-
- job->pass = 2;
- job->sequence_id++; // for job grouping
-
- job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
- strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
+ [self PrepareJob];
- job->select_subtitle = subtitle_tmp;
+ /* Destination file */
+ job->file = [[fDstFile2Field stringValue] UTF8String];
- hb_add( fHandle, job );
- }
- else
- {
- job->subtitle_scan = 0;
- job->pass = 0;
- job->sequence_id++; // for job grouping
- hb_add( fHandle, job );
- }
+ if( [fSubForcedCheck state] == NSOnState )
+ job->subtitle_force = 1;
+ else
+ job->subtitle_force = 0;
+
+ /*
+ * subtitle of -1 is a scan
+ */
+ if( job->subtitle == -1 )
+ {
+ char *x264opts_tmp;
+
+ /*
+ * When subtitle scan is enabled do a fast pre-scan job
+ * which will determine which subtitles to enable, if any.
+ */
+ job->pass = -1;
+ x264opts_tmp = job->x264opts;
+ job->subtitle = -1;
+
+ job->x264opts = NULL;
+
+ job->indepth_scan = 1;
+
+ job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
+ *(job->select_subtitle) = NULL;
+
+ /*
+ * Add the pre-scan job
+ */
+ job->sequence_id++; // for job grouping
+ hb_add( fHandle, job );
+
+ job->x264opts = x264opts_tmp;
+ }
+ else
+ job->select_subtitle = NULL;
+
+ /* No subtitle were selected, so reset the subtitle to -1 (which before
+ * this point meant we were scanning
+ */
+ if( job->subtitle == -2 )
+ job->subtitle = -1;
+
+ if( [fVidTwoPassCheck state] == NSOnState )
+ {
+ hb_subtitle_t **subtitle_tmp = job->select_subtitle;
+ job->indepth_scan = 0;
+
+ job->pass = 1;
+ job->sequence_id++; // for job grouping
+ hb_add( fHandle, job );
+
+ job->pass = 2;
+ job->sequence_id++; // for job grouping
+
+ job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
+ strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
+
+ job->select_subtitle = subtitle_tmp;
+
+ hb_add( fHandle, job );
+ }
+ else
+ {
+ job->indepth_scan = 0;
+ job->pass = 0;
+ job->sequence_id++; // for job grouping
+ hb_add( fHandle, job );
+ }
+ NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
[[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
/* Lets try to update stuff, taken from remove in the queue controller */
[fQueueController performSelectorOnMainThread: @selector( updateQueueUI )
withObject: NULL waitUntilDone: NO];
- }
}
+/* Rip: puts up an alert before ultimately calling doRip
+*/
- (IBAction) Rip: (id) sender
{
/* Rip or Cancel ? */
[self Cancel: sender];
return;
}
- /* if there is no job in the queue, then add it to the queue and rip
- otherwise, there are already jobs in queue, so just rip the queue */
- int count = hb_count( fHandle );
- if( count < 1 )
- {
- [self addToQueue: sender];
- }
- /* We check for duplicate name here */
- if( [[NSFileManager defaultManager] fileExistsAtPath:
- [fDstFile2Field stringValue]] )
+ // If there are jobs in the queue, then this is a rip the queue
+
+ if (hb_count( fHandle ) > 0)
+ {
+ [self doRip];
+ return;
+ }
+
+ // Before adding jobs to the queue, check for a valid destination.
+
+ NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
+ 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,
NULL, NULL, [NSString stringWithFormat:
_( @"Do you want to overwrite %@?" ),
[fDstFile2Field stringValue]] );
- return;
+
+ // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
}
- /* 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);
- }
- else
- {
- [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
- [self _Rip];
- }
-
-
+ 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)
+ {
+ [self doAddToQueue];
+ }
+ NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
+ [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
+ [self doRip];
+ }
}
+/* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
+ want to overwrite an exiting movie file.
+*/
- (void) overWriteAlertDone: (NSWindow *) sheet
returnCode: (int) returnCode contextInfo: (void *) contextInfo
{
if( returnCode == NSAlertAlternateReturn )
{
- [self _Rip];
+ /* 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 )
+ {
+ [self doAddToQueue];
+ }
+
+ NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
+ [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
+ [self doRip];
}
}
[NSApp terminate: self];
}
-- (void) _Rip
+- (void) remindUserOfSleepOrShutdown
+{
+ 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];
+ }
+ }
+
+}
+
+
+- (void) doRip
{
/* Let libhb do the job */
hb_start( fHandle );
/*set the fEncodeState State */
fEncodeState = 1;
+}
+
+
- [fMenuPauseEncode setEnabled: YES];
+
+//------------------------------------------------------------------------------------
+// Removes all jobs from the queue. Does not cancel the current processing job.
+//------------------------------------------------------------------------------------
+- (void) doDeleteQueuedJobs
+{
+ hb_job_t * job;
+ while( ( job = hb_job( fHandle, 0 ) ) )
+ hb_rem( fHandle, job );
}
-- (IBAction) Cancel: (id) sender
+//------------------------------------------------------------------------------------
+// Cancels the current job and proceeds with the next one in the queue.
+//------------------------------------------------------------------------------------
+- (void) doCancelCurrentJob
{
- NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
- _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
- @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
- _( @"Encoding won't be recoverable." ) );
+ // 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.
+
+ hb_stop( fHandle );
+ fEncodeState = 2; // don't alert at end of processing since this was a cancel
+
}
-- (void) _Cancel: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo
+//------------------------------------------------------------------------------------
+// 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.
+//------------------------------------------------------------------------------------
+- (IBAction)Cancel: (id)sender
{
- if( returnCode == NSAlertAlternateReturn )
+ if (!fHandle) return;
+
+ 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]];
+
+ // 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]);
+
+ // 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)
{
- hb_stop( fHandle );
- /*set the fEncodeState State */
- fEncodeState = 2;
+ [self doDeleteQueuedJobs];
+ [self doCancelCurrentJob];
}
}
+
+
+
+
- (IBAction) Pause: (id) sender
{
hb_state_t s;
/* Pixel Ratio Setting */
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
{
-
job->pixel_ratio = 1 ;
}
else
}
-
hb_list_t * list = hb_get_titles( fHandle );
hb_title_t * title = (hb_title_t *)
hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
- (IBAction) formatPopUpChanged: (id) sender
{
NSString * string = [fDstFile2Field stringValue];
+ NSString * selectedCodecs = [fDstCodecsPopUp titleOfSelectedItem];
int format = [fDstFormatPopUp indexOfSelectedItem];
char * ext = NULL;
/* Initially set the large file (64 bit formatting) output checkbox to hidden */
_( @"AVC/H.264 Video / MP3 Audio" )];
[fDstCodecsPopUp addItemWithTitle:
_( @"AVC/H.264 Video / Vorbis Audio" )];
- /* We disable the create chapters checkbox here since we are NOT .mp4
- and make sure it is unchecked*/
+ /* We enable the create chapters checkbox here since */
[fCreateChapterMarkers setEnabled: YES];
break;
}
+ if ( SuccessfulScan ) {
+ [fDstCodecsPopUp selectItemWithTitle:selectedCodecs];
+ if ( [fDstCodecsPopUp selectedItem] == NULL )
+ [fDstCodecsPopUp selectItemAtIndex:0];
+ }
[self codecsPopUpChanged: NULL];
/* Add/replace to the correct extension */
int audioCodecsSupportMono = ((audio->codec == HB_ACODEC_AC3 ||
audio->codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC);
int audioCodecsSupport6Ch = ((audio->codec == HB_ACODEC_AC3 ||
- audio->codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC);
+ audio->codec == HB_ACODEC_DCA) && (acodec == HB_ACODEC_FAAC ||
+ acodec == HB_ACODEC_VORBIS));
/* check for AC-3 passthru */
if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
break;
case HB_ACODEC_VORBIS:
+ if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
+ {
+ /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
+ minbitrate = 192;
+ /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
+ maxbitrate = 384;
+ break;
+ }
+ else
+ {
/* Vorbis causes a crash if we use a bitrate below 48 kbps */
minbitrate = 48;
/* Vorbis can cope with 384 kbps quite happily, even for stereo */
maxbitrate = 384;
break;
+ }
default:
/* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
{
if ([fSubPopUp indexOfSelectedItem] == 0)
{
- [fSubForcedCheck setState: NSOffState];
- [fSubForcedCheck setEnabled: NO];
+ [fSubForcedCheck setState: NSOffState];
+ [fSubForcedCheck setEnabled: NO];
}
else
{
- [fSubForcedCheck setEnabled: YES];
+ [fSubForcedCheck setEnabled: YES];
}
}
- (IBAction) calculateBitrate: (id) sender
{
- if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
+ if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
{
return;
}
[fVidBitrateField setIntValue: hb_calc_bitrate( job,
[fVidTargetSizeField intValue] )];
-
-
}
/* Method to determine if we should change the UI
[fPresetSelectedDisplay setStringValue: @"Custom"];
curUserPresetChosenNum = nil;
-
-
}
}
- (IBAction) showAddPresetPanel: (id) sender
{
/* Deselect the currently selected Preset if there is one*/
- [tableView deselectRow:[tableView selectedRow]];
+ [tableView deselectRow:[tableView selectedRow]];
- /* Populate the preset picture settings popup here */
- [fPresetNewPicSettingsPopUp removeAllItems];
- [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
- [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
- [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
- [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];
+ /* Populate the preset picture settings popup here */
+ [fPresetNewPicSettingsPopUp removeAllItems];
+ [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
+ [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
+ [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
+ [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];
- /* Erase info from the input fields fPresetNewDesc*/
+ /* Erase info from the input fields fPresetNewDesc*/
[fPresetNewName setStringValue: @""];
[fPresetNewDesc setStringValue: @""];
/* Show the panel */
- [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow
- modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
- [NSApp runModalForWindow: fAddPresetPanel];
- [NSApp endSheet: fAddPresetPanel];
- [fAddPresetPanel orderOut: self];
-
-
+ [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
}
+
- (IBAction) closeAddPresetPanel: (id) sender
{
- [NSApp stopModal];
+ [NSApp endSheet: fAddPresetPanel];
+ [fAddPresetPanel orderOut: self];
}
-
- (IBAction)addUserPreset:(id)sender
{
-
- /* Here we create a custom user preset */
- [UserPresets addObject:[self createPreset]];
- /* Erase info from the input fields */
- [fPresetNewName setStringValue: @""];
- [fPresetNewDesc setStringValue: @""];
- /* We stop the modal window for the new preset */
- [NSApp stopModal];
- [self addPreset];
-
-
+ if (![[fPresetNewName stringValue] length])
+ NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
+ else
+ {
+ /* Here we create a custom user preset */
+ [UserPresets addObject:[self createPreset]];
+ [self addPreset];
+
+ [self closeAddPresetPanel:NULL];
+ }
}
- (void)addPreset
{
/* if there is a description for the preset, we show it in the tooltip */
if ([[UserPresets objectAtIndex:rowIndex] valueForKey:@"PresetDescription"])
{
- loc_tip = [NSString stringWithFormat: @"%@",[[UserPresets objectAtIndex:rowIndex] valueForKey:@"PresetDescription"]];
- return (loc_tip);
+ loc_tip = [NSString stringWithFormat: @"%@",[[UserPresets objectAtIndex:rowIndex] valueForKey:@"PresetDescription"]];
+ return (loc_tip);
}
else
{
- loc_tip = @"No description available";
+ loc_tip = @"No description available";
}
return (loc_tip);
[self savePreset];
}
-
- (void)savePreset
{
[UserPresets writeToFile:UserPresetsFile atomically:YES];
}
-
-
-- (void) controlTextDidBeginEditing: (NSNotification *) notification
-{
- [self calculateBitrate: NULL];
-}
-
-- (void) controlTextDidEndEditing: (NSNotification *) notification
-{
- [self calculateBitrate: NULL];
-}
-
- (void) controlTextDidChange: (NSNotification *) notification
{
[self calculateBitrate: NULL];
}
/**
- * Creates preferences controller, shows preferences window modally, and
- * releases the controller after user has closed the window.
+ * Shows preferences window modally.
*/
-- (IBAction)showPreferencesWindow:(id)sender
+- (IBAction) showPreferencesWindow: (id) sender
{
- HBPreferencesController *controller = [[HBPreferencesController alloc] init];
- [controller runModal:nil];
- [controller release];
+ NSWindow * window = [fPreferencesController window];
+ if (![window isVisible])
+ [window center];
+
+ [window makeKeyAndOrderFront: nil];
}
/**