OSDN Git Service

f46d0221a6df5a81fad560cea0a6499e537fcc6f
[handbrake-jp/handbrake-jp-git.git] / macosx / HBController.mm
1 /* $Id: HBController.mm,v 1.24 2003/10/06 21:13:45 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://beos.titer.org/handbrake/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include <paths.h>
8 #include <IOKit/IOKitLib.h>
9 #include <IOKit/IOBSD.h>
10 #include <IOKit/storage/IOMedia.h>
11 #include <IOKit/storage/IODVDMedia.h>
12
13 #include "HBController.h"
14 #include "Manager.h"
15
16 @implementation HBController
17
18 - (void) applicationDidFinishLaunching: (NSNotification *) notification
19 {
20     /* Init libhb */
21     fManager = new HBManager( true );
22
23     /* Update the GUI every 1/10 sec */
24     [NSTimer scheduledTimerWithTimeInterval: 0.1
25         target: self selector: @selector( UpdateIntf: )
26         userInfo: nil repeats: FALSE];
27 }
28
29 - (NSApplicationTerminateReply) applicationShouldTerminate:
30     (NSApplication *) app
31 {
32     /* Clean up */
33     delete fManager;
34
35     return NSTerminateNow;
36 }
37
38 - (void) awakeFromNib
39 {
40     [[fScanMatrix cellAtRow: 0 column: 0]
41         setAction: @selector( ScanEnableIntf: )];
42     [[fScanMatrix cellAtRow: 0 column: 0] setTarget: self];
43     [[fScanMatrix cellAtRow: 1 column: 0]
44         setAction: @selector( ScanEnableIntf: )];
45     [[fScanMatrix cellAtRow: 1 column: 0] setTarget: self];
46     [fScanProgress setStyle: NSProgressIndicatorSpinningStyle];
47     [fScanProgress setDisplayedWhenStopped: NO];
48     [fRipProgress setIndeterminate: NO];
49     [fTitlePopUp removeAllItems];
50     [fAudioPopUp removeAllItems];
51
52     char string[1024]; memset( string, 0, 1024 );
53     snprintf( string, 1024, "%s/Desktop/Movie.avi", getenv( "HOME" ) );
54     [fFileField setStringValue: [NSString stringWithCString: string]];
55
56     /* Show the scan view */
57     [fWindow setContentSize: [fScanView frame].size];
58     [fWindow setContentView: fScanView];
59     [fWindow center];
60
61     /* Detect DVD drives */
62     [self DetectDrives];
63     [self ScanEnableIntf: self];
64
65     /* Init a blank view, used in window resizing animation */
66     fBlankView = [[NSView alloc] init];
67 }
68
69 - (BOOL) windowShouldClose: (id) sender
70 {
71     /* Stop the application when the user closes the window */
72     [NSApp terminate: self];
73     return YES;
74 }
75
76 - (IBAction) BrowseDVD: (id) sender
77 {
78     /* Open a panel to let the user choose and update the text field */
79     NSOpenPanel * panel = [NSOpenPanel openPanel];
80
81     [panel setAllowsMultipleSelection: NO];
82     [panel setCanChooseFiles: NO];
83     [panel setCanChooseDirectories: YES ];
84
85     [panel beginSheetForDirectory: nil file: nil types: nil
86         modalForWindow: fWindow modalDelegate: self
87         didEndSelector: @selector( BrowseDVDDone:returnCode:contextInfo: )
88         contextInfo: nil];
89 }
90
91 - (void) BrowseDVDDone: (NSOpenPanel *) sheet
92     returnCode: (int) returnCode contextInfo: (void *) contextInfo
93 {
94     if( returnCode == NSOKButton )
95     {
96         [fDVDFolderField setStringValue:
97             [[sheet filenames] objectAtIndex: 0]];
98     }
99 }
100
101 - (IBAction) BrowseFile: (id) sender
102 {
103     /* Open a panel to let the user choose and update the text field */
104     NSSavePanel * panel = [NSSavePanel savePanel];
105
106     [panel beginSheetForDirectory: nil file: nil
107         modalForWindow: fWindow modalDelegate: self
108         didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
109         contextInfo: nil];
110 }
111
112 - (void) BrowseFileDone: (NSSavePanel *) sheet
113     returnCode: (int) returnCode contextInfo: (void *) contextInfo
114 {
115     if( returnCode == NSOKButton )
116     {
117         [fFileField setStringValue: [sheet filename]];
118     }
119 }
120
121 - (IBAction) Scan: (id) sender
122 {
123     /* Ask the manager to start scanning the specified volume */
124     
125     if( ![fScanMatrix selectedRow] )
126     {
127         /* DVD drive */
128         fManager->ScanVolumes( (char*) [[fDVDPopUp titleOfSelectedItem]
129                                            cString] );
130     }
131     else
132     {
133         /* DVD folder */
134         fManager->ScanVolumes( (char*) [[fDVDFolderField stringValue]
135                                            cString] );
136     }
137 }
138
139 - (IBAction) ShowPicturePanel: (id) sender
140 {
141     HBTitle * title = (HBTitle*)
142         fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
143     
144     [fPictureGLView SetManager: fManager];
145     [fPictureGLView SetTitle: title];
146
147     fPicture = 0;
148     [fPictureGLView ShowPicture: fPicture];
149
150     [fWidthStepper  setValueWraps: NO];
151     [fWidthStepper  setIncrement: 16];
152     [fWidthStepper  setMinValue: 16];
153     [fWidthStepper  setMaxValue: title->fOutWidthMax];
154     [fWidthStepper  setIntValue: title->fOutWidth];
155     [fWidthField    setIntValue: title->fOutWidth];
156     [fTopStepper  setValueWraps: NO];
157     [fTopStepper    setIncrement: 2];
158     [fTopStepper    setMinValue: 0];
159     [fTopStepper    setMaxValue: title->fInHeight / 4];
160     [fTopStepper    setIntValue: title->fTopCrop];
161     [fTopField      setIntValue: title->fTopCrop];
162     [fBottomStepper setValueWraps: NO];
163     [fBottomStepper setIncrement: 2];
164     [fBottomStepper setMinValue: 0];
165     [fBottomStepper setMaxValue: title->fInHeight / 4];
166     [fBottomStepper setIntValue: title->fBottomCrop];
167     [fBottomField   setIntValue: title->fBottomCrop];
168     [fLeftStepper   setValueWraps: NO];
169     [fLeftStepper   setIncrement: 2];
170     [fLeftStepper   setMinValue: 0];
171     [fLeftStepper   setMaxValue: title->fInWidth / 4];
172     [fLeftStepper   setIntValue: title->fLeftCrop];
173     [fLeftField     setIntValue: title->fLeftCrop];
174     [fRightStepper  setValueWraps: NO];
175     [fRightStepper  setIncrement: 2];
176     [fRightStepper  setMinValue: 0];
177     [fRightStepper  setMaxValue: title->fInWidth / 4];
178     [fRightStepper  setIntValue: title->fRightCrop];
179     [fRightField    setIntValue: title->fRightCrop];
180
181     char string[1024]; memset( string, 0, 1024 );
182     sprintf( string, "Final size: %dx%d",
183              title->fOutWidth, title->fOutHeight );
184     [fInfoField setStringValue: [NSString stringWithCString: string]];
185
186     /* Resize the panel */
187     NSSize newSize;
188     /* XXX */
189     newSize.width  = 762 /*fPicturePanelSize.width*/ +
190         title->fOutWidthMax - 720;
191     newSize.height = 754 /*fPicturePanelSize.height*/ +
192         title->fOutHeightMax - 576;
193     [fPicturePanel setContentSize: newSize];
194
195     [NSApp beginSheet: fPicturePanel
196         modalForWindow: fWindow
197         modalDelegate: nil
198         didEndSelector: nil
199         contextInfo: nil];
200     [NSApp runModalForWindow: fPicturePanel];
201     [NSApp endSheet: fPicturePanel];
202     [fPicturePanel orderOut: self];
203 }
204
205 - (IBAction) ClosePanel: (id) sender
206 {
207     [NSApp stopModal];
208 }
209
210 - (IBAction) Rip: (id) sender
211 {
212     /* Rip or Cancel ? */
213     if( [[fRipButton title] compare: @"Cancel" ] == NSOrderedSame )
214     {
215         [self Cancel: self];
216         return;
217     }
218     
219     /* Get the specified title & audio track(s) */
220     HBTitle * title = (HBTitle*)
221         fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
222     HBAudio * audio = (HBAudio*)
223         title->fAudioList->ItemAt( [fAudioPopUp indexOfSelectedItem] );
224
225     /* Use user settings */
226     title->fBitrate    = [fVideoStepper intValue];
227     audio->fOutBitrate = [fAudioStepper intValue];
228     title->fTwoPass    = ( [fTwoPassCheck state] == NSOnState );
229
230     /* Let libhb do the job */
231     fManager->StartRip( title, audio, NULL,
232                         (char*) [[fFileField stringValue] cString] );
233 }
234
235 - (IBAction) Cancel: (id) sender
236 {
237     fManager->StopRip();
238 }
239
240 - (IBAction) Suspend: (id) sender
241 {
242     if( [[fSuspendButton title] compare: @"Resume" ] == NSOrderedSame )
243     {
244         [self Resume: self];
245         return;
246     }
247
248     fManager->SuspendRip();
249 }
250
251 - (IBAction) Resume: (id) sender
252 {
253     fManager->ResumeRip();
254 }
255
256 - (IBAction) PreviousPicture: (id) sender
257 {
258     if( fPicture > 0 )
259     {
260         fPicture--;
261         [fPictureGLView ShowPicture: fPicture];
262     }
263 }
264
265 - (IBAction) NextPicture: (id) sender
266 {
267     if( fPicture < 9 )
268     {
269         fPicture++;
270         [fPictureGLView ShowPicture: fPicture];
271     }
272 }
273
274 - (IBAction) UpdatePicture: (id) sender
275 {
276     HBTitle * title = (HBTitle*)
277         fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
278     title->fOutWidth    = [fWidthStepper intValue];
279     title->fDeinterlace = ( [fDeinterlaceCheck state] == NSOnState );
280     title->fTopCrop     = [fTopStepper intValue];
281     title->fBottomCrop  = [fBottomStepper intValue];
282     title->fLeftCrop    = [fLeftStepper intValue];
283     title->fRightCrop   = [fRightStepper intValue];
284
285     [fPictureGLView ShowPicture: fPicture];
286
287     [fWidthStepper  setIntValue: title->fOutWidth];
288     [fTopStepper    setIntValue: title->fTopCrop];
289     [fBottomStepper setIntValue: title->fBottomCrop];
290     [fLeftStepper   setIntValue: title->fLeftCrop];
291     [fRightStepper  setIntValue: title->fRightCrop];
292     [fWidthField    setIntValue: [fWidthStepper intValue]];
293     [fTopField      setIntValue: [fTopStepper intValue]];
294     [fBottomField   setIntValue: [fBottomStepper intValue]];
295     [fLeftField     setIntValue: [fLeftStepper intValue]];
296     [fRightField    setIntValue: [fRightStepper intValue]];
297
298     char string[1024]; memset( string, 0, 1024 );
299     sprintf( string, "Final size: %dx%d",
300              title->fOutWidth, title->fOutHeight );
301     [fInfoField setStringValue: [NSString stringWithCString: string]];
302 }
303
304 - (void) UpdateIntf: (NSTimer *) timer
305 {
306     /* Ask libhb about what's happening now */
307     if( fManager->NeedUpdate() )
308     {
309         HBStatus status = fManager->GetStatus();
310
311         switch( status.fMode )
312         {
313             case HB_MODE_NEED_VOLUME:
314                 break;
315
316             case HB_MODE_SCANNING:
317             {
318                 [fScanMatrix setEnabled: NO];
319                 [fDVDPopUp setEnabled: NO];
320                 [fDVDFolderField setEnabled: NO];
321                 [fScanBrowseButton setEnabled: NO];
322                 [fScanProgress startAnimation: self];
323                 [fScanButton setEnabled: NO];
324
325                 char string[1024]; memset( string, 0, 1024 );
326                 if( status.fScannedTitle )
327                 {
328                     sprintf( string, "Scanning %s, title %d...",
329                              status.fScannedVolume,
330                              status.fScannedTitle );
331                 }
332                 else
333                 {
334                     sprintf( string, "Opening %s...",
335                              status.fScannedVolume );
336                 }
337                 [fScanStatusField setStringValue:
338                     [NSString stringWithCString: string]];
339
340                 break;
341             }
342
343             case HB_MODE_INVALID_VOLUME:
344             {
345                 [fScanMatrix setEnabled: YES];
346                 [self ScanEnableIntf: self];
347                 [fScanProgress stopAnimation: self];
348                 [fScanButton setEnabled: YES];
349
350                 [fScanStatusField setStringValue:
351                     @"Invalid volume, try again" ];
352                 break;
353             }
354
355             case HB_MODE_READY_TO_RIP:
356             {
357                 fTitleList = status.fTitleList;
358                 
359                 /* Show a temporary empty view while the window
360                    resizing animation */
361                 [fWindow setContentView: fBlankView ];
362
363                 /* Actually resize it */
364                 NSRect newFrame;
365                 newFrame = [NSWindow contentRectForFrameRect: [fWindow frame]
366                              styleMask: [fWindow styleMask]];
367                 newFrame.origin.y    += newFrame.size.height -
368                                             [fRipView frame].size.height;
369                 newFrame.size.height  = [fRipView frame].size.height;
370                 newFrame.size.width   = [fRipView frame].size.width;
371                 newFrame = [NSWindow frameRectForContentRect: newFrame
372                              styleMask: [fWindow styleMask]];
373                 [fWindow setFrame: newFrame display: YES animate: YES];
374
375                 /* Show the new GUI */
376                 [fWindow setContentView: fRipView ];
377                 [fSuspendButton setEnabled: NO];
378                 
379                 HBTitle * title;
380                 for( uint32_t i = 0; i < fTitleList->CountItems(); i++ )
381                 {
382                     title = (HBTitle*) fTitleList->ItemAt( i );
383                     char string[1024]; memset( string, 0, 1024 );
384                     sprintf( string, "%d (%02lld:%02lld:%02lld)",
385                              title->fIndex, title->fLength / 3600,
386                              ( title->fLength % 3600 ) / 60,
387                              title->fLength % 60 );
388                     [[fTitlePopUp menu] addItemWithTitle:
389                         [NSString stringWithCString: string]
390                         action: @selector( UpdatePopUp: )
391                         keyEquivalent: @""];
392                 }
393                 [self UpdatePopUp: self];
394                 
395                 break;
396             }
397
398             case HB_MODE_ENCODING:
399             {
400                 [fTitlePopUp setEnabled: NO];
401                 [fAudioPopUp setEnabled: NO];
402                 [fVideoField setEnabled: NO];
403                 [fVideoStepper setEnabled: NO];
404                 [fAudioField setEnabled: NO];
405                 [fAudioStepper setEnabled: NO];
406                 [fTwoPassCheck setEnabled: NO];
407                 [fCropButton setEnabled: NO];
408                 [fFileField setEnabled: NO];
409                 [fRipBrowseButton setEnabled: NO];
410                 [fRipButton setTitle: @"Cancel"];
411                 [fSuspendButton setEnabled: YES];
412                 [fSuspendButton setTitle: @"Suspend"];
413             
414                 if( !status.fPosition )
415                 {
416                     [fRipStatusField setStringValue: @"Starting..."];
417                     [fRipProgress setIndeterminate: YES];
418                     [fRipProgress startAnimation: self];;
419                 }
420                 else
421                 {
422                     char string[1024]; memset( string, 0, 1024 );
423                     sprintf( string, "Encoding: %.2f %%, %.2f fps "
424                              "(%02d:%02d:%02d remaining)",
425                              100 * status.fPosition, status.fFrameRate,
426                              status.fRemainingTime / 3600,
427                              ( status.fRemainingTime % 3600 ) / 60,
428                              status.fRemainingTime % 60 );
429                     [fRipStatusField setStringValue:
430                         [NSString stringWithCString: string]];
431                     [fRipProgress setIndeterminate: NO];
432                     [fRipProgress setDoubleValue: 100 * status.fPosition];
433                 }
434                 
435                 break;
436             }
437
438             case HB_MODE_SUSPENDED:
439             {
440                 char string[1024]; memset( string, 0, 1024 );
441                 sprintf( string, "Encoding: %.2f %%, %.2f fps (PAUSED)",
442                          100 * status.fPosition, status.fFrameRate) ;
443                 [fRipStatusField setStringValue:
444                     [NSString stringWithCString: string]];
445                 
446                 [fRipProgress setDoubleValue: 100 * status.fPosition];
447
448                 [fSuspendButton setTitle: @"Resume"];
449                 break;
450             }
451
452             case HB_MODE_DONE:
453             case HB_MODE_CANCELED:
454             case HB_MODE_ERROR:
455                 [fTitlePopUp setEnabled: YES];
456                 [fAudioPopUp setEnabled: YES];
457                 [fVideoField setEnabled: YES];
458                 [fVideoStepper setEnabled: YES];
459                 [fAudioField setEnabled: YES];
460                 [fAudioStepper setEnabled: YES];
461                 [fTwoPassCheck setEnabled: YES];
462                 [fCropButton setEnabled: YES];
463                 [fFileField setEnabled: YES];
464                 [fRipBrowseButton setEnabled: YES];
465                 [fRipButton setEnabled: YES];
466                 [fRipButton setTitle: @"Rip"];
467                 [fSuspendButton setEnabled: NO];
468                 [fSuspendButton setTitle: @"Suspend"];
469
470                 if( status.fMode == HB_MODE_DONE )
471                 {
472                     [fRipStatusField setStringValue: @"Done." ];
473                     [fRipProgress setDoubleValue: 100];
474                     NSBeep();
475                     [NSApp requestUserAttention: NSInformationalRequest];
476                     [NSApp beginSheet: fDonePanel
477                         modalForWindow: fWindow
478                         modalDelegate: nil
479                         didEndSelector: nil
480                         contextInfo: nil];
481                     [NSApp runModalForWindow: fDonePanel];
482                     [NSApp endSheet: fDonePanel];
483                     [fDonePanel orderOut: self];
484                 }
485                 else if( status.fMode == HB_MODE_CANCELED )
486                 {
487                     [fRipStatusField setStringValue: @"Canceled." ];
488                     [fRipProgress setDoubleValue: 0];
489                 }
490                 else
491                 {
492                     [fRipStatusField setStringValue: @"An error occured." ];
493                     [fRipProgress setDoubleValue: 0];
494                 }
495
496                 /* Warn the finder to update itself */
497                 [[NSWorkspace sharedWorkspace] noteFileSystemChanged:
498                     [fFileField stringValue]];
499                 break;
500
501             default:
502                 break;
503         }
504     }
505
506     /* Do it again 1/10 second later */
507     [NSTimer scheduledTimerWithTimeInterval: 0.1
508         target: self selector: @selector( UpdateIntf: )
509         userInfo: nil repeats: FALSE];
510 }
511
512 - (void) DetectDrives
513 {
514     /* Empty the current popup */
515     [fDVDPopUp removeAllItems];
516     
517     /* Scan DVD drives (stolen from VLC) */
518     io_object_t next_media;
519     mach_port_t master_port;
520     kern_return_t kern_result;
521     io_iterator_t media_iterator;
522     CFMutableDictionaryRef classes_to_match;
523
524     kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
525     if( kern_result != KERN_SUCCESS )
526     {
527         return;
528     }
529
530     classes_to_match = IOServiceMatching( kIODVDMediaClass );
531     if( classes_to_match == NULL )
532     {
533         return;
534     }
535
536     CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectable ),
537                           kCFBooleanTrue );
538
539     kern_result =
540         IOServiceGetMatchingServices( master_port, classes_to_match,
541                                       &media_iterator );
542     if( kern_result != KERN_SUCCESS )
543     {
544         return;
545     }
546
547     next_media = IOIteratorNext( media_iterator );
548     if( next_media != NULL )
549     {
550         char psz_buf[0x32];
551         size_t dev_path_length;
552         CFTypeRef str_bsd_path;
553         do
554         {
555             str_bsd_path =
556                 IORegistryEntryCreateCFProperty( next_media,
557                                                  CFSTR( kIOBSDName ),
558                                                  kCFAllocatorDefault,
559                                                  0 );
560             if( str_bsd_path == NULL )
561             {
562                 IOObjectRelease( next_media );
563                 continue;
564             }
565
566             snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
567             dev_path_length = strlen( psz_buf );
568
569             if( CFStringGetCString( (CFStringRef) str_bsd_path,
570                                     (char*)&psz_buf + dev_path_length,
571                                     sizeof(psz_buf) - dev_path_length,
572                                     kCFStringEncodingASCII ) )
573             {
574                 [[fDVDPopUp menu] addItemWithTitle:
575                     [NSString stringWithCString: psz_buf]
576                     action: nil keyEquivalent: @""];
577             }
578
579             CFRelease( str_bsd_path );
580
581             IOObjectRelease( next_media );
582
583         } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL );
584     }
585
586     IOObjectRelease( media_iterator );
587 }
588
589 - (void) ScanEnableIntf: (id) sender
590 {
591     if( ![fScanMatrix selectedRow] )
592     {
593         [fDVDPopUp setEnabled: YES];
594         [fDVDFolderField setEnabled: NO];
595         [fScanBrowseButton setEnabled: NO];
596         [fScanButton setEnabled: ( [fDVDPopUp selectedItem] != nil )];
597     }
598     else
599     {
600         [fDVDPopUp setEnabled: NO];
601         [fDVDFolderField setEnabled: YES];
602         [fScanBrowseButton setEnabled: YES];
603         [fScanButton setEnabled: YES];
604     }
605 }
606
607 - (void) UpdatePopUp: (id) sender
608 {
609     HBTitle * title = (HBTitle*)
610         fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
611
612     [fAudioPopUp removeAllItems];
613     
614     HBAudio * audio;
615     for( uint32_t i = 0; i < title->fAudioList->CountItems(); i++ )
616     {
617         audio = (HBAudio*) title->fAudioList->ItemAt( i );
618
619         /* We cannot use NSPopUpButton's addItemWithTitle because
620            it checks for duplicate entries */
621         [[fAudioPopUp menu] addItemWithTitle:
622             [NSString stringWithCString: audio->fDescription]
623             action: nil keyEquivalent: @""];
624     }
625 }
626
627 @end