OSDN Git Service

by dynflash
[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                         
310                         }
311
312             [self TitlePopUpChanged: NULL];
313             [self EnableUI: YES];
314             [fPauseButton setEnabled: NO];
315             [fRipButton   setEnabled: YES];
316             break;
317         }
318 #undef p
319
320 #define p s.param.working
321         case HB_STATE_WORKING:
322         {
323             float progress_total;
324             NSMutableString * string;
325
326             /* Update text field */
327             string = [NSMutableString stringWithFormat:
328                 _( @"Encoding: task %d of %d, %.2f %%" ),
329                 p.job_cur, p.job_count, 100.0 * p.progress];
330             if( p.seconds > -1 )
331             {
332                 [string appendFormat:
333                     _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),
334                     p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
335             }
336             [fStatusField setStringValue: string];
337
338             /* Update slider */
339             progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
340             [fRipIndicator setDoubleValue: 100.0 * progress_total];
341
342             /* Update dock icon */
343             [self UpdateDockIcon: progress_total];
344
345             [fPauseButton setEnabled: YES];
346             [fPauseButton setTitle: _( @"Pause" )];
347             [fRipButton setEnabled: YES];
348             [fRipButton setTitle: _( @"Cancel" )];
349             break;
350         }
351 #undef p
352
353         case HB_STATE_PAUSED:
354             [fStatusField setStringValue: _( @"Paused" )];
355             [fPauseButton setEnabled: YES];
356             [fPauseButton setTitle: _( @"Resume" )];
357             [fRipButton setEnabled: YES];
358             [fRipButton setTitle: _( @"Cancel" )];
359             break;
360
361         case HB_STATE_WORKDONE:
362         {
363             [self EnableUI: YES];
364             [fStatusField setStringValue: _( @"Done." )];
365             [fRipIndicator setDoubleValue: 0.0];
366             [fRipButton setTitle: _( @"Rip" )];
367
368             /* Restore dock icon */
369             [self UpdateDockIcon: -1.0];
370
371             [fPauseButton setEnabled: NO];
372             [fPauseButton setTitle: _( @"Pause" )];
373             [fRipButton setEnabled: YES];
374             [fRipButton setTitle: _( @"Rip" )];
375
376             /* FIXME */
377             hb_job_t * job;
378             while( ( job = hb_job( fHandle, 0 ) ) )
379             {
380                 hb_rem( fHandle, job );
381             }
382             break;
383         }
384     }
385
386     /* FIXME: we should only do that when necessary */
387     if( [fQueueCheck state] == NSOnState )
388     {
389         int count = hb_count( fHandle );
390         if( count )
391         {
392             [fQueueCheck setTitle: [NSString stringWithFormat:
393                 @"Enable queue (%d task%s in queue)",
394                 count, ( count > 1 ) ? "s" : ""]];
395         }
396         else
397         {
398             [fQueueCheck setTitle: @"Enable queue (no task in queue)"];
399         }
400     }
401
402     [[NSRunLoop currentRunLoop] addTimer: [NSTimer
403         scheduledTimerWithTimeInterval: 0.2 target: self
404         selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
405         forMode: NSModalPanelRunLoopMode];
406 }
407
408 - (void) EnableUI: (bool) b
409 {
410     NSControl * controls[] =
411       { fSrcDVD1Field, fSrcDVD2Field, fSrcTitleField, fSrcTitlePopUp,
412         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
413         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
414         fDstFormatField, fDstFormatPopUp, fDstCodecsField,
415         fDstCodecsPopUp, fDstFile1Field, fDstFile2Field,
416         fDstBrowseButton, fVidRateField, fVidRatePopUp,
417         fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
418         fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
419         fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp,
420         fAudRateField, fAudRatePopUp, fAudBitrateField,
421         fAudBitratePopUp, fPictureButton };
422
423     for( unsigned i = 0;
424          i < sizeof( controls ) / sizeof( NSControl * ); i++ )
425     {
426         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
427         {
428             NSTextField * tf = (NSTextField *) controls[i];
429             if( ![tf isBezeled] )
430             {
431                 [tf setTextColor: b ? [NSColor controlTextColor] :
432                     [NSColor disabledControlTextColor]];
433                 continue;
434             }
435         }
436         [controls[i] setEnabled: b];
437     }
438
439     [self VideoMatrixChanged: NULL];
440 }
441
442 - (IBAction) ShowScanPanel: (id) sender
443 {
444     [fScanController Show];
445 }
446
447 - (BOOL) windowShouldClose: (id) sender
448 {
449     /* Stop the application when the user closes the window */
450     [NSApp terminate: self];
451     return YES;
452 }
453
454 - (IBAction) VideoMatrixChanged: (id) sender;
455 {
456     bool target, bitrate, quality;
457
458     target = bitrate = quality = false;
459     if( [fVidQualityMatrix isEnabled] )
460     {
461         switch( [fVidQualityMatrix selectedRow] )
462         {
463             case 0:
464                 target = true;
465                 break;
466             case 1:
467                 bitrate = true;
468                 break;
469             case 2:
470                 quality = true;
471                 break;
472         }
473     }
474     [fVidTargetSizeField  setEnabled: target];
475     [fVidBitrateField     setEnabled: bitrate];
476     [fVidQualitySlider    setEnabled: quality];
477     [fVidTwoPassCheck     setEnabled: !quality &&
478         [fVidQualityMatrix isEnabled]];
479     if( quality )
480     {
481         [fVidTwoPassCheck setState: NSOffState];
482     }
483
484     [self QualitySliderChanged: sender];
485     [self CalculateBitrate:     sender];
486 }
487
488 - (IBAction) QualitySliderChanged: (id) sender
489 {
490     [fVidConstantCell setTitle: [NSString stringWithFormat:
491         _( @"Constant quality: %.0f %%" ), 100.0 *
492         [fVidQualitySlider floatValue]]];
493 }
494
495 - (IBAction) BrowseFile: (id) sender
496 {
497     /* Open a panel to let the user choose and update the text field */
498     NSSavePanel * panel = [NSSavePanel savePanel];
499
500     [panel beginSheetForDirectory: NULL file: NULL
501         modalForWindow: fWindow modalDelegate: self
502         didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
503         contextInfo: NULL];
504 }
505
506 - (void) BrowseFileDone: (NSSavePanel *) sheet
507     returnCode: (int) returnCode contextInfo: (void *) contextInfo
508 {
509     if( returnCode == NSOKButton )
510     {
511         [fDstFile2Field setStringValue: [sheet filename]];
512         [self FormatPopUpChanged: NULL];
513     }
514 }
515
516 - (IBAction) ShowPicturePanel: (id) sender
517 {
518     hb_list_t  * list  = hb_get_titles( fHandle );
519     hb_title_t * title = (hb_title_t *) hb_list_item( list,
520             [fSrcTitlePopUp indexOfSelectedItem] );
521
522     /* Resize the panel */
523     NSSize newSize;
524     newSize.width  = 246 + title->width;
525     newSize.height = 80 + title->height;
526     [fPicturePanel setContentSize: newSize];
527
528     [fPictureController SetTitle: title];
529
530     [NSApp beginSheet: fPicturePanel modalForWindow: fWindow
531         modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
532     [NSApp runModalForWindow: fPicturePanel];
533     [NSApp endSheet: fPicturePanel];
534     [fPicturePanel orderOut: self];
535 }
536
537 - (IBAction) ShowQueuePanel: (id) sender
538 {
539     /* Update the OutlineView */
540     [fQueueController Update: sender];
541
542     /* Show the panel */
543     [NSApp beginSheet: fQueuePanel modalForWindow: fWindow
544         modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
545     [NSApp runModalForWindow: fQueuePanel];
546     [NSApp endSheet: fQueuePanel];
547     [fQueuePanel orderOut: self];
548 }
549
550 - (void) PrepareJob
551 {
552     hb_list_t  * list  = hb_get_titles( fHandle );
553     hb_title_t * title = (hb_title_t *) hb_list_item( list,
554             [fSrcTitlePopUp indexOfSelectedItem] );
555     hb_job_t * job = title->job;
556
557     /* Chapter selection */
558     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
559     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
560
561     /* Format and codecs */
562     int format = [fDstFormatPopUp indexOfSelectedItem];
563     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
564     job->mux    = FormatSettings[format][codecs] & HB_MUX_MASK;
565     job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK;
566     job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
567
568     if( ( job->vcodec & HB_VCODEC_FFMPEG ) &&
569         [fVidEncoderPopUp indexOfSelectedItem] > 0 )
570     {
571         job->vcodec = HB_VCODEC_XVID;
572     }
573     if( job->vcodec & HB_VCODEC_X264 )
574     {
575          if ([fVidEncoderPopUp indexOfSelectedItem] < 1 )
576             {
577                 /* Just use new Baseline Level 3.0 
578                 Lets Deprecate Baseline Level 1.3*/
579                 job->h264_level = 30;
580                 job->mux = HB_MUX_IPOD;
581                 }
582         job->h264_13 = [fVidEncoderPopUp indexOfSelectedItem];
583     }
584
585     /* Video settings */
586     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
587     {
588         job->vrate      = 27000000;
589         job->vrate_base = hb_video_rates[[fVidRatePopUp
590             indexOfSelectedItem]-1].rate;
591     }
592     else
593     {
594         job->vrate      = title->rate;
595         job->vrate_base = title->rate_base;
596     }
597
598     switch( [fVidQualityMatrix selectedRow] )
599     {
600         case 0:
601             /* Target size.
602                Bitrate should already have been calculated and displayed
603                in fVidBitrateField, so let's just use it */
604         case 1:
605             job->vquality = -1.0;
606             job->vbitrate = [fVidBitrateField intValue];
607             break;
608         case 2:
609             job->vquality = [fVidQualitySlider floatValue];
610             job->vbitrate = 0;
611             break;
612     }
613
614     job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
615
616     /* Subtitle settings */
617     job->subtitle = [fSubPopUp indexOfSelectedItem] - 1;
618
619     /* Audio tracks */
620     job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1;
621     job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1;
622     job->audios[2] = -1;
623
624     /* Audio settings */
625     job->arate = hb_audio_rates[[fAudRatePopUp
626                      indexOfSelectedItem]].rate;
627     job->abitrate = hb_audio_bitrates[[fAudBitratePopUp
628                         indexOfSelectedItem]].rate;
629 }
630
631 - (IBAction) EnableQueue: (id) sender
632 {
633     bool e = ( [fQueueCheck state] == NSOnState );
634     [fQueueAddButton  setHidden: !e];
635     [fQueueShowButton setHidden: !e];
636     [fRipButton       setTitle: e ? @"Start" : @"Rip"];
637 }
638
639 - (IBAction) AddToQueue: (id) sender
640 {
641     hb_list_t  * list  = hb_get_titles( fHandle );
642     hb_title_t * title = (hb_title_t *) hb_list_item( list,
643             [fSrcTitlePopUp indexOfSelectedItem] );
644     hb_job_t * job = title->job;
645
646     [self PrepareJob];
647
648     /* Destination file */
649     job->file = strdup( [[fDstFile2Field stringValue] UTF8String] );
650
651     if( [fVidTwoPassCheck state] == NSOnState )
652     {
653         job->pass = 1;
654         hb_add( fHandle, job );
655         job->pass = 2;
656         hb_add( fHandle, job );
657     }
658     else
659     {
660         job->pass = 0;
661         hb_add( fHandle, job );
662     }
663 }
664
665 - (IBAction) Rip: (id) sender
666 {
667     /* Rip or Cancel ? */
668     if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
669     {
670         [self Cancel: sender];
671         return;
672     }
673
674     if( [fQueueCheck state] == NSOffState )
675     {
676         [self AddToQueue: sender];
677     }
678
679     if( [[NSFileManager defaultManager] fileExistsAtPath:
680             [fDstFile2Field stringValue]] )
681     {
682         NSBeginCriticalAlertSheet( _( @"File already exists" ),
683             _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
684             @selector( OverwriteAlertDone:returnCode:contextInfo: ),
685             NULL, NULL, [NSString stringWithFormat:
686             _( @"Do you want to overwrite %@?" ),
687             [fDstFile2Field stringValue]] );
688         return;
689     }
690
691     [self _Rip];
692 }
693
694 - (void) OverwriteAlertDone: (NSWindow *) sheet
695     returnCode: (int) returnCode contextInfo: (void *) contextInfo
696 {
697     if( returnCode == NSAlertAlternateReturn )
698     {
699         [self _Rip];
700     }
701 }
702
703 - (void) UpdateAlertDone: (NSWindow *) sheet
704     returnCode: (int) returnCode contextInfo: (void *) contextInfo
705 {
706     if( returnCode == NSAlertAlternateReturn )
707     {
708         /* Show scan panel */
709         [self performSelectorOnMainThread: @selector(ShowScanPanel:)
710             withObject: NULL waitUntilDone: NO];
711         return;
712     }
713
714     /* Go to HandBrake homepage and exit */
715     [self OpenHomepage: NULL];
716     [NSApp terminate: self];
717 }
718
719 - (void) _Rip
720 {
721     /* Let libhb do the job */
722     hb_start( fHandle );
723
724     /* Disable interface */
725     [self EnableUI: NO];
726     [fPauseButton setEnabled: NO];
727     [fRipButton   setEnabled: NO];
728 }
729
730 - (IBAction) Cancel: (id) sender
731 {
732     NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
733         _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
734         @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
735         _( @"Encoding won't be recoverable." ) );
736 }
737
738 - (void) _Cancel: (NSWindow *) sheet
739     returnCode: (int) returnCode contextInfo: (void *) contextInfo
740 {
741     if( returnCode == NSAlertAlternateReturn )
742     {
743         hb_stop( fHandle );
744         [fPauseButton setEnabled: NO];
745         [fRipButton   setEnabled: NO];
746     }
747 }
748
749 - (IBAction) Pause: (id) sender
750 {
751     [fPauseButton setEnabled: NO];
752     [fRipButton   setEnabled: NO];
753
754     if( [[fPauseButton title] isEqualToString: _( @"Resume" )] )
755     {
756         hb_resume( fHandle );
757     }
758     else
759     {
760         hb_pause( fHandle );
761     }
762 }
763
764 - (IBAction) TitlePopUpChanged: (id) sender
765 {
766     hb_list_t  * list  = hb_get_titles( fHandle );
767     hb_title_t * title = (hb_title_t*)
768         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
769
770     /* Update chapter popups */
771     [fSrcChapterStartPopUp removeAllItems];
772     [fSrcChapterEndPopUp   removeAllItems];
773     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
774     {
775         [fSrcChapterStartPopUp addItemWithTitle: [NSString
776             stringWithFormat: @"%d", i + 1]];
777         [fSrcChapterEndPopUp addItemWithTitle: [NSString
778             stringWithFormat: @"%d", i + 1]];
779     }
780     [fSrcChapterStartPopUp selectItemAtIndex: 0];
781     [fSrcChapterEndPopUp   selectItemAtIndex:
782         hb_list_count( title->list_chapter ) - 1];
783     [self ChapterPopUpChanged: NULL];
784
785     /* Update subtitle popups */
786     hb_subtitle_t * subtitle;
787     [fSubPopUp removeAllItems];
788     [fSubPopUp addItemWithTitle: @"None"];
789     for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
790     {
791         subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
792
793         /* We cannot use NSPopUpButton's addItemWithTitle because
794            it checks for duplicate entries */
795         [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
796             subtitle->lang] action: NULL keyEquivalent: @""];
797     }
798     [fSubPopUp selectItemAtIndex: 0];
799
800     /* Update lang popups */
801     hb_audio_t * audio;
802         // PRI CHANGES 02/12/06
803         NSString * audiotmppri;
804         NSString * audiosearchpri=[[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
805         int indxpri=0;
806         // End of pri changes 02/12/06
807     [fAudLang1PopUp removeAllItems];
808     [fAudLang2PopUp removeAllItems];
809     [fAudLang1PopUp addItemWithTitle: _( @"None" )];
810     [fAudLang2PopUp addItemWithTitle: _( @"None" )];
811     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
812     {
813         audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
814         // PRI CHANGES 02/12/06
815                 audiotmppri=(NSString *) [NSString stringWithCString: audio->lang];
816                 // Try to find the desired default language
817            if ([audiotmppri hasPrefix:audiosearchpri] && indxpri==0)
818                 {
819                         indxpri=i+1;
820                 }
821         // End of pri changes 02/12/06
822
823         [[fAudLang1PopUp menu] addItemWithTitle:
824             [NSString stringWithCString: audio->lang]
825             action: NULL keyEquivalent: @""];
826         [[fAudLang2PopUp menu] addItemWithTitle:
827             [NSString stringWithCString: audio->lang]
828             action: NULL keyEquivalent: @""];
829     }
830         // PRI CHANGES 02/12/06
831         if (indxpri==0) { indxpri=1; }
832     [fAudLang1PopUp selectItemAtIndex: indxpri];
833         // End of pri changes 02/12/06
834     [fAudLang2PopUp selectItemAtIndex: 0];
835 }
836
837 - (IBAction) ChapterPopUpChanged: (id) sender
838 {
839     hb_list_t  * list  = hb_get_titles( fHandle );
840     hb_title_t * title = (hb_title_t *)
841         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
842
843     hb_chapter_t * chapter;
844     int64_t        duration = 0;
845     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
846          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
847     {
848         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
849         duration += chapter->duration;
850     }
851     
852     duration /= 90000; /* pts -> seconds */
853     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
854         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
855         duration % 60]];
856
857     [self CalculateBitrate: sender];
858 }
859
860 - (IBAction) FormatPopUpChanged: (id) sender
861 {
862     NSString * string = [fDstFile2Field stringValue];
863     int format = [fDstFormatPopUp indexOfSelectedItem];
864     char * ext = NULL;
865
866     /* Update the codecs popup */
867     [fDstCodecsPopUp removeAllItems];
868     switch( format )
869     {
870         case 0:
871             ext = "mp4";
872             [fDstCodecsPopUp addItemWithTitle:
873                 _( @"MPEG-4 Video / AAC Audio" )];
874             [fDstCodecsPopUp addItemWithTitle:
875                 _( @"AVC/H.264 Video / AAC Audio" )];
876             break;
877         case 1: 
878             ext = "avi";
879             [fDstCodecsPopUp addItemWithTitle:
880                 _( @"MPEG-4 Video / MP3 Audio" )];
881             [fDstCodecsPopUp addItemWithTitle:
882                 _( @"MPEG-4 Video / AC-3 Audio" )];
883             [fDstCodecsPopUp addItemWithTitle:
884                 _( @"AVC/H.264 Video / MP3 Audio" )];
885             [fDstCodecsPopUp addItemWithTitle:
886                 _( @"AVC/H.264 Video / AC-3 Audio" )];
887             break;
888         case 2:
889             ext = "ogm";
890             [fDstCodecsPopUp addItemWithTitle:
891                 _( @"MPEG-4 Video / Vorbis Audio" )];
892             [fDstCodecsPopUp addItemWithTitle:
893                 _( @"MPEG-4 Video / MP3 Audio" )];
894             break;
895     }
896     [self CodecsPopUpChanged: NULL];
897
898     /* Add/replace to the correct extension */
899     if( [string characterAtIndex: [string length] - 4] == '.' )
900     {
901         [fDstFile2Field setStringValue: [NSString stringWithFormat:
902             @"%@.%s", [string substringToIndex: [string length] - 4],
903             ext]];
904     }
905     else
906     {
907         [fDstFile2Field setStringValue: [NSString stringWithFormat:
908             @"%@.%s", string, ext]];
909     }
910 }
911
912 - (IBAction) CodecsPopUpChanged: (id) sender
913 {
914     int format = [fDstFormatPopUp indexOfSelectedItem];
915     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
916
917     /* Update the encoder popup */
918     if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
919     {
920         /* MPEG-4 -> H.264 */
921         [fVidEncoderPopUp removeAllItems];
922                 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 Baseline iPod)"];
923         [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 Main)"];
924         
925     }
926     else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
927     {
928         /* H.264 -> MPEG-4 */
929         [fVidEncoderPopUp removeAllItems];
930         [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
931         [fVidEncoderPopUp addItemWithTitle: @"XviD"];
932         [fVidEncoderPopUp selectItemAtIndex: 0];
933     }
934
935     if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
936     {
937         /* AC-3 pass-through: disable samplerate and bitrate */
938         [fAudRatePopUp    setEnabled: NO];
939         [fAudBitratePopUp setEnabled: NO];
940     }
941     else
942     {
943         [fAudRatePopUp    setEnabled: YES];
944         [fAudBitratePopUp setEnabled: YES];
945     }
946
947     [self CalculateBitrate: sender];
948 }
949
950 - (IBAction) CalculateBitrate: (id) sender
951 {
952     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
953     {
954         return;
955     }
956
957     hb_list_t  * list  = hb_get_titles( fHandle );
958     hb_title_t * title = (hb_title_t *) hb_list_item( list,
959             [fSrcTitlePopUp indexOfSelectedItem] );
960     hb_job_t * job = title->job;
961
962     [self PrepareJob];
963
964     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
965             [fVidTargetSizeField intValue] )];
966 }
967
968 - (void) controlTextDidBeginEditing: (NSNotification *) notification
969 {
970     [self CalculateBitrate: NULL];
971 }
972
973 - (void) controlTextDidEndEditing: (NSNotification *) notification
974 {
975     [self CalculateBitrate: NULL];
976 }
977
978 - (void) controlTextDidChange: (NSNotification *) notification
979 {
980     [self CalculateBitrate: NULL];
981 }
982
983 - (IBAction) OpenHomepage: (id) sender
984 {
985     [[NSWorkspace sharedWorkspace] openURL: [NSURL
986         URLWithString:@"http://handbrake.m0k.org/"]];
987 }
988
989 - (IBAction) OpenForums: (id) sender
990 {
991     [[NSWorkspace sharedWorkspace] openURL: [NSURL
992         URLWithString:@"http://handbrake.m0k.org/forum/"]];
993 }
994
995 @end