1 #import "ExpressController.h"
3 #define INSERT_STRING @"Insert a DVD"
4 #define TOOLBAR_START @"TOOLBAR_START"
5 #define TOOLBAR_PAUSE @"TOOLBAR_PAUSE"
6 #define TOOLBAR_OPEN @"TOOLBAR_OPEN"
7 #define TOOLBAR_ADVANCED @"TOOLBAR_ADVANCED"
9 #define p fState->param
11 @interface ExpressController (Private)
13 - (void) openUpdateDrives: (NSDictionary *) drives;
14 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
15 returnCode contextInfo: (void *) contextInfo;
16 - (void) openEnable: (BOOL) b;
19 - (void) convertEnable: (BOOL) b;
23 @implementation ExpressController
25 /***********************************************************************
26 * Application delegate methods
27 **********************************************************************/
30 NSEnumerator * enumerator;
32 /* NSToolbar initializations */
33 fToolbar = [[NSToolbar alloc] initWithIdentifier: @"InstantHandBrake Toolbar"];
34 [fToolbar setDelegate: self];
35 [fToolbar setAllowsUserCustomization: YES];
36 [fToolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
37 [fToolbar setVisible:NO];
38 [fWindow setToolbar: fToolbar];
40 /* Show the "Open DVD" interface */
41 fDriveDetector = [[DriveDetector alloc] initWithCallback: self
42 selector: @selector( openUpdateDrives: )];
44 [self openEnable: YES];
45 [fWindow setContentSize: [fOpenView frame].size];
46 [fWindow setContentView: fOpenView];
48 [fWindow makeKeyAndOrderFront: nil];
50 /* NSTableView initializations */
51 NSButtonCell * buttonCell;
52 NSTableColumn * tableColumn;
53 enumerator = [[fConvertTableView tableColumns] objectEnumerator];
54 while( ( tableColumn = [enumerator nextObject] ) )
56 [tableColumn setEditable: NO];
58 tableColumn = [fConvertTableView tableColumnWithIdentifier: @"Check"];
59 buttonCell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
60 [buttonCell setEditable: YES];
61 [buttonCell setButtonType: NSSwitchButton];
62 [tableColumn setDataCell: buttonCell];
65 fConvertFolderString = [NSString stringWithFormat:@"%@/Desktop", NSHomeDirectory()];
66 [fConvertFolderString retain];
69 - (void) applicationWillFinishLaunching: (NSNotification *) n
71 fCore = [[HBCore alloc] init];
72 [fCore openInDebugMode:NO checkForUpdates:NO];
73 fHandle = [fCore hb_handle];
74 fState = [fCore hb_state];
75 fList = hb_get_titles( fHandle );
77 [[NSNotificationCenter defaultCenter] addObserver:self
78 selector:@selector(scanningSource:)
79 name:@"HBCoreScanningNotification" object:nil];
81 [[NSNotificationCenter defaultCenter] addObserver:self
82 selector:@selector(scanDone:)
83 name:@"HBCoreScanDoneNotification" object:nil];
85 [[NSNotificationCenter defaultCenter] addObserver:self
86 selector:@selector(muxing:)
87 name:@"HBCoreMuxingNotification" object:nil];
89 [[NSNotificationCenter defaultCenter] addObserver:self
90 selector:@selector(working:)
91 name:@"HBCoreWorkingNotification" object:nil];
93 [[NSNotificationCenter defaultCenter] addObserver:self
94 selector:@selector(workDone:)
95 name:@"HBCoreWorkDoneNotification" object:nil];
98 - (void) applicationWillTerminate: (NSNotification *) n
103 - (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident
104 willBeInsertedIntoToolbar: (BOOL) flag
106 NSToolbarItem * item;
107 item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
109 if ([ident isEqualToString: TOOLBAR_START])
111 [item setLabel: NSLocalizedString(@"Start", "Start")];
112 [item setImage: [NSImage imageNamed: @"Play"]];
113 [item setTarget: self];
114 [item setAction: @selector(convertGo:)];
116 else if ([ident isEqualToString: TOOLBAR_PAUSE])
118 [item setLabel: NSLocalizedString(@"Pause", "Pause")];
119 [item setImage: [NSImage imageNamed: @"Pause"]];
120 [item setTarget: self];
121 [item setAction: @selector(pauseGo:)];
123 else if ([ident isEqualToString: TOOLBAR_OPEN])
125 [item setLabel: NSLocalizedString(@"Open", "Open")];
126 [item setImage: [NSImage imageNamed: @"pref-audio"]];
127 [item setTarget: self];
128 [item setAction: @selector(openShow:)];
130 else if ([ident isEqualToString: TOOLBAR_ADVANCED])
132 [item setLabel: NSLocalizedString(@"Advanced", "Advanced")];
133 [item setImage: [NSImage imageNamed: @"pref-advanced"]];
134 [item setTarget: self];
135 [item setAction: @selector(setPrefView:)];
136 [item setAutovalidates: NO];
147 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
149 return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
150 TOOLBAR_OPEN, NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_ADVANCED, nil];
153 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
155 return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
156 TOOLBAR_OPEN, TOOLBAR_ADVANCED, nil];
159 /***********************************************************************
160 * Tableview datasource methods
161 **********************************************************************/
162 - (int) numberOfRowsInTableView: (NSTableView *) t
167 return hb_list_count( fList );
170 - (id) tableView:(NSTableView *) t objectValueForTableColumn:
171 (NSTableColumn *) col row: (int) row
173 if( [[col identifier] isEqualToString: @"Check"] )
175 return [fConvertCheckArray objectAtIndex: row];
179 hb_title_t * title = hb_list_item( fList, row );
180 if( [[col identifier] isEqualToString: @"Title"] )
182 return [@"Title " stringByAppendingFormat: @"%d",
185 else if( [[col identifier] isEqualToString: @"Duration"] )
187 if( title->hours > 0 )
189 return [NSString stringWithFormat:
190 @"%d hour%s %d min%s", title->hours,
191 title->hours > 1 ? "s" : "", title->minutes,
192 title->minutes > 1 ? "s": ""];
194 else if( title->minutes > 0 )
196 return [NSString stringWithFormat:
197 @"%d min%s %d sec%s", title->minutes,
198 title->minutes > 1 ? "s" : "", title->seconds,
199 title->seconds > 1 ? "s": ""];
203 return [NSString stringWithFormat: @"%d seconds",
211 - (void) tableView: (NSTableView *) t setObjectValue: (id) object
212 forTableColumn: (NSTableColumn *) col row: (int) row
214 if( [[col identifier] isEqualToString: @"Check"] )
216 [fConvertCheckArray replaceObjectAtIndex: row withObject: object];
220 /***********************************************************************
221 * User events methods
222 **********************************************************************/
223 - (void) openShow: (id) sender
225 NSRect frame = [fWindow frame];
226 float offset = [fConvertView frame].size.height -
227 [fOpenView frame].size.height;
229 frame.origin.y += offset;
230 frame.size.height -= offset;
231 [fWindow setContentView: fEmptyView];
232 [fWindow setFrame: frame display: YES animate: YES];
233 [fToolbar setVisible:NO];
234 [fWindow setContentView: fOpenView];
236 [fDriveDetector run];
239 - (void) openMatrixChanged: (id) sender
241 [self openEnable: YES];
242 if( [fOpenMatrix selectedRow] )
244 [self openBrowse: self];
248 - (void) openBrowse: (id) sender
250 NSOpenPanel * panel = [NSOpenPanel openPanel];
251 [panel setAllowsMultipleSelection: NO];
252 [panel setCanChooseFiles: YES];
253 [panel setCanChooseDirectories: YES ];
254 [panel beginSheetForDirectory: nil file: nil types: nil
255 modalForWindow: fWindow modalDelegate: self
256 didEndSelector: @selector( openBrowseDidEnd:returnCode:contextInfo: )
260 - (void) openGo: (id) sender
262 [self openEnable: NO];
263 [fOpenIndicator setIndeterminate: YES];
264 [fOpenIndicator startAnimation: nil];
265 [fOpenProgressField setStringValue: @"Opening..."];
266 [fDriveDetector stop];
268 if( [fOpenMatrix selectedRow] )
270 hb_scan( fHandle, [fOpenFolderString UTF8String], 0 );
274 hb_scan( fHandle, [[fDrives objectForKey: [fOpenPopUp
275 titleOfSelectedItem]] UTF8String], 0 );
279 - (void) convertGo: (id) sender
283 for( i = 0; i < hb_list_count( fList ); i++ )
285 if( ![[fConvertCheckArray objectAtIndex: i] boolValue] )
288 hb_title_t * title = hb_list_item( fList, i );
289 hb_job_t * job = title->job;
292 int aspect = title->aspect;
293 if( [fConvertAspectPopUp indexOfSelectedItem] == 1)
295 aspect = 4 * HB_ASPECT_BASE / 3;
299 job->vbitrate = 1000;
300 if( [fConvertMaxWidthPopUp indexOfSelectedItem] == 1)
305 //job->deinterlace = 1;
309 hb_set_size( job, aspect, pixels );
311 } while(job->width > maxwidth);
313 if( [fConvertFormatPopUp indexOfSelectedItem] == 0 )
316 job->mux = HB_MUX_IPOD;
317 job->vcodec = HB_VCODEC_X264;
318 job->h264_level = 30;
320 else if( [fConvertFormatPopUp indexOfSelectedItem] == 1 )
323 job->mux = HB_MUX_MP4;
324 job->vcodec = HB_VCODEC_FFMPEG;
329 job->mux = HB_MUX_PSP;
330 job->vrate = 27000000;
331 job->vrate_base = 900900; /* 29.97 fps */
332 job->vcodec = HB_VCODEC_FFMPEG;
337 aspect = 16 * HB_ASPECT_BASE / 9;
339 if( [fConvertAspectPopUp indexOfSelectedItem] )
344 hb_set_size( job, aspect, pixels );
347 job->vquality = -1.0;
351 /* Audio selection */
353 lang = [[fConvertAudioPopUp titleOfSelectedItem] UTF8String];
355 for( j = 0; j < hb_list_count( title->list_audio ); j++ )
357 /* Choose the first track that matches the language */
358 audio = hb_list_item( title->list_audio, j );
359 if( !strcmp( lang, audio->lang_simple ) )
365 if( job->audios[0] == -1 )
367 /* If the language isn't available in this title, choose
373 job->audio_mixdowns[0] = HB_AMIXDOWN_DOLBYPLII;
375 /* Subtitle selection */
376 hb_subtitle_t * subtitle;
377 lang = [[fConvertSubtitlePopUp titleOfSelectedItem] UTF8String];
379 for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
381 /* Choose the first track that matches the language */
382 subtitle = hb_list_item( title->list_subtitle, j );
383 if( !strcmp( lang, subtitle->lang ) )
390 job->file = strdup( [[NSString stringWithFormat:
391 @"%@/%s - Title %d.m4v", fConvertFolderString,
392 title->name, title->index] UTF8String] );
393 hb_add( fHandle, job );
398 [self convertEnable: NO];
401 - (void) convertCancel: (id) sender
404 [self convertEnable: YES];
409 /***********************************************************************
411 **********************************************************************/
413 @implementation ExpressController (Private)
415 - (void) openUpdateDrives: (NSDictionary *) drives
421 fDrives = [[NSDictionary alloc] initWithDictionary: drives];
424 NSEnumerator * enumerator = [fDrives keyEnumerator];
425 [fOpenPopUp removeAllItems];
426 while( ( device = [enumerator nextObject] ) )
428 [fOpenPopUp addItemWithTitle: device];
431 if( ![fOpenPopUp numberOfItems] )
433 [fOpenPopUp addItemWithTitle: INSERT_STRING];
435 [fOpenPopUp selectItemAtIndex: 0];
436 if( [fOpenMatrix isEnabled] )
438 [self openEnable: YES];
442 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
443 returnCode contextInfo: (void *) contextInfo
445 if( returnCode != NSOKButton )
448 if( fOpenFolderString )
449 [fOpenFolderString release];
450 fOpenFolderString = [[[sheet filenames] objectAtIndex: 0] retain];
451 [fOpenFolderField setStringValue: [fOpenFolderString lastPathComponent]];
455 - (BOOL)validateToolbarItem: (NSToolbarItem *) toolbarItem
457 NSString * ident = [toolbarItem itemIdentifier];
459 if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorking isEqualToString:[fCore state]])
461 [toolbarItem setAction: @selector(convertCancel:)];
462 [toolbarItem setLabel:NSLocalizedString(@"Cancel", @"Cancel")];
465 else if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorkDone isEqualToString:[fCore state]])
467 [toolbarItem setAction: @selector(convertGo:)];
468 [toolbarItem setLabel:NSLocalizedString(@"Convert", @"Convert")];
471 else if ([ident isEqualToString: TOOLBAR_OPEN] && [HBStateWorking isEqualToString:[fCore state]])
479 - (void) openEnable: (BOOL) b
481 [fOpenMatrix setEnabled: b];
482 [fOpenPopUp setEnabled: b];
483 [fOpenFolderField setEnabled: b];
484 [fOpenBrowseButton setEnabled: b];
488 if( [fOpenMatrix selectedRow] )
490 [fOpenPopUp setEnabled: NO];
494 [fOpenFolderField setEnabled: NO];
495 [fOpenBrowseButton setEnabled: NO];
496 if( [[fOpenPopUp titleOfSelectedItem]
497 isEqualToString: INSERT_STRING] )
504 - (void) scanningSource: (NSNotification *) n
506 [fOpenIndicator setIndeterminate: NO];
507 [fOpenIndicator setDoubleValue: 100.0 *
508 ( (float) p.scanning.title_cur - 0.5 ) / p.scanning.title_count];
509 [fOpenProgressField setStringValue: [NSString
510 stringWithFormat: @"Scanning title %d of %d...",
511 p.scanning.title_cur, p.scanning.title_count]];
514 - (void) scanDone: (NSNotification *) n
516 [fOpenIndicator setIndeterminate: NO];
517 [fOpenIndicator setDoubleValue: 0.0];
519 [self openEnable: YES];
521 if( hb_list_count( fList ) )
527 [fDriveDetector run];
535 fConvertCheckArray = [[NSMutableArray alloc] initWithCapacity:
536 hb_list_count( fList )];
537 [fConvertAudioPopUp removeAllItems];
538 [fConvertSubtitlePopUp removeAllItems];
539 [fConvertSubtitlePopUp addItemWithTitle: @"None"];
540 for( i = 0; i < hb_list_count( fList ); i++ )
542 /* Default is to convert titles longer than 30 minutes. */
543 hb_title_t * title = hb_list_item( fList, i );
544 [fConvertCheckArray addObject: [NSNumber numberWithBool:
545 ( 60 * title->hours + title->minutes > 30 )]];
547 /* Update audio popup */
549 for( j = 0; j < hb_list_count( title->list_audio ); j++ )
551 audio = hb_list_item( title->list_audio, j );
552 [fConvertAudioPopUp addItemWithTitle:
553 [NSString stringWithUTF8String: audio->lang_simple]];
555 [fConvertAudioPopUp selectItemWithTitle: @"English"];
557 if ( [fConvertAudioPopUp selectedItem] == nil )
558 [fConvertAudioPopUp selectItemWithTitle: @"Unknown"];
560 /* Update subtitle popup */
561 hb_subtitle_t * subtitle;
562 for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
564 subtitle = hb_list_item( title->list_subtitle, j );
565 [fConvertSubtitlePopUp addItemWithTitle:
566 [NSString stringWithUTF8String: subtitle->lang]];
569 [fConvertTableView reloadData];
571 NSRect frame = [fWindow frame];
572 float offset = [fConvertView frame].size.height -
573 [fOpenView frame].size.height;
574 float hoffset = [fConvertView frame].size.width -
575 [fOpenView frame].size.width;
576 frame.origin.y -= offset;
577 frame.size.height += offset;
578 frame.size.width += hoffset;
579 [fWindow setContentView: fEmptyView];
580 [fWindow setFrame: frame display: YES animate: YES];
581 [fToolbar setVisible:YES];
582 [fWindow setContentView: fConvertView];
585 NSMenuItem * item = [fConvertFolderPopUp itemAtIndex: 0];
586 [item setTitle: [fConvertFolderString lastPathComponent]];
587 NSImage * image32 = [[NSWorkspace sharedWorkspace] iconForFile:
588 fConvertFolderString];
589 NSImage * image16 = [[NSImage alloc] initWithSize:
592 [[NSGraphicsContext currentContext]
593 setImageInterpolation: NSImageInterpolationHigh];
594 [image32 drawInRect: NSMakeRect(0,0,16,16)
595 fromRect: NSMakeRect(0,0,32,32) operation: NSCompositeCopy
597 [image16 unlockFocus];
598 [item setImage: image16];
601 [self convertEnable: YES];
604 - (void) convertEnable: (BOOL) b
606 [fConvertTableView setEnabled: b];
607 [fConvertFolderPopUp setEnabled: b];
608 [fConvertFormatPopUp setEnabled: b];
609 [fConvertAspectPopUp setEnabled: b];
610 [fConvertMaxWidthPopUp setEnabled: b];
611 [fConvertAudioPopUp setEnabled: b];
612 [fConvertSubtitlePopUp setEnabled: b];
613 [fConvertOpenButton setEnabled: b];
616 /***********************************************************************
618 ***********************************************************************
619 * Shows a progression bar on the dock icon, filled according to
620 * 'progress' (0.0 <= progress <= 1.0).
621 * Called with progress < 0.0 or progress > 1.0, restores the original
623 **********************************************************************/
624 - (void) UpdateDockIcon: (float) progress
628 NSBitmapImageRep * bmp;
630 uint32_t black = htonl( 0x000000FF );
631 uint32_t red = htonl( 0xFF0000FF );
632 uint32_t white = htonl( 0xFFFFFFFF );
633 int row_start, row_end;
636 /* Get application original icon */
637 icon = [NSImage imageNamed: @"NSApplicationIcon"];
639 if( progress < 0.0 || progress > 1.0 )
641 [NSApp setApplicationIconImage: icon];
645 /* Get it in a raw bitmap form */
646 tiff = [icon TIFFRepresentationUsingCompression:
647 NSTIFFCompressionNone factor: 1.0];
648 bmp = [NSBitmapImageRep imageRepWithData: tiff];
650 /* Draw the progression bar */
651 /* It's pretty simple (ugly?) now, but I'm no designer */
653 row_start = 3 * (int) [bmp size].height / 4;
654 row_end = 7 * (int) [bmp size].height / 8;
656 for( i = row_start; i < row_start + 2; i++ )
658 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
659 for( j = 0; j < (int) [bmp size].width; j++ )
664 for( i = row_start + 2; i < row_end - 2; i++ )
666 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
669 for( j = 2; j < (int) [bmp size].width - 2; j++ )
671 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
683 for( i = row_end - 2; i < row_end; i++ )
685 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
686 for( j = 0; j < (int) [bmp size].width; j++ )
692 /* Now update the dock icon */
693 tiff = [bmp TIFFRepresentationUsingCompression:
694 NSTIFFCompressionNone factor: 1.0];
695 icon = [[NSImage alloc] initWithData: tiff];
696 [NSApp setApplicationIconImage: icon];
700 - (void) working: (NSNotification *) n
702 float progress_total = ( p.working.progress + p.working.job_cur - 1 ) / p.working.job_count;
703 NSMutableString * string = [NSMutableString
704 stringWithFormat: @"Converting: %.1f %%",
705 100.0 * progress_total];
706 if( p.working.seconds > -1 )
708 [string appendFormat: @" (%.1f fps, ", p.working.rate_avg];
709 if( p.working.hours > 0 )
711 [string appendFormat: @"%d hour%s %d min%s",
712 p.working.hours, p.working.hours == 1 ? "" : "s",
713 p.working.minutes, p.working.minutes == 1 ? "" : "s"];
715 else if( p.working.minutes > 0 )
717 [string appendFormat: @"%d min%s %d sec%s",
718 p.working.minutes, p.working.minutes == 1 ? "" : "s",
719 p.working.seconds, p.working.seconds == 1 ? "" : "s"];
723 [string appendFormat: @"%d second%s",
724 p.working.seconds, p.working.seconds == 1 ? "" : "s"];
726 [string appendString: @" left)"];
728 [fConvertInfoString setStringValue: string];
729 [fConvertIndicator setIndeterminate: NO];
730 [fConvertIndicator setDoubleValue: 100.0 * progress_total];
731 [self UpdateDockIcon: progress_total];
735 - (void) muxing: (NSNotification *) n
737 [fConvertInfoString setStringValue: NSLocalizedString(@"Muxing...",@"Muxing...")];
738 [fConvertIndicator setIndeterminate: YES];
739 [fConvertIndicator startAnimation: nil];
740 [self UpdateDockIcon: 1.0];
743 - (void) workDone: (NSNotification *) n
745 [fConvertIndicator setIndeterminate: NO];
746 [fConvertIndicator setDoubleValue: 0.0];
747 [self UpdateDockIcon: -1.0];
748 [self convertEnable: YES];
750 [fConvertInfoString setStringValue: NSLocalizedString(@"Done.",@"Done.")];
753 while( ( job = hb_job( fHandle, 0 ) ) )
755 hb_rem( fHandle, job );