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.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
7 #include "Controller.h"
9 #define _(a) NSLocalizedString(a,NULL)
11 static int FormatSettings[3][4] =
12 { { HB_MUX_MP4 | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC,
13 HB_MUX_MP4 | HB_VCODEC_X264 | HB_ACODEC_FAAC,
16 { HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
17 HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_AC3,
18 HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_LAME,
19 HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_AC3 },
20 { HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS,
21 HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
25 /*******************************
26 * HBController implementation *
27 *******************************/
28 @implementation HBController
37 - (void) applicationDidFinishLaunching: (NSNotification *) notification
43 fHandle = hb_init( HB_DEBUG_NONE, [[NSUserDefaults
44 standardUserDefaults] boolForKey:@"CheckForUpdates"] );
46 /* Init others controllers */
47 [fScanController SetHandle: fHandle];
48 [fPictureController SetHandle: fHandle];
49 [fQueueController SetHandle: fHandle];
51 /* Call UpdateUI every 2/10 sec */
52 [[NSRunLoop currentRunLoop] addTimer: [NSTimer
53 scheduledTimerWithTimeInterval: 0.2 target: self
54 selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
55 forMode: NSModalPanelRunLoopMode];
57 if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
59 /* Update available - tell the user */
60 NSBeginInformationalAlertSheet( _( @"Update is available" ),
61 _( @"Go get it!" ), _( @"Discard" ), NULL, fWindow, self,
62 @selector( UpdateAlertDone:returnCode:contextInfo: ),
63 NULL, NULL, [NSString stringWithFormat:
64 _( @"HandBrake %s (build %d) is now available for download." ),
69 /* Show scan panel ASAP */
70 [self performSelectorOnMainThread: @selector(ShowScanPanel:)
71 withObject: NULL waitUntilDone: NO];
74 - (NSApplicationTerminateReply) applicationShouldTerminate:
77 if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
80 return NSTerminateCancel;
85 return NSTerminateNow;
92 [self TranslateStrings];
95 [fDstFormatPopUp removeAllItems];
96 [fDstFormatPopUp addItemWithTitle: _( @"MP4 file" )];
97 [fDstFormatPopUp addItemWithTitle: _( @"AVI file" )];
98 [fDstFormatPopUp addItemWithTitle: _( @"OGM file" )];
99 [fDstFormatPopUp selectItemAtIndex: 0];
101 [self FormatPopUpChanged: NULL];
103 [fDstFile2Field setStringValue: [NSString stringWithFormat:
104 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
107 [fVidEncoderPopUp removeAllItems];
108 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
109 [fVidEncoderPopUp addItemWithTitle: @"XviD"];
112 [fVidTargetSizeField setIntValue: 700];
113 [fVidBitrateField setIntValue: 1000];
114 [fVidQualityMatrix selectCell: fVidBitrateCell];
115 [self VideoMatrixChanged: NULL];
117 /* Video framerate */
118 [fVidRatePopUp removeAllItems];
119 [fVidRatePopUp addItemWithTitle: _( @"Same as source" )];
120 for( int i = 0; i < hb_video_rates_count; i++ )
122 [fVidRatePopUp addItemWithTitle:
123 [NSString stringWithCString: hb_video_rates[i].string]];
125 [fVidRatePopUp selectItemAtIndex: 0];
128 [fAudBitratePopUp removeAllItems];
129 for( int i = 0; i < hb_audio_bitrates_count; i++ )
131 [fAudBitratePopUp addItemWithTitle:
132 [NSString stringWithCString: hb_audio_bitrates[i].string]];
134 [fAudBitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
136 /* Audio samplerate */
137 [fAudRatePopUp removeAllItems];
138 for( int i = 0; i < hb_audio_rates_count; i++ )
140 [fAudRatePopUp addItemWithTitle:
141 [NSString stringWithCString: hb_audio_rates[i].string]];
143 [fAudRatePopUp selectItemAtIndex: hb_audio_rates_default];
146 [fStatusField setStringValue: @""];
149 [fPauseButton setEnabled: NO];
150 [fRipButton setEnabled: NO];
153 - (void) TranslateStrings
155 [fSrcDVD1Field setStringValue: _( @"DVD:" )];
156 [fSrcTitleField setStringValue: _( @"Title:" )];
157 [fSrcChapterField setStringValue: _( @"Chapters:" )];
158 [fSrcChapterToField setStringValue: _( @"to" )];
159 [fSrcDuration1Field setStringValue: _( @"Duration:" )];
161 [fDstFormatField setStringValue: _( @"File format:" )];
162 [fDstCodecsField setStringValue: _( @"Codecs:" )];
163 [fDstFile1Field setStringValue: _( @"File:" )];
164 [fDstBrowseButton setTitle: _( @"Browse" )];
166 [fVidRateField setStringValue: _( @"Framerate (fps):" )];
167 [fVidEncoderField setStringValue: _( @"Encoder:" )];
168 [fVidQualityField setStringValue: _( @"Quality:" )];
171 /***********************************************************************
173 ***********************************************************************
174 * Shows a progression bar on the dock icon, filled according to
175 * 'progress' (0.0 <= progress <= 1.0).
176 * Called with progress < 0.0 or progress > 1.0, restores the original
178 **********************************************************************/
179 - (void) UpdateDockIcon: (float) progress
183 NSBitmapImageRep * bmp;
185 uint32_t black = htonl( 0x000000FF );
186 uint32_t red = htonl( 0xFF0000FF );
187 uint32_t white = htonl( 0xFFFFFFFF );
188 int row_start, row_end;
191 /* Get application original icon */
192 icon = [NSImage imageNamed: @"NSApplicationIcon"];
194 if( progress < 0.0 || progress > 1.0 )
196 [NSApp setApplicationIconImage: icon];
200 /* Get it in a raw bitmap form */
201 tiff = [icon TIFFRepresentationUsingCompression:
202 NSTIFFCompressionNone factor: 1.0];
203 bmp = [NSBitmapImageRep imageRepWithData: tiff];
205 /* Draw the progression bar */
206 /* It's pretty simple (ugly?) now, but I'm no designer */
208 row_start = 3 * (int) [bmp size].height / 4;
209 row_end = 7 * (int) [bmp size].height / 8;
211 for( i = row_start; i < row_start + 2; i++ )
213 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
214 for( j = 0; j < (int) [bmp size].width; j++ )
219 for( i = row_start + 2; i < row_end - 2; i++ )
221 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
224 for( j = 2; j < (int) [bmp size].width - 2; j++ )
226 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
238 for( i = row_end - 2; i < row_end; i++ )
240 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
241 for( j = 0; j < (int) [bmp size].width; j++ )
247 /* Now update the dock icon */
248 tiff = [bmp TIFFRepresentationUsingCompression:
249 NSTIFFCompressionNone factor: 1.0];
250 icon = [[NSImage alloc] initWithData: tiff];
251 [NSApp setApplicationIconImage: icon];
255 - (void) UpdateUI: (NSTimer *) timer
258 hb_get_state( fHandle, &s );
265 case HB_STATE_SCANNING:
266 [fScanController UpdateUI: &s];
269 #define p s.param.scandone
270 case HB_STATE_SCANDONE:
275 [fScanController UpdateUI: &s];
277 list = hb_get_titles( fHandle );
279 if( !hb_list_count( list ) )
284 [fSrcTitlePopUp removeAllItems];
285 for( int i = 0; i < hb_list_count( list ); i++ )
287 title = (hb_title_t *) hb_list_item( list, i );
288 [fSrcDVD2Field setStringValue: [NSString
289 stringWithUTF8String: title->dvd]];
290 [fSrcTitlePopUp addItemWithTitle: [NSString
291 stringWithFormat: @"%d - %02dh%02dm%02ds",
292 title->index, title->hours, title->minutes,
296 [self TitlePopUpChanged: NULL];
297 [self EnableUI: YES];
298 [fPauseButton setEnabled: NO];
299 [fRipButton setEnabled: YES];
304 #define p s.param.working
305 case HB_STATE_WORKING:
307 float progress_total;
308 NSMutableString * string;
310 /* Update text field */
311 string = [NSMutableString stringWithFormat:
312 _( @"Encoding: task %d of %d, %.2f %%" ),
313 p.job_cur, p.job_count, 100.0 * p.progress];
316 [string appendFormat:
317 _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),
318 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
320 [fStatusField setStringValue: string];
323 progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
324 [fRipIndicator setDoubleValue: 100.0 * progress_total];
326 /* Update dock icon */
327 [self UpdateDockIcon: progress_total];
329 [fPauseButton setEnabled: YES];
330 [fPauseButton setTitle: _( @"Pause" )];
331 [fRipButton setEnabled: YES];
332 [fRipButton setTitle: _( @"Cancel" )];
337 case HB_STATE_PAUSED:
338 [fStatusField setStringValue: _( @"Paused" )];
339 [fPauseButton setEnabled: YES];
340 [fPauseButton setTitle: _( @"Resume" )];
341 [fRipButton setEnabled: YES];
342 [fRipButton setTitle: _( @"Cancel" )];
345 case HB_STATE_WORKDONE:
347 [self EnableUI: YES];
348 [fStatusField setStringValue: _( @"Done." )];
349 [fRipIndicator setDoubleValue: 0.0];
350 [fRipButton setTitle: _( @"Rip" )];
352 /* Restore dock icon */
353 [self UpdateDockIcon: -1.0];
355 [fPauseButton setEnabled: NO];
356 [fPauseButton setTitle: _( @"Pause" )];
357 [fRipButton setEnabled: YES];
358 [fRipButton setTitle: _( @"Rip" )];
362 while( ( job = hb_job( fHandle, 0 ) ) )
364 hb_rem( fHandle, job );
370 /* FIXME: we should only do that when necessary */
371 if( [fQueueCheck state] == NSOnState )
373 int count = hb_count( fHandle );
376 [fQueueCheck setTitle: [NSString stringWithFormat:
377 @"Enable queue (%d task%s in queue)",
378 count, ( count > 1 ) ? "s" : ""]];
382 [fQueueCheck setTitle: @"Enable queue (no task in queue)"];
386 [[NSRunLoop currentRunLoop] addTimer: [NSTimer
387 scheduledTimerWithTimeInterval: 0.2 target: self
388 selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
389 forMode: NSModalPanelRunLoopMode];
392 - (void) EnableUI: (bool) b
394 NSControl * controls[] =
395 { fSrcDVD1Field, fSrcDVD2Field, fSrcTitleField, fSrcTitlePopUp,
396 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
397 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
398 fDstFormatField, fDstFormatPopUp, fDstCodecsField,
399 fDstCodecsPopUp, fDstFile1Field, fDstFile2Field,
400 fDstBrowseButton, fVidRateField, fVidRatePopUp,
401 fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
402 fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
403 fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp,
404 fAudRateField, fAudRatePopUp, fAudBitrateField,
405 fAudBitratePopUp, fPictureButton };
408 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
410 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
412 NSTextField * tf = (NSTextField *) controls[i];
413 if( ![tf isBezeled] )
415 [tf setTextColor: b ? [NSColor controlTextColor] :
416 [NSColor disabledControlTextColor]];
420 [controls[i] setEnabled: b];
423 [self VideoMatrixChanged: NULL];
426 - (IBAction) ShowScanPanel: (id) sender
428 [fScanController Show];
431 - (BOOL) windowShouldClose: (id) sender
433 /* Stop the application when the user closes the window */
434 [NSApp terminate: self];
438 - (IBAction) VideoMatrixChanged: (id) sender;
440 bool target, bitrate, quality;
442 target = bitrate = quality = false;
443 if( [fVidQualityMatrix isEnabled] )
445 switch( [fVidQualityMatrix selectedRow] )
458 [fVidTargetSizeField setEnabled: target];
459 [fVidBitrateField setEnabled: bitrate];
460 [fVidQualitySlider setEnabled: quality];
461 [fVidTwoPassCheck setEnabled: !quality &&
462 [fVidQualityMatrix isEnabled]];
465 [fVidTwoPassCheck setState: NSOffState];
468 [self QualitySliderChanged: sender];
469 [self CalculateBitrate: sender];
472 - (IBAction) QualitySliderChanged: (id) sender
474 [fVidConstantCell setTitle: [NSString stringWithFormat:
475 _( @"Constant quality: %.0f %%" ), 100.0 *
476 [fVidQualitySlider floatValue]]];
479 - (IBAction) BrowseFile: (id) sender
481 /* Open a panel to let the user choose and update the text field */
482 NSSavePanel * panel = [NSSavePanel savePanel];
484 [panel beginSheetForDirectory: NULL file: NULL
485 modalForWindow: fWindow modalDelegate: self
486 didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
490 - (void) BrowseFileDone: (NSSavePanel *) sheet
491 returnCode: (int) returnCode contextInfo: (void *) contextInfo
493 if( returnCode == NSOKButton )
495 [fDstFile2Field setStringValue: [sheet filename]];
496 [self FormatPopUpChanged: NULL];
500 - (IBAction) ShowPicturePanel: (id) sender
502 hb_list_t * list = hb_get_titles( fHandle );
503 hb_title_t * title = (hb_title_t *) hb_list_item( list,
504 [fSrcTitlePopUp indexOfSelectedItem] );
506 /* Resize the panel */
508 newSize.width = 246 + title->width;
509 newSize.height = 80 + title->height;
510 [fPicturePanel setContentSize: newSize];
512 [fPictureController SetTitle: title];
514 [NSApp beginSheet: fPicturePanel modalForWindow: fWindow
515 modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
516 [NSApp runModalForWindow: fPicturePanel];
517 [NSApp endSheet: fPicturePanel];
518 [fPicturePanel orderOut: self];
521 - (IBAction) ShowQueuePanel: (id) sender
523 /* Update the OutlineView */
524 [fQueueController Update: sender];
527 [NSApp beginSheet: fQueuePanel modalForWindow: fWindow
528 modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
529 [NSApp runModalForWindow: fQueuePanel];
530 [NSApp endSheet: fQueuePanel];
531 [fQueuePanel orderOut: self];
536 hb_list_t * list = hb_get_titles( fHandle );
537 hb_title_t * title = (hb_title_t *) hb_list_item( list,
538 [fSrcTitlePopUp indexOfSelectedItem] );
539 hb_job_t * job = title->job;
541 /* Chapter selection */
542 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
543 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
545 /* Format and codecs */
546 int format = [fDstFormatPopUp indexOfSelectedItem];
547 int codecs = [fDstCodecsPopUp indexOfSelectedItem];
548 job->mux = FormatSettings[format][codecs] & HB_MUX_MASK;
549 job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK;
550 job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
552 if( ( job->vcodec & HB_VCODEC_FFMPEG ) &&
553 [fVidEncoderPopUp indexOfSelectedItem] > 0 )
555 job->vcodec = HB_VCODEC_XVID;
557 if( job->vcodec & HB_VCODEC_X264 )
559 job->h264_13 = [fVidEncoderPopUp indexOfSelectedItem];
563 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
565 job->vrate = 27000000;
566 job->vrate_base = hb_video_rates[[fVidRatePopUp
567 indexOfSelectedItem]-1].rate;
571 job->vrate = title->rate;
572 job->vrate_base = title->rate_base;
575 switch( [fVidQualityMatrix selectedRow] )
579 Bitrate should already have been calculated and displayed
580 in fVidBitrateField, so let's just use it */
582 job->vquality = -1.0;
583 job->vbitrate = [fVidBitrateField intValue];
586 job->vquality = [fVidQualitySlider floatValue];
591 job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
593 /* Subtitle settings */
594 job->subtitle = [fSubPopUp indexOfSelectedItem] - 1;
597 job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1;
598 job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1;
602 job->arate = hb_audio_rates[[fAudRatePopUp
603 indexOfSelectedItem]].rate;
604 job->abitrate = hb_audio_bitrates[[fAudBitratePopUp
605 indexOfSelectedItem]].rate;
608 - (IBAction) EnableQueue: (id) sender
610 bool e = ( [fQueueCheck state] == NSOnState );
611 [fQueueAddButton setHidden: !e];
612 [fQueueShowButton setHidden: !e];
613 [fRipButton setTitle: e ? @"Start" : @"Rip"];
616 - (IBAction) AddToQueue: (id) sender
618 hb_list_t * list = hb_get_titles( fHandle );
619 hb_title_t * title = (hb_title_t *) hb_list_item( list,
620 [fSrcTitlePopUp indexOfSelectedItem] );
621 hb_job_t * job = title->job;
625 /* Destination file */
626 job->file = strdup( [[fDstFile2Field stringValue] UTF8String] );
628 if( [fVidTwoPassCheck state] == NSOnState )
631 hb_add( fHandle, job );
633 hb_add( fHandle, job );
638 hb_add( fHandle, job );
642 - (IBAction) Rip: (id) sender
644 /* Rip or Cancel ? */
645 if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
647 [self Cancel: sender];
651 if( [fQueueCheck state] == NSOffState )
653 [self AddToQueue: sender];
656 if( [[NSFileManager defaultManager] fileExistsAtPath:
657 [fDstFile2Field stringValue]] )
659 NSBeginCriticalAlertSheet( _( @"File already exists" ),
660 _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
661 @selector( OverwriteAlertDone:returnCode:contextInfo: ),
662 NULL, NULL, [NSString stringWithFormat:
663 _( @"Do you want to overwrite %@?" ),
664 [fDstFile2Field stringValue]] );
671 - (void) OverwriteAlertDone: (NSWindow *) sheet
672 returnCode: (int) returnCode contextInfo: (void *) contextInfo
674 if( returnCode == NSAlertAlternateReturn )
680 - (void) UpdateAlertDone: (NSWindow *) sheet
681 returnCode: (int) returnCode contextInfo: (void *) contextInfo
683 if( returnCode == NSAlertAlternateReturn )
685 /* Show scan panel */
686 [self performSelectorOnMainThread: @selector(ShowScanPanel:)
687 withObject: NULL waitUntilDone: NO];
691 /* Go to HandBrake homepage and exit */
692 [self OpenHomepage: NULL];
693 [NSApp terminate: self];
698 /* Let libhb do the job */
701 /* Disable interface */
703 [fPauseButton setEnabled: NO];
704 [fRipButton setEnabled: NO];
707 - (IBAction) Cancel: (id) sender
709 NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
710 _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
711 @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
712 _( @"Encoding won't be recoverable." ) );
715 - (void) _Cancel: (NSWindow *) sheet
716 returnCode: (int) returnCode contextInfo: (void *) contextInfo
718 if( returnCode == NSAlertAlternateReturn )
721 [fPauseButton setEnabled: NO];
722 [fRipButton setEnabled: NO];
726 - (IBAction) Pause: (id) sender
728 [fPauseButton setEnabled: NO];
729 [fRipButton setEnabled: NO];
731 if( [[fPauseButton title] isEqualToString: _( @"Resume" )] )
733 hb_resume( fHandle );
741 - (IBAction) TitlePopUpChanged: (id) sender
743 hb_list_t * list = hb_get_titles( fHandle );
744 hb_title_t * title = (hb_title_t*)
745 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
747 /* Update chapter popups */
748 [fSrcChapterStartPopUp removeAllItems];
749 [fSrcChapterEndPopUp removeAllItems];
750 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
752 [fSrcChapterStartPopUp addItemWithTitle: [NSString
753 stringWithFormat: @"%d", i + 1]];
754 [fSrcChapterEndPopUp addItemWithTitle: [NSString
755 stringWithFormat: @"%d", i + 1]];
757 [fSrcChapterStartPopUp selectItemAtIndex: 0];
758 [fSrcChapterEndPopUp selectItemAtIndex:
759 hb_list_count( title->list_chapter ) - 1];
760 [self ChapterPopUpChanged: NULL];
762 /* Update subtitle popups */
763 hb_subtitle_t * subtitle;
764 [fSubPopUp removeAllItems];
765 [fSubPopUp addItemWithTitle: @"None"];
766 for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
768 subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
770 /* We cannot use NSPopUpButton's addItemWithTitle because
771 it checks for duplicate entries */
772 [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
773 subtitle->lang] action: NULL keyEquivalent: @""];
775 [fSubPopUp selectItemAtIndex: 0];
777 /* Update lang popups */
779 [fAudLang1PopUp removeAllItems];
780 [fAudLang2PopUp removeAllItems];
781 [fAudLang1PopUp addItemWithTitle: _( @"None" )];
782 [fAudLang2PopUp addItemWithTitle: _( @"None" )];
783 for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
785 audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
787 [[fAudLang1PopUp menu] addItemWithTitle:
788 [NSString stringWithCString: audio->lang]
789 action: NULL keyEquivalent: @""];
790 [[fAudLang2PopUp menu] addItemWithTitle:
791 [NSString stringWithCString: audio->lang]
792 action: NULL keyEquivalent: @""];
794 [fAudLang1PopUp selectItemAtIndex: 1];
795 [fAudLang2PopUp selectItemAtIndex: 0];
798 - (IBAction) ChapterPopUpChanged: (id) sender
800 hb_list_t * list = hb_get_titles( fHandle );
801 hb_title_t * title = (hb_title_t *)
802 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
804 hb_chapter_t * chapter;
805 int64_t duration = 0;
806 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
807 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
809 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
810 duration += chapter->duration;
813 duration /= 90000; /* pts -> seconds */
814 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
815 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
818 [self CalculateBitrate: sender];
821 - (IBAction) FormatPopUpChanged: (id) sender
823 NSString * string = [fDstFile2Field stringValue];
824 int format = [fDstFormatPopUp indexOfSelectedItem];
827 /* Update the codecs popup */
828 [fDstCodecsPopUp removeAllItems];
833 [fDstCodecsPopUp addItemWithTitle:
834 _( @"MPEG-4 Video / AAC Audio" )];
835 [fDstCodecsPopUp addItemWithTitle:
836 _( @"AVC/H.264 Video / AAC Audio" )];
840 [fDstCodecsPopUp addItemWithTitle:
841 _( @"MPEG-4 Video / MP3 Audio" )];
842 [fDstCodecsPopUp addItemWithTitle:
843 _( @"MPEG-4 Video / AC-3 Audio" )];
844 [fDstCodecsPopUp addItemWithTitle:
845 _( @"AVC/H.264 Video / MP3 Audio" )];
846 [fDstCodecsPopUp addItemWithTitle:
847 _( @"AVC/H.264 Video / AC-3 Audio" )];
851 [fDstCodecsPopUp addItemWithTitle:
852 _( @"MPEG-4 Video / Vorbis Audio" )];
853 [fDstCodecsPopUp addItemWithTitle:
854 _( @"MPEG-4 Video / MP3 Audio" )];
857 [self CodecsPopUpChanged: NULL];
859 /* Add/replace to the correct extension */
860 if( [string characterAtIndex: [string length] - 4] == '.' )
862 [fDstFile2Field setStringValue: [NSString stringWithFormat:
863 @"%@.%s", [string substringToIndex: [string length] - 4],
868 [fDstFile2Field setStringValue: [NSString stringWithFormat:
869 @"%@.%s", string, ext]];
873 - (IBAction) CodecsPopUpChanged: (id) sender
875 int format = [fDstFormatPopUp indexOfSelectedItem];
876 int codecs = [fDstCodecsPopUp indexOfSelectedItem];
878 /* Update the encoder popup */
879 if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
881 /* MPEG-4 -> H.264 */
882 [fVidEncoderPopUp removeAllItems];
883 [fVidEncoderPopUp addItemWithTitle: @"x264 (Main profile)"];
884 [fVidEncoderPopUp addItemWithTitle: @"x264 (Baseline profile)"];
886 else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
888 /* H.264 -> MPEG-4 */
889 [fVidEncoderPopUp removeAllItems];
890 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
891 [fVidEncoderPopUp addItemWithTitle: @"XviD"];
892 [fVidEncoderPopUp selectItemAtIndex: 0];
895 if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
897 /* AC-3 pass-through: disable samplerate and bitrate */
898 [fAudRatePopUp setEnabled: NO];
899 [fAudBitratePopUp setEnabled: NO];
903 [fAudRatePopUp setEnabled: YES];
904 [fAudBitratePopUp setEnabled: YES];
907 [self CalculateBitrate: sender];
910 - (IBAction) CalculateBitrate: (id) sender
912 if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
917 hb_list_t * list = hb_get_titles( fHandle );
918 hb_title_t * title = (hb_title_t *) hb_list_item( list,
919 [fSrcTitlePopUp indexOfSelectedItem] );
920 hb_job_t * job = title->job;
924 [fVidBitrateField setIntValue: hb_calc_bitrate( job,
925 [fVidTargetSizeField intValue] )];
928 - (void) controlTextDidBeginEditing: (NSNotification *) notification
930 [self CalculateBitrate: NULL];
933 - (void) controlTextDidEndEditing: (NSNotification *) notification
935 [self CalculateBitrate: NULL];
938 - (void) controlTextDidChange: (NSNotification *) notification
940 [self CalculateBitrate: NULL];
943 - (IBAction) OpenHomepage: (id) sender
945 [[NSWorkspace sharedWorkspace] openURL: [NSURL
946 URLWithString:@"http://handbrake.m0k.org/"]];
949 - (IBAction) OpenForums: (id) sender
951 [[NSWorkspace sharedWorkspace] openURL: [NSURL
952 URLWithString:@"http://handbrake.m0k.org/forum/"]];