OSDN Git Service

Upgrade ffmpeg to Revision 7444, corrected the patch for clean apply.
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.mm
1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
2
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. */
6
7 #include "Controller.h"
8
9 #define _(a) NSLocalizedString(a,NULL)
10
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,
14       0,
15       0 },
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,
22       0,
23       0 } };
24
25 /*******************************
26  * HBController implementation *
27  *******************************/
28 @implementation HBController
29
30 - init
31 {
32     self    = [super init];
33     fHandle = NULL;
34     return self;
35 }
36
37 - (void) applicationDidFinishLaunching: (NSNotification *) notification
38 {
39     int    build;
40     char * version;
41
42     /* Init libhb */
43     fHandle = hb_init( HB_DEBUG_NONE, [[NSUserDefaults
44         standardUserDefaults] boolForKey:@"CheckForUpdates"] );
45
46     /* Init others controllers */
47     [fScanController    SetHandle: fHandle];
48     [fPictureController SetHandle: fHandle];
49     [fQueueController   SetHandle: fHandle];
50
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];
56
57     if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
58     {
59         /* Update available - tell the user */
60                 /* TEMPORARILY COMMENT OUT AS UPDATE CHECK IS NOT ACCURATE */
61                  /*
62         NSBeginInformationalAlertSheet( _( @"Update is available" ),
63             _( @"Go get it!" ), _( @"Discard" ), NULL, fWindow, self,
64             @selector( UpdateAlertDone:returnCode:contextInfo: ),
65             NULL, NULL, [NSString stringWithFormat:
66             _( @"HandBrake %s (build %d) is now available for download." ),
67             version, build] );
68         return;
69                 */
70     }
71
72     /* Show scan panel ASAP */
73     [self performSelectorOnMainThread: @selector(ShowScanPanel:)
74         withObject: NULL waitUntilDone: NO];
75 }
76
77 - (NSApplicationTerminateReply) applicationShouldTerminate:
78     (NSApplication *) app
79 {
80     if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
81     {
82         [self Cancel: NULL];
83         return NSTerminateCancel;
84     }
85     
86     /* Clean up */
87     hb_close( &fHandle );
88     return NSTerminateNow;
89 }
90
91 - (void) awakeFromNib
92 {
93     [fWindow center];
94
95     [self TranslateStrings];
96
97     /* Destination box */
98     [fDstFormatPopUp removeAllItems];
99     [fDstFormatPopUp addItemWithTitle: _( @"MP4 file" )];
100     [fDstFormatPopUp addItemWithTitle: _( @"AVI file" )];
101     [fDstFormatPopUp addItemWithTitle: _( @"OGM file" )];
102     [fDstFormatPopUp selectItemAtIndex: 0];
103
104     [self FormatPopUpChanged: NULL];
105
106     [fDstFile2Field setStringValue: [NSString stringWithFormat:
107         @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
108
109     /* Video encoder */
110     [fVidEncoderPopUp removeAllItems];
111     [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
112     [fVidEncoderPopUp addItemWithTitle: @"XviD"];
113
114     /* Video quality */
115     [fVidTargetSizeField setIntValue: 700];
116     [fVidBitrateField    setIntValue: 1000];
117     [fVidQualityMatrix   selectCell: fVidBitrateCell];
118     [self VideoMatrixChanged: NULL];
119
120     /* Video framerate */
121     [fVidRatePopUp removeAllItems];
122     [fVidRatePopUp addItemWithTitle: _( @"Same as source" )];
123     for( int i = 0; i < hb_video_rates_count; i++ )
124     {
125         [fVidRatePopUp addItemWithTitle:
126             [NSString stringWithCString: hb_video_rates[i].string]];
127     }
128     [fVidRatePopUp selectItemAtIndex: 0];
129
130     /* Audio bitrate */
131     [fAudBitratePopUp removeAllItems];
132     for( int i = 0; i < hb_audio_bitrates_count; i++ )
133     {
134         [fAudBitratePopUp addItemWithTitle:
135             [NSString stringWithCString: hb_audio_bitrates[i].string]];
136     }
137     [fAudBitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
138
139     /* Audio samplerate */
140     [fAudRatePopUp removeAllItems];
141     for( int i = 0; i < hb_audio_rates_count; i++ )
142     {
143         [fAudRatePopUp addItemWithTitle:
144             [NSString stringWithCString: hb_audio_rates[i].string]];
145     }
146     [fAudRatePopUp selectItemAtIndex: hb_audio_rates_default];
147
148     /* Bottom */
149     [fStatusField setStringValue: @""];
150
151     [self EnableUI: NO];
152     [fPauseButton setEnabled: NO];
153     [fRipButton setEnabled: NO];
154 }
155
156 - (void) TranslateStrings
157 {
158     [fSrcDVD1Field      setStringValue: _( @"DVD:" )];
159     [fSrcTitleField     setStringValue: _( @"Title:" )];
160     [fSrcChapterField   setStringValue: _( @"Chapters:" )];
161     [fSrcChapterToField setStringValue: _( @"to" )];
162     [fSrcDuration1Field setStringValue: _( @"Duration:" )];
163
164     [fDstFormatField    setStringValue: _( @"File format:" )];
165     [fDstCodecsField    setStringValue: _( @"Codecs:" )];
166     [fDstFile1Field     setStringValue: _( @"File:" )];
167     [fDstBrowseButton   setTitle:       _( @"Browse" )];
168
169     [fVidRateField      setStringValue: _( @"Framerate (fps):" )];
170     [fVidEncoderField   setStringValue: _( @"Encoder:" )];
171     [fVidQualityField   setStringValue: _( @"Quality:" )];
172 }
173
174 /***********************************************************************
175  * UpdateDockIcon
176  ***********************************************************************
177  * Shows a progression bar on the dock icon, filled according to
178  * 'progress' (0.0 <= progress <= 1.0).
179  * Called with progress < 0.0 or progress > 1.0, restores the original
180  * icon.
181  **********************************************************************/
182 - (void) UpdateDockIcon: (float) progress
183 {
184     NSImage * icon;
185     NSData * tiff;
186     NSBitmapImageRep * bmp;
187     uint32_t * pen;
188     uint32_t black = htonl( 0x000000FF );
189     uint32_t red   = htonl( 0xFF0000FF );
190     uint32_t white = htonl( 0xFFFFFFFF );
191     int row_start, row_end;
192     int i, j;
193
194     /* Get application original icon */
195     icon = [NSImage imageNamed: @"NSApplicationIcon"];
196
197     if( progress < 0.0 || progress > 1.0 )
198     {
199         [NSApp setApplicationIconImage: icon];
200         return;
201     }
202
203     /* Get it in a raw bitmap form */
204     tiff = [icon TIFFRepresentationUsingCompression:
205             NSTIFFCompressionNone factor: 1.0];
206     bmp = [NSBitmapImageRep imageRepWithData: tiff];
207     
208     /* Draw the progression bar */
209     /* It's pretty simple (ugly?) now, but I'm no designer */
210
211     row_start = 3 * (int) [bmp size].height / 4;
212     row_end   = 7 * (int) [bmp size].height / 8;
213
214     for( i = row_start; i < row_start + 2; i++ )
215     {
216         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
217         for( j = 0; j < (int) [bmp size].width; j++ )
218         {
219             pen[j] = black;
220         }
221     }
222     for( i = row_start + 2; i < row_end - 2; i++ )
223     {
224         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
225         pen[0] = black;
226         pen[1] = black;
227         for( j = 2; j < (int) [bmp size].width - 2; j++ )
228         {
229             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
230             {
231                 pen[j] = red;
232             }
233             else
234             {
235                 pen[j] = white;
236             }
237         }
238         pen[j]   = black;
239         pen[j+1] = black;
240     }
241     for( i = row_end - 2; i < row_end; i++ )
242     {
243         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
244         for( j = 0; j < (int) [bmp size].width; j++ )
245         {
246             pen[j] = black;
247         }
248     }
249
250     /* Now update the dock icon */
251     tiff = [bmp TIFFRepresentationUsingCompression:
252             NSTIFFCompressionNone factor: 1.0];
253     icon = [[NSImage alloc] initWithData: tiff];
254     [NSApp setApplicationIconImage: icon];
255     [icon release];
256 }
257
258 - (void) UpdateUI: (NSTimer *) timer
259 {
260
261     hb_state_t s;
262     hb_get_state( fHandle, &s );
263
264     switch( s.state )
265     {
266         case HB_STATE_IDLE:
267             break;
268
269         case HB_STATE_SCANNING:
270             [fScanController UpdateUI: &s];
271             break;
272
273 #define p s.param.scandone
274         case HB_STATE_SCANDONE:
275         {
276             hb_list_t  * list;
277             hb_title_t * title;
278
279             [fScanController UpdateUI: &s];
280
281             list = hb_get_titles( fHandle );
282
283             if( !hb_list_count( list ) )
284             {
285                 break;
286             }
287
288             [fSrcTitlePopUp removeAllItems];
289             for( int i = 0; i < hb_list_count( list ); i++ )
290             {
291                 title = (hb_title_t *) hb_list_item( list, i );
292                 /*Set DVD Name at top of window*/
293                                 [fSrcDVD2Field setStringValue: [NSString
294                   stringWithUTF8String: title->name]];  
295                                 
296                                 /* Use the dvd name in the default output field here 
297                                 May want to add code to remove blank spaces for some dvd names*/
298                                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
299                 @"%@/Desktop/%@.mp4", NSHomeDirectory(),[NSString
300                   stringWithUTF8String: title->name]]];
301                                 
302                             [fSrcTitlePopUp addItemWithTitle: [NSString
303                     stringWithFormat: @"%d - %02dh%02dm%02ds",
304                     title->index, title->hours, title->minutes,
305                     title->seconds]];
306                         
307             }
308
309             [self TitlePopUpChanged: NULL];
310             [self EnableUI: YES];
311             [fPauseButton setEnabled: NO];
312             [fRipButton   setEnabled: YES];
313             break;
314         }
315 #undef p
316
317 #define p s.param.working
318         case HB_STATE_WORKING:
319         {
320             float progress_total;
321             NSMutableString * string;
322
323             /* Update text field */
324             string = [NSMutableString stringWithFormat:
325                 _( @"Encoding: task %d of %d, %.2f %%" ),
326                 p.job_cur, p.job_count, 100.0 * p.progress];
327             if( p.seconds > -1 )
328             {
329                 [string appendFormat:
330                     _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),
331                     p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
332             }
333             [fStatusField setStringValue: string];
334
335             /* Update slider */
336             progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
337             [fRipIndicator setDoubleValue: 100.0 * progress_total];
338
339             /* Update dock icon */
340             [self UpdateDockIcon: progress_total];
341
342             [fPauseButton setEnabled: YES];
343             [fPauseButton setTitle: _( @"Pause" )];
344             [fRipButton setEnabled: YES];
345             [fRipButton setTitle: _( @"Cancel" )];
346             break;
347         }
348 #undef p
349
350 #define p s.param.muxing
351         case HB_STATE_MUXING:
352         {
353             float progress_total;
354             NSMutableString * string;
355                         
356             /* Update text field */
357             string = [NSMutableString stringWithFormat:
358                 _( @"Muxing: %.2f %%" ), 100.0 * p.progress];
359             [fStatusField setStringValue: string];
360                         
361             /* Update slider */
362             [fRipIndicator setDoubleValue: 100.0 * p.progress];
363                         
364             /* Update dock icon */
365             [self UpdateDockIcon: 100.0 * p.progress];
366                         
367             [fPauseButton setEnabled: YES];
368             [fPauseButton setTitle: _( @"Pause" )];
369             [fRipButton setEnabled: YES];
370             [fRipButton setTitle: _( @"Cancel" )];
371             break;
372         }
373 #undef p
374                         
375         case HB_STATE_PAUSED:
376             [fStatusField setStringValue: _( @"Paused" )];
377             [fPauseButton setEnabled: YES];
378             [fPauseButton setTitle: _( @"Resume" )];
379             [fRipButton setEnabled: YES];
380             [fRipButton setTitle: _( @"Cancel" )];
381             break;
382
383         case HB_STATE_WORKDONE:
384         {
385             [self EnableUI: YES];
386             [fStatusField setStringValue: _( @"Done." )];
387             [fRipIndicator setDoubleValue: 0.0];
388             [fRipButton setTitle: _( @"Rip" )];
389
390             /* Restore dock icon */
391             [self UpdateDockIcon: -1.0];
392
393             [fPauseButton setEnabled: NO];
394             [fPauseButton setTitle: _( @"Pause" )];
395             [fRipButton setEnabled: YES];
396             [fRipButton setTitle: _( @"Rip" )];
397
398             /* FIXME */
399             hb_job_t * job;
400             while( ( job = hb_job( fHandle, 0 ) ) )
401             {
402                 hb_rem( fHandle, job );
403             }
404             break;
405         }
406     }
407
408     /* FIXME: we should only do that when necessary */
409     if( [fQueueCheck state] == NSOnState )
410     {
411         int count = hb_count( fHandle );
412         if( count )
413         {
414             [fQueueCheck setTitle: [NSString stringWithFormat:
415                 @"Enable queue (%d task%s in queue)",
416                 count, ( count > 1 ) ? "s" : ""]];
417         }
418         else
419         {
420             [fQueueCheck setTitle: @"Enable queue (no task in queue)"];
421         }
422     }
423
424     [[NSRunLoop currentRunLoop] addTimer: [NSTimer
425         scheduledTimerWithTimeInterval: 0.2 target: self
426         selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
427         forMode: NSModalPanelRunLoopMode];
428 }
429
430 - (void) EnableUI: (bool) b
431 {
432     NSControl * controls[] =
433       { fSrcDVD1Field, fSrcDVD2Field, fSrcTitleField, fSrcTitlePopUp,
434         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
435         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
436         fDstFormatField, fDstFormatPopUp, fDstCodecsField,
437         fDstCodecsPopUp, fDstFile1Field, fDstFile2Field,
438         fDstBrowseButton, fVidRateField, fVidRatePopUp,
439         fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
440         fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
441         fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp,
442         fAudRateField, fAudRatePopUp, fAudBitrateField,
443         fAudBitratePopUp, fPictureButton, fQueueCheck };
444
445     for( unsigned i = 0;
446          i < sizeof( controls ) / sizeof( NSControl * ); i++ )
447     {
448         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
449         {
450             NSTextField * tf = (NSTextField *) controls[i];
451             if( ![tf isBezeled] )
452             {
453                 [tf setTextColor: b ? [NSColor controlTextColor] :
454                     [NSColor disabledControlTextColor]];
455                 continue;
456             }
457         }
458         [controls[i] setEnabled: b];
459     }
460
461     [self VideoMatrixChanged: NULL];
462 }
463
464 - (IBAction) ShowScanPanel: (id) sender
465 {
466     [fScanController Show];
467 }
468
469 - (BOOL) windowShouldClose: (id) sender
470 {
471     /* Stop the application when the user closes the window */
472     [NSApp terminate: self];
473     return YES;
474 }
475
476 - (IBAction) VideoMatrixChanged: (id) sender;
477 {
478     bool target, bitrate, quality;
479
480     target = bitrate = quality = false;
481     if( [fVidQualityMatrix isEnabled] )
482     {
483         switch( [fVidQualityMatrix selectedRow] )
484         {
485             case 0:
486                 target = true;
487                 break;
488             case 1:
489                 bitrate = true;
490                 break;
491             case 2:
492                 quality = true;
493                 break;
494         }
495     }
496     [fVidTargetSizeField  setEnabled: target];
497     [fVidBitrateField     setEnabled: bitrate];
498     [fVidQualitySlider    setEnabled: quality];
499     [fVidTwoPassCheck     setEnabled: !quality &&
500         [fVidQualityMatrix isEnabled]];
501     if( quality )
502     {
503         [fVidTwoPassCheck setState: NSOffState];
504     }
505
506     [self QualitySliderChanged: sender];
507     [self CalculateBitrate:     sender];
508 }
509
510 - (IBAction) QualitySliderChanged: (id) sender
511 {
512     [fVidConstantCell setTitle: [NSString stringWithFormat:
513         _( @"Constant quality: %.0f %%" ), 100.0 *
514         [fVidQualitySlider floatValue]]];
515 }
516
517 - (IBAction) BrowseFile: (id) sender
518 {
519     /* Open a panel to let the user choose and update the text field */
520     NSSavePanel * panel = [NSSavePanel savePanel];
521
522     [panel beginSheetForDirectory: NULL file: NULL
523         modalForWindow: fWindow modalDelegate: self
524         didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
525         contextInfo: NULL];
526 }
527
528 - (void) BrowseFileDone: (NSSavePanel *) sheet
529     returnCode: (int) returnCode contextInfo: (void *) contextInfo
530 {
531     if( returnCode == NSOKButton )
532     {
533         [fDstFile2Field setStringValue: [sheet filename]];
534         [self FormatPopUpChanged: NULL];
535     }
536 }
537
538 - (IBAction) ShowPicturePanel: (id) sender
539 {
540     hb_list_t  * list  = hb_get_titles( fHandle );
541     hb_title_t * title = (hb_title_t *) hb_list_item( list,
542             [fSrcTitlePopUp indexOfSelectedItem] );
543
544     /* Resize the panel */
545     NSSize newSize;
546     newSize.width  = 246 + title->width;
547     newSize.height = 80 + title->height;
548     [fPicturePanel setContentSize: newSize];
549
550     [fPictureController SetTitle: title];
551
552     [NSApp beginSheet: fPicturePanel modalForWindow: fWindow
553         modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
554     [NSApp runModalForWindow: fPicturePanel];
555     [NSApp endSheet: fPicturePanel];
556     [fPicturePanel orderOut: self];
557 }
558
559 - (IBAction) ShowQueuePanel: (id) sender
560 {
561     /* Update the OutlineView */
562     [fQueueController Update: sender];
563
564     /* Show the panel */
565     [NSApp beginSheet: fQueuePanel modalForWindow: fWindow
566         modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
567     [NSApp runModalForWindow: fQueuePanel];
568     [NSApp endSheet: fQueuePanel];
569     [fQueuePanel orderOut: self];
570 }
571
572 - (void) PrepareJob
573 {
574     hb_list_t  * list  = hb_get_titles( fHandle );
575     hb_title_t * title = (hb_title_t *) hb_list_item( list,
576             [fSrcTitlePopUp indexOfSelectedItem] );
577     hb_job_t * job = title->job;
578
579     /* Chapter selection */
580     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
581     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
582
583     /* Format and codecs */
584     int format = [fDstFormatPopUp indexOfSelectedItem];
585     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
586     job->mux    = FormatSettings[format][codecs] & HB_MUX_MASK;
587     job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK;
588     job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
589
590     if( ( job->vcodec & HB_VCODEC_FFMPEG ) &&
591         [fVidEncoderPopUp indexOfSelectedItem] > 0 )
592     {
593         job->vcodec = HB_VCODEC_XVID;
594     }
595     if( job->vcodec & HB_VCODEC_X264 )
596     {
597          if ([fVidEncoderPopUp indexOfSelectedItem] > 0 )
598             {
599                 /* Just use new Baseline Level 3.0 
600                 Lets Deprecate Baseline Level 1.3*/
601                 job->h264_level = 30;
602                 job->mux = HB_MUX_IPOD;
603                 }
604                 
605                 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor */
606                 // job->crf = 1;
607                 
608         job->h264_13 = [fVidEncoderPopUp indexOfSelectedItem];
609     }
610
611     /* Video settings */
612     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
613     {
614         job->vrate      = 27000000;
615         job->vrate_base = hb_video_rates[[fVidRatePopUp
616             indexOfSelectedItem]-1].rate;
617     }
618     else
619     {
620         job->vrate      = title->rate;
621         job->vrate_base = title->rate_base;
622     }
623
624     switch( [fVidQualityMatrix selectedRow] )
625     {
626         case 0:
627             /* Target size.
628                Bitrate should already have been calculated and displayed
629                in fVidBitrateField, so let's just use it */
630         case 1:
631             job->vquality = -1.0;
632             job->vbitrate = [fVidBitrateField intValue];
633             break;
634         case 2:
635             job->vquality = [fVidQualitySlider floatValue];
636             job->vbitrate = 0;
637             break;
638     }
639
640     job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
641
642     /* Subtitle settings */
643     job->subtitle = [fSubPopUp indexOfSelectedItem] - 1;
644
645     /* Audio tracks */
646     job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1;
647     job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1;
648     job->audios[2] = -1;
649
650     /* Audio settings */
651     job->arate = hb_audio_rates[[fAudRatePopUp
652                      indexOfSelectedItem]].rate;
653     job->abitrate = hb_audio_bitrates[[fAudBitratePopUp
654                         indexOfSelectedItem]].rate;
655 }
656
657 - (IBAction) EnableQueue: (id) sender
658 {
659     bool e = ( [fQueueCheck state] == NSOnState );
660     [fQueueAddButton  setHidden: !e];
661     [fQueueShowButton setHidden: !e];
662     [fRipButton       setTitle: e ? @"Start" : @"Rip"];
663 }
664
665 - (IBAction) AddToQueue: (id) sender
666 {
667     hb_list_t  * list  = hb_get_titles( fHandle );
668     hb_title_t * title = (hb_title_t *) hb_list_item( list,
669             [fSrcTitlePopUp indexOfSelectedItem] );
670     hb_job_t * job = title->job;
671
672     [self PrepareJob];
673
674     /* Destination file */
675     job->file = strdup( [[fDstFile2Field stringValue] UTF8String] );
676
677     if( [fVidTwoPassCheck state] == NSOnState )
678     {
679         job->pass = 1;
680         hb_add( fHandle, job );
681         job->pass = 2;
682         hb_add( fHandle, job );
683     }
684     else
685     {
686         job->pass = 0;
687         hb_add( fHandle, job );
688     }
689 }
690
691 - (IBAction) Rip: (id) sender
692 {
693     /* Rip or Cancel ? */
694     if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
695     {
696         [self Cancel: sender];
697         return;
698     }
699
700     if( [fQueueCheck state] == NSOffState )
701     {
702         [self AddToQueue: sender];
703     }
704
705     if( [[NSFileManager defaultManager] fileExistsAtPath:
706             [fDstFile2Field stringValue]] )
707     {
708         NSBeginCriticalAlertSheet( _( @"File already exists" ),
709             _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
710             @selector( OverwriteAlertDone:returnCode:contextInfo: ),
711             NULL, NULL, [NSString stringWithFormat:
712             _( @"Do you want to overwrite %@?" ),
713             [fDstFile2Field stringValue]] );
714         return;
715     }
716
717     [self _Rip];
718 }
719
720 - (void) OverwriteAlertDone: (NSWindow *) sheet
721     returnCode: (int) returnCode contextInfo: (void *) contextInfo
722 {
723     if( returnCode == NSAlertAlternateReturn )
724     {
725         [self _Rip];
726     }
727 }
728
729 - (void) UpdateAlertDone: (NSWindow *) sheet
730     returnCode: (int) returnCode contextInfo: (void *) contextInfo
731 {
732     if( returnCode == NSAlertAlternateReturn )
733     {
734         /* Show scan panel */
735         [self performSelectorOnMainThread: @selector(ShowScanPanel:)
736             withObject: NULL waitUntilDone: NO];
737         return;
738     }
739
740     /* Go to HandBrake homepage and exit */
741     [self OpenHomepage: NULL];
742     [NSApp terminate: self];
743 }
744
745 - (void) _Rip
746 {
747     /* Let libhb do the job */
748     hb_start( fHandle );
749
750     /* Disable interface */
751     [self EnableUI: NO];
752     [fPauseButton setEnabled: NO];
753     [fRipButton   setEnabled: NO];
754 }
755
756 - (IBAction) Cancel: (id) sender
757 {
758     NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
759         _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
760         @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
761         _( @"Encoding won't be recoverable." ) );
762 }
763
764 - (void) _Cancel: (NSWindow *) sheet
765     returnCode: (int) returnCode contextInfo: (void *) contextInfo
766 {
767     if( returnCode == NSAlertAlternateReturn )
768     {
769         hb_stop( fHandle );
770         [fPauseButton setEnabled: NO];
771         [fRipButton   setEnabled: NO];
772     }
773 }
774
775 - (IBAction) Pause: (id) sender
776 {
777     [fPauseButton setEnabled: NO];
778     [fRipButton   setEnabled: NO];
779
780     if( [[fPauseButton title] isEqualToString: _( @"Resume" )] )
781     {
782         hb_resume( fHandle );
783     }
784     else
785     {
786         hb_pause( fHandle );
787     }
788 }
789
790 - (IBAction) TitlePopUpChanged: (id) sender
791 {
792     hb_list_t  * list  = hb_get_titles( fHandle );
793     hb_title_t * title = (hb_title_t*)
794         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
795
796     /* Update chapter popups */
797     [fSrcChapterStartPopUp removeAllItems];
798     [fSrcChapterEndPopUp   removeAllItems];
799     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
800     {
801         [fSrcChapterStartPopUp addItemWithTitle: [NSString
802             stringWithFormat: @"%d", i + 1]];
803         [fSrcChapterEndPopUp addItemWithTitle: [NSString
804             stringWithFormat: @"%d", i + 1]];
805     }
806     [fSrcChapterStartPopUp selectItemAtIndex: 0];
807     [fSrcChapterEndPopUp   selectItemAtIndex:
808         hb_list_count( title->list_chapter ) - 1];
809     [self ChapterPopUpChanged: NULL];
810
811     /* Update subtitle popups */
812     hb_subtitle_t * subtitle;
813     [fSubPopUp removeAllItems];
814     [fSubPopUp addItemWithTitle: @"None"];
815     for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
816     {
817         subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
818
819         /* We cannot use NSPopUpButton's addItemWithTitle because
820            it checks for duplicate entries */
821         [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
822             subtitle->lang] action: NULL keyEquivalent: @""];
823     }
824     [fSubPopUp selectItemAtIndex: 0];
825
826     /* Update lang popups */
827     hb_audio_t * audio;
828         // PRI CHANGES 02/12/06
829         NSString * audiotmppri;
830         NSString * audiosearchpri=[[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
831         int indxpri=0;
832         // End of pri changes 02/12/06
833     [fAudLang1PopUp removeAllItems];
834     [fAudLang2PopUp removeAllItems];
835     [fAudLang1PopUp addItemWithTitle: _( @"None" )];
836     [fAudLang2PopUp addItemWithTitle: _( @"None" )];
837     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
838     {
839         audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
840         // PRI CHANGES 02/12/06
841                 audiotmppri=(NSString *) [NSString stringWithCString: audio->lang];
842                 // Try to find the desired default language
843            if ([audiotmppri hasPrefix:audiosearchpri] && indxpri==0)
844                 {
845                         indxpri=i+1;
846                 }
847         // End of pri changes 02/12/06
848
849         [[fAudLang1PopUp menu] addItemWithTitle:
850             [NSString stringWithCString: audio->lang]
851             action: NULL keyEquivalent: @""];
852         [[fAudLang2PopUp menu] addItemWithTitle:
853             [NSString stringWithCString: audio->lang]
854             action: NULL keyEquivalent: @""];
855     }
856         // PRI CHANGES 02/12/06
857         if (indxpri==0) { indxpri=1; }
858     [fAudLang1PopUp selectItemAtIndex: indxpri];
859         // End of pri changes 02/12/06
860     [fAudLang2PopUp selectItemAtIndex: 0];
861 }
862
863 - (IBAction) ChapterPopUpChanged: (id) sender
864 {
865     hb_list_t  * list  = hb_get_titles( fHandle );
866     hb_title_t * title = (hb_title_t *)
867         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
868
869     hb_chapter_t * chapter;
870     int64_t        duration = 0;
871     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
872          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
873     {
874         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
875         duration += chapter->duration;
876     }
877     
878     duration /= 90000; /* pts -> seconds */
879     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
880         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
881         duration % 60]];
882
883     [self CalculateBitrate: sender];
884 }
885
886 - (IBAction) FormatPopUpChanged: (id) sender
887 {
888     NSString * string = [fDstFile2Field stringValue];
889     int format = [fDstFormatPopUp indexOfSelectedItem];
890     char * ext = NULL;
891
892     /* Update the codecs popup */
893     [fDstCodecsPopUp removeAllItems];
894     switch( format )
895     {
896         case 0:
897             ext = "mp4";
898             [fDstCodecsPopUp addItemWithTitle:
899                 _( @"MPEG-4 Video / AAC Audio" )];
900             [fDstCodecsPopUp addItemWithTitle:
901                 _( @"AVC/H.264 Video / AAC Audio" )];
902             break;
903         case 1: 
904             ext = "avi";
905             [fDstCodecsPopUp addItemWithTitle:
906                 _( @"MPEG-4 Video / MP3 Audio" )];
907             [fDstCodecsPopUp addItemWithTitle:
908                 _( @"MPEG-4 Video / AC-3 Audio" )];
909             [fDstCodecsPopUp addItemWithTitle:
910                 _( @"AVC/H.264 Video / MP3 Audio" )];
911             [fDstCodecsPopUp addItemWithTitle:
912                 _( @"AVC/H.264 Video / AC-3 Audio" )];
913             break;
914         case 2:
915             ext = "ogm";
916             [fDstCodecsPopUp addItemWithTitle:
917                 _( @"MPEG-4 Video / Vorbis Audio" )];
918             [fDstCodecsPopUp addItemWithTitle:
919                 _( @"MPEG-4 Video / MP3 Audio" )];
920             break;
921     }
922     [self CodecsPopUpChanged: NULL];
923
924     /* Add/replace to the correct extension */
925     if( [string characterAtIndex: [string length] - 4] == '.' )
926     {
927         [fDstFile2Field setStringValue: [NSString stringWithFormat:
928             @"%@.%s", [string substringToIndex: [string length] - 4],
929             ext]];
930     }
931     else
932     {
933         [fDstFile2Field setStringValue: [NSString stringWithFormat:
934             @"%@.%s", string, ext]];
935     }
936 }
937
938 - (IBAction) CodecsPopUpChanged: (id) sender
939 {
940     int format = [fDstFormatPopUp indexOfSelectedItem];
941     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
942
943     /* Update the encoder popup */
944     if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
945     {
946         /* MPEG-4 -> H.264 */
947         [fVidEncoderPopUp removeAllItems];
948                 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 Main)"];
949                 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 iPod)"];
950         
951         
952     }
953     else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
954     {
955         /* H.264 -> MPEG-4 */
956         [fVidEncoderPopUp removeAllItems];
957         [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
958         [fVidEncoderPopUp addItemWithTitle: @"XviD"];
959         [fVidEncoderPopUp selectItemAtIndex: 0];
960     }
961
962     if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
963     {
964         /* AC-3 pass-through: disable samplerate and bitrate */
965         [fAudRatePopUp    setEnabled: NO];
966         [fAudBitratePopUp setEnabled: NO];
967     }
968     else
969     {
970         [fAudRatePopUp    setEnabled: YES];
971         [fAudBitratePopUp setEnabled: YES];
972     }
973
974     [self CalculateBitrate: sender];
975 }
976
977 - (IBAction) CalculateBitrate: (id) sender
978 {
979     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
980     {
981         return;
982     }
983
984     hb_list_t  * list  = hb_get_titles( fHandle );
985     hb_title_t * title = (hb_title_t *) hb_list_item( list,
986             [fSrcTitlePopUp indexOfSelectedItem] );
987     hb_job_t * job = title->job;
988
989     [self PrepareJob];
990
991     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
992             [fVidTargetSizeField intValue] )];
993 }
994
995 - (void) controlTextDidBeginEditing: (NSNotification *) notification
996 {
997     [self CalculateBitrate: NULL];
998 }
999
1000 - (void) controlTextDidEndEditing: (NSNotification *) notification
1001 {
1002     [self CalculateBitrate: NULL];
1003 }
1004
1005 - (void) controlTextDidChange: (NSNotification *) notification
1006 {
1007     [self CalculateBitrate: NULL];
1008 }
1009
1010 - (IBAction) OpenHomepage: (id) sender
1011 {
1012     [[NSWorkspace sharedWorkspace] openURL: [NSURL
1013         URLWithString:@"http://handbrake.m0k.org/"]];
1014 }
1015
1016 - (IBAction) OpenForums: (id) sender
1017 {
1018     [[NSWorkspace sharedWorkspace] openURL: [NSURL
1019         URLWithString:@"http://handbrake.m0k.org/forum/"]];
1020 }
1021
1022 @end