1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 #import "HBDVDDetector.h"
13 #import "HBPreviewController.h"
15 unsigned int maximumNumberOfAllowedAudioTracks = 24;
16 NSString *HBContainerChangedNotification = @"HBContainerChangedNotification";
17 NSString *keyContainerTag = @"keyContainerTag";
18 NSString *HBTitleChangedNotification = @"HBTitleChangedNotification";
19 NSString *keyTitleTag = @"keyTitleTag";
21 #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType"
23 /* We setup the toolbar values here ShowPreviewIdentifier */
24 static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier";
25 static NSString * StartEncodingIdentifier = @"Start Encoding Item Identifier";
26 static NSString * PauseEncodingIdentifier = @"Pause Encoding Item Identifier";
27 static NSString * ShowQueueIdentifier = @"Show Queue Item Identifier";
28 static NSString * AddToQueueIdentifier = @"Add to Queue Item Identifier";
29 static NSString * ShowPictureIdentifier = @"Show Picture Window Item Identifier";
30 static NSString * ShowPreviewIdentifier = @"Show Preview Window Item Identifier";
31 static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier";
32 static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier";
35 /*******************************
36 * HBController implementation *
37 *******************************/
38 @implementation HBController
40 + (unsigned int) maximumNumberOfAllowedAudioTracks { return maximumNumberOfAllowedAudioTracks; }
50 /* replace bundled app icon with one which is 32/64-bit savvy */
51 #if defined( __LP64__ )
52 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]];
54 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]];
56 if( fApplicationIcon != nil )
57 [NSApp setApplicationIconImage:fApplicationIcon];
59 [HBPreferencesController registerUserDefaults];
61 fQueueEncodeLibhb = NULL;
62 /* Check for check for the app support directory here as
63 * outputPanel needs it right away, as may other future methods
65 NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
67 YES ) objectAtIndex:0];
68 AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
69 stringByAppendingPathComponent:@"HandBrake"];
70 if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
72 [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
75 /* Check for and create the App Support Preview directory if necessary */
76 NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"];
77 if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
79 [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
82 outputPanel = [[HBOutputPanelController alloc] init];
83 fPictureController = [[PictureController alloc] init];
84 fQueueController = [[HBQueueController alloc] init];
85 fAdvancedOptions = [[HBAdvancedController alloc] init];
86 /* we init the HBPresets class which currently is only used
87 * for updating built in presets, may move more functionality
90 fPresetsBuiltin = [[HBPresets alloc] init];
91 fPreferencesController = [[HBPreferencesController alloc] init];
92 /* Lets report the HandBrake version number here to the activity log and text log file */
93 NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
94 [self writeToActivityLog: "%s", [versionStringFull UTF8String]];
100 - (void) applicationDidFinishLaunching: (NSNotification *) notification
102 /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
103 int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
104 fHandle = hb_init(loggingLevel, 0);
105 /* Optional dvd nav UseDvdNav*/
106 hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]);
107 /* Init a separate instance of libhb for user scanning and setting up jobs */
108 fQueueEncodeLibhb = hb_init(loggingLevel, 0);
110 // Set the Growl Delegate
111 [GrowlApplicationBridge setGrowlDelegate: self];
112 /* Init others controllers */
113 [fPictureController SetHandle: fHandle];
114 [fPictureController setHBController: self];
116 [fQueueController setHandle: fQueueEncodeLibhb];
117 [fQueueController setHBController: self];
119 fChapterTitlesDelegate = [[ChapterTitles alloc] init];
120 [fChapterTable setDataSource:fChapterTitlesDelegate];
121 [fChapterTable setDelegate:fChapterTitlesDelegate];
123 /* setup the subtitles delegate and connections to table */
124 fSubtitlesDelegate = [[HBSubtitles alloc] init];
125 [fSubtitlesTable setDataSource:fSubtitlesDelegate];
126 [fSubtitlesTable setDelegate:fSubtitlesDelegate];
127 [fSubtitlesTable setRowHeight:25.0];
129 /* setup the audio controller */
130 [fAudioDelegate setHBController: self];
131 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(autoSetM4vExtension:) name: HBMixdownChangedNotification object: nil];
133 [fPresetsOutlineView setAutosaveName:@"Presets View"];
134 [fPresetsOutlineView setAutosaveExpandedItems:YES];
136 dockIconProgress = 0;
138 /* Init QueueFile .plist */
139 [self loadQueueFile];
140 /* Run hbInstances to get any info on other instances as well as set the
141 * pid number for this instance in the case of multi-instance encoding. */
142 hbInstanceNum = [self hbInstances];
144 /* If we are a single instance it is safe to clean up the previews if there are any
145 * left over. This is a bit of a kludge but will prevent a build up of old instance
146 * live preview cruft. No danger of removing an active preview directory since they
147 * are created later in HBPreviewController if they don't exist at the moment a live
148 * preview encode is initiated. */
149 if (hbInstanceNum == 1)
151 NSString *PreviewDirectory = [NSString stringWithFormat:@"~/Library/Application Support/HandBrake/Previews"];
152 PreviewDirectory = [PreviewDirectory stringByExpandingTildeInPath];
154 NSArray *files = [ [NSFileManager defaultManager] contentsOfDirectoryAtPath: PreviewDirectory error: &error ];
155 for( NSString *file in files )
157 if( file != @"." && file != @".." )
159 [ [NSFileManager defaultManager] removeItemAtPath: [ PreviewDirectory stringByAppendingPathComponent: file ] error: &error ];
162 //an error occurred...
163 [self writeToActivityLog: "Could not remove existing preview at : %s",[file UTF8String] ];
172 /* Call UpdateUI every 1/2 sec */
174 [[NSRunLoop currentRunLoop] addTimer:[NSTimer
175 scheduledTimerWithTimeInterval:0.5
177 selector:@selector(updateUI:)
178 userInfo:nil repeats:YES]
179 forMode:NSDefaultRunLoopMode];
182 // Open debug output window now if it was visible when HB was closed
183 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
184 [self showDebugOutputPanel:nil];
186 // Open queue window now if it was visible when HB was closed
187 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
188 [self showQueueWindow:nil];
190 [self openMainWindow:nil];
192 /* We have to set the bool to tell hb what to do after a scan
193 * Initially we set it to NO until we start processing the queue
195 applyQueueToScan = NO;
197 /* Now we re-check the queue array to see if there are
198 * any remaining encodes to be done in it and ask the
199 * user if they want to reload the queue */
200 if ([QueueFileArray count] > 0)
202 /* run getQueueStats to see whats in the queue file */
203 [self getQueueStats];
204 /* this results in these values
205 * fEncodingQueueItem = 0;
207 * fCompletedCount = 0;
208 * fCanceledCount = 0;
212 /*On Screen Notification*/
213 NSString * alertTitle;
215 /* We check to see if there is already another instance of hb running.
216 * Note: hbInstances == 1 means we are the only instance of HandBrake.app
218 if (hbInstanceNum > 1)
220 alertTitle = [NSString stringWithFormat:
221 NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
222 NSBeginCriticalAlertSheet(
224 NSLocalizedString(@"Reload Queue", nil),
228 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
229 NSLocalizedString(@" HandBrake will now load up the existing queue.", nil));
233 if (fWorkingCount > 0 || fPendingCount > 0)
235 if (fWorkingCount > 0)
237 alertTitle = [NSString stringWithFormat:
238 NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item(s) and %d Pending Item(s) In Your Queue.", @""),
239 fWorkingCount,fPendingCount];
243 alertTitle = [NSString stringWithFormat:
244 NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
248 NSBeginCriticalAlertSheet(
250 NSLocalizedString(@"Reload Queue", nil),
252 NSLocalizedString(@"Empty Queue", nil),
254 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
255 NSLocalizedString(@" Do you want to reload them ?", nil));
259 /* Since we addressed any pending or previously encoding items above, we go ahead and make sure the queue
260 * is empty of any finished items or cancelled items */
261 [self clearQueueAllItems];
262 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
263 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
265 [self browseSources:nil];
268 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
270 [self browseSources:(id)fOpenSourceTitleMMenu];
278 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
279 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
281 [self browseSources:nil];
284 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
286 [self browseSources:(id)fOpenSourceTitleMMenu];
289 currentQueueEncodeNameString = @"";
293 #pragma mark Multiple Instances
295 /* hbInstances checks to see if other instances of HB are running and also sets the pid for this instance for multi-instance queue encoding */
297 /* Note for now since we are in early phases of multi-instance I have put in quite a bit of logging. Can be removed as we see fit. */
300 /* check to see if another instance of HandBrake.app is running */
301 NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications];
302 NSDictionary *runningAppsDictionary;
304 NSString * thisInstanceAppPath = [[NSBundle mainBundle] bundlePath];
305 NSString * runningInstanceAppPath;
306 int runningInstancePidNum;
307 [self writeToActivityLog: "hbInstances path to this instance: %s", [thisInstanceAppPath UTF8String]];
308 for (runningAppsDictionary in runningAppDictionaries)
310 if ([[runningAppsDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"])
312 /*Report the path to each active instances app path */
313 runningInstancePidNum = [[runningAppsDictionary valueForKey:@"NSApplicationProcessIdentifier"] intValue];
314 runningInstanceAppPath = [runningAppsDictionary valueForKey:@"NSApplicationPath"];
315 [self writeToActivityLog: "hbInstance found instance pidnum:%d at path: %s", runningInstancePidNum, [runningInstanceAppPath UTF8String]];
316 /* see if this is us by comparing the app path */
317 if ([runningInstanceAppPath isEqualToString: thisInstanceAppPath])
319 /* If so this is our pidnum */
320 [self writeToActivityLog: "hbInstance MATCH FOUND, our pidnum is:%d", runningInstancePidNum];
321 /* Get the PID number for this hb instance, used in multi instance encoding */
322 pidNum = runningInstancePidNum;
323 /* Report this pid to the activity log */
324 [self writeToActivityLog: "Pid for this instance:%d", pidNum];
325 /* Tell fQueueController what our pidNum is */
326 [fQueueController setPidNum:pidNum];
341 - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
344 [self writeToActivityLog: "didDimissReloadQueue number of hb instances:%d", hbInstanceNum];
345 if (returnCode == NSAlertOtherReturn)
347 [self writeToActivityLog: "didDimissReloadQueue NSAlertOtherReturn Chosen"];
348 [self clearQueueAllItems];
350 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
351 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
353 [self browseSources:nil];
356 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
358 [self browseSources:(id)fOpenSourceTitleMMenu];
363 [self writeToActivityLog: "didDimissReloadQueue First Button Chosen"];
364 if (hbInstanceNum == 1)
367 [self setQueueEncodingItemsAsPending];
369 [self showQueueWindow:NULL];
373 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
378 hb_get_state( fQueueEncodeLibhb, &s );
380 if ( s.state != HB_STATE_IDLE )
382 int result = NSRunCriticalAlertPanel(
383 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
384 NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil),
385 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" );
387 if (result == NSAlertDefaultReturn)
389 return NSTerminateNow;
392 return NSTerminateCancel;
395 // Warn if items still in the queue
396 else if ( fPendingCount > 0 )
398 int result = NSRunCriticalAlertPanel(
399 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
400 NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil),
401 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
403 if ( result == NSAlertDefaultReturn )
404 return NSTerminateNow;
406 return NSTerminateCancel;
409 return NSTerminateNow;
412 - (void)applicationWillTerminate:(NSNotification *)aNotification
414 [currentQueueEncodeNameString release];
415 [browsedSourceDisplayName release];
416 [outputPanel release];
417 [fQueueController release];
418 [fPreviewController release];
419 [fPictureController release];
420 [fApplicationIcon release];
423 hb_close(&fQueueEncodeLibhb);
429 - (void) awakeFromNib
432 [fWindow setExcludedFromWindowsMenu:NO];
434 [fAdvancedOptions setView:fAdvancedView];
436 /* lets setup our presets drawer for drag and drop here */
437 [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
438 [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
439 [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
441 /* Initialize currentScanCount so HB can use it to
442 evaluate successive scans */
443 currentScanCount = 0;
446 /* Init UserPresets .plist */
449 fRipIndicatorShown = NO; // initially out of view in the nib
451 /* For 64 bit builds, the threaded animation in the progress
452 * indicators conflicts with the animation in the advanced tab
453 * for reasons not completely clear. jbrjake found a note in the
454 * 10.5 dev notes regarding this possiblility. It was also noted
455 * that unless specified, setUsesThreadedAnimation defaults to true.
456 * So, at least for now we set the indicator animation to NO for
457 * both the scan and regular progress indicators for both 32 and 64 bit
458 * as it test out fine on both and there is no reason our progress indicators
459 * should require their own thread.
462 [fScanIndicator setUsesThreadedAnimation:NO];
463 [fRipIndicator setUsesThreadedAnimation:NO];
467 /* Show/Dont Show Presets drawer upon launch based
468 on user preference DefaultPresetsDrawerShow*/
469 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 )
471 [fPresetDrawer setDelegate:self];
472 NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults]
473 stringForKey:@"Drawer Size"] );
474 if( drawerSize.width )
475 [fPresetDrawer setContentSize: drawerSize];
476 [fPresetDrawer open];
479 /* Initially set the dvd angle widgets to hidden (dvdnav only) */
480 [fSrcAngleLabel setHidden:YES];
481 [fSrcAnglePopUp setHidden:YES];
483 /* Setup the start / stop popup */
484 [fEncodeStartStopPopUp removeAllItems];
485 [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"];
486 [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"];
487 [fEncodeStartStopPopUp addItemWithTitle: @"Frames"];
488 /* Align the start / stop widgets with the chapter popups */
489 [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
490 [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
492 [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
493 [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
496 NSMenuItem *menuItem;
497 [fDstFormatPopUp removeAllItems];
499 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
500 [menuItem setTag: HB_MUX_MP4];
502 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
503 [menuItem setTag: HB_MUX_MKV];
505 [fDstFormatPopUp selectItemAtIndex: 0];
507 [self formatPopUpChanged:nil];
509 /* We enable the create chapters checkbox here since we are .mp4 */
510 [fCreateChapterMarkers setEnabled: YES];
511 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
513 [fCreateChapterMarkers setState: NSOnState];
519 [fDstFile2Field setStringValue: [NSString stringWithFormat:
520 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
523 [fVidEncoderPopUp removeAllItems];
524 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
528 [fVidBitrateField setIntValue: 1000];
529 [fVidQualityMatrix selectCell: fVidBitrateCell];
530 [self videoMatrixChanged:nil];
532 /* Video framerate */
533 [fVidRatePopUp removeAllItems];
534 [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
535 for( int i = 0; i < hb_video_rates_count; i++ )
537 if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
539 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
540 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]];
542 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
544 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
545 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]];
547 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
549 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
550 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]];
554 [fVidRatePopUp addItemWithTitle:
555 [NSString stringWithUTF8String: hb_video_rates[i].string]];
558 [fVidRatePopUp selectItemAtIndex: 0];
560 /* Set Auto Crop to On at launch */
561 [fPictureController setAutoCrop:YES];
564 [fStatusField setStringValue: @""];
569 /* We disable the Turbo 1st pass checkbox since we are not x264 */
570 [fVidTurboPassCheck setEnabled: NO];
571 [fVidTurboPassCheck setState: NSOffState];
574 /* lets get our default prefs here */
575 [self getDefaultPresets:nil];
576 /* lets initialize the current successful scancount here to 0 */
577 currentSuccessfulScanCount = 0;
580 - (void) enableUI: (bool) b
582 NSControl * controls[] =
583 { fSrcTitleField, fSrcTitlePopUp,
584 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
585 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
586 fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
587 fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
588 fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp,
589 fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
590 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
591 fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
592 fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,
593 fSrcFrameEndEncodingField, fLoadChaptersButton, fSaveChaptersButton, fFrameratePfrCheck};
596 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
598 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
600 NSTextField * tf = (NSTextField *) controls[i];
601 if( ![tf isBezeled] )
603 [tf setTextColor: b ? [NSColor controlTextColor] :
604 [NSColor disabledControlTextColor]];
608 [controls[i] setEnabled: b];
615 /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
616 [self calculatePictureSizing:nil];
617 /* Also enable the preview window hud controls */
618 [fPictureController enablePreviewHudControls];
623 [fPresetsOutlineView setEnabled: NO];
624 [fPictureController disablePreviewHudControls];
627 [self videoMatrixChanged:nil];
628 [fAdvancedOptions enableUI:b];
632 /***********************************************************************
634 ***********************************************************************
635 * Shows a progression bar on the dock icon, filled according to
636 * 'progress' (0.0 <= progress <= 1.0).
637 * Called with progress < 0.0 or progress > 1.0, restores the original
639 **********************************************************************/
640 - (void) UpdateDockIcon: (float) progress
643 NSBitmapImageRep * bmp;
645 uint32_t black = htonl( 0x000000FF );
646 uint32_t red = htonl( 0xFF0000FF );
647 uint32_t white = htonl( 0xFFFFFFFF );
648 int row_start, row_end;
651 if( progress < 0.0 || progress > 1.0 )
653 [NSApp setApplicationIconImage: fApplicationIcon];
657 /* Get it in a raw bitmap form */
658 tiff = [fApplicationIcon TIFFRepresentationUsingCompression:
659 NSTIFFCompressionNone factor: 1.0];
660 bmp = [NSBitmapImageRep imageRepWithData: tiff];
662 /* Draw the progression bar */
663 /* It's pretty simple (ugly?) now, but I'm no designer */
665 row_start = 3 * (int) [bmp size].height / 4;
666 row_end = 7 * (int) [bmp size].height / 8;
668 for( i = row_start; i < row_start + 2; i++ )
670 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
671 for( j = 0; j < (int) [bmp size].width; j++ )
676 for( i = row_start + 2; i < row_end - 2; i++ )
678 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
681 for( j = 2; j < (int) [bmp size].width - 2; j++ )
683 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
695 for( i = row_end - 2; i < row_end; i++ )
697 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
698 for( j = 0; j < (int) [bmp size].width; j++ )
704 /* Now update the dock icon */
705 tiff = [bmp TIFFRepresentationUsingCompression:
706 NSTIFFCompressionNone factor: 1.0];
707 NSImage* icon = [[NSImage alloc] initWithData: tiff];
708 [NSApp setApplicationIconImage: icon];
712 - (void) updateUI: (NSTimer *) timer
715 /* Update UI for fHandle (user scanning instance of libhb ) */
718 list = hb_get_titles( fHandle );
719 /* check to see if there has been a new scan done
720 this bypasses the constraints of HB_STATE_WORKING
721 not allowing setting a newly scanned source */
722 int checkScanCount = hb_get_scancount( fHandle );
723 if( checkScanCount > currentScanCount )
725 currentScanCount = checkScanCount;
726 [fScanIndicator setIndeterminate: NO];
727 [fScanIndicator setDoubleValue: 0.0];
728 [fScanIndicator setHidden: YES];
729 [self showNewScan:nil];
733 hb_get_state( fHandle, &s );
739 #define p s.param.scanning
740 case HB_STATE_SCANNING:
742 [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
743 NSLocalizedString( @"Scanning title %d of %d...", @"" ),
744 p.title_cur, p.title_count]];
745 [fScanIndicator setHidden: NO];
746 [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)];
751 #define p s.param.scandone
752 case HB_STATE_SCANDONE:
754 [fScanIndicator setIndeterminate: NO];
755 [fScanIndicator setDoubleValue: 0.0];
756 [fScanIndicator setHidden: YES];
757 [self writeToActivityLog:"ScanDone state received from fHandle"];
758 [self showNewScan:nil];
759 [[fWindow toolbar] validateVisibleItems];
765 #define p s.param.working
766 case HB_STATE_WORKING:
773 #define p s.param.muxing
774 case HB_STATE_MUXING:
781 case HB_STATE_PAUSED:
784 case HB_STATE_WORKDONE:
791 /* Update UI for fQueueEncodeLibhb */
793 // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb
794 /* check to see if there has been a new scan done
795 this bypasses the constraints of HB_STATE_WORKING
796 not allowing setting a newly scanned source */
798 checkScanCount = hb_get_scancount( fQueueEncodeLibhb );
799 if( checkScanCount > currentScanCount )
801 currentScanCount = checkScanCount;
805 hb_get_state( fQueueEncodeLibhb, &s );
811 #define p s.param.scanning
812 case HB_STATE_SCANNING:
814 [fStatusField setStringValue: [NSString stringWithFormat:
815 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
816 p.title_cur, p.title_count]];
818 /* Set the status string in fQueueController as well */
819 [fQueueController setQueueStatusString: [NSString stringWithFormat:
820 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
821 p.title_cur, p.title_count]];
826 #define p s.param.scandone
827 case HB_STATE_SCANDONE:
829 [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
830 [self processNewQueueEncode];
831 [[fWindow toolbar] validateVisibleItems];
838 #define p s.param.working
840 case HB_STATE_SEARCHING:
842 NSMutableString * string;
843 NSString * pass_desc;
845 /* Update text field */
847 //string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
848 /* For now, do not announce "pass x of x for the search phase ... */
849 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... : %.2f %%", @"" ), 100.0 * p.progress];
853 [string appendFormat:
854 NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
857 [fStatusField setStringValue: string];
858 /* Set the status string in fQueueController as well */
859 [fQueueController setQueueStatusString: string];
861 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
862 [fRipIndicator setIndeterminate: NO];
863 [fRipIndicator setDoubleValue:100.0 * progress_total];
865 // If progress bar hasn't been revealed at the bottom of the window, do
866 // that now. This code used to be in doRip. I moved it to here to handle
867 // the case where hb_start is called by HBQueueController and not from
869 if( !fRipIndicatorShown )
871 NSRect frame = [fWindow frame];
872 if( frame.size.width <= 591 )
873 frame.size.width = 591;
874 frame.size.height += 36;
875 frame.origin.y -= 36;
876 [fWindow setFrame:frame display:YES animate:YES];
877 fRipIndicatorShown = YES;
881 /* Update dock icon */
882 /* Note not done yet */
887 case HB_STATE_WORKING:
889 NSMutableString * string;
890 NSString * pass_desc;
891 /* Update text field */
892 if (p.job_cur == 1 && p.job_count > 1)
894 if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
896 pass_desc = @"(subtitle scan)";
908 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: %@ \nPass %d %@ of %d, %.2f %%", @"" ), currentQueueEncodeNameString, p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
912 [string appendFormat:
913 NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
914 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
916 [fStatusField setStringValue: string];
917 [fQueueController setQueueStatusString:string];
920 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
921 [fRipIndicator setIndeterminate: NO];
922 [fRipIndicator setDoubleValue:100.0 * progress_total];
924 // If progress bar hasn't been revealed at the bottom of the window, do
925 // that now. This code used to be in doRip. I moved it to here to handle
926 // the case where hb_start is called by HBQueueController and not from
928 if( !fRipIndicatorShown )
930 NSRect frame = [fWindow frame];
931 if( frame.size.width <= 591 )
932 frame.size.width = 591;
933 frame.size.height += 36;
934 frame.origin.y -= 36;
935 [fWindow setFrame:frame display:YES animate:YES];
936 fRipIndicatorShown = YES;
940 /* Update dock icon */
941 if( dockIconProgress < 100.0 * progress_total )
943 [self UpdateDockIcon: progress_total];
944 dockIconProgress += 5;
951 #define p s.param.muxing
952 case HB_STATE_MUXING:
954 /* Update text field */
955 [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )];
956 /* Set the status string in fQueueController as well */
957 [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )];
959 [fRipIndicator setIndeterminate: YES];
960 [fRipIndicator startAnimation: nil];
962 /* Update dock icon */
963 [self UpdateDockIcon: 1.0];
969 case HB_STATE_PAUSED:
970 [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
971 [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )];
975 case HB_STATE_WORKDONE:
977 // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
978 // or someone calling hb_stop. In the latter case, hb_stop does not clear
979 // out the remaining passes/jobs in the queue. We'll do that here.
981 // Delete all remaining jobs of this encode.
982 [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )];
983 /* Set the status string in fQueueController as well */
984 [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )];
985 [fRipIndicator setIndeterminate: NO];
986 [fRipIndicator stopAnimation: nil];
987 [fRipIndicator setDoubleValue: 0.0];
988 [[fWindow toolbar] validateVisibleItems];
990 /* Restore dock icon */
991 [self UpdateDockIcon: -1.0];
992 dockIconProgress = 0;
994 if( fRipIndicatorShown )
996 NSRect frame = [fWindow frame];
997 if( frame.size.width <= 591 )
998 frame.size.width = 591;
999 frame.size.height += -36;
1000 frame.origin.y -= -36;
1001 [fWindow setFrame:frame display:YES animate:YES];
1002 fRipIndicatorShown = NO;
1004 /* Since we are done with this encode, tell output to stop writing to the
1005 * individual encode log
1007 [outputPanel endEncodeLog];
1008 /* Check to see if the encode state has not been cancelled
1009 to determine if we should check for encode done notifications */
1010 if( fEncodeState != 2 )
1012 NSString *pathOfFinishedEncode;
1013 /* Get the output file name for the finished encode */
1014 pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"];
1016 /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */
1017 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
1018 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
1020 /* If Play System Alert has been selected in Preferences */
1021 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES )
1025 [self showGrowlDoneNotification:pathOfFinishedEncode];
1029 [self sendToMetaX:pathOfFinishedEncode];
1031 /* since we have successfully completed an encode, we increment the queue counter */
1032 [self incrementQueueItemDone:currentQueueEncodeIndex];
1040 /* Since we can use multiple instance off of the same queue file it is imperative that we keep the QueueFileArray updated off of the QueueFile.plist
1041 * so we go ahead and do it in this existing timer as opposed to using a new one */
1043 NSMutableArray * tempQueueArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1044 [QueueFileArray setArray:tempQueueArray];
1045 [tempQueueArray release];
1046 /* Send Fresh QueueFileArray to fQueueController to update queue window */
1047 [fQueueController setQueueArray: QueueFileArray];
1048 [self getQueueStats];
1052 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
1053 - (void) writeToActivityLog:(const char *) format, ...
1056 va_start(args, format);
1060 vsnprintf( str, 1024, format, args );
1062 time_t _now = time( NULL );
1063 struct tm * now = localtime( &_now );
1064 fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
1070 #pragma mark Toolbar
1071 // ============================================================
1072 // NSToolbar Related Methods
1073 // ============================================================
1075 - (void) setupToolbar {
1076 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
1078 [toolbar setAllowsUserCustomization: YES];
1079 [toolbar setAutosavesConfiguration: YES];
1080 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1082 [toolbar setDelegate: self];
1084 [fWindow setToolbar: toolbar];
1087 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
1088 (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
1089 NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
1091 if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
1093 [item setLabel: @"Toggle Presets"];
1094 [item setPaletteLabel: @"Toggler Presets"];
1095 [item setToolTip: @"Open/Close Preset Drawer"];
1096 [item setImage: [NSImage imageNamed: @"Drawer"]];
1097 [item setTarget: self];
1098 [item setAction: @selector(toggleDrawer:)];
1099 [item setAutovalidates: NO];
1101 else if ([itemIdent isEqualToString: StartEncodingIdentifier])
1103 [item setLabel: @"Start"];
1104 [item setPaletteLabel: @"Start Encoding"];
1105 [item setToolTip: @"Start Encoding"];
1106 [item setImage: [NSImage imageNamed: @"Play"]];
1107 [item setTarget: self];
1108 [item setAction: @selector(Rip:)];
1110 else if ([itemIdent isEqualToString: ShowQueueIdentifier])
1112 [item setLabel: @"Show Queue"];
1113 [item setPaletteLabel: @"Show Queue"];
1114 [item setToolTip: @"Show Queue"];
1115 [item setImage: [NSImage imageNamed: @"Queue"]];
1116 [item setTarget: self];
1117 [item setAction: @selector(showQueueWindow:)];
1118 [item setAutovalidates: NO];
1120 else if ([itemIdent isEqualToString: AddToQueueIdentifier])
1122 [item setLabel: @"Add to Queue"];
1123 [item setPaletteLabel: @"Add to Queue"];
1124 [item setToolTip: @"Add to Queue"];
1125 [item setImage: [NSImage imageNamed: @"AddToQueue"]];
1126 [item setTarget: self];
1127 [item setAction: @selector(addToQueue:)];
1129 else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
1131 [item setLabel: @"Pause"];
1132 [item setPaletteLabel: @"Pause Encoding"];
1133 [item setToolTip: @"Pause Encoding"];
1134 [item setImage: [NSImage imageNamed: @"Pause"]];
1135 [item setTarget: self];
1136 [item setAction: @selector(Pause:)];
1138 else if ([itemIdent isEqualToString: ShowPictureIdentifier])
1140 [item setLabel: @"Picture Settings"];
1141 [item setPaletteLabel: @"Show Picture Settings"];
1142 [item setToolTip: @"Show Picture Settings"];
1143 [item setImage: [NSImage imageNamed: @"pref-picture"]];
1144 [item setTarget: self];
1145 [item setAction: @selector(showPicturePanel:)];
1147 else if ([itemIdent isEqualToString: ShowPreviewIdentifier])
1149 [item setLabel: @"Preview Window"];
1150 [item setPaletteLabel: @"Show Preview"];
1151 [item setToolTip: @"Show Preview"];
1152 //[item setImage: [NSImage imageNamed: @"pref-picture"]];
1153 [item setImage: [NSImage imageNamed: @"Brushed_Window"]];
1154 [item setTarget: self];
1155 [item setAction: @selector(showPreviewWindow:)];
1157 else if ([itemIdent isEqualToString: ShowActivityIdentifier])
1159 [item setLabel: @"Activity Window"];
1160 [item setPaletteLabel: @"Show Activity Window"];
1161 [item setToolTip: @"Show Activity Window"];
1162 [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
1163 [item setTarget: self];
1164 [item setAction: @selector(showDebugOutputPanel:)];
1165 [item setAutovalidates: NO];
1167 else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
1169 [item setLabel: @"Source"];
1170 [item setPaletteLabel: @"Source"];
1171 [item setToolTip: @"Choose Video Source"];
1172 [item setImage: [NSImage imageNamed: @"Source"]];
1173 [item setTarget: self];
1174 [item setAction: @selector(browseSources:)];
1184 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1186 return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
1187 PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1188 NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
1191 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1193 return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
1194 ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
1195 NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1196 NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
1199 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1201 NSString * ident = [toolbarItem itemIdentifier];
1207 hb_get_state( fHandle, &s );
1208 if (s.state == HB_STATE_SCANNING)
1211 if ([ident isEqualToString: ChooseSourceIdentifier])
1213 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1214 [toolbarItem setLabel: @"Cancel Scan"];
1215 [toolbarItem setPaletteLabel: @"Cancel Scanning"];
1216 [toolbarItem setToolTip: @"Cancel Scanning Source"];
1220 if ([ident isEqualToString: StartEncodingIdentifier] || [ident isEqualToString: AddToQueueIdentifier])
1225 if ([ident isEqualToString: ChooseSourceIdentifier])
1227 [toolbarItem setImage: [NSImage imageNamed: @"Source"]];
1228 [toolbarItem setLabel: @"Source"];
1229 [toolbarItem setPaletteLabel: @"Source"];
1230 [toolbarItem setToolTip: @"Choose Video Source"];
1235 hb_get_state2( fQueueEncodeLibhb, &s );
1237 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING)
1239 if ([ident isEqualToString: StartEncodingIdentifier])
1241 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1242 [toolbarItem setLabel: @"Stop"];
1243 [toolbarItem setPaletteLabel: @"Stop"];
1244 [toolbarItem setToolTip: @"Stop Encoding"];
1247 if ([ident isEqualToString: PauseEncodingIdentifier])
1249 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1250 [toolbarItem setLabel: @"Pause"];
1251 [toolbarItem setPaletteLabel: @"Pause Encoding"];
1252 [toolbarItem setToolTip: @"Pause Encoding"];
1257 if ([ident isEqualToString: AddToQueueIdentifier])
1259 if ([ident isEqualToString: ShowPictureIdentifier])
1261 if ([ident isEqualToString: ShowPreviewIdentifier])
1265 else if (s.state == HB_STATE_PAUSED)
1267 if ([ident isEqualToString: PauseEncodingIdentifier])
1269 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1270 [toolbarItem setLabel: @"Resume"];
1271 [toolbarItem setPaletteLabel: @"Resume Encoding"];
1272 [toolbarItem setToolTip: @"Resume Encoding"];
1275 if ([ident isEqualToString: StartEncodingIdentifier])
1277 if ([ident isEqualToString: AddToQueueIdentifier])
1279 if ([ident isEqualToString: ShowPictureIdentifier])
1281 if ([ident isEqualToString: ShowPreviewIdentifier])
1284 else if (s.state == HB_STATE_SCANNING)
1286 else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
1288 if ([ident isEqualToString: StartEncodingIdentifier])
1290 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1291 if (hb_count(fHandle) > 0)
1292 [toolbarItem setLabel: @"Start Queue"];
1294 [toolbarItem setLabel: @"Start"];
1295 [toolbarItem setPaletteLabel: @"Start Encoding"];
1296 [toolbarItem setToolTip: @"Start Encoding"];
1299 if ([ident isEqualToString: AddToQueueIdentifier])
1301 if ([ident isEqualToString: ShowPictureIdentifier])
1303 if ([ident isEqualToString: ShowPreviewIdentifier])
1308 /* If there are any pending queue items, make sure the start/stop button is active */
1309 if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0)
1311 if ([ident isEqualToString: ShowQueueIdentifier])
1313 if ([ident isEqualToString: ToggleDrawerIdentifier])
1315 if ([ident isEqualToString: ChooseSourceIdentifier])
1317 if ([ident isEqualToString: ShowActivityIdentifier])
1323 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
1325 SEL action = [menuItem action];
1328 hb_get_state2( fHandle, &s );
1332 if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
1333 return SuccessfulScan && [fWindow attachedSheet] == nil;
1335 if (action == @selector(browseSources:))
1337 if (s.state == HB_STATE_SCANNING)
1340 return [fWindow attachedSheet] == nil;
1342 if (action == @selector(selectDefaultPreset:))
1343 return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
1344 if (action == @selector(Pause:))
1346 if (s.state == HB_STATE_WORKING)
1348 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
1349 [menuItem setTitle:@"Pause Encoding"];
1352 else if (s.state == HB_STATE_PAUSED)
1354 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
1355 [menuItem setTitle:@"Resume Encoding"];
1361 if (action == @selector(Rip:))
1363 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1365 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1366 [menuItem setTitle:@"Stop Encoding"];
1369 else if (SuccessfulScan)
1371 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1372 [menuItem setTitle:@"Start Encoding"];
1373 return [fWindow attachedSheet] == nil;
1379 if( action == @selector(setDefaultPreset:) )
1381 return [fPresetsOutlineView selectedRow] != -1;
1388 #pragma mark Encode Done Actions
1389 // register a test notification and make
1390 // it enabled by default
1391 #define SERVICE_NAME @"Encode Done"
1392 - (NSDictionary *)registrationDictionaryForGrowl
1394 NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1395 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
1396 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
1399 return registrationDictionary;
1402 -(void)showGrowlDoneNotification:(NSString *) filePath
1404 /* This end of encode action is called as each encode rolls off of the queue */
1405 /* Setup the Growl stuff ... */
1406 NSString * finishedEncode = filePath;
1407 /* strip off the path to just show the file name */
1408 finishedEncode = [finishedEncode lastPathComponent];
1409 NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
1410 [GrowlApplicationBridge
1411 notifyWithTitle:@"Put down that cocktail..."
1412 description:growlMssg
1413 notificationName:SERVICE_NAME
1419 -(void)sendToMetaX:(NSString *) filePath
1421 /* This end of encode action is called as each encode rolls off of the queue */
1422 if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
1424 NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey: @"SendCompletedEncodeToApp"];
1425 if (![sendToApp isEqualToString:@"None"])
1427 [self writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
1428 NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", filePath, @"\")"]];
1429 [myScript executeAndReturnError: nil];
1436 - (void) queueCompletedAlerts
1438 /* If Play System Alert has been selected in Preferences */
1439 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES )
1444 /* If Alert Window or Window and Growl has been selected */
1445 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
1446 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
1448 /*On Screen Notification*/
1450 status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil);
1451 [NSApp requestUserAttention:NSCriticalRequest];
1454 /* If sleep has been selected */
1455 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
1458 NSDictionary* errorDict;
1459 NSAppleEventDescriptor* returnDescriptor = nil;
1460 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1461 @"tell application \"Finder\" to sleep"];
1462 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1463 [scriptObject release];
1465 /* If Shutdown has been selected */
1466 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
1469 NSDictionary* errorDict;
1470 NSAppleEventDescriptor* returnDescriptor = nil;
1471 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1472 @"tell application \"Finder\" to shut down"];
1473 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1474 [scriptObject release];
1479 #pragma mark Get New Source
1481 /*Opens the source browse window, called from Open Source widgets */
1482 - (IBAction) browseSources: (id) sender
1486 hb_get_state( fHandle, &s );
1487 if (s.state == HB_STATE_SCANNING)
1489 [self cancelScanning:nil];
1494 NSOpenPanel * panel;
1496 panel = [NSOpenPanel openPanel];
1497 [panel setAllowsMultipleSelection: NO];
1498 [panel setCanChooseFiles: YES];
1499 [panel setCanChooseDirectories: YES ];
1500 NSString * sourceDirectory;
1501 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1503 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1507 sourceDirectory = @"~/Desktop";
1508 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1510 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1511 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1513 [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1514 modalForWindow: fWindow modalDelegate: self
1515 didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1516 contextInfo: sender];
1519 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1520 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1522 /* we convert the sender content of contextInfo back into a variable called sender
1523 * mostly just for consistency for evaluation later
1525 id sender = (id)contextInfo;
1526 /* User selected a file to open */
1527 if( returnCode == NSOKButton )
1529 /* Free display name allocated previously by this code */
1530 [browsedSourceDisplayName release];
1532 NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1533 /* we set the last searched source directory in the prefs here */
1534 NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1535 [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1536 /* we order out sheet, which is the browse window as we need to open
1537 * the title selection sheet right away
1539 [sheet orderOut: self];
1541 if (sender == fOpenSourceTitleMMenu || [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1543 /* We put the chosen source path in the source display text field for the
1544 * source title selection sheet in which the user specifies the specific title to be
1545 * scanned as well as the short source name in fSrcDsplyNameTitleScan just for display
1546 * purposes in the title panel
1549 [fScanSrcTitlePathField setStringValue:scanPath];
1550 NSString *displayTitlescanSourceName;
1552 if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1554 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1555 we have to use the title->path value so we get the proper name of the volume if a physical dvd is the source*/
1556 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1560 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1561 displayTitlescanSourceName = [scanPath lastPathComponent];
1563 /* we set the source display name in the title selection dialogue */
1564 [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1565 /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1566 browsedSourceDisplayName = [displayTitlescanSourceName retain];
1567 /* We show the actual sheet where the user specifies the title to be scanned
1568 * as we are going to do a title specific scan
1570 [self showSourceTitleScanPanel:nil];
1574 /* We are just doing a standard full source scan, so we specify "0" to libhb */
1575 NSString *path = [[sheet filenames] objectAtIndex: 0];
1577 /* We check to see if the chosen file at path is a package */
1578 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1580 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1581 /* We check to see if this is an .eyetv package */
1582 if ([[path pathExtension] isEqualToString: @"eyetv"])
1584 [self writeToActivityLog:"trying to open eyetv package"];
1585 /* We're looking at an EyeTV package - try to open its enclosed
1587 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1589 int n = [[path stringByAppendingString: @"/"]
1590 completePathIntoString: &mpgname caseSensitive: YES
1591 matchesIntoArray: nil
1592 filterTypes: [NSArray arrayWithObject: @"mpg"]];
1595 /* Found an mpeg inside the eyetv package, make it our scan path
1596 and call performScan on the enclosed mpeg */
1598 [self writeToActivityLog:"found mpeg in eyetv package"];
1599 [self performScan:path scanTitleNum:0];
1603 /* We did not find an mpeg file in our package, so we do not call performScan */
1604 [self writeToActivityLog:"no valid mpeg in eyetv package"];
1607 /* We check to see if this is a .dvdmedia package */
1608 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1610 /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1611 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1612 [self writeToActivityLog:"trying to open dvdmedia package"];
1613 [self performScan:path scanTitleNum:0];
1617 /* The package is not an eyetv package, so we do not call performScan */
1618 [self writeToActivityLog:"unable to open package"];
1621 else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1623 /* path is not a package, so we call perform scan directly on our file */
1624 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1626 [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1627 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1628 browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1632 [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1633 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1634 /* make sure we remove any path extension as this can also be an '.mpg' file */
1635 browsedSourceDisplayName = [[path lastPathComponent] retain];
1637 applyQueueToScan = NO;
1638 [self performScan:path scanTitleNum:0];
1646 - (IBAction)showAboutPanel:(id)sender
1648 NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
1649 fApplicationIcon, @"ApplicationIcon",
1651 [NSApp orderFrontStandardAboutPanelWithOptions:d];
1655 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1656 - (IBAction) showSourceTitleScanPanel: (id) sender
1658 /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1661 [fScanSrcTitleNumField setStringValue: @"0"];
1662 /* Show the panel */
1663 [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1666 - (IBAction) closeSourceTitleScanPanel: (id) sender
1668 [NSApp endSheet: fScanSrcTitlePanel];
1669 [fScanSrcTitlePanel orderOut: self];
1671 if(sender == fScanSrcTitleOpenButton)
1673 /* We setup the scan status in the main window to indicate a source title scan */
1674 [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1675 [fScanIndicator setHidden: NO];
1676 [fScanIndicator setIndeterminate: YES];
1677 [fScanIndicator startAnimation: nil];
1679 /* We use the performScan method to actually perform the specified scan passing the path and the title
1682 applyQueueToScan = NO;
1683 [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1687 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1688 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1691 /* use a bool to determine whether or not we can decrypt using vlc */
1692 BOOL cancelScanDecrypt = 0;
1694 NSString *path = scanPath;
1695 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1697 // Notify ChapterTitles that there's no title
1698 [fChapterTitlesDelegate resetWithTitle:nil];
1699 [fChapterTable reloadData];
1701 // Notify Subtitles that there's no title
1702 [fSubtitlesDelegate resetWithTitle:nil];
1703 [fSubtitlesTable reloadData];
1705 // Notify anyone interested (audio controller) that there's no title
1706 [[NSNotificationCenter defaultCenter] postNotification:
1707 [NSNotification notificationWithName: HBTitleChangedNotification
1709 userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
1710 [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag,
1713 [self enableUI: NO];
1715 if( [detector isVideoDVD] )
1717 // The chosen path was actually on a DVD, so use the raw block
1718 // device path instead.
1719 path = [detector devicePath];
1720 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1722 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1723 void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
1726 /*compatible vlc not found, so we set the bool to cancel scanning to 1 */
1727 cancelScanDecrypt = 1;
1728 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1730 status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is incompatible (Note: 32 bit vlc is not compatible with 64 bit HandBrake and vice-versa).",@"Please download and install VLC media player if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
1731 [NSApp requestUserAttention:NSCriticalRequest];
1733 if (status == NSAlertDefaultReturn)
1735 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1736 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1738 else if (status == NSAlertAlternateReturn)
1740 /* User chose to cancel the scan */
1741 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1745 /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
1746 cancelScanDecrypt = 0;
1747 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1753 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1754 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1760 if (cancelScanDecrypt == 0)
1762 /* we actually pass the scan off to libhb here */
1763 /* If there is no title number passed to scan, we use "0"
1764 * which causes the default behavior of a full source scan
1770 if (scanTitleNum > 0)
1772 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1774 /* We use our advanced pref to determine how many previews to scan */
1775 int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
1776 /* We use our advanced pref to determine the minimum title length to use in seconds*/
1777 uint64_t min_title_duration_seconds = 90000L * [[[NSUserDefaults standardUserDefaults] objectForKey:@"MinTitleScanSeconds"] intValue];
1778 /* set title to NULL */
1780 [self writeToActivityLog: "Minimum length of title for scan: %d", min_title_duration_seconds];
1781 hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 , min_title_duration_seconds );
1782 [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1786 - (IBAction) cancelScanning:(id)sender
1788 hb_scan_stop(fHandle);
1791 - (IBAction) showNewScan:(id)sender
1794 hb_title_t * title = NULL;
1795 int feature_title=0; // Used to store the main feature title
1797 list = hb_get_titles( fHandle );
1799 if( !hb_list_count( list ) )
1801 /* We display a message if a valid dvd source was not chosen */
1802 [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1803 SuccessfulScan = NO;
1805 // Notify ChapterTitles that there's no title
1806 [fSubtitlesDelegate resetWithTitle:nil];
1807 [fSubtitlesTable reloadData];
1809 // Notify Subtitles that there's no title
1810 [fChapterTitlesDelegate resetWithTitle:nil];
1811 [fChapterTable reloadData];
1813 // Notify anyone interested (audio controller) that there's no title
1814 [[NSNotificationCenter defaultCenter] postNotification:
1815 [NSNotification notificationWithName: HBTitleChangedNotification
1817 userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
1818 [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag,
1823 if (applyQueueToScan == YES)
1825 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1826 [self writeToActivityLog: "showNewScan: This is a queued item rescan"];
1829 else if (applyQueueToScan == NO)
1831 [self writeToActivityLog: "showNewScan: This is a new source item scan"];
1835 [self writeToActivityLog: "showNewScan: cannot grok scan status"];
1838 /* We increment the successful scancount here by one,
1839 which we use at the end of this function to tell the gui
1840 if this is the first successful scan since launch and whether
1841 or not we should set all settings to the defaults */
1842 currentSuccessfulScanCount++;
1844 [[fWindow toolbar] validateVisibleItems];
1846 [fSrcTitlePopUp removeAllItems];
1847 for( int i = 0; i < hb_list_count( list ); i++ )
1849 title = (hb_title_t *) hb_list_item( list, i );
1851 currentSource = [NSString stringWithUTF8String: title->name];
1852 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1853 if (!browsedSourceDisplayName)
1855 browsedSourceDisplayName = @"NoNameDetected";
1857 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1859 // use the correct extension based on the container
1860 int format = [fDstFormatPopUp indexOfSelectedItem];
1867 /* If its a queue rescan for edit, get the queue item output path */
1868 /* if not, its a new source scan. */
1869 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1870 if (applyQueueToScan == YES)
1872 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@", [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"DestinationPath"]]];
1874 else if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1876 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1877 @"%@/%@.%s", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension],ext]];
1881 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1882 @"%@/Desktop/%@.%s", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension],ext]];
1885 // set m4v extension if necessary - do not override user-specified .mp4 extension
1886 if (format == 0 && applyQueueToScan != YES)
1888 [self autoSetM4vExtension: sender];
1891 /* See if this is the main feature according to libhb */
1892 if (title->index == title->job->feature)
1897 [fSrcTitlePopUp addItemWithTitle: [NSString
1898 stringWithFormat: @"%@ %d - %02dh%02dm%02ds",
1899 currentSource, title->index, title->hours, title->minutes,
1903 /* if we are a stream, select the first title */
1904 if (title->type == HB_STREAM_TYPE)
1906 [fSrcTitlePopUp selectItemAtIndex: 0];
1910 /* if not then select the main feature title */
1911 [fSrcTitlePopUp selectItemAtIndex: feature_title];
1913 [self titlePopUpChanged:nil];
1915 SuccessfulScan = YES;
1916 [self enableUI: YES];
1918 /* if its the initial successful scan after awakeFromNib */
1919 if (currentSuccessfulScanCount == 1)
1921 [self encodeStartStopPopUpChanged:nil];
1923 [self selectDefaultPreset:nil];
1925 // Open preview window now if it was visible when HB was closed
1926 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
1927 [self showPreviewWindow:nil];
1929 // Open picture sizing window now if it was visible when HB was closed
1930 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
1931 [self showPicturePanel:nil];
1934 if (applyQueueToScan == YES)
1936 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1937 [self writeToActivityLog: "showNewScan: calling applyQueueSettingsToMainWindow"];
1938 [self applyQueueSettingsToMainWindow:nil];
1949 #pragma mark New Output Destination
1951 - (IBAction) browseFile: (id) sender
1953 /* Open a panel to let the user choose and update the text field */
1954 NSSavePanel * panel = [NSSavePanel savePanel];
1955 /* We get the current file name and path from the destination field here */
1956 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1957 modalForWindow: fWindow modalDelegate: self
1958 didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1962 - (void) browseFileDone: (NSSavePanel *) sheet
1963 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1965 if( returnCode == NSOKButton )
1967 [fDstFile2Field setStringValue: [sheet filename]];
1968 /* Save this path to the prefs so that on next browse destination window it opens there */
1969 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1970 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1976 #pragma mark Main Window Control
1978 - (IBAction) openMainWindow: (id) sender
1980 [fWindow makeKeyAndOrderFront:nil];
1983 - (BOOL) windowShouldClose: (id) sender
1988 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1991 [fWindow makeKeyAndOrderFront:nil];
1996 - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize {
1997 [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"];
2002 #pragma mark Queue File
2004 - (void) loadQueueFile {
2005 /* We declare the default NSFileManager into fileManager */
2006 NSFileManager * fileManager = [NSFileManager defaultManager];
2007 /*We define the location of the user presets file */
2008 QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist";
2009 QueueFile = [[QueueFile stringByExpandingTildeInPath]retain];
2010 /* We check for the Queue.plist */
2011 if ([fileManager fileExistsAtPath:QueueFile] == 0)
2013 [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
2016 QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
2017 /* lets check to see if there is anything in the queue file .plist */
2018 if (nil == QueueFileArray)
2020 /* if not, then lets initialize an empty array */
2021 QueueFileArray = [[NSMutableArray alloc] init];
2025 /* ONLY clear out encoded items if we are single instance */
2026 if (hbInstanceNum == 1)
2028 [self clearQueueEncodedItems];
2033 - (void)addQueueFileItem
2035 [QueueFileArray addObject:[self createQueueFileItem]];
2036 [self saveQueueFileItem];
2040 - (void) removeQueueFileItem:(int) queueItemToRemove
2042 [QueueFileArray removeObjectAtIndex:queueItemToRemove];
2043 [self saveQueueFileItem];
2047 - (void)saveQueueFileItem
2049 [QueueFileArray writeToFile:QueueFile atomically:YES];
2050 [fQueueController setQueueArray: QueueFileArray];
2051 [self getQueueStats];
2054 - (void)getQueueStats
2056 /* lets get the stats on the status of the queue array */
2058 fEncodingQueueItem = 0;
2060 fCompletedCount = 0;
2064 /* We use a number system to set the encode status of the queue item
2066 * 0 == already encoded
2067 * 1 == is being encoded
2068 * 2 == is yet to be encoded
2073 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2075 while (tempObject = [enumerator nextObject])
2077 NSDictionary *thisQueueDict = tempObject;
2078 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
2082 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
2085 fEncodingQueueItem = i;
2086 /* check to see if we are the instance doing this encoding */
2087 if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum)
2089 currentQueueEncodeIndex = i;
2093 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending
2097 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled
2104 /* Set the queue status field in the main window */
2105 NSMutableString * string;
2106 if (fPendingCount == 0)
2108 string = [NSMutableString stringWithFormat: NSLocalizedString( @"No encode pending", @"" )];
2110 else if (fPendingCount == 1)
2112 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
2116 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encodes pending", @"" ), fPendingCount];
2118 [fQueueStatus setStringValue:string];
2121 /* Used to get the next pending queue item index and return it if found */
2122 - (int)getNextPendingQueueIndex
2124 /* initialize nextPendingIndex to -1, this value tells incrementQueueItemDone that there are no pending items in the queue */
2125 int nextPendingIndex = -1;
2126 BOOL nextPendingFound = NO;
2127 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2130 while (tempObject = [enumerator nextObject])
2132 NSDictionary *thisQueueDict = tempObject;
2133 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2 && nextPendingFound == NO) // pending
2135 nextPendingFound = YES;
2136 nextPendingIndex = [QueueFileArray indexOfObject: tempObject];
2137 [self writeToActivityLog: "getNextPendingQueueIndex next pending encode index is:%d", nextPendingIndex];
2141 return nextPendingIndex;
2145 /* This method will set any item marked as encoding back to pending
2146 * currently used right after a queue reload
2148 - (void) setQueueEncodingItemsAsPending
2150 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2152 NSMutableArray *tempArray;
2153 tempArray = [NSMutableArray array];
2154 /* we look here to see if the preset is we move on to the next one */
2155 while ( tempObject = [enumerator nextObject] )
2157 /* We want to keep any queue item that is pending or was previously being encoded */
2158 if ([[tempObject objectForKey:@"Status"] intValue] == 1 || [[tempObject objectForKey:@"Status"] intValue] == 2)
2160 /* If the queue item is marked as "encoding" (1)
2161 * then change its status back to pending (2) which effectively
2162 * puts it back into the queue to be encoded
2164 if ([[tempObject objectForKey:@"Status"] intValue] == 1)
2166 [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"];
2168 [tempArray addObject:tempObject];
2172 [QueueFileArray setArray:tempArray];
2173 [self saveQueueFileItem];
2177 /* This method will clear the queue of any encodes that are not still pending
2178 * this includes both successfully completed encodes as well as cancelled encodes */
2179 - (void) clearQueueEncodedItems
2181 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2183 NSMutableArray *tempArray;
2184 tempArray = [NSMutableArray array];
2185 /* we look here to see if the preset is we move on to the next one */
2186 while ( tempObject = [enumerator nextObject] )
2188 /* If the queue item is either completed (0) or cancelled (3) from the
2189 * last session, then we put it in tempArray to be deleted from QueueFileArray.
2190 * NOTE: this means we retain pending (2) and also an item that is marked as
2191 * still encoding (1). If the queue has an item that is still marked as encoding
2192 * from a previous session, we can conlude that HB was either shutdown, or crashed
2193 * during the encodes so we keep it and tell the user in the "Load Queue Alert"
2195 if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3)
2197 [tempArray addObject:tempObject];
2201 [QueueFileArray removeObjectsInArray:tempArray];
2202 [self saveQueueFileItem];
2205 /* This method will clear the queue of all encodes. effectively creating an empty queue */
2206 - (void) clearQueueAllItems
2208 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2210 NSMutableArray *tempArray;
2211 tempArray = [NSMutableArray array];
2212 /* we look here to see if the preset is we move on to the next one */
2213 while ( tempObject = [enumerator nextObject] )
2215 [tempArray addObject:tempObject];
2218 [QueueFileArray removeObjectsInArray:tempArray];
2219 [self saveQueueFileItem];
2222 /* This method will duplicate prepareJob however into the
2223 * queue .plist instead of into the job structure so it can
2224 * be recalled later */
2225 - (NSDictionary *)createQueueFileItem
2227 NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init];
2229 hb_list_t * list = hb_get_titles( fHandle );
2230 hb_title_t * title = (hb_title_t *) hb_list_item( list,
2231 [fSrcTitlePopUp indexOfSelectedItem] );
2232 hb_job_t * job = title->job;
2236 /* We use a number system to set the encode status of the queue item
2237 * 0 == already encoded
2238 * 1 == is being encoded
2239 * 2 == is yet to be encoded
2242 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"];
2243 /* Source and Destination Information */
2245 [queueFileJob setObject:[NSString stringWithUTF8String: title->path] forKey:@"SourcePath"];
2246 [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"];
2247 [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"];
2248 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"];
2250 /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */
2251 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
2253 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"];
2255 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
2257 [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"];
2259 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
2261 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"];
2263 /* Chapter encode info */
2264 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"];
2265 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"];
2266 /* Time (pts) encode info */
2267 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"];
2268 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"];
2269 /* Frame number encode info */
2270 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"];
2271 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"];
2274 /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
2275 int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
2276 [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
2278 [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
2280 /* Lets get the preset info if there is any */
2281 [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2282 [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2284 [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
2285 /* Chapter Markers*/
2286 /* If we have only one chapter or a title without chapters, set chapter markers to off */
2287 if ([fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem])
2289 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"];
2293 [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
2296 /* We need to get the list of chapter names to put into an array and store
2297 * in our queue, so they can be reapplied in prepareJob when this queue
2298 * item comes up if Chapter Markers is set to on.
2301 NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init];
2302 int chaptercount = hb_list_count( fTitle->list_chapter );
2303 for( i = 0; i < chaptercount; i++ )
2305 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i );
2306 if( chapter != NULL )
2308 [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]];
2311 [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"];
2312 [ChapterNamesArray autorelease];
2314 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2315 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
2316 /* Mux mp4 with http optimization */
2317 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
2318 /* Add iPod uuid atom */
2319 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
2323 [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
2324 /* x264 Option String */
2325 [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
2327 [queueFileJob setObject:[NSNumber numberWithInt:[[fVidQualityMatrix selectedCell] tag] + 1] forKey:@"VideoQualityType"];
2328 [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
2329 [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
2331 [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
2332 [queueFileJob setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
2334 /* 2 Pass Encoding */
2335 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
2336 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
2337 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
2339 /* Picture Sizing */
2340 /* Use Max Picture settings for whatever the dvd is.*/
2341 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2342 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2343 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2344 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2345 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2346 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
2347 /* if we are custom anamorphic, store the exact storage, par and display dims */
2348 if (fTitle->job->anamorphic.mode == 3)
2350 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PicturePARModulus"];
2352 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"];
2353 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"];
2355 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"];
2356 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"];
2358 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"];
2359 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"];
2362 NSString * pictureSummary;
2363 pictureSummary = [fPictureSizeField stringValue];
2364 [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"];
2365 /* Set crop settings here */
2366 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2367 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2368 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2369 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2370 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2372 /* Picture Filters */
2373 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
2374 [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
2376 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
2377 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
2378 [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
2380 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
2381 [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
2383 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
2384 [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
2386 [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"];
2388 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
2391 [fAudioDelegate prepareAudioForQueueFileJob: queueFileJob];
2394 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
2395 [queueFileJob setObject:[NSArray arrayWithArray: subtitlesArray] forKey:@"SubtitleList"];
2396 [subtitlesArray autorelease];
2398 /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */
2400 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"];
2402 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"];
2405 [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"];
2409 [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"];
2412 [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"];
2413 [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"];
2414 [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"];
2416 /* Picture Sizing */
2417 /* Use Max Picture settings for whatever the dvd is.*/
2418 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2419 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2420 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2421 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2422 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2424 /* Set crop settings here */
2425 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2426 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2427 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2428 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2429 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2433 /* we need to auto relase the queueFileJob and return it */
2434 [queueFileJob autorelease];
2435 return queueFileJob;
2439 /* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */
2440 - (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
2442 NSUInteger index = [indexSet lastIndex];
2443 NSUInteger aboveInsertIndexCount = 0;
2446 NSUInteger removeIndex;
2448 if (index >= insertIndex)
2450 removeIndex = index + aboveInsertIndexCount;
2451 aboveInsertIndexCount++;
2455 removeIndex = index;
2459 id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
2460 [QueueFileArray removeObjectAtIndex:removeIndex];
2461 [QueueFileArray insertObject:object atIndex:insertIndex];
2464 index = [indexSet indexLessThanIndex:index];
2466 /* We save all of the Queue data here
2467 * and it also gets sent back to the queue controller*/
2468 [self saveQueueFileItem];
2474 #pragma mark Queue Job Processing
2476 - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum
2478 /* Mark the encode just finished as done (status 0)*/
2479 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:0] forKey:@"Status"];
2481 /* We save all of the Queue data here */
2482 [self saveQueueFileItem];
2484 /* Since we have now marked a queue item as done
2485 * we can go ahead and increment currentQueueEncodeIndex
2486 * so that if there is anything left in the queue we can
2487 * go ahead and move to the next item if we want to */
2488 int queueItems = [QueueFileArray count];
2489 /* Check to see if there are any more pending items in the queue */
2490 int newQueueItemIndex = [self getNextPendingQueueIndex];
2491 /* If we still have more pending items in our queue, lets go to the next one */
2492 if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
2494 /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
2495 currentQueueEncodeIndex = newQueueItemIndex;
2496 /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
2497 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2498 [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
2499 [self saveQueueFileItem];
2500 /* now we can go ahead and scan the new pending queue item */
2501 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
2506 [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
2507 /*Since there are no more items to encode, go to queueCompletedAlerts for user specified alerts after queue completed*/
2508 [self queueCompletedAlerts];
2512 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
2513 - (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
2515 /* Tell HB to output a new activity log file for this encode */
2516 [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
2518 /* We now flag the queue item as being owned by this instance of HB using the PID */
2519 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:pidNum] forKey:@"EncodingPID"];
2520 /* Get the currentQueueEncodeNameString from the queue item to display in the status field */
2521 currentQueueEncodeNameString = [[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"] lastPathComponent]retain];
2522 /* We save all of the Queue data here */
2523 [self saveQueueFileItem];
2525 /* use a bool to determine whether or not we can decrypt using vlc */
2526 BOOL cancelScanDecrypt = 0;
2527 /* set the bool so that showNewScan knows to apply the appropriate queue
2528 * settings as this is a queue rescan
2530 NSString *path = scanPath;
2531 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
2533 if( [detector isVideoDVD] )
2535 // The chosen path was actually on a DVD, so use the raw block
2536 // device path instead.
2537 path = [detector devicePath];
2538 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
2540 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
2541 void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
2544 /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
2545 cancelScanDecrypt = 1;
2546 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
2548 status = NSRunAlertPanel(@"HandBrake could not find VLC.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
2549 [NSApp requestUserAttention:NSCriticalRequest];
2551 if (status == NSAlertDefaultReturn)
2553 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
2554 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
2556 else if (status == NSAlertAlternateReturn)
2558 /* User chose to cancel the scan */
2559 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
2563 /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
2564 cancelScanDecrypt = 0;
2565 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
2571 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
2573 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
2577 if (cancelScanDecrypt == 0)
2579 /* we actually pass the scan off to libhb here */
2580 /* If there is no title number passed to scan, we use "0"
2581 * which causes the default behavior of a full source scan
2587 if (scanTitleNum > 0)
2589 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
2592 /* Only scan 10 previews before an encode - additional previews are only useful for autocrop and static previews,
2593 * which are already taken care of at this point */
2594 hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, 10, 0, 0 );
2598 /* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */
2599 - (void) processNewQueueEncode
2601 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
2602 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
2603 hb_job_t * job = title->job;
2605 if( !hb_list_count( list ) )
2607 [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
2610 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2611 [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
2612 [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
2613 job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
2617 * If scanning we need to do some extra setup of the job.
2619 if( job->indepth_scan == 1 )
2624 * When subtitle scan is enabled do a fast pre-scan job
2625 * which will determine which subtitles to enable, if any.
2628 x264opts_tmp = job->x264opts;
2630 job->x264opts = NULL;
2632 job->indepth_scan = 1;
2636 * Add the pre-scan job
2638 hb_add( fQueueEncodeLibhb, job );
2639 job->x264opts = x264opts_tmp;
2643 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 )
2645 job->indepth_scan = 0;
2651 hb_add( fQueueEncodeLibhb, job );
2655 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
2656 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
2658 hb_add( fQueueEncodeLibhb, job );
2663 job->indepth_scan = 0;
2666 hb_add( fQueueEncodeLibhb, job );
2669 NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent];
2670 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
2671 /* Lets mark our new encode as 1 or "Encoding" */
2672 [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2673 [self saveQueueFileItem];
2675 /* we need to clean up the subtitle tracks after the job(s) have been set */
2676 int num_subtitle_tracks = hb_list_count(job->list_subtitle);
2678 for(ii = 0; ii < num_subtitle_tracks; ii++)
2680 hb_subtitle_t * subtitle;
2681 subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0);
2684 hb_list_rem(job->list_subtitle, subtitle);
2688 /* We should be all setup so let 'er rip */
2693 #pragma mark Queue Item Editing
2695 /* Rescans the chosen queue item back into the main window */
2696 - (void)rescanQueueItemToMainWindow:(NSString *) scanPath scanTitleNum: (int) scanTitleNum selectedQueueItem: (int) selectedQueueItem
2698 fqueueEditRescanItemNum = selectedQueueItem;
2699 [self writeToActivityLog: "rescanQueueItemToMainWindow: Re-scanning queue item at index:%d",fqueueEditRescanItemNum];
2700 applyQueueToScan = YES;
2701 /* Make sure we release the display name before reassigning it */
2702 [browsedSourceDisplayName release];
2703 /* Set the browsedSourceDisplayName for showNewScan */
2704 browsedSourceDisplayName = [[[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"SourceName"] retain];
2705 [self performScan:scanPath scanTitleNum:scanTitleNum];
2709 /* We use this method after a queue item rescan for edit.
2710 * it largely mirrors -selectPreset in terms of structure.
2711 * Assumes that a queue item has been reloaded into the main window.
2713 - (IBAction)applyQueueSettingsToMainWindow:(id)sender
2715 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:fqueueEditRescanItemNum];
2716 hb_job_t * job = fTitle->job;
2719 [self writeToActivityLog: "applyQueueSettingsToMainWindow: queue item found"];
2721 /* Set title number and chapters */
2722 /* since the queue only scans a single title, its already been selected in showNewScan
2723 so do not try to reset it here. However if we do decide to do full source scans on
2724 a queue edit rescan, we would need it. So leaving in for now but commenting out. */
2725 //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1];
2727 [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1];
2728 [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1];
2731 [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]];
2732 [self formatPopUpChanged:nil];
2734 /* Chapter Markers*/
2735 [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]];
2736 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2737 [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]];
2738 /* Mux mp4 with http optimization */
2739 [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]];
2742 /* We set the advanced opt string here if applicable*/
2743 [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]];
2744 [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]];
2746 /* Lets run through the following functions to get variables set there */
2747 [self videoEncoderPopUpChanged:nil];
2748 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
2749 [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]];
2750 [self calculateBitrate:nil];
2753 [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0];
2755 [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]];
2756 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
2757 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
2758 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
2759 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
2760 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
2761 if ([[queueToApply objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
2763 /* For the quality slider we need to convert the old percent's to the new rf scales */
2764 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]);
2765 [fVidQualitySlider setFloatValue:rf];
2770 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
2771 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
2773 [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2777 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
2778 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2782 [self videoMatrixChanged:nil];
2784 /* Video framerate */
2785 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
2786 detected framerate in the fVidRatePopUp so we use index 0*/
2787 if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
2789 [fVidRatePopUp selectItemAtIndex: 0];
2793 [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]];
2796 /* 2 Pass Encoding */
2797 [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]];
2798 [self twoPassCheckboxChanged:nil];
2799 /* Turbo 1st pass for 2 Pass Encoding */
2800 [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]];
2805 /* Now lets add our new tracks to the audio list here */
2806 [fAudioDelegate addTracksFromQueue: queueToApply];
2809 /* Crashy crashy right now, working on it */
2810 [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]];
2811 [fSubtitlesTable reloadData];
2812 /* Picture Settings */
2814 /* If Cropping is set to custom, then recall all four crop values from
2815 when the preset was created and apply them */
2816 if ([[queueToApply objectForKey:@"PictureAutoCrop"] intValue] == 0)
2818 [fPictureController setAutoCrop:NO];
2820 /* Here we use the custom crop values saved at the time the preset was saved */
2821 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
2822 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
2823 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
2824 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
2827 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
2829 [fPictureController setAutoCrop:YES];
2830 /* Here we use the auto crop values determined right after scan */
2831 job->crop[0] = AutoCropTop;
2832 job->crop[1] = AutoCropBottom;
2833 job->crop[2] = AutoCropLeft;
2834 job->crop[3] = AutoCropRight;
2838 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2840 /* we check to make sure the presets width/height does not exceed the sources width/height */
2841 if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"] intValue])
2843 /* if so, then we use the sources height and width to avoid scaling up */
2844 //job->width = fTitle->width;
2845 //job->height = fTitle->height;
2846 [self revertPictureSizeToMax:nil];
2848 else // source width/height is >= the preset height/width
2850 /* we can go ahead and use the presets values for height and width */
2851 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
2852 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
2854 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
2855 if (job->keep_ratio == 1)
2857 hb_fix_aspect( job, HB_KEEP_WIDTH );
2858 if( job->height > fTitle->height )
2860 job->height = fTitle->height;
2861 hb_fix_aspect( job, HB_KEEP_HEIGHT );
2864 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
2865 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2869 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
2870 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
2871 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
2874 [fPictureController setUseDecomb:1];
2875 [fPictureController setDecomb:0];
2876 [fPictureController setDeinterlace:0];
2877 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2879 /* we are using decomb */
2881 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2883 [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]];
2885 /* if we are using "Custom" in the decomb setting, also set the custom string*/
2886 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
2888 [fPictureController setDecombCustomString:[queueToApply objectForKey:@"PictureDecombCustom"]];
2894 /* We are using Deinterlace */
2896 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] > 0)
2898 [fPictureController setUseDecomb:0];
2899 [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]];
2900 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
2901 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
2903 [fPictureController setDeinterlaceCustomString:[queueToApply objectForKey:@"PictureDeinterlaceCustom"]];
2910 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] > 0)
2912 [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]];
2913 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
2914 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
2916 [fPictureController setDetelecineCustomString:[queueToApply objectForKey:@"PictureDetelecineCustom"]];
2921 [fPictureController setDetelecine:0];
2925 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] > 0)
2927 [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]];
2928 /* if we are using "Custom" in the denoise setting, also set the custom string*/
2929 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1)
2931 [fPictureController setDenoiseCustomString:[queueToApply objectForKey:@"PictureDenoiseCustom"]];
2936 [fPictureController setDenoise:0];
2940 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] == 1)
2942 /* if its a one, then its the old on/off deblock, set on to 5*/
2943 [fPictureController setDeblock:5];
2947 /* use the settings intValue */
2948 [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]];
2951 if ([[queueToApply objectForKey:@"VideoGrayScale"] intValue] == 1)
2953 [fPictureController setGrayscale:1];
2957 [fPictureController setGrayscale:0];
2960 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
2961 [fPictureController SetTitle:fTitle];
2962 [self calculatePictureSizing:nil];
2964 /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
2965 //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2966 //[queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2967 if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
2969 /* Deselect the currently selected Preset if there is one*/
2970 //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO];
2971 //[self selectPreset:nil];
2973 //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]];
2974 /* Change UI to show "Custom" settings are being used */
2975 //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]];
2977 curUserPresetChosenNum = nil;
2981 /* Deselect the currently selected Preset if there is one*/
2982 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2983 /* Change UI to show "Custom" settings are being used */
2984 [fPresetSelectedDisplay setStringValue: @"Custom"];
2986 //curUserPresetChosenNum = nil;
2989 /* We need to set this bool back to NO, in case the user wants to do a scan */
2990 //applyQueueToScan = NO;
2992 /* Not that source is loaded and settings applied, delete the queue item from the queue */
2993 [self writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d",fqueueEditRescanItemNum];
2994 [self removeQueueFileItem:fqueueEditRescanItemNum];
3000 #pragma mark Live Preview
3001 /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview
3002 * can encode to its temp preview directory and playback. This is *not* used for any actual user
3005 - (void) prepareJobForPreview
3007 hb_list_t * list = hb_get_titles( fHandle );
3008 hb_title_t * title = (hb_title_t *) hb_list_item( list,
3009 [fSrcTitlePopUp indexOfSelectedItem] );
3010 hb_job_t * job = title->job;
3011 hb_audio_config_t * audio;
3012 /* set job->angle for libdvdnav */
3013 job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1;
3014 /* Chapter selection */
3015 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
3016 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
3018 /* Format (Muxer) and Video Encoder */
3019 job->mux = [[fDstFormatPopUp selectedItem] tag];
3020 job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
3022 job->chapter_markers = 0;
3024 if( job->vcodec & HB_VCODEC_X264 )
3027 /* Below Sends x264 options to the core library if x264 is selected*/
3028 /* Lets use this as per Nyx, Thanks Nyx!*/
3029 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3030 /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */
3031 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
3036 /* Video settings */
3037 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3038 * and detelecine is on, so we handle that in the logic below
3041 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
3043 /* a specific framerate has been chosen */
3044 job->vrate = 27000000;
3045 job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate;
3046 /* We are not same as source so we set job->cfr to 1
3047 * to enable constant frame rate since user has specified
3048 * a specific framerate*/
3049 if ([fFrameratePfrCheck state] == 1)
3060 /* We are same as source (variable) */
3061 job->vrate = title->rate;
3062 job->vrate_base = title->rate_base;
3063 /* We are same as source so we set job->cfr to 0
3064 * to enable true same as source framerate */
3066 /* If we are same as source and we have detelecine on, we need to turn on
3069 if ([fPictureController detelecine] == 1)
3075 switch( [[fVidQualityMatrix selectedCell] tag] )
3079 job->vquality = -1.0;
3080 job->vbitrate = [fVidBitrateField intValue];
3083 /* Constant Quality */
3084 job->vquality = [fVidQualityRFField floatValue];
3089 /* Subtitle settings */
3090 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
3097 bool one_burned = FALSE;
3100 NSEnumerator *enumerator = [subtitlesArray objectEnumerator];
3102 while (tempObject = [enumerator nextObject])
3105 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3106 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3107 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3108 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3110 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3111 * we want to ignore it for display as well as encoding.
3115 /* if i is 0, then we are in the first item of the subtitles which we need to
3116 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3117 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3120 /* if we are on the first track and using "Foreign Audio Search" */
3121 if (i == 0 && subtitle == 1)
3123 /* NOTE: Currently foreign language search is borked for preview.
3124 * Commented out but left in for initial commit. */
3127 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3129 job->indepth_scan = 1;
3133 job->select_subtitle_config.dest = PASSTHRUSUB;
3137 job->select_subtitle_config.dest = RENDERSUB;
3140 job->select_subtitle_config.force = force;
3141 job->select_subtitle_config.default_track = def;
3146 /* for the actual source tracks, we must subtract the non source entries so
3147 * that the menu index matches the source subtitle_list index for convenience */
3150 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3151 * Foreign Language Search is 1) so subtract 2 */
3152 subtitle = subtitle - 2;
3156 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3159 subtitle = subtitle - 1;
3162 /* We are setting a source subtitle so access the source subtitle info */
3163 hb_subtitle_t * subt;
3165 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3167 /* if we are getting the subtitles from an external srt file */
3168 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3170 hb_subtitle_config_t sub_config;
3172 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3174 /* we need to srncpy file path and char code */
3175 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 255);
3176 sub_config.src_filename[255] = 0;
3177 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 39);
3178 sub_config.src_codeset[39] = 0;
3180 sub_config.force = 0;
3181 sub_config.dest = PASSTHRUSUB;
3182 sub_config.default_track = def;
3184 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3189 hb_subtitle_config_t sub_config = subt->config;
3191 if ( !burned && subt->format == PICTURESUB )
3193 sub_config.dest = PASSTHRUSUB;
3195 else if ( burned && subt->format == PICTURESUB )
3197 // Only allow one subtitle to be burned into the video
3203 /* Besides VOBSUBS we can also burn in SSA text subs */
3204 if (subt->source == SSASUB && burned)
3206 sub_config.dest = RENDERSUB;
3209 sub_config.force = force;
3210 sub_config.default_track = def;
3211 hb_subtitle_add( job, &sub_config, subtitle );
3221 [subtitlesArray autorelease];
3224 /* Audio tracks and mixdowns */
3225 [fAudioDelegate prepareAudioForJob: job];
3231 /* Though Grayscale is not really a filter, per se
3232 * we put it here since its in the filters panel
3235 if ([fPictureController grayscale])
3244 /* Initialize the filters list */
3245 job->filters = hb_list_init();
3247 /* Now lets call the filters if applicable.
3248 * The order of the filters is critical
3252 hb_filter_detelecine.settings = NULL;
3253 if ([fPictureController detelecine] == 1)
3255 /* use a custom detelecine string */
3256 hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3257 hb_list_add( job->filters, &hb_filter_detelecine );
3259 if ([fPictureController detelecine] == 2)
3262 hb_list_add( job->filters, &hb_filter_detelecine );
3267 if ([fPictureController useDecomb] == 1)
3270 /* we add the custom string if present */
3271 hb_filter_decomb.settings = NULL;
3272 if ([fPictureController decomb] == 1)
3274 /* use a custom decomb string */
3275 hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3276 hb_list_add( job->filters, &hb_filter_decomb );
3278 if ([fPictureController decomb] == 2)
3280 /* Run old deinterlacer fd by default */
3281 //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3282 hb_list_add( job->filters, &hb_filter_decomb );
3289 if ([fPictureController deinterlace] == 1)
3291 /* we add the custom string if present */
3292 hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3293 hb_list_add( job->filters, &hb_filter_deinterlace );
3295 else if ([fPictureController deinterlace] == 2)
3297 /* Run old deinterlacer fd by default */
3298 hb_filter_deinterlace.settings = "-1";
3299 hb_list_add( job->filters, &hb_filter_deinterlace );
3301 else if ([fPictureController deinterlace] == 3)
3303 /* Yadif mode 0 (without spatial deinterlacing.) */
3304 hb_filter_deinterlace.settings = "2";
3305 hb_list_add( job->filters, &hb_filter_deinterlace );
3307 else if ([fPictureController deinterlace] == 4)
3309 /* Yadif (with spatial deinterlacing) */
3310 hb_filter_deinterlace.settings = "0";
3311 hb_list_add( job->filters, &hb_filter_deinterlace );
3317 if ([fPictureController denoise] == 1) // custom in popup
3319 /* we add the custom string if present */
3320 hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String];
3321 hb_list_add( job->filters, &hb_filter_denoise );
3323 else if ([fPictureController denoise] == 2) // Weak in popup
3325 hb_filter_denoise.settings = "2:1:2:3";
3326 hb_list_add( job->filters, &hb_filter_denoise );
3328 else if ([fPictureController denoise] == 3) // Medium in popup
3330 hb_filter_denoise.settings = "3:2:2:3";
3331 hb_list_add( job->filters, &hb_filter_denoise );
3333 else if ([fPictureController denoise] == 4) // Strong in popup
3335 hb_filter_denoise.settings = "7:7:5:5";
3336 hb_list_add( job->filters, &hb_filter_denoise );
3340 /* Deblock (uses pp7 default) */
3341 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
3342 * the macgui's purposes a value of 0 actually means to not even use the filter
3343 * current hb_filter_deblock.settings valid ranges are from 5 - 15
3345 if ([fPictureController deblock] != 0)
3347 NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3348 hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3349 hb_list_add( job->filters, &hb_filter_deblock );
3356 #pragma mark Job Handling
3362 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3363 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
3364 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3365 hb_job_t * job = title->job;
3366 hb_audio_config_t * audio;
3367 /* Title Angle for dvdnav */
3368 job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3370 if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
3372 /* Chapter selection */
3373 [self writeToActivityLog: "Start / Stop set to chapters"];
3374 job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3375 job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3377 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
3379 /* we are pts based start / stop */
3380 [self writeToActivityLog: "Start / Stop set to seconds ..."];
3382 /* Point A to Point B. Time to time in seconds.*/
3383 /* get the start seconds from the start seconds field */
3384 int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
3385 job->pts_to_start = start_seconds * 90000LL;
3386 /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3387 int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
3388 job->pts_to_stop = stop_seconds * 90000LL;
3391 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
3393 /* we are frame based start / stop */
3394 [self writeToActivityLog: "Start / Stop set to frames ..."];
3396 /* Point A to Point B. Frame to frame */
3397 /* get the start frame from the start frame field */
3398 int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
3399 job->frame_to_start = start_frame;
3400 /* get the frame to stop on from the end frame field */
3401 int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
3402 job->frame_to_stop = stop_frame;
3409 /* Format (Muxer) and Video Encoder */
3410 job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3411 job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3414 /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3415 if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3417 job->largeFileSize = 1;
3421 job->largeFileSize = 0;
3423 /* We set http optimized mp4 here */
3424 if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3426 job->mp4_optimize = 1;
3430 job->mp4_optimize = 0;
3434 /* We set the chapter marker extraction here based on the format being
3435 mpeg4 or mkv and the checkbox being checked */
3436 if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3438 job->chapter_markers = 1;
3440 /* now lets get our saved chapter names out the array in the queue file
3441 * and insert them back into the title chapter list. We have it here,
3442 * because unless we are inserting chapter markers there is no need to
3443 * spend the overhead of iterating through the chapter names array imo
3444 * Also, note that if for some reason we don't apply chapter names, the
3445 * chapters just come out 001, 002, etc. etc.
3448 NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3450 NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3452 while (tempObject = [enumerator nextObject])
3454 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3455 if( chapter != NULL )
3457 strncpy( chapter->title, [tempObject UTF8String], 1023);
3458 chapter->title[1023] = '\0';
3465 job->chapter_markers = 0;
3468 if( job->vcodec & HB_VCODEC_X264 )
3470 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3480 /* Below Sends x264 options to the core library if x264 is selected*/
3481 /* Lets use this as per Nyx, Thanks Nyx!*/
3482 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3483 /* Turbo first pass if two pass and Turbo First pass is selected */
3484 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3486 /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3487 NSString *firstPassOptStringTurbo = @":ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3488 /* append the "Turbo" string variable to the existing opts string.
3489 Note: the "Turbo" string must be appended, not prepended to work properly*/
3490 NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3491 strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3495 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3501 /* Picture Size Settings */
3502 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
3503 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
3505 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
3506 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
3507 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
3508 if ([[queueToApply objectForKey:@"PicturePAR"] intValue] == 3)
3510 /* insert our custom values here for capuj */
3511 job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"] intValue];
3512 job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"] intValue];
3514 job->modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue];
3516 job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"] intValue];
3517 job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"] intValue];
3519 job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"] floatValue];
3520 job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"] floatValue];
3523 /* Here we use the crop values saved at the time the preset was saved */
3524 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
3525 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
3526 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
3527 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
3529 /* Video settings */
3532 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3533 * and detelecine is on, so we handle that in the logic below
3536 if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3538 /* a specific framerate has been chosen */
3539 job->vrate = 27000000;
3540 job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3541 /* We are not same as source so we set job->cfr to 1
3542 * to enable constant frame rate since user has specified
3543 * a specific framerate*/
3545 if ([[queueToApply objectForKey:@"VideoFrameratePFR"] intValue] == 1)
3556 /* We are same as source (variable) */
3557 job->vrate = [[queueToApply objectForKey:@"JobVrate"] intValue];
3558 job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3559 /* We are same as source so we set job->cfr to 0
3560 * to enable true same as source framerate */
3562 /* If we are same as source and we have detelecine on, we need to turn on
3565 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3571 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3573 job->vquality = -1.0;
3574 job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3576 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3578 job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3583 job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3588 #pragma mark Process Subtitles to libhb
3590 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3591 * which means that we need to account for the offset of non source language settings in from
3592 * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3593 * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3594 * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3595 * to the source tracks position in title->list_subtitle.
3602 bool one_burned = FALSE;
3605 NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3607 while (tempObject = [enumerator nextObject])
3610 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3611 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3612 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3613 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3615 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3616 * we want to ignore it for display as well as encoding.
3620 /* if i is 0, then we are in the first item of the subtitles which we need to
3621 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3622 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3625 /* if we are on the first track and using "Foreign Audio Search" */
3626 if (i == 0 && subtitle == 1)
3628 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3630 job->indepth_scan = 1;
3634 job->select_subtitle_config.dest = PASSTHRUSUB;
3638 job->select_subtitle_config.dest = RENDERSUB;
3641 job->select_subtitle_config.force = force;
3642 job->select_subtitle_config.default_track = def;
3647 /* for the actual source tracks, we must subtract the non source entries so
3648 * that the menu index matches the source subtitle_list index for convenience */
3651 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3652 * Foreign Language Search is 1) so subtract 2 */
3653 subtitle = subtitle - 2;
3657 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3660 subtitle = subtitle - 1;
3663 /* We are setting a source subtitle so access the source subtitle info */
3664 hb_subtitle_t * subt;
3666 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3668 /* if we are getting the subtitles from an external srt file */
3669 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3671 hb_subtitle_config_t sub_config;
3673 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3675 /* we need to srncpy file name and codeset */
3676 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3677 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3679 sub_config.force = 0;
3680 sub_config.dest = PASSTHRUSUB;
3681 sub_config.default_track = def;
3683 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3689 hb_subtitle_config_t sub_config = subt->config;
3691 if ( !burned && subt->format == PICTURESUB )
3693 sub_config.dest = PASSTHRUSUB;
3697 // Only allow one subtitle to be burned into the video
3703 /* Besides VOBSUBS we can also burn in SSA text subs */
3704 if (subt->source == SSASUB && burned)
3706 sub_config.dest = RENDERSUB;
3709 sub_config.force = force;
3710 sub_config.default_track = def;
3711 hb_subtitle_add( job, &sub_config, subtitle );
3722 /* Audio tracks and mixdowns */
3723 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3724 int audiotrack_count = hb_list_count(job->list_audio);
3725 for( int i = 0; i < audiotrack_count;i++)
3727 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3728 hb_list_rem(job->list_audio, temp_audio);
3730 /* Now lets add our new tracks to the audio list here */
3731 for (unsigned int counter = 0; counter < maximumNumberOfAllowedAudioTracks; counter++) {
3732 NSString *prefix = [NSString stringWithFormat: @"Audio%d", counter + 1];
3733 if ([[queueToApply objectForKey: [prefix stringByAppendingString: @"Track"]] intValue] > 0) {
3734 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3735 hb_audio_config_init(audio);
3736 audio->in.track = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Track"]] intValue] - 1;
3737 /* We go ahead and assign values to our audio->out.<properties> */
3738 audio->out.track = audio->in.track;
3739 audio->out.dynamic_range_compression = [[queueToApply objectForKey: [prefix stringByAppendingString: @"TrackDRCSlider"]] floatValue];
3740 prefix = [NSString stringWithFormat: @"JobAudio%d", counter + 1];
3741 audio->out.codec = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Encoder"]] intValue];
3742 audio->out.mixdown = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Mixdown"]] intValue];
3743 audio->out.bitrate = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Bitrate"]] intValue];
3744 audio->out.samplerate = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Samplerate"]] intValue];
3746 hb_audio_add( job, audio );
3752 job->filters = hb_list_init();
3754 /* Now lets call the filters if applicable.
3755 * The order of the filters is critical
3758 hb_filter_detelecine.settings = NULL;
3759 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3761 /* use a custom detelecine string */
3762 hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3763 hb_list_add( job->filters, &hb_filter_detelecine );
3765 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3767 /* Use libhb's default values */
3768 hb_list_add( job->filters, &hb_filter_detelecine );
3771 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3774 /* we add the custom string if present */
3775 hb_filter_decomb.settings = NULL;
3776 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3778 /* use a custom decomb string */
3779 hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
3780 hb_list_add( job->filters, &hb_filter_decomb );
3782 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
3784 /* Use libhb default */
3785 hb_list_add( job->filters, &hb_filter_decomb );
3793 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3795 /* we add the custom string if present */
3796 hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
3797 hb_list_add( job->filters, &hb_filter_deinterlace );
3799 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
3801 /* Run old deinterlacer fd by default */
3802 hb_filter_deinterlace.settings = "-1";
3803 hb_list_add( job->filters, &hb_filter_deinterlace );
3805 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
3807 /* Yadif mode 0 (without spatial deinterlacing.) */
3808 hb_filter_deinterlace.settings = "2";
3809 hb_list_add( job->filters, &hb_filter_deinterlace );
3811 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
3813 /* Yadif (with spatial deinterlacing) */
3814 hb_filter_deinterlace.settings = "0";
3815 hb_list_add( job->filters, &hb_filter_deinterlace );
3821 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup
3823 /* we add the custom string if present */
3824 hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
3825 hb_list_add( job->filters, &hb_filter_denoise );
3827 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup
3829 hb_filter_denoise.settings = "2:1:2:3";
3830 hb_list_add( job->filters, &hb_filter_denoise );
3832 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup
3834 hb_filter_denoise.settings = "3:2:2:3";
3835 hb_list_add( job->filters, &hb_filter_denoise );
3837 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup
3839 hb_filter_denoise.settings = "7:7:5:5";
3840 hb_list_add( job->filters, &hb_filter_denoise );
3844 /* Deblock (uses pp7 default) */
3845 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
3846 * the macgui's purposes a value of 0 actually means to not even use the filter
3847 * current hb_filter_deblock.settings valid ranges are from 5 - 15
3849 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
3851 hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
3852 hb_list_add( job->filters, &hb_filter_deblock );
3854 [self writeToActivityLog: "prepareJob exiting"];
3859 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
3861 - (IBAction) addToQueue: (id) sender
3863 /* We get the destination directory from the destination field here */
3864 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3865 /* We check for a valid destination here */
3866 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
3868 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3875 BOOL fileExistsInQueue;
3876 fileExistsInQueue = NO;
3878 /* We check for and existing file here */
3879 if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
3884 /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
3886 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
3888 while (tempObject = [enumerator nextObject])
3890 NSDictionary *thisQueueDict = tempObject;
3891 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
3893 fileExistsInQueue = YES;
3899 if(fileExists == YES)
3901 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
3902 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3903 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3904 NULL, NULL, [NSString stringWithFormat:
3905 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3906 [fDstFile2Field stringValue]] );
3908 else if (fileExistsInQueue == YES)
3910 NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
3911 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3912 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3913 NULL, NULL, [NSString stringWithFormat:
3914 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3915 [fDstFile2Field stringValue]] );
3919 [self doAddToQueue];
3923 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
3924 the user if they want to overwrite an exiting movie file.
3926 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
3927 returnCode: (int) returnCode contextInfo: (void *) contextInfo
3929 if( returnCode == NSAlertAlternateReturn )
3930 [self doAddToQueue];
3933 - (void) doAddToQueue
3935 [self addQueueFileItem ];
3940 /* Rip: puts up an alert before ultimately calling doRip
3942 - (IBAction) Rip: (id) sender
3944 [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
3945 /* Rip or Cancel ? */
3947 hb_get_state2( fQueueEncodeLibhb, &s );
3949 if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
3951 [self Cancel: sender];
3955 /* We check to see if we need to warn the user that the computer will go to sleep
3956 or shut down when encoding is finished */
3957 [self remindUserOfSleepOrShutdown];
3959 // If there are pending jobs in the queue, then this is a rip the queue
3960 if (fPendingCount > 0)
3962 currentQueueEncodeIndex = [self getNextPendingQueueIndex];
3963 /* here lets start the queue with the first pending item */
3964 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
3969 // Before adding jobs to the queue, check for a valid destination.
3971 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3972 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
3974 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3978 /* We check for duplicate name here */
3979 if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
3981 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
3982 NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3983 @selector( overWriteAlertDone:returnCode:contextInfo: ),
3984 NULL, NULL, [NSString stringWithFormat:
3985 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3986 [fDstFile2Field stringValue]] );
3988 // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
3992 /* if there are no pending jobs in the queue, then add this one to the queue and rip
3993 otherwise, just rip the queue */
3994 if(fPendingCount == 0)
3996 [self doAddToQueue];
3999 /* go right to processing the new queue encode */
4000 currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4001 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4006 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
4007 want to overwrite an exiting movie file.
4009 - (void) overWriteAlertDone: (NSWindow *) sheet
4010 returnCode: (int) returnCode contextInfo: (void *) contextInfo
4012 if( returnCode == NSAlertAlternateReturn )
4014 /* if there are no jobs in the queue, then add this one to the queue and rip
4015 otherwise, just rip the queue */
4016 if( fPendingCount == 0 )
4018 [self doAddToQueue];
4021 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4022 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
4023 currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4024 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4029 - (void) remindUserOfSleepOrShutdown
4031 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
4033 /*Warn that computer will sleep after encoding*/
4036 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);
4037 [NSApp requestUserAttention:NSCriticalRequest];
4038 if ( reminduser == NSAlertAlternateReturn )
4040 [self showPreferencesWindow:nil];
4043 else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
4045 /*Warn that computer will shut down after encoding*/
4048 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);
4049 [NSApp requestUserAttention:NSCriticalRequest];
4050 if ( reminduser == NSAlertAlternateReturn )
4052 [self showPreferencesWindow:nil];
4061 /* Let libhb do the job */
4062 hb_start( fQueueEncodeLibhb );
4063 /*set the fEncodeState State */
4068 //------------------------------------------------------------------------------------
4069 // Displays an alert asking user if the want to cancel encoding of current job.
4070 // Cancel: returns immediately after posting the alert. Later, when the user
4071 // acknowledges the alert, doCancelCurrentJob is called.
4072 //------------------------------------------------------------------------------------
4073 - (IBAction)Cancel: (id)sender
4075 if (!fQueueController) return;
4077 hb_pause( fQueueEncodeLibhb );
4078 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
4080 // Which window to attach the sheet to?
4081 NSWindow * docWindow;
4082 if ([sender respondsToSelector: @selector(window)])
4083 docWindow = [sender window];
4085 docWindow = fWindow;
4087 NSBeginCriticalAlertSheet(
4089 NSLocalizedString(@"Continue Encoding", nil),
4090 NSLocalizedString(@"Cancel Current and Stop", nil),
4091 NSLocalizedString(@"Cancel Current and Continue", nil),
4093 nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
4094 NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
4096 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
4099 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
4101 hb_resume( fQueueEncodeLibhb );
4102 if (returnCode == NSAlertOtherReturn)
4104 [self doCancelCurrentJob]; // <- this also stops libhb
4106 if (returnCode == NSAlertAlternateReturn)
4108 [self doCancelCurrentJobAndStop];
4112 //------------------------------------------------------------------------------------
4113 // Cancels and deletes the current job and stops libhb from processing the remaining
4115 //------------------------------------------------------------------------------------
4116 - (void) doCancelCurrentJob
4118 // Stop the current job. hb_stop will only cancel the current pass and then set
4119 // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
4120 // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
4121 // remaining passes of the job and then start the queue back up if there are any
4125 hb_stop( fQueueEncodeLibhb );
4127 // Delete all remaining jobs since libhb doesn't do this on its own.
4129 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4130 hb_rem( fQueueEncodeLibhb, job );
4132 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4134 // now that we've stopped the currently encoding job, lets mark it as cancelled
4135 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4136 // and as always, save it in the queue .plist...
4137 /* We save all of the Queue data here */
4138 [self saveQueueFileItem];
4140 // ... and see if there are more items left in our queue
4141 int queueItems = [QueueFileArray count];
4142 /* If we still have more items in our queue, lets go to the next one */
4143 /* Check to see if there are any more pending items in the queue */
4144 int newQueueItemIndex = [self getNextPendingQueueIndex];
4145 /* If we still have more pending items in our queue, lets go to the next one */
4146 if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
4148 /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
4149 currentQueueEncodeIndex = newQueueItemIndex;
4150 /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
4151 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
4152 [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
4153 [self saveQueueFileItem];
4154 /* now we can go ahead and scan the new pending queue item */
4155 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4160 [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
4164 - (void) doCancelCurrentJobAndStop
4166 hb_stop( fQueueEncodeLibhb );
4168 // Delete all remaining jobs since libhb doesn't do this on its own.
4170 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4171 hb_rem( fQueueEncodeLibhb, job );
4174 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4176 // now that we've stopped the currently encoding job, lets mark it as cancelled
4177 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4178 // and as always, save it in the queue .plist...
4179 /* We save all of the Queue data here */
4180 [self saveQueueFileItem];
4181 // so now lets move to
4182 currentQueueEncodeIndex++ ;
4183 [self writeToActivityLog: "cancelling current job and stopping the queue"];
4185 - (IBAction) Pause: (id) sender
4188 hb_get_state2( fQueueEncodeLibhb, &s );
4190 if( s.state == HB_STATE_PAUSED )
4192 hb_resume( fQueueEncodeLibhb );
4196 hb_pause( fQueueEncodeLibhb );
4201 #pragma mark GUI Controls Changed Methods
4203 - (IBAction) titlePopUpChanged: (id) sender
4205 hb_list_t * list = hb_get_titles( fHandle );
4206 hb_title_t * title = (hb_title_t*)
4207 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4209 /* If we are a stream type and a batch scan, grok the output file name from title->name upon title change */
4210 if (title->type == HB_STREAM_TYPE && hb_list_count( list ) > 1 )
4212 /* we set the default name according to the new title->name */
4213 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4214 @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4215 [NSString stringWithUTF8String: title->name],
4216 [[fDstFile2Field stringValue] pathExtension]]];
4218 /* Change the source to read out the parent folder also */
4219 [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]];
4222 /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */
4223 int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
4224 [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]];
4225 [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]];
4226 /* For point a to point b frame encoding, set the start and end fields to 0 and the title duration * announced fps in seconds respectively */
4227 [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]];
4228 //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]];
4229 [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]];
4231 /* If Auto Naming is on. We create an output filename of dvd name - title number */
4232 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4234 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4235 @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4236 [browsedSourceDisplayName stringByDeletingPathExtension],
4238 [[fDstFile2Field stringValue] pathExtension]]];
4240 /* Update encode start / stop variables */
4244 /* Update chapter popups */
4245 [fSrcChapterStartPopUp removeAllItems];
4246 [fSrcChapterEndPopUp removeAllItems];
4247 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4249 [fSrcChapterStartPopUp addItemWithTitle: [NSString
4250 stringWithFormat: @"%d", i + 1]];
4251 [fSrcChapterEndPopUp addItemWithTitle: [NSString
4252 stringWithFormat: @"%d", i + 1]];
4255 [fSrcChapterStartPopUp selectItemAtIndex: 0];
4256 [fSrcChapterEndPopUp selectItemAtIndex:
4257 hb_list_count( title->list_chapter ) - 1];
4258 [self chapterPopUpChanged:nil];
4260 /* if using dvd nav, show the angle widget */
4261 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4263 [fSrcAngleLabel setHidden:NO];
4264 [fSrcAnglePopUp setHidden:NO];
4266 [fSrcAnglePopUp removeAllItems];
4267 for( int i = 0; i < title->angle_count; i++ )
4269 [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4271 [fSrcAnglePopUp selectItemAtIndex: 0];
4275 [fSrcAngleLabel setHidden:YES];
4276 [fSrcAnglePopUp setHidden:YES];
4279 /* Start Get and set the initial pic size for display */
4280 hb_job_t * job = title->job;
4283 /* Set Auto Crop to on upon selecting a new title */
4284 [fPictureController setAutoCrop:YES];
4286 /* We get the originial output picture width and height and put them
4287 in variables for use with some presets later on */
4288 PicOrigOutputWidth = job->width;
4289 PicOrigOutputHeight = job->height;
4290 AutoCropTop = job->crop[0];
4291 AutoCropBottom = job->crop[1];
4292 AutoCropLeft = job->crop[2];
4293 AutoCropRight = job->crop[3];
4295 /* Reset the new title in fPictureController && fPreviewController*/
4296 [fPictureController SetTitle:title];
4299 /* Update Subtitle Table */
4300 [fSubtitlesDelegate resetWithTitle:title];
4301 [fSubtitlesTable reloadData];
4304 /* Update chapter table */
4305 [fChapterTitlesDelegate resetWithTitle:title];
4306 [fChapterTable reloadData];
4308 /* Update audio table */
4309 [[NSNotificationCenter defaultCenter] postNotification:
4310 [NSNotification notificationWithName: HBTitleChangedNotification
4312 userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
4313 [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag,
4316 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4317 int audiotrack_count = hb_list_count(job->list_audio);
4318 for( int i = 0; i < audiotrack_count;i++)
4320 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4321 hb_list_rem(job->list_audio, temp_audio);
4325 [fVidRatePopUp selectItemAtIndex: 0];
4327 /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4328 [self calculatePictureSizing:nil];
4330 /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4331 [self selectPreset:nil];
4334 - (IBAction) encodeStartStopPopUpChanged: (id) sender;
4336 if( [fEncodeStartStopPopUp isEnabled] )
4338 /* We are chapters */
4339 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
4341 [fSrcChapterStartPopUp setHidden: NO];
4342 [fSrcChapterEndPopUp setHidden: NO];
4344 [fSrcTimeStartEncodingField setHidden: YES];
4345 [fSrcTimeEndEncodingField setHidden: YES];
4347 [fSrcFrameStartEncodingField setHidden: YES];
4348 [fSrcFrameEndEncodingField setHidden: YES];
4350 [self chapterPopUpChanged:nil];
4352 /* We are time based (seconds) */
4353 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
4355 [fSrcChapterStartPopUp setHidden: YES];
4356 [fSrcChapterEndPopUp setHidden: YES];
4358 [fSrcTimeStartEncodingField setHidden: NO];
4359 [fSrcTimeEndEncodingField setHidden: NO];
4361 [fSrcFrameStartEncodingField setHidden: YES];
4362 [fSrcFrameEndEncodingField setHidden: YES];
4364 [self startEndSecValueChanged:nil];
4366 /* We are frame based */
4367 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
4369 [fSrcChapterStartPopUp setHidden: YES];
4370 [fSrcChapterEndPopUp setHidden: YES];
4372 [fSrcTimeStartEncodingField setHidden: YES];
4373 [fSrcTimeEndEncodingField setHidden: YES];
4375 [fSrcFrameStartEncodingField setHidden: NO];
4376 [fSrcFrameEndEncodingField setHidden: NO];
4378 [self startEndFrameValueChanged:nil];
4383 - (IBAction) chapterPopUpChanged: (id) sender
4386 /* If start chapter popup is greater than end chapter popup,
4387 we set the end chapter popup to the same as start chapter popup */
4388 if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4390 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4394 hb_list_t * list = hb_get_titles( fHandle );
4395 hb_title_t * title = (hb_title_t *)
4396 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4398 hb_chapter_t * chapter;
4399 int64_t duration = 0;
4400 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4401 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4403 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4404 duration += chapter->duration;
4407 duration /= 90000; /* pts -> seconds */
4408 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4409 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4412 //[self calculateBitrate: sender];
4414 if ( [fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem] )
4416 /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4417 [fCreateChapterMarkers setEnabled: NO];
4418 [fCreateChapterMarkers setState: NSOffState];
4422 [fCreateChapterMarkers setEnabled: YES];
4426 - (IBAction) startEndSecValueChanged: (id) sender
4429 int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue];
4430 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4431 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4434 //[self calculateBitrate: sender];
4438 - (IBAction) startEndFrameValueChanged: (id) sender
4440 hb_list_t * list = hb_get_titles( fHandle );
4441 hb_title_t * title = (hb_title_t*)
4442 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4444 int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base);
4445 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4446 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4449 //[self calculateBitrate: sender];
4453 - (IBAction) formatPopUpChanged: (id) sender
4455 NSString * string = [fDstFile2Field stringValue];
4456 int format = [fDstFormatPopUp indexOfSelectedItem];
4458 /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4459 [fDstMp4LargeFileCheck setHidden: YES];
4460 [fDstMp4HttpOptFileCheck setHidden: YES];
4461 [fDstMp4iPodFileCheck setHidden: YES];
4463 /* Update the Video Codec PopUp */
4464 /* lets get the tag of the currently selected item first so we might reset it later */
4465 int selectedVidEncoderTag;
4466 selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4468 /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4469 [fVidEncoderPopUp removeAllItems];
4470 NSMenuItem *menuItem;
4471 /* These video encoders are available to all of our current muxers, so lets list them once here */
4472 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4473 [menuItem setTag: HB_VCODEC_FFMPEG];
4478 [self autoSetM4vExtension: nil];
4479 /* Add additional video encoders here */
4480 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4481 [menuItem setTag: HB_VCODEC_X264];
4482 /* We show the mp4 option checkboxes here since we are mp4 */
4483 [fCreateChapterMarkers setEnabled: YES];
4484 [fDstMp4LargeFileCheck setHidden: NO];
4485 [fDstMp4HttpOptFileCheck setHidden: NO];
4486 [fDstMp4iPodFileCheck setHidden: NO];
4491 /* Add additional video encoders here */
4492 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4493 [menuItem setTag: HB_VCODEC_X264];
4494 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4495 [menuItem setTag: HB_VCODEC_THEORA];
4496 /* We enable the create chapters checkbox here */
4497 [fCreateChapterMarkers setEnabled: YES];
4502 /* tell fSubtitlesDelegate we have a new video container */
4504 [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4505 [fSubtitlesTable reloadData];
4507 /* post a notification for any interested observers to indicate that our video container has changed */
4508 [[NSNotificationCenter defaultCenter] postNotification:
4509 [NSNotification notificationWithName: HBContainerChangedNotification
4511 userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
4512 [NSNumber numberWithInt: [[fDstFormatPopUp selectedItem] tag]], keyContainerTag,
4515 /* if we have a previously selected vid encoder tag, then try to select it */
4516 if (selectedVidEncoderTag)
4518 [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4522 [fVidEncoderPopUp selectItemAtIndex: 0];
4527 [self autoSetM4vExtension: sender];
4529 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4531 if( SuccessfulScan )
4533 /* Add/replace to the correct extension */
4535 if( [fVidEncoderPopUp selectedItem] == nil )
4538 [fVidEncoderPopUp selectItemAtIndex:0];
4539 [self videoEncoderPopUpChanged:nil];
4541 /* changing the format may mean that we can / can't offer mono or 6ch, */
4542 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4544 /* We call the method to properly enable/disable turbo 2 pass */
4545 [self twoPassCheckboxChanged: sender];
4546 /* We call method method to change UI to reflect whether a preset is used or not*/
4549 [self customSettingUsed: sender];
4552 - (IBAction) autoSetM4vExtension: (id) sender
4554 if ( [fDstFormatPopUp indexOfSelectedItem] )
4557 NSString * extension = @"mp4";
4559 BOOL anyCodecAC3 = [fAudioDelegate anyCodecMatches: HB_ACODEC_AC3] || [fAudioDelegate anyCodecMatches: HB_ACODEC_AC3_PASS];
4561 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"DefaultMpegExtension"] isEqualToString: @".m4v"] ||
4562 ((YES == anyCodecAC3 || [fCreateChapterMarkers state] == NSOnState) &&
4563 [[[NSUserDefaults standardUserDefaults] objectForKey:@"DefaultMpegExtension"] isEqualToString: @"Auto"] ))
4568 if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4571 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4572 [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4575 /* Method to determine if we should change the UI
4576 To reflect whether or not a Preset is being used or if
4577 the user is using "Custom" settings by determining the sender*/
4578 - (IBAction) customSettingUsed: (id) sender
4580 if ([sender stringValue])
4582 /* Deselect the currently selected Preset if there is one*/
4583 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4584 /* Change UI to show "Custom" settings are being used */
4585 [fPresetSelectedDisplay setStringValue: @"Custom"];
4587 curUserPresetChosenNum = nil;
4589 [self calculateBitrate:nil];
4594 #pragma mark - Video
4596 - (IBAction) videoEncoderPopUpChanged: (id) sender
4598 hb_job_t * job = fTitle->job;
4599 int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4601 [fAdvancedOptions setHidden:YES];
4602 /* If we are using x264 then show the x264 advanced panel*/
4603 if (videoEncoder == HB_VCODEC_X264)
4605 [fAdvancedOptions setHidden:NO];
4606 [self autoSetM4vExtension: sender];
4609 if (videoEncoder == HB_VCODEC_FFMPEG)
4611 /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4612 container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4613 anything other than MP4.
4615 [fDstMp4iPodFileCheck setEnabled: NO];
4616 [fDstMp4iPodFileCheck setState: NSOffState];
4620 [fDstMp4iPodFileCheck setEnabled: YES];
4622 [self setupQualitySlider];
4623 [self calculatePictureSizing: sender];
4624 [self twoPassCheckboxChanged: sender];
4628 - (IBAction) twoPassCheckboxChanged: (id) sender
4630 /* check to see if x264 is chosen */
4631 if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4633 if( [fVidTwoPassCheck state] == NSOnState)
4635 [fVidTurboPassCheck setHidden: NO];
4639 [fVidTurboPassCheck setHidden: YES];
4640 [fVidTurboPassCheck setState: NSOffState];
4642 /* Make sure Two Pass is checked if Turbo is checked */
4643 if( [fVidTurboPassCheck state] == NSOnState)
4645 [fVidTwoPassCheck setState: NSOnState];
4650 [fVidTurboPassCheck setHidden: YES];
4651 [fVidTurboPassCheck setState: NSOffState];
4654 /* We call method method to change UI to reflect whether a preset is used or not*/
4655 [self customSettingUsed: sender];
4658 - (IBAction ) videoFrameRateChanged: (id) sender
4660 /* Hide and set the PFR Checkbox to OFF if we are set to Same as Source */
4661 if ([fVidRatePopUp indexOfSelectedItem] == 0)
4663 [fFrameratePfrCheck setHidden:YES];
4664 [fFrameratePfrCheck setState:0];
4668 [fFrameratePfrCheck setHidden:NO];
4671 /* We call method method to calculatePictureSizing to error check detelecine*/
4672 [self calculatePictureSizing: sender];
4674 /* We call method method to change UI to reflect whether a preset is used or not*/
4675 [self customSettingUsed: sender];
4678 - (IBAction) videoMatrixChanged: (id) sender;
4680 /* We use the selectedCell: tag of the fVidQualityMatrix instead of selectedRow
4681 * so that the order of the video controls can be switched around.
4682 * Constant quality is 1 and Average bitrate is 0 for reference. */
4683 bool bitrate, quality;
4684 bitrate = quality = false;
4685 if( [fVidQualityMatrix isEnabled] )
4687 switch( [[fVidQualityMatrix selectedCell] tag] )
4698 [fVidBitrateField setEnabled: bitrate];
4699 [fVidQualitySlider setEnabled: quality];
4700 [fVidQualityRFField setEnabled: quality];
4701 [fVidQualityRFLabel setEnabled: quality];
4702 [fVidTwoPassCheck setEnabled: !quality &&
4703 [fVidQualityMatrix isEnabled]];
4706 [fVidTwoPassCheck setState: NSOffState];
4707 [fVidTurboPassCheck setHidden: YES];
4708 [fVidTurboPassCheck setState: NSOffState];
4711 [self qualitySliderChanged: sender];
4712 //[self calculateBitrate: sender];
4713 [self customSettingUsed: sender];
4716 /* Use this method to setup the quality slider for cq/rf values depending on
4717 * the video encoder selected.
4719 - (void) setupQualitySlider
4721 /* Get the current slider maxValue to check for a change in slider scale later
4722 * so that we can choose a new similar value on the new slider scale */
4723 float previousMaxValue = [fVidQualitySlider maxValue];
4724 float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4725 NSString * qpRFLabelString = @"QP:";
4727 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4729 [fVidQualitySlider setMinValue:0.0];
4730 [fVidQualitySlider setMaxValue:51.0];
4731 /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4732 int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4733 [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4734 qpRFLabelString = @"RF:";
4737 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4739 [fVidQualitySlider setMinValue:1.0];
4740 [fVidQualitySlider setMaxValue:31.0];
4741 [fVidQualitySlider setNumberOfTickMarks:31];
4744 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4746 [fVidQualitySlider setMinValue:0.0];
4747 [fVidQualitySlider setMaxValue:63.0];
4748 [fVidQualitySlider setNumberOfTickMarks:64];
4750 [fVidQualityRFLabel setStringValue:qpRFLabelString];
4752 /* check to see if we have changed slider scales */
4753 if (previousMaxValue != [fVidQualitySlider maxValue])
4755 /* if so, convert the old setting to the new scale as close as possible based on percentages */
4756 float rf = ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4757 [fVidQualitySlider setFloatValue:rf];
4760 [self qualitySliderChanged:nil];
4763 - (IBAction) qualitySliderChanged: (id) sender
4765 /* Our constant quality slider is in a range based
4766 * on each encoders qp/rf values. The range depends
4767 * on the encoder. Also, the range is inverse of quality
4768 * for all of the encoders *except* for theora
4769 * (ie. as the "quality" goes up, the cq or rf value
4770 * actually goes down). Since the IB sliders always set
4771 * their max value at the right end of the slider, we
4772 * will calculate the inverse, so as the slider floatValue
4773 * goes up, we will show the inverse in the rf field
4774 * so, the floatValue at the right for x264 would be 51
4775 * and our rf field needs to show 0 and vice versa.
4778 float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
4779 /* If the encoder is theora, use the float, otherwise use the inverse float*/
4780 //float sliderRfToPercent;
4781 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4783 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
4787 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
4789 /* Show a warning if x264 and rf 0 which is lossless */
4790 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264 && sliderRfInverse == 0.0)
4792 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f (Warning: Lossless)", sliderRfInverse]];
4795 [self customSettingUsed: sender];
4798 - (void) controlTextDidChange: (NSNotification *) notification
4800 [self calculateBitrate:nil];
4803 - (IBAction) calculateBitrate: (id) sender
4805 if( !fHandle || ![fVidQualityMatrix selectedRow] || !SuccessfulScan )
4810 hb_list_t * list = hb_get_titles( fHandle );
4811 hb_title_t * title = (hb_title_t *) hb_list_item( list,
4812 [fSrcTitlePopUp indexOfSelectedItem] );
4813 hb_job_t * job = title->job;
4814 hb_audio_config_t * audio;
4815 /* For hb_calc_bitrate in addition to the Target Size in MB out of the
4816 * Target Size Field, we also need the job info for the Muxer, the Chapters
4817 * as well as all of the audio track info.
4818 * This used to be accomplished by simply calling prepareJob here, however
4819 * since the resilient queue sets the queue array values instead of the job
4820 * values directly, we duplicate the old prepareJob code here for the variables
4823 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
4824 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
4825 job->mux = [[fDstFormatPopUp selectedItem] tag];
4827 /* Audio goes here */
4828 [fAudioDelegate prepareAudioForJob: job];
4833 #pragma mark - Picture
4835 /* lets set the picture size back to the max from right after title scan
4836 Lets use an IBAction here as down the road we could always use a checkbox
4837 in the gui to easily take the user back to max. Remember, the compiler
4838 resolves IBActions down to -(void) during compile anyway */
4839 - (IBAction) revertPictureSizeToMax: (id) sender
4841 hb_job_t * job = fTitle->job;
4842 /* Here we apply the title source and height */
4843 job->width = fTitle->width;
4844 job->height = fTitle->height;
4846 [self calculatePictureSizing: sender];
4847 /* We call method to change UI to reflect whether a preset is used or not*/
4848 [self customSettingUsed: sender];
4852 * Registers changes made in the Picture Settings Window.
4855 - (void)pictureSettingsDidChange
4857 [self calculatePictureSizing:nil];
4860 /* Get and Display Current Pic Settings in main window */
4861 - (IBAction) calculatePictureSizing: (id) sender
4863 if (fTitle->job->anamorphic.mode > 0)
4865 fTitle->job->keep_ratio = 0;
4868 if (fTitle->job->anamorphic.mode != 1) // we are not strict so show the modulus
4870 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@, Modulus: %d", [fPictureController getPictureSizeInfoString], fTitle->job->modulus]];
4874 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
4876 NSString *picCropping;
4877 /* Set the display field for crop as per boolean */
4878 if (![fPictureController autoCrop])
4880 picCropping = @"Custom";
4884 picCropping = @"Auto";
4886 picCropping = [picCropping stringByAppendingString:[NSString stringWithFormat:@" %d/%d/%d/%d",fTitle->job->crop[0],fTitle->job->crop[1],fTitle->job->crop[2],fTitle->job->crop[3]]];
4888 [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
4890 NSString *videoFilters;
4893 if ([fPictureController detelecine] == 2)
4895 videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
4897 else if ([fPictureController detelecine] == 1)
4899 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
4903 if ([fPictureController useDecomb] == 1)
4906 if ([fPictureController decomb] == 2)
4908 videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
4910 else if ([fPictureController decomb] == 1)
4912 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
4918 if ([fPictureController deinterlace] > 0)
4920 fTitle->job->deinterlace = 1;
4924 fTitle->job->deinterlace = 0;
4927 if ([fPictureController deinterlace] == 2)
4929 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
4931 else if ([fPictureController deinterlace] == 3)
4933 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
4935 else if ([fPictureController deinterlace] == 4)
4937 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
4939 else if ([fPictureController deinterlace] == 1)
4941 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
4947 if ([fPictureController denoise] == 2)
4949 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
4951 else if ([fPictureController denoise] == 3)
4953 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
4955 else if ([fPictureController denoise] == 4)
4957 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
4959 else if ([fPictureController denoise] == 1)
4961 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
4965 if ([fPictureController deblock] > 0)
4967 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
4971 if ([fPictureController grayscale])
4973 videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
4975 [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
4977 //[fPictureController reloadStillPreview];
4982 #pragma mark - Audio and Subtitles
4987 - (BOOL) hasValidPresetSelected
4990 return ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1);
4993 // This causes all audio tracks from the title to be used based on the current preset
4994 - (IBAction) addAllAudioTracks: (id) sender
4997 [fAudioDelegate addAllTracksFromPreset: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
5001 - (IBAction) browseImportSrtFile: (id) sender
5004 NSOpenPanel * panel;
5006 panel = [NSOpenPanel openPanel];
5007 [panel setAllowsMultipleSelection: NO];
5008 [panel setCanChooseFiles: YES];
5009 [panel setCanChooseDirectories: NO ];
5010 NSString * sourceDirectory;
5011 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"])
5013 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"];
5017 sourceDirectory = @"~/Desktop";
5018 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
5020 /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */
5021 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil];
5022 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
5023 modalForWindow: fWindow modalDelegate: self
5024 didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: )
5025 contextInfo: sender];
5028 - (void) browseImportSrtFileDone: (NSSavePanel *) sheet
5029 returnCode: (int) returnCode contextInfo: (void *) contextInfo
5031 if( returnCode == NSOKButton )
5033 NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent];
5034 NSString *importSrtFilePath = [sheet filename];
5035 [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"];
5037 /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */
5038 [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath];
5040 [fSubtitlesTable reloadData];
5046 #pragma mark Open New Windows
5048 - (IBAction) openHomepage: (id) sender
5050 [[NSWorkspace sharedWorkspace] openURL: [NSURL
5051 URLWithString:@"http://handbrake.fr/"]];
5054 - (IBAction) openForums: (id) sender
5056 [[NSWorkspace sharedWorkspace] openURL: [NSURL
5057 URLWithString:@"http://forum.handbrake.fr/"]];
5059 - (IBAction) openUserGuide: (id) sender
5061 [[NSWorkspace sharedWorkspace] openURL: [NSURL
5062 URLWithString:@"http://trac.handbrake.fr/wiki/HandBrakeGuide"]];
5066 * Shows debug output window.
5068 - (IBAction)showDebugOutputPanel:(id)sender
5070 [outputPanel showOutputPanel:sender];
5074 * Shows preferences window.
5076 - (IBAction) showPreferencesWindow: (id) sender
5078 NSWindow * window = [fPreferencesController window];
5079 if (![window isVisible])
5082 [window makeKeyAndOrderFront: nil];
5086 * Shows queue window.
5088 - (IBAction) showQueueWindow:(id)sender
5090 [fQueueController showQueueWindow:sender];
5094 - (IBAction) toggleDrawer:(id)sender {
5095 [fPresetDrawer toggle:self];
5099 * Shows Picture Settings Window.
5102 - (IBAction) showPicturePanel: (id) sender
5104 [fPictureController showPictureWindow:sender];
5107 - (void) picturePanelFullScreen
5109 [fPictureController setToFullScreenMode];
5112 - (void) picturePanelWindowed
5114 [fPictureController setToWindowedMode];
5117 - (IBAction) showPreviewWindow: (id) sender
5119 [fPictureController showPreviewWindow:sender];
5123 #pragma mark Preset Outline View Methods
5124 #pragma mark - Required
5125 /* These are required by the NSOutlineView Datasource Delegate */
5128 /* used to specify the number of levels to show for each item */
5129 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
5131 /* currently use no levels to test outline view viability */
5132 if (item == nil) // for an outline view the root level of the hierarchy is always nil
5134 return [UserPresets count];
5138 /* we need to return the count of the array in ChildrenArray for this folder */
5139 NSArray *children = nil;
5140 children = [item objectForKey:@"ChildrenArray"];
5141 if ([children count] > 0)
5143 return [children count];
5152 /* We use this to deterimine children of an item */
5153 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
5156 /* we need to return the count of the array in ChildrenArray for this folder */
5157 NSArray *children = nil;
5160 children = UserPresets;
5164 if ([item objectForKey:@"ChildrenArray"])
5166 children = [item objectForKey:@"ChildrenArray"];
5169 if ((children == nil) || ( [children count] <= (NSUInteger) index))
5175 return [children objectAtIndex:index];
5179 // We are only one level deep, so we can't be asked about children
5180 //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
5184 /* We use this to determine if an item should be expandable */
5185 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
5188 /* we need to return the count of the array in ChildrenArray for this folder */
5189 NSArray *children= nil;
5192 children = UserPresets;
5196 if ([item objectForKey:@"ChildrenArray"])
5198 children = [item objectForKey:@"ChildrenArray"];
5202 /* To deterimine if an item should show a disclosure triangle
5203 * we could do it by the children count as so:
5204 * if ([children count] < 1)
5205 * However, lets leave the triangle show even if there are no
5206 * children to help indicate a folder, just like folder in the
5207 * finder can show a disclosure triangle even when empty
5210 /* We need to determine if the item is a folder */
5211 if ([[item objectForKey:@"Folder"] intValue] == 1)
5222 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
5224 // Our outline view has no levels, but we can still expand every item. Doing so
5225 // just makes the row taller. See heightOfRowByItem below.
5226 //return ![(HBQueueOutlineView*)outlineView isDragging];
5232 /* Used to tell the outline view which information is to be displayed per item */
5233 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5235 /* We have two columns right now, icon and PresetName */
5237 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5239 return [item objectForKey:@"PresetName"];
5248 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
5250 return [NSKeyedUnarchiver unarchiveObjectWithData:object];
5252 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
5254 return [NSKeyedArchiver archivedDataWithRootObject:item];
5257 #pragma mark - Added Functionality (optional)
5258 /* Use to customize the font and display characteristics of the title cell */
5259 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
5261 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5265 NSColor *shadowColor;
5266 txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
5267 /*check to see if its a selected row */
5268 if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
5271 fontColor = [NSColor blackColor];
5272 shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
5276 if ([[item objectForKey:@"Type"] intValue] == 0)
5278 fontColor = [NSColor blueColor];
5280 else // User created preset, use a black font
5282 fontColor = [NSColor blackColor];
5284 /* check to see if its a folder */
5285 //if ([[item objectForKey:@"Folder"] intValue] == 1)
5287 //fontColor = [NSColor greenColor];
5292 /* We use Bold Text for the HB Default */
5293 if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
5295 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5297 /* We use Bold Text for the User Specified Default */
5298 if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
5300 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5304 [cell setTextColor:fontColor];
5305 [cell setFont:txtFont];
5310 /* We use this to edit the name field in the outline view */
5311 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5313 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5318 [theRecord setObject:object forKey:@"PresetName"];
5322 [fPresetsOutlineView reloadData];
5323 /* We save all of the preset data here */
5327 /* We use this to provide tooltips for the items in the presets outline view */
5328 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
5330 //if ([[tc identifier] isEqualToString:@"PresetName"])
5332 /* initialize the tooltip contents variable */
5334 /* if there is a description for the preset, we show it in the tooltip */
5335 if ([item objectForKey:@"PresetDescription"])
5337 loc_tip = [item objectForKey:@"PresetDescription"];
5342 loc_tip = @"No description available";
5348 - (void) outlineViewSelectionDidChange: (NSNotification *) ignored
5351 [self willChangeValueForKey: @"hasValidPresetSelected"];
5352 [self didChangeValueForKey: @"hasValidPresetSelected"];
5357 #pragma mark Preset Outline View Methods (dragging related)
5360 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
5362 // Dragging is only allowed for custom presets.
5363 //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
5364 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
5368 // Don't retain since this is just holding temporaral drag information, and it is
5369 //only used during a drag! We could put this in the pboard actually.
5370 fDraggedNodes = items;
5371 // Provide data for our custom type, and simple NSStrings.
5372 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
5374 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
5375 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
5380 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
5383 // Don't allow dropping ONTO an item since they can't really contain any children.
5385 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
5386 if (isOnDropTypeProposal)
5387 return NSDragOperationNone;
5389 // Don't allow dropping INTO an item since they can't really contain any children as of yet.
5392 index = [fPresetsOutlineView rowForItem: item] + 1;
5396 // Don't allow dropping into the Built In Presets.
5397 if (index < presetCurrentBuiltInCount)
5399 return NSDragOperationNone;
5400 index = MAX (index, presetCurrentBuiltInCount);
5403 [outlineView setDropItem:item dropChildIndex:index];
5404 return NSDragOperationGeneric;
5409 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
5411 /* first, lets see if we are dropping into a folder */
5412 if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
5414 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
5415 childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
5416 [childrenArray addObject:item];
5417 [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
5418 [childrenArray autorelease];
5420 else // We are not, so we just move the preset into the existing array
5422 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
5424 NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
5425 while (obj = [enumerator nextObject])
5427 [moveItems addIndex:[UserPresets indexOfObject:obj]];
5429 // Successful drop, lets rearrange the view and save it all
5430 [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
5432 [fPresetsOutlineView reloadData];
5437 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
5439 NSUInteger index = [indexSet lastIndex];
5440 NSUInteger aboveInsertIndexCount = 0;
5442 NSUInteger removeIndex;
5444 if (index >= insertIndex)
5446 removeIndex = index + aboveInsertIndexCount;
5447 aboveInsertIndexCount++;
5451 removeIndex = index;
5455 id object = [[array objectAtIndex:removeIndex] retain];
5456 [array removeObjectAtIndex:removeIndex];
5457 [array insertObject:object atIndex:insertIndex];
5460 index = [indexSet indexLessThanIndex:index];
5465 #pragma mark - Functional Preset NSOutlineView Methods
5467 - (IBAction)selectPreset:(id)sender
5470 if (YES == [self hasValidPresetSelected])
5472 chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
5473 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
5475 if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
5477 [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
5481 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
5485 [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
5486 [self formatPopUpChanged:nil];
5488 /* Chapter Markers*/
5489 [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
5490 /* check to see if we have only one chapter */
5491 [self chapterPopUpChanged:nil];
5493 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
5494 [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
5495 /* Mux mp4 with http optimization */
5496 [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
5499 [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
5500 /* We set the advanced opt string here if applicable*/
5501 [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
5503 /* Lets run through the following functions to get variables set there */
5504 [self videoEncoderPopUpChanged:nil];
5505 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
5506 [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
5507 [self calculateBitrate:nil];
5511 int qualityType = [[chosenPreset objectForKey:@"VideoQualityType"] intValue] - 1;
5512 /* Note since the removal of Target Size encoding, the possible values for VideoQuality type are 0 - 1.
5513 * Therefore any preset that uses the old 2 for Constant Quality would now use 1 since there is one less index
5514 * for the fVidQualityMatrix. It should also be noted that any preset that used the deprecated Target Size
5515 * setting of 0 would set us to 0 or ABR since ABR is now tagged 0. Fortunately this does not affect any built-in
5516 * presets since they all use Constant Quality or Average Bitrate.*/
5517 if (qualityType == -1)
5521 [fVidQualityMatrix selectCellWithTag:qualityType];
5523 [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
5525 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
5526 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
5527 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
5528 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
5529 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
5530 if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
5532 /* For the quality slider we need to convert the old percent's to the new rf scales */
5533 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
5534 [fVidQualitySlider setFloatValue:rf];
5539 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
5540 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
5542 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
5546 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
5547 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
5551 [self videoMatrixChanged:nil];
5553 /* Video framerate */
5554 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
5555 detected framerate in the fVidRatePopUp so we use index 0*/
5556 if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
5558 [fVidRatePopUp selectItemAtIndex: 0];
5562 [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
5565 [fFrameratePfrCheck setState:[[chosenPreset objectForKey:@"VideoFrameratePFR"] intValue]];
5566 [self videoFrameRateChanged:nil];
5568 /* 2 Pass Encoding */
5569 [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
5570 [self twoPassCheckboxChanged:nil];
5572 /* Turbo 1st pass for 2 Pass Encoding */
5573 [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
5576 [fAudioDelegate addTracksFromPreset: chosenPreset];
5579 [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
5580 /* Forced Subtitles */
5581 [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
5583 /* Picture Settings */
5584 /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
5585 * height, width, keep ar, anamorphic and crop settings.
5586 * picture filters are handled separately below.
5588 /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None"
5589 * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the
5590 * preset completely ignores any picture sizing values in the preset.
5592 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0)
5594 hb_job_t * job = fTitle->job;
5596 /* If Cropping is set to custom, then recall all four crop values from
5597 when the preset was created and apply them */
5598 if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0)
5600 [fPictureController setAutoCrop:NO];
5602 /* Here we use the custom crop values saved at the time the preset was saved */
5603 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue];
5604 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue];
5605 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue];
5606 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue];
5609 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
5611 [fPictureController setAutoCrop:YES];
5612 /* Here we use the auto crop values determined right after scan */
5613 job->crop[0] = AutoCropTop;
5614 job->crop[1] = AutoCropBottom;
5615 job->crop[2] = AutoCropLeft;
5616 job->crop[3] = AutoCropRight;
5621 if ([chosenPreset objectForKey:@"PictureModulus"])
5623 job->modulus = [[chosenPreset objectForKey:@"PictureModulus"] intValue];
5630 /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
5631 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1)
5633 /* Use Max Picture settings for whatever the dvd is.*/
5634 [self revertPictureSizeToMax:nil];
5635 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
5636 if (job->keep_ratio == 1)
5638 hb_fix_aspect( job, HB_KEEP_WIDTH );
5639 if( job->height > fTitle->height )
5641 job->height = fTitle->height;
5642 hb_fix_aspect( job, HB_KEEP_HEIGHT );
5645 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
5647 else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
5649 /* we check to make sure the presets width/height does not exceed the sources width/height */
5650 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"] intValue])
5652 /* if so, then we use the sources height and width to avoid scaling up */
5653 //job->width = fTitle->width;
5654 //job->height = fTitle->height;
5655 [self revertPictureSizeToMax:nil];
5657 else // source width/height is >= the preset height/width
5659 /* we can go ahead and use the presets values for height and width */
5660 job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue];
5661 job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue];
5663 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
5664 if (job->keep_ratio == 1)
5666 int height = fTitle->height;
5668 if ( job->height && job->height < fTitle->height )
5669 height = job->height;
5671 hb_fix_aspect( job, HB_KEEP_WIDTH );
5672 // Make sure the resulting height is less than
5673 // the title height and less than the height
5674 // requested in the preset.
5675 if( job->height > height )
5677 job->height = height;
5678 hb_fix_aspect( job, HB_KEEP_HEIGHT );
5681 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
5682 if ( job->anamorphic.mode > 0 )
5684 int w, h, par_w, par_h;
5686 job->anamorphic.par_width = fTitle->pixel_aspect_width;
5687 job->anamorphic.par_height = fTitle->pixel_aspect_height;
5688 job->maxWidth = job->width;
5689 job->maxHeight = job->height;
5690 hb_set_anamorphic_size( job, &w, &h, &par_w, &par_h );
5701 /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
5702 if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"] intValue] > 0)
5706 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
5707 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
5708 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
5711 [fPictureController setUseDecomb:1];
5712 [fPictureController setDecomb:0];
5713 [fPictureController setDeinterlace:0];
5714 if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
5716 /* we are using decomb */
5718 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
5720 [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
5722 /* if we are using "Custom" in the decomb setting, also set the custom string*/
5723 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1)
5725 [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];
5731 /* We are using Deinterlace */
5733 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
5735 [fPictureController setUseDecomb:0];
5736 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
5737 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
5738 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1)
5740 [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];
5747 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
5749 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
5750 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
5751 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
5753 [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];
5758 [fPictureController setDetelecine:0];
5762 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
5764 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
5765 /* if we are using "Custom" in the denoise setting, also set the custom string*/
5766 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1)
5768 [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];
5773 [fPictureController setDenoise:0];
5777 if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
5779 /* if its a one, then its the old on/off deblock, set on to 5*/
5780 [fPictureController setDeblock:5];
5784 /* use the settings intValue */
5785 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
5788 if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
5790 [fPictureController setGrayscale:1];
5794 [fPictureController setGrayscale:0];
5797 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
5798 [fPictureController SetTitle:fTitle];
5799 [fPictureController SetTitle:fTitle];
5800 [self calculatePictureSizing:nil];
5806 #pragma mark Manage Presets
5808 - (void) loadPresets {
5809 /* We declare the default NSFileManager into fileManager */
5810 NSFileManager * fileManager = [NSFileManager defaultManager];
5811 /*We define the location of the user presets file */
5812 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
5813 UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
5814 /* We check for the presets.plist */
5815 if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
5817 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
5820 UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
5821 if (nil == UserPresets)
5823 UserPresets = [[NSMutableArray alloc] init];
5824 [self addFactoryPresets:nil];
5826 [fPresetsOutlineView reloadData];
5828 [self checkBuiltInsForUpdates];
5831 - (void) checkBuiltInsForUpdates {
5833 BOOL updateBuiltInPresets = NO;
5835 NSEnumerator *enumerator = [UserPresets objectEnumerator];
5837 while (tempObject = [enumerator nextObject])
5839 /* iterate through the built in presets to see if any have an old build number */
5840 NSMutableDictionary *thisPresetDict = tempObject;
5841 /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
5842 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)
5844 if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
5846 updateBuiltInPresets = YES;
5851 /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
5852 if ( updateBuiltInPresets == YES)
5854 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
5856 /* Show an alert window that built in presets will be updated */
5857 /*On Screen Notification*/
5860 status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
5861 [NSApp requestUserAttention:NSCriticalRequest];
5863 /* when alert is dismissed, go ahead and update the built in presets */
5864 [self addFactoryPresets:nil];
5870 - (IBAction) addPresetPicDropdownChanged: (id) sender
5872 if ([fPresetNewPicSettingsPopUp indexOfSelectedItem] == 1)
5874 [fPresetNewPicWidthHeightBox setHidden:NO];
5878 [fPresetNewPicWidthHeightBox setHidden:YES];
5882 - (IBAction) showAddPresetPanel: (id) sender
5884 /* Deselect the currently selected Preset if there is one*/
5885 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
5887 /* Populate the preset picture settings popup here */
5888 [fPresetNewPicSettingsPopUp removeAllItems];
5889 [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
5890 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Custom"];
5891 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
5892 [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];
5893 /* Uncheck the preset use filters checkbox */
5894 [fPresetNewPicFiltersCheck setState:NSOffState];
5895 // fPresetNewFolderCheck
5896 [fPresetNewFolderCheck setState:NSOffState];
5897 /* Erase info from the input fields*/
5898 [fPresetNewName setStringValue: @""];
5899 [fPresetNewDesc setStringValue: @""];
5901 /* Initialize custom height and width settings to current values */
5903 [fPresetNewPicWidth setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->width]];
5904 [fPresetNewPicHeight setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->height]];
5905 [self addPresetPicDropdownChanged:nil];
5906 /* Show the panel */
5907 [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
5910 - (IBAction) closeAddPresetPanel: (id) sender
5912 [NSApp endSheet: fAddPresetPanel];
5913 [fAddPresetPanel orderOut: self];
5916 - (IBAction)addUserPreset:(id)sender
5918 if (![[fPresetNewName stringValue] length])
5919 NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
5922 /* Here we create a custom user preset */
5923 [UserPresets addObject:[self createPreset]];
5926 [self closeAddPresetPanel:nil];
5933 /* We Reload the New Table data for presets */
5934 [fPresetsOutlineView reloadData];
5935 /* We save all of the preset data here */
5943 /* We Sort the Presets By Factory or Custom */
5944 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
5945 ascending:YES] autorelease];
5946 /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/
5948 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
5949 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
5950 //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
5953 /* Since we can drag and drop our custom presets, lets just sort by type and not name */
5954 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
5955 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
5956 [UserPresets setArray:sortedArray];
5961 - (IBAction)insertPreset:(id)sender
5963 int index = [fPresetsOutlineView selectedRow];
5964 [UserPresets insertObject:[self createPreset] atIndex:index];
5965 [fPresetsOutlineView reloadData];
5969 - (NSDictionary *)createPreset
5971 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
5972 /* Preset build number */
5973 [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
5974 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
5975 /* Get the New Preset Name from the field in the AddPresetPanel */
5976 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
5977 /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
5978 [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
5979 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
5980 [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
5981 /*Set whether or not this is default, at creation set to 0*/
5982 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
5983 if ([fPresetNewFolderCheck state] == YES)
5985 /* initialize and set an empty array for children here since we are a new folder */
5986 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
5987 [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
5988 [childrenArray autorelease];
5990 else // we are not creating a preset folder, so we go ahead with the rest of the preset info
5992 /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
5993 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
5994 /* Get whether or not to use the current Picture Filter settings for the preset */
5995 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
5997 /* Get New Preset Description from the field in the AddPresetPanel*/
5998 [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
6000 [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
6001 /* Chapter Markers fCreateChapterMarkers*/
6002 [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
6003 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6004 [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
6005 /* Mux mp4 with http optimization */
6006 [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
6007 /* Add iPod uuid atom */
6008 [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
6012 [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
6013 /* x264 Option String */
6014 [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
6016 /* though there are actually only 0 - 1 types available in the ui we need to map to the old 0 - 2
6017 * set of indexes from when we had 0 == Target , 1 == Abr and 2 == Constant Quality for presets
6018 * to take care of any legacy presets. */
6019 [preset setObject:[NSNumber numberWithInt:[[fVidQualityMatrix selectedCell] tag] +1 ] forKey:@"VideoQualityType"];
6020 [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
6021 [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
6023 /* Video framerate */
6024 if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
6026 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
6028 else // we can record the actual titleOfSelectedItem
6030 [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
6032 [preset setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
6034 /* 2 Pass Encoding */
6035 [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
6036 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
6037 [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
6038 /*Picture Settings*/
6039 hb_job_t * job = fTitle->job;
6041 /* Picture Sizing */
6042 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
6043 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicWidth intValue]] forKey:@"PictureWidth"];
6044 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicHeight intValue]] forKey:@"PictureHeight"];
6045 [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
6046 [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
6047 [preset setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
6049 /* Set crop settings here */
6050 [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
6051 [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
6052 [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
6053 [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
6054 [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
6056 /* Picture Filters */
6057 [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
6058 [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
6059 [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
6060 [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
6061 [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
6062 [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
6063 [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
6064 [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
6065 [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
6066 [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
6067 [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
6070 NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
6071 [fAudioDelegate prepareAudioForPreset: audioListArray];
6074 [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
6077 /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
6078 * subititle array code. */
6080 //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
6081 /* Forced Subtitles */
6082 //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
6084 [preset autorelease];
6091 [UserPresets writeToFile:UserPresetsFile atomically:YES];
6092 /* We get the default preset in case it changed */
6093 [self getDefaultPresets:nil];
6097 - (IBAction)deletePreset:(id)sender
6101 if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
6105 /* Alert user before deleting preset */
6107 status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
6109 if ( status == NSAlertDefaultReturn )
6111 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6112 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6113 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
6115 NSEnumerator *enumerator;
6116 NSMutableArray *presetsArrayToMod;
6117 NSMutableArray *tempArray;
6119 /* If we are a root level preset, we are modding the UserPresets array */
6120 if (presetToModLevel == 0)
6122 presetsArrayToMod = UserPresets;
6124 else // We have a parent preset, so we modify the chidren array object for key
6126 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
6129 enumerator = [presetsArrayToMod objectEnumerator];
6130 tempArray = [NSMutableArray array];
6132 while (tempObject = [enumerator nextObject])
6134 NSDictionary *thisPresetDict = tempObject;
6135 if (thisPresetDict == presetToMod)
6137 [tempArray addObject:tempObject];
6141 [presetsArrayToMod removeObjectsInArray:tempArray];
6142 [fPresetsOutlineView reloadData];
6149 #pragma mark Import Export Preset(s)
6151 - (IBAction) browseExportPresetFile: (id) sender
6153 /* Open a panel to let the user choose where and how to save the export file */
6154 NSSavePanel * panel = [NSSavePanel savePanel];
6155 /* We get the current file name and path from the destination field here */
6156 NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
6158 [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
6159 modalForWindow: fWindow modalDelegate: self
6160 didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
6164 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
6165 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6167 if( returnCode == NSOKButton )
6169 NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6170 NSString *exportPresetsFile = [sheet filename];
6171 [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
6172 /* We check for the presets.plist */
6173 if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
6175 [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
6177 NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
6178 if (nil == presetsToExport)
6180 presetsToExport = [[NSMutableArray alloc] init];
6182 /* now get and add selected presets to export */
6185 if (YES == [self hasValidPresetSelected])
6187 [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6188 [presetsToExport writeToFile:exportPresetsFile atomically:YES];
6196 - (IBAction) browseImportPresetFile: (id) sender
6199 NSOpenPanel * panel;
6201 panel = [NSOpenPanel openPanel];
6202 [panel setAllowsMultipleSelection: NO];
6203 [panel setCanChooseFiles: YES];
6204 [panel setCanChooseDirectories: NO ];
6205 NSString * sourceDirectory;
6206 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
6208 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
6212 sourceDirectory = @"~/Desktop";
6213 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
6215 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
6216 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
6218 /* set this for allowed file types, not sure if we should allow xml or not */
6219 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
6220 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
6221 modalForWindow: fWindow modalDelegate: self
6222 didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
6223 contextInfo: sender];
6226 - (void) browseImportPresetDone: (NSSavePanel *) sheet
6227 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6229 if( returnCode == NSOKButton )
6231 NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6232 NSString *importPresetsFile = [sheet filename];
6233 [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
6234 /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file */
6235 NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
6236 /* iterate though the new array of presets to import and add them to our presets array */
6238 NSEnumerator *enumerator = [presetsToImport objectEnumerator];
6240 while (tempObject = [enumerator nextObject])
6242 /* make any changes to the incoming preset we see fit */
6243 /* make sure the incoming preset is not tagged as default */
6244 [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6245 /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
6246 NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
6247 [tempObject setObject:prependedName forKey:@"PresetName"];
6249 /* actually add the new preset to our presets array */
6250 [UserPresets addObject:tempObject];
6253 [presetsToImport autorelease];
6261 #pragma mark Manage Default Preset
6263 - (IBAction)getDefaultPresets:(id)sender
6265 presetHbDefault = nil;
6266 presetUserDefault = nil;
6267 presetUserDefaultParent = nil;
6268 presetUserDefaultParentParent = nil;
6269 NSMutableDictionary *presetHbDefaultParent = nil;
6270 NSMutableDictionary *presetHbDefaultParentParent = nil;
6273 BOOL userDefaultFound = NO;
6274 presetCurrentBuiltInCount = 0;
6275 /* First we iterate through the root UserPresets array to check for defaults */
6276 NSEnumerator *enumerator = [UserPresets objectEnumerator];
6278 while (tempObject = [enumerator nextObject])
6280 NSMutableDictionary *thisPresetDict = tempObject;
6281 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
6283 presetHbDefault = thisPresetDict;
6285 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
6287 presetUserDefault = thisPresetDict;
6288 userDefaultFound = YES;
6290 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
6292 presetCurrentBuiltInCount++; // <--increment the current number of built in presets
6296 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
6297 if ([thisPresetDict objectForKey:@"ChildrenArray"])
6299 NSMutableDictionary *thisPresetDictParent = thisPresetDict;
6300 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
6302 while (tempObject = [enumerator nextObject])
6304 NSMutableDictionary *thisPresetDict = tempObject;
6305 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
6307 presetHbDefault = thisPresetDict;
6308 presetHbDefaultParent = thisPresetDictParent;
6310 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
6312 presetUserDefault = thisPresetDict;
6313 presetUserDefaultParent = thisPresetDictParent;
6314 userDefaultFound = YES;
6317 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
6318 if ([thisPresetDict objectForKey:@"ChildrenArray"])
6320 NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
6321 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
6323 while (tempObject = [enumerator nextObject])
6325 NSMutableDictionary *thisPresetDict = tempObject;
6326 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
6328 presetHbDefault = thisPresetDict;
6329 presetHbDefaultParent = thisPresetDictParent;
6330 presetHbDefaultParentParent = thisPresetDictParentParent;
6332 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
6334 presetUserDefault = thisPresetDict;
6335 presetUserDefaultParent = thisPresetDictParent;
6336 presetUserDefaultParentParent = thisPresetDictParentParent;
6337 userDefaultFound = YES;
6346 /* check to see if a user specified preset was found, if not then assign the parents for
6347 * the presetHbDefault so that we can open the parents for the nested presets
6349 if (userDefaultFound == NO)
6351 presetUserDefaultParent = presetHbDefaultParent;
6352 presetUserDefaultParentParent = presetHbDefaultParentParent;
6356 - (IBAction)setDefaultPreset:(id)sender
6358 /* We need to determine if the item is a folder */
6359 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
6365 NSEnumerator *enumerator = [UserPresets objectEnumerator];
6367 /* First make sure the old user specified default preset is removed */
6368 while (tempObject = [enumerator nextObject])
6370 NSMutableDictionary *thisPresetDict = tempObject;
6371 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
6373 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6376 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
6377 if ([thisPresetDict objectForKey:@"ChildrenArray"])
6379 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
6382 while (tempObject = [enumerator nextObject])
6384 NSMutableDictionary *thisPresetDict1 = tempObject;
6385 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
6387 [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6389 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
6390 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
6392 NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
6395 while (tempObject = [enumerator nextObject])
6397 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
6399 [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6412 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6413 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6414 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
6417 NSMutableArray *presetsArrayToMod;
6418 NSMutableArray *tempArray;
6420 /* If we are a root level preset, we are modding the UserPresets array */
6421 if (presetToModLevel == 0)
6423 presetsArrayToMod = UserPresets;
6425 else // We have a parent preset, so we modify the chidren array object for key
6427 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
6430 enumerator = [presetsArrayToMod objectEnumerator];
6431 tempArray = [NSMutableArray array];
6433 while (tempObject = [enumerator nextObject])
6435 NSDictionary *thisPresetDict = tempObject;
6436 if (thisPresetDict == presetToMod)
6438 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
6440 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
6447 /* We save all of the preset data here */
6449 /* We Reload the New Table data for presets */
6450 [fPresetsOutlineView reloadData];
6453 - (IBAction)selectDefaultPreset:(id)sender
6455 NSMutableDictionary *presetToMod;
6456 /* if there is a user specified default, we use it */
6457 if (presetUserDefault)
6459 presetToMod = presetUserDefault;
6461 else if (presetHbDefault) //else we use the built in default presetHbDefault
6463 presetToMod = presetHbDefault;
6470 if (presetUserDefaultParent != nil)
6472 [fPresetsOutlineView expandItem:presetUserDefaultParent];
6475 if (presetUserDefaultParentParent != nil)
6477 [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
6481 [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
6482 [self selectPreset:nil];
6487 #pragma mark Manage Built In Presets
6490 - (IBAction)deleteFactoryPresets:(id)sender
6493 NSEnumerator *enumerator = [UserPresets objectEnumerator];
6497 NSMutableArray *tempArray;
6500 tempArray = [NSMutableArray array];
6501 /* we look here to see if the preset is we move on to the next one */
6502 while ( tempObject = [enumerator nextObject] )
6504 /* if the preset is "Factory" then we put it in the array of
6505 presets to delete */
6506 if ([[tempObject objectForKey:@"Type"] intValue] == 0)
6508 [tempArray addObject:tempObject];
6512 [UserPresets removeObjectsInArray:tempArray];
6513 [fPresetsOutlineView reloadData];
6518 /* We use this method to recreate new, updated factory presets */
6519 - (IBAction)addFactoryPresets:(id)sender
6522 /* First, we delete any existing built in presets */
6523 [self deleteFactoryPresets: sender];
6524 /* Then we generate new built in presets programmatically with fPresetsBuiltin
6525 * which is all setup in HBPresets.h and HBPresets.m*/
6526 [fPresetsBuiltin generateBuiltinPresets:UserPresets];
6527 /* update build number for built in presets */
6528 /* iterate though the new array of presets to import and add them to our presets array */
6530 NSEnumerator *enumerator = [UserPresets objectEnumerator];
6532 while (tempObject = [enumerator nextObject])
6534 /* Record the apps current build number in the PresetBuildNumber key */
6535 if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
6537 /* Preset build number */
6538 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
6542 /* report the built in preset updating to the activity log */
6543 [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
6551 #pragma mark Chapter Files Import / Export
6553 - (IBAction) browseForChapterFile: (id) sender
6555 /* Open a panel to let the user choose the file */
6556 NSOpenPanel * panel = [NSOpenPanel openPanel];
6557 /* We get the current file name and path from the destination field here */
6558 [panel beginSheetForDirectory: [NSString stringWithFormat:@"%@/",
6559 [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]]
6561 types: [NSArray arrayWithObjects:@"csv",nil]
6562 modalForWindow: fWindow modalDelegate: self
6563 didEndSelector: @selector( browseForChapterFileDone:returnCode:contextInfo: )
6567 - (void) browseForChapterFileDone: (NSOpenPanel *) sheet
6568 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6570 NSArray *chaptersArray; /* temp array for chapters */
6571 NSMutableArray *chaptersMutableArray; /* temp array for chapters */
6572 NSString *chapterName; /* temp string from file */
6575 if( returnCode == NSOKButton ) /* if they click OK */
6577 chapterName = [[NSString alloc] initWithContentsOfFile:[sheet filename] encoding:NSUTF8StringEncoding error:NULL];
6578 chaptersArray = [chapterName componentsSeparatedByString:@"\n"];
6579 chaptersMutableArray= [chaptersArray mutableCopy];
6580 chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
6581 if ([chaptersMutableArray count] > 0)
6583 /* if last item is empty remove it */
6584 if ([[chaptersMutableArray objectAtIndex:[chaptersArray count]-1] length] == 0)
6586 [chaptersMutableArray removeLastObject];
6589 /* if chapters in table is not equal to array count */
6590 if ((unsigned int) chapters != [chaptersMutableArray count])
6593 [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
6594 defaultButton:NSLocalizedString(@"OK", @"OK")
6595 alternateButton:NULL
6597 informativeTextWithFormat:NSLocalizedString(@"%d chapters expected, %d chapters found in %@", @"%d chapters expected, %d chapters found in %@"),
6598 chapters, [chaptersMutableArray count], [[sheet filename] lastPathComponent]] runModal];
6601 /* otherwise, go ahead and populate table with array */
6602 for (i=0; i<chapters; i++)
6605 if([[chaptersMutableArray objectAtIndex:i] length] > 5)
6607 /* avoid a segfault */
6608 /* Get the Range.location of the first comma in the line and then put everything after that into chapterTitle */
6609 NSRange firstCommaRange = [[chaptersMutableArray objectAtIndex:i] rangeOfString:@","];
6610 NSString *chapterTitle = [[chaptersMutableArray objectAtIndex:i] substringFromIndex:firstCommaRange.location + 1];
6611 /* Since we store our chapterTitle commas as "\," for the cli, we now need to remove the escaping "\" from the title */
6612 chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"\\," withString:@","];
6613 [fChapterTitlesDelegate tableView:fChapterTable
6614 setObjectValue:chapterTitle
6615 forTableColumn:fChapterTableNameColumn
6621 [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
6622 defaultButton:NSLocalizedString(@"OK", @"OK")
6623 alternateButton:NULL
6625 informativeTextWithFormat:NSLocalizedString(@"%@ was not formatted as expected.", @"%@ was not formatted as expected."), [[sheet filename] lastPathComponent]] runModal];
6626 [fChapterTable reloadData];
6630 [fChapterTable reloadData];
6634 - (IBAction) browseForChapterFileSave: (id) sender
6636 NSSavePanel *panel = [NSSavePanel savePanel];
6637 /* Open a panel to let the user save to a file */
6638 [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"csv",nil]];
6639 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]
6640 file: [[[[fDstFile2Field stringValue] lastPathComponent] stringByDeletingPathExtension]
6641 stringByAppendingString:@"-chapters.csv"]
6642 modalForWindow: fWindow
6644 didEndSelector: @selector( browseForChapterFileSaveDone:returnCode:contextInfo: )
6648 - (void) browseForChapterFileSaveDone: (NSSavePanel *) sheet
6649 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6651 NSString *chapterName; /* pointer for string for later file-writing */
6652 NSString *chapterTitle;
6653 NSError *saveError = [[NSError alloc] init];
6654 int chapters, i; /* ints for the number of chapters in the table and the loop */
6656 if( returnCode == NSOKButton ) /* if they clicked OK */
6658 chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
6659 chapterName = [NSString string];
6660 for (i=0; i<chapters; i++)
6662 /* put each chapter title from the table into the array */
6664 { /* if i is from 0 to 8 (chapters 1 to 9) add two leading zeros */
6665 chapterName = [chapterName stringByAppendingFormat:@"00%d,",i+1];
6668 { /* if i is from 9 to 98 (chapters 10 to 99) add one leading zero */
6669 chapterName = [chapterName stringByAppendingFormat:@"0%d,",i+1];
6672 { /* in case i is from 99 to 998 (chapters 100 to 999) no leading zeros */
6673 chapterName = [chapterName stringByAppendingFormat:@"%d,",i+1];
6676 chapterTitle = [fChapterTitlesDelegate tableView:fChapterTable objectValueForTableColumn:fChapterTableNameColumn row:i];
6677 /* escape any commas in the chapter name with "\," */
6678 chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"," withString:@"\\,"];
6679 chapterName = [chapterName stringByAppendingString:chapterTitle];
6680 if (i+1 != chapters)
6681 { /* if not the last chapter */
6682 chapterName = [chapterName stringByAppendingString:@ "\n"];
6687 /* try to write it to where the user wanted */
6688 if (![chapterName writeToFile:[sheet filename]
6690 encoding:NSUTF8StringEncoding
6694 [[NSAlert alertWithError:saveError] runModal];
6701 /*******************************
6702 * Subclass of the HBPresetsOutlineView *
6703 *******************************/
6705 @implementation HBPresetsOutlineView
6706 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
6710 // By default, NSTableView only drags an image of the first column. Change this to
6711 // drag an image of the queue's icon and PresetName columns.
6712 NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
6713 return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
6718 - (void) mouseDown:(NSEvent *)theEvent
6720 [super mouseDown:theEvent];
6726 - (BOOL) isDragging;