OSDN Git Service

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