1 /* $Id: HBController.mm,v 1.24 2003/10/06 21:13:45 titer Exp $
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. */
8 #include <IOKit/IOKitLib.h>
9 #include <IOKit/IOBSD.h>
10 #include <IOKit/storage/IOMedia.h>
11 #include <IOKit/storage/IODVDMedia.h>
13 #include "HBController.h"
16 @implementation HBController
18 - (void) applicationDidFinishLaunching: (NSNotification *) notification
21 fManager = new HBManager( true );
23 /* Update the GUI every 1/10 sec */
24 [NSTimer scheduledTimerWithTimeInterval: 0.1
25 target: self selector: @selector( UpdateIntf: )
26 userInfo: nil repeats: FALSE];
29 - (NSApplicationTerminateReply) applicationShouldTerminate:
35 return NSTerminateNow;
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];
52 char string[1024]; memset( string, 0, 1024 );
53 snprintf( string, 1024, "%s/Desktop/Movie.avi", getenv( "HOME" ) );
54 [fFileField setStringValue: [NSString stringWithCString: string]];
56 /* Show the scan view */
57 [fWindow setContentSize: [fScanView frame].size];
58 [fWindow setContentView: fScanView];
61 /* Detect DVD drives */
63 [self ScanEnableIntf: self];
65 /* Init a blank view, used in window resizing animation */
66 fBlankView = [[NSView alloc] init];
69 - (BOOL) windowShouldClose: (id) sender
71 /* Stop the application when the user closes the window */
72 [NSApp terminate: self];
76 - (IBAction) BrowseDVD: (id) sender
78 /* Open a panel to let the user choose and update the text field */
79 NSOpenPanel * panel = [NSOpenPanel openPanel];
81 [panel setAllowsMultipleSelection: NO];
82 [panel setCanChooseFiles: NO];
83 [panel setCanChooseDirectories: YES ];
85 [panel beginSheetForDirectory: nil file: nil types: nil
86 modalForWindow: fWindow modalDelegate: self
87 didEndSelector: @selector( BrowseDVDDone:returnCode:contextInfo: )
91 - (void) BrowseDVDDone: (NSOpenPanel *) sheet
92 returnCode: (int) returnCode contextInfo: (void *) contextInfo
94 if( returnCode == NSOKButton )
96 [fDVDFolderField setStringValue:
97 [[sheet filenames] objectAtIndex: 0]];
101 - (IBAction) BrowseFile: (id) sender
103 /* Open a panel to let the user choose and update the text field */
104 NSSavePanel * panel = [NSSavePanel savePanel];
106 [panel beginSheetForDirectory: nil file: nil
107 modalForWindow: fWindow modalDelegate: self
108 didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
112 - (void) BrowseFileDone: (NSSavePanel *) sheet
113 returnCode: (int) returnCode contextInfo: (void *) contextInfo
115 if( returnCode == NSOKButton )
117 [fFileField setStringValue: [sheet filename]];
121 - (IBAction) Scan: (id) sender
123 /* Ask the manager to start scanning the specified volume */
125 if( ![fScanMatrix selectedRow] )
128 fManager->ScanVolumes( (char*) [[fDVDPopUp titleOfSelectedItem]
134 fManager->ScanVolumes( (char*) [[fDVDFolderField stringValue]
139 - (IBAction) ShowPicturePanel: (id) sender
141 HBTitle * title = (HBTitle*)
142 fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
144 [fPictureGLView SetManager: fManager];
145 [fPictureGLView SetTitle: title];
148 [fPictureGLView ShowPicture: fPicture];
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];
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]];
186 /* Resize the panel */
189 newSize.width = 762 /*fPicturePanelSize.width*/ +
190 title->fOutWidthMax - 720;
191 newSize.height = 754 /*fPicturePanelSize.height*/ +
192 title->fOutHeightMax - 576;
193 [fPicturePanel setContentSize: newSize];
195 [NSApp beginSheet: fPicturePanel
196 modalForWindow: fWindow
200 [NSApp runModalForWindow: fPicturePanel];
201 [NSApp endSheet: fPicturePanel];
202 [fPicturePanel orderOut: self];
205 - (IBAction) ClosePanel: (id) sender
210 - (IBAction) Rip: (id) sender
212 /* Rip or Cancel ? */
213 if( [[fRipButton title] compare: @"Cancel" ] == NSOrderedSame )
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] );
225 /* Use user settings */
226 title->fBitrate = [fVideoStepper intValue];
227 audio->fOutBitrate = [fAudioStepper intValue];
228 title->fTwoPass = ( [fTwoPassCheck state] == NSOnState );
230 /* Let libhb do the job */
231 fManager->StartRip( title, audio, NULL,
232 (char*) [[fFileField stringValue] cString] );
235 - (IBAction) Cancel: (id) sender
240 - (IBAction) Suspend: (id) sender
242 if( [[fSuspendButton title] compare: @"Resume" ] == NSOrderedSame )
248 fManager->SuspendRip();
251 - (IBAction) Resume: (id) sender
253 fManager->ResumeRip();
256 - (IBAction) PreviousPicture: (id) sender
261 [fPictureGLView ShowPicture: fPicture];
265 - (IBAction) NextPicture: (id) sender
270 [fPictureGLView ShowPicture: fPicture];
274 - (IBAction) UpdatePicture: (id) sender
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];
285 [fPictureGLView ShowPicture: fPicture];
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]];
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]];
304 - (void) UpdateIntf: (NSTimer *) timer
306 /* Ask libhb about what's happening now */
307 if( fManager->NeedUpdate() )
309 HBStatus status = fManager->GetStatus();
311 switch( status.fMode )
313 case HB_MODE_NEED_VOLUME:
316 case HB_MODE_SCANNING:
318 [fScanMatrix setEnabled: NO];
319 [fDVDPopUp setEnabled: NO];
320 [fDVDFolderField setEnabled: NO];
321 [fScanBrowseButton setEnabled: NO];
322 [fScanProgress startAnimation: self];
323 [fScanButton setEnabled: NO];
325 char string[1024]; memset( string, 0, 1024 );
326 if( status.fScannedTitle )
328 sprintf( string, "Scanning %s, title %d...",
329 status.fScannedVolume,
330 status.fScannedTitle );
334 sprintf( string, "Opening %s...",
335 status.fScannedVolume );
337 [fScanStatusField setStringValue:
338 [NSString stringWithCString: string]];
343 case HB_MODE_INVALID_VOLUME:
345 [fScanMatrix setEnabled: YES];
346 [self ScanEnableIntf: self];
347 [fScanProgress stopAnimation: self];
348 [fScanButton setEnabled: YES];
350 [fScanStatusField setStringValue:
351 @"Invalid volume, try again" ];
355 case HB_MODE_READY_TO_RIP:
357 fTitleList = status.fTitleList;
359 /* Show a temporary empty view while the window
360 resizing animation */
361 [fWindow setContentView: fBlankView ];
363 /* Actually resize it */
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];
375 /* Show the new GUI */
376 [fWindow setContentView: fRipView ];
377 [fSuspendButton setEnabled: NO];
380 for( uint32_t i = 0; i < fTitleList->CountItems(); i++ )
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: )
393 [self UpdatePopUp: self];
398 case HB_MODE_ENCODING:
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"];
414 if( !status.fPosition )
416 [fRipStatusField setStringValue: @"Starting..."];
417 [fRipProgress setIndeterminate: YES];
418 [fRipProgress startAnimation: self];;
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];
438 case HB_MODE_SUSPENDED:
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]];
446 [fRipProgress setDoubleValue: 100 * status.fPosition];
448 [fSuspendButton setTitle: @"Resume"];
453 case HB_MODE_CANCELED:
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"];
470 if( status.fMode == HB_MODE_DONE )
472 [fRipStatusField setStringValue: @"Done." ];
473 [fRipProgress setDoubleValue: 100];
475 [NSApp requestUserAttention: NSInformationalRequest];
476 [NSApp beginSheet: fDonePanel
477 modalForWindow: fWindow
481 [NSApp runModalForWindow: fDonePanel];
482 [NSApp endSheet: fDonePanel];
483 [fDonePanel orderOut: self];
485 else if( status.fMode == HB_MODE_CANCELED )
487 [fRipStatusField setStringValue: @"Canceled." ];
488 [fRipProgress setDoubleValue: 0];
492 [fRipStatusField setStringValue: @"An error occured." ];
493 [fRipProgress setDoubleValue: 0];
496 /* Warn the finder to update itself */
497 [[NSWorkspace sharedWorkspace] noteFileSystemChanged:
498 [fFileField stringValue]];
506 /* Do it again 1/10 second later */
507 [NSTimer scheduledTimerWithTimeInterval: 0.1
508 target: self selector: @selector( UpdateIntf: )
509 userInfo: nil repeats: FALSE];
512 - (void) DetectDrives
514 /* Empty the current popup */
515 [fDVDPopUp removeAllItems];
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;
524 kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
525 if( kern_result != KERN_SUCCESS )
530 classes_to_match = IOServiceMatching( kIODVDMediaClass );
531 if( classes_to_match == NULL )
536 CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectable ),
540 IOServiceGetMatchingServices( master_port, classes_to_match,
542 if( kern_result != KERN_SUCCESS )
547 next_media = IOIteratorNext( media_iterator );
548 if( next_media != NULL )
551 size_t dev_path_length;
552 CFTypeRef str_bsd_path;
556 IORegistryEntryCreateCFProperty( next_media,
560 if( str_bsd_path == NULL )
562 IOObjectRelease( next_media );
566 snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
567 dev_path_length = strlen( psz_buf );
569 if( CFStringGetCString( (CFStringRef) str_bsd_path,
570 (char*)&psz_buf + dev_path_length,
571 sizeof(psz_buf) - dev_path_length,
572 kCFStringEncodingASCII ) )
574 [[fDVDPopUp menu] addItemWithTitle:
575 [NSString stringWithCString: psz_buf]
576 action: nil keyEquivalent: @""];
579 CFRelease( str_bsd_path );
581 IOObjectRelease( next_media );
583 } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL );
586 IOObjectRelease( media_iterator );
589 - (void) ScanEnableIntf: (id) sender
591 if( ![fScanMatrix selectedRow] )
593 [fDVDPopUp setEnabled: YES];
594 [fDVDFolderField setEnabled: NO];
595 [fScanBrowseButton setEnabled: NO];
596 [fScanButton setEnabled: ( [fDVDPopUp selectedItem] != nil )];
600 [fDVDPopUp setEnabled: NO];
601 [fDVDFolderField setEnabled: YES];
602 [fScanBrowseButton setEnabled: YES];
603 [fScanButton setEnabled: YES];
607 - (void) UpdatePopUp: (id) sender
609 HBTitle * title = (HBTitle*)
610 fTitleList->ItemAt( [fTitlePopUp indexOfSelectedItem] );
612 [fAudioPopUp removeAllItems];
615 for( uint32_t i = 0; i < title->fAudioList->CountItems(); i++ )
617 audio = (HBAudio*) title->fAudioList->ItemAt( i );
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: @""];