OSDN Git Service

119d9eb56c5e929db3c05f74839c968638a935b0
[handbrake-jp/handbrake-jp-git.git] / macosx / ExpressController.m
1 #import "ExpressController.h"
2 #import "DriveDetector.h"
3
4 #define INSERT_STRING @"Insert a DVD"
5
6 @interface ExpressController (Private)
7
8 - (void) openUpdateDrives: (NSDictionary *) drives;
9 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
10     returnCode contextInfo: (void *) contextInfo;
11 - (void) openEnable: (BOOL) b;
12 - (void) openTimer: (NSTimer *) timer;
13
14 - (void) convertShow;
15 - (void) convertEnable: (BOOL) b;
16 - (void) convertTimer: (NSTimer *) timer;
17
18 @end
19
20 @implementation ExpressController
21
22 /***********************************************************************
23  * Application delegate methods
24  **********************************************************************/
25 - (void) awakeFromNib
26 {
27     NSEnumerator * enumerator;
28
29     /* Show the "Open DVD" interface */
30     fDriveDetector = [[DriveDetector alloc] initWithCallback: self
31         selector: @selector( openUpdateDrives: )];
32     [fDriveDetector run];
33     [self openEnable: YES];
34     [fWindow setContentSize: [fOpenView frame].size];
35     [fWindow setContentView: fOpenView];
36     [fWindow center];
37     [fWindow makeKeyAndOrderFront: nil];
38
39     /* NSTableView initializations */
40      NSButtonCell * buttonCell;
41      NSTableColumn * tableColumn;
42      enumerator = [[fConvertTableView tableColumns] objectEnumerator];
43      while( ( tableColumn = [enumerator nextObject] ) )
44      {
45          [tableColumn setEditable: NO];
46      }
47      tableColumn = [fConvertTableView tableColumnWithIdentifier: @"Check"];
48      buttonCell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
49     [buttonCell setEditable: YES];
50     [buttonCell setButtonType: NSSwitchButton];
51     [tableColumn setDataCell: buttonCell];
52
53     /* Preferences */
54     fConvertFolderString = [@"~/Movies" stringByExpandingTildeInPath];
55     [fConvertFolderString retain];
56 }
57
58 - (void) applicationWillFinishLaunching: (NSNotification *) n
59 {
60     fHandle = hb_init_express( HB_DEBUG_NONE, 0 );
61     fList   = hb_get_titles( fHandle );
62 }
63
64 - (void) applicationWillTerminate: (NSNotification *) n
65 {
66     hb_close( &fHandle );
67 }
68
69 /***********************************************************************
70  * Tableview datasource methods
71  **********************************************************************/
72 - (int) numberOfRowsInTableView: (NSTableView *) t
73 {
74     if( !fHandle )
75         return 0;
76
77     return hb_list_count( fList );
78 }
79
80 - (id) tableView:(NSTableView *) t objectValueForTableColumn:
81     (NSTableColumn *) col row: (int) row
82 {
83     if( [[col identifier] isEqualToString: @"Check"] )
84     {
85         return [fConvertCheckArray objectAtIndex: row];
86     }
87     else
88     {
89         hb_title_t * title = hb_list_item( fList, row );
90         if( [[col identifier] isEqualToString: @"Title"] )
91         {
92             return [@"Title " stringByAppendingFormat: @"%d",
93                     title->index];
94         }
95         else if( [[col identifier] isEqualToString: @"Duration"] )
96         {
97             if( title->hours > 0 )
98             {
99                 return [NSString stringWithFormat:
100                     @"%d hour%s %d min%s", title->hours,
101                     title->hours > 1 ? "s" : "", title->minutes,
102                     title->minutes > 1 ? "s": ""];
103             }
104             else if( title->minutes > 0 )
105             {
106                 return [NSString stringWithFormat:
107                     @"%d min%s %d sec%s", title->minutes,
108                     title->minutes > 1 ? "s" : "", title->seconds,
109                     title->seconds > 1 ? "s": ""];
110             }
111             else
112             {
113                 return [NSString stringWithFormat: @"%d seconds",
114                         title->seconds];
115             }
116         }
117     }
118     return nil;
119 }
120
121 - (void) tableView: (NSTableView *) t setObjectValue: (id) object
122     forTableColumn: (NSTableColumn *) col row: (int) row
123 {
124     if( [[col identifier] isEqualToString: @"Check"] )
125     {
126         [fConvertCheckArray replaceObjectAtIndex: row withObject: object];
127     }
128 }
129
130 /***********************************************************************
131  * User events methods
132  **********************************************************************/
133 - (void) openShow: (id) sender
134 {
135     NSRect frame  = [fWindow frame];
136     float  offset = [fConvertView frame].size.height -
137                     [fOpenView frame].size.height;
138
139     frame.origin.y    += offset;
140     frame.size.height -= offset;
141     [fWindow setContentView: fEmptyView];
142     [fWindow setFrame: frame display: YES animate: YES];
143     [fWindow setContentView: fOpenView];
144
145     [fDriveDetector run];
146 }
147
148 - (void) openMatrixChanged: (id) sender
149 {
150     [self openEnable: YES];
151     if( [fOpenMatrix selectedRow] )
152     {
153         [self openBrowse: self];
154     }
155 }
156
157 - (void) openBrowse: (id) sender
158 {
159     NSOpenPanel * panel = [NSOpenPanel openPanel];
160     [panel setAllowsMultipleSelection: NO];
161     [panel setCanChooseFiles: YES];
162     [panel setCanChooseDirectories: YES ];
163     [panel beginSheetForDirectory: nil file: nil types: nil
164         modalForWindow: fWindow modalDelegate: self
165         didEndSelector: @selector( openBrowseDidEnd:returnCode:contextInfo: )
166         contextInfo: nil];                                                      
167 }
168
169 - (void) openGo: (id) sender
170 {
171     [self openEnable: NO];
172     [fOpenIndicator setIndeterminate: YES];
173     [fOpenIndicator startAnimation: nil];
174     [fOpenProgressField setStringValue: @"Opening..."];
175     [fDriveDetector stop];
176
177     if( [fOpenMatrix selectedRow] )
178     {
179         hb_scan( fHandle, [fOpenFolderString UTF8String], 0 );
180     }
181     else
182     {
183         hb_scan( fHandle, [[fDrives objectForKey: [fOpenPopUp
184                  titleOfSelectedItem]] UTF8String], 0 );
185     }
186
187     NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval: 2.0
188         target: self selector: @selector( openTimer: ) userInfo: nil
189         repeats: YES];
190 }
191
192 - (void) convertGo: (id) sender
193 {
194     int i, j;
195
196     for( i = 0; i < hb_list_count( fList ); i++ )
197     {
198         if( ![[fConvertCheckArray objectAtIndex: i] boolValue] )
199             continue;
200
201         hb_title_t * title = hb_list_item( fList, i );
202         hb_job_t   * job   = title->job;
203
204         int pixels, aspect;
205                 pixels = 307200;
206                 if( [fConvertAspectPopUp indexOfSelectedItem] == 0)
207                 {
208                         aspect = -1;
209                 }
210                 else
211                 {
212             aspect = 4 * HB_ASPECT_BASE / 3;
213                 }
214
215                 do
216                 {
217                         hb_set_size( job, aspect, pixels );
218                         pixels -= 10;
219                 } while(job->width > 640);
220                 
221         if( [fConvertFormatPopUp indexOfSelectedItem] == 0 )
222         {
223             /* iPod / H.264 */
224             job->mux      = HB_MUX_IPOD;
225             job->vcodec   = HB_VCODEC_X264;
226                         job->h264_level = 30;
227                         job->vbitrate = 1000;
228                         job->deinterlace = 1;
229         }
230         else if( [fConvertFormatPopUp indexOfSelectedItem] == 1 )
231         {
232             /* iPod / MPEG-4 */
233             job->mux      = HB_MUX_MP4;
234             job->vcodec   = HB_VCODEC_FFMPEG;
235             job->vbitrate = 1500;
236                         job->deinterlace = 1;
237         }
238         else
239         {
240             /* PSP / MPEG-4 */
241             job->mux        = HB_MUX_PSP;
242             job->vrate      = 27000000;
243             job->vrate_base = 900900;   /* 29.97 fps */
244             job->vcodec     = HB_VCODEC_FFMPEG;
245             job->vbitrate   = 600;
246             pixels          = 76800;
247             job->arate      = 24000;
248             job->abitrate   = 96;
249             aspect          = 16 * HB_ASPECT_BASE / 9;
250
251                         if( [fConvertAspectPopUp indexOfSelectedItem] )
252                         {
253                                 aspect = -1;
254                         }
255
256                         hb_set_size( job, aspect, pixels );
257         }
258
259         job->vquality = -1.0;
260
261         const char * lang;
262
263         /* Audio selection */
264         hb_audio_t * audio;
265         lang = [[fConvertAudioPopUp titleOfSelectedItem] UTF8String];
266         job->audios[0] = -1;
267         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
268         {
269             /* Choose the first track that matches the language */
270             audio = hb_list_item( title->list_audio, j );
271             if( !strcmp( lang, audio->lang_simple ) )
272             {
273                 job->audios[0] = j;
274                 break;
275             }
276         }
277         if( job->audios[0] == -1 )
278         {
279             /* If the language isn't available in this title, choose
280                the first track */
281             job->audios[0] = 0;
282         }
283         job->audios[1] = -1;
284
285         /* Subtitle selection */
286         hb_subtitle_t * subtitle;
287         lang = [[fConvertSubtitlePopUp titleOfSelectedItem] UTF8String];
288         job->subtitle = -1;
289         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
290         {
291             /* Choose the first track that matches the language */
292             subtitle = hb_list_item( title->list_subtitle, j );
293             if( !strcmp( lang, subtitle->lang ) )
294             {
295                 job->subtitle = j;
296                 break;
297             }
298         }
299         
300 //        job->file = strdup( [[NSString stringWithFormat:
301 //                @"%@/%s - Title %d - %@.m4v", fConvertFolderString,
302 //                title->name, title->index, [fConvertFormatPopUp
303 //                titleOfSelectedItem]] UTF8String] );
304         job->file = strdup( [[NSString stringWithFormat:
305                 @"%@/%s.m4v", fConvertFolderString, title->name] UTF8String]);
306         hb_add( fHandle, job );
307     }
308
309     hb_start( fHandle );
310
311     NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval: 2.0
312         target: self selector: @selector( convertTimer: ) userInfo: nil
313         repeats: YES];
314
315     [self convertEnable: NO];
316 }
317
318 - (void) convertCancel: (id) sender
319 {
320     hb_stop( fHandle );
321     [self convertEnable: YES];
322 }
323
324 @end
325
326 /***********************************************************************
327  * Private methods
328  **********************************************************************/
329
330 @implementation ExpressController (Private)
331
332 - (void) openUpdateDrives: (NSDictionary *) drives
333 {
334     if( fDrives )
335     {
336         [fDrives release];
337     }
338     fDrives = [[NSDictionary alloc] initWithDictionary: drives];
339
340     NSString * device;
341     NSEnumerator * enumerator = [fDrives keyEnumerator];
342     [fOpenPopUp removeAllItems];
343     while( ( device = [enumerator nextObject] ) )
344     {
345         [fOpenPopUp addItemWithTitle: device];
346     }
347
348     if( ![fOpenPopUp numberOfItems] )
349     {
350         [fOpenPopUp addItemWithTitle: INSERT_STRING];
351     }
352     [fOpenPopUp selectItemAtIndex: 0];
353     if( [fOpenMatrix isEnabled] )
354     {
355         [self openEnable: YES];
356     }
357 }
358
359 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
360     returnCode contextInfo: (void *) contextInfo
361 {
362     if( returnCode != NSOKButton )
363         return;
364
365     if( fOpenFolderString )
366         [fOpenFolderString release];
367     fOpenFolderString = [[[sheet filenames] objectAtIndex: 0] retain];
368     [fOpenFolderField setStringValue: [fOpenFolderString lastPathComponent]];
369     [self openGo: self];
370 }
371
372 - (void) openEnable: (BOOL) b
373 {
374     [fOpenMatrix       setEnabled: b];
375     [fOpenPopUp        setEnabled: b];
376     [fOpenFolderField  setEnabled: b];
377     [fOpenBrowseButton setEnabled: b];
378     [fOpenGoButton     setEnabled: b];
379
380     if( b )
381     {
382         if( [fOpenMatrix selectedRow] )
383         {
384             [fOpenPopUp setEnabled: NO];
385         }
386         else
387         {
388             [fOpenFolderField  setEnabled: NO];
389             [fOpenBrowseButton setEnabled: NO];
390             if( [[fOpenPopUp titleOfSelectedItem]
391                     isEqualToString: INSERT_STRING] )
392             {
393                 [fOpenGoButton setEnabled: NO];
394             }
395         }
396     }
397 }
398
399 - (void) openTimer: (NSTimer *) timer
400 {
401     hb_state_t s;
402     hb_get_state( fHandle, &s );
403     switch( s.state )
404     {
405 #define p s.param.scanning
406         case HB_STATE_SCANNING:
407             [fOpenIndicator setIndeterminate: NO];
408             [fOpenIndicator setDoubleValue: 100.0 *
409                 ( (float) p.title_cur - 0.5 ) / p.title_count];
410             [fOpenProgressField setStringValue: [NSString
411                 stringWithFormat: @"Scanning title %d of %d...",
412                 p.title_cur, p.title_count]];
413             break;
414 #undef p
415
416         case HB_STATE_SCANDONE:
417             [timer invalidate];
418
419             [fOpenIndicator setIndeterminate: NO];
420             [fOpenIndicator setDoubleValue: 0.0];
421             [self openEnable: YES];
422
423             if( hb_list_count( fList ) )
424             {
425                 [self convertShow];
426             }
427             else
428             {
429                 [fDriveDetector run];
430             }
431             break;
432
433         default:
434             break;
435     }
436 }
437
438 - (void) convertShow
439 {
440     int i, j;
441
442     fConvertCheckArray = [[NSMutableArray alloc] initWithCapacity:
443         hb_list_count( fList )];
444     [fConvertAudioPopUp removeAllItems];
445     [fConvertSubtitlePopUp removeAllItems];
446     [fConvertSubtitlePopUp addItemWithTitle: @"None"];
447     for( i = 0; i < hb_list_count( fList ); i++ )
448     {
449         /* Default is to convert titles longer than 30 minutes. */
450         hb_title_t * title = hb_list_item( fList, i );
451         [fConvertCheckArray addObject: [NSNumber numberWithBool:
452             ( 60 * title->hours + title->minutes > 30 )]];
453
454         /* Update audio popup */
455         hb_audio_t * audio;
456         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
457         {
458             audio = hb_list_item( title->list_audio, j );
459             [fConvertAudioPopUp addItemWithTitle:
460                 [NSString stringWithUTF8String: audio->lang_simple]];
461         }
462                 [fConvertAudioPopUp selectItemWithTitle: @"English"];
463
464         /* Update subtitle popup */
465         hb_subtitle_t * subtitle;
466         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
467         {
468             subtitle = hb_list_item( title->list_subtitle, j );
469             [fConvertSubtitlePopUp addItemWithTitle:
470                 [NSString stringWithUTF8String: subtitle->lang]];
471         }
472     }
473     [fConvertTableView reloadData];
474
475     NSRect frame  = [fWindow frame];
476     float  offset = [fConvertView frame].size.height -
477                     [fOpenView frame].size.height;
478     frame.origin.y    -= offset;
479     frame.size.height += offset;
480     [fWindow setContentView: fEmptyView];
481     [fWindow setFrame: frame display: YES animate: YES];
482     [fWindow setContentView: fConvertView];
483
484     /* Folder popup */
485     NSMenuItem * item = [fConvertFolderPopUp itemAtIndex: 0];
486     [item setTitle: [fConvertFolderString lastPathComponent]];
487     NSImage * image32 = [[NSWorkspace sharedWorkspace] iconForFile:
488         fConvertFolderString];
489     NSImage * image16 = [[NSImage alloc] initWithSize:
490         NSMakeSize(16,16)];
491     [image16 lockFocus];
492     [[NSGraphicsContext currentContext]
493         setImageInterpolation: NSImageInterpolationHigh];
494     [image32 drawInRect: NSMakeRect(0,0,16,16)
495         fromRect: NSMakeRect(0,0,32,32) operation: NSCompositeCopy
496         fraction: 1.0];
497     [image16 unlockFocus];                                                      
498     [item setImage: image16];
499     [image16 release];
500
501     [self convertEnable: YES];
502 }
503
504 - (void) convertEnable: (BOOL) b
505 {
506     [fConvertTableView setEnabled: b];
507     [fConvertFolderPopUp setEnabled: b];
508     [fConvertFormatPopUp setEnabled: b];
509     [fConvertAspectPopUp setEnabled: b];
510     [fConvertAudioPopUp setEnabled: b];
511     [fConvertSubtitlePopUp setEnabled: b];
512     [fConvertOpenButton setEnabled: b];
513     if( b )
514     {
515         [fConvertGoButton setTitle: @"Convert"];
516         [fConvertGoButton setAction: @selector(convertGo:)];
517     }
518     else
519     {
520         [fConvertGoButton setTitle: @"Cancel"];
521         [fConvertGoButton setAction: @selector(convertCancel:)];
522     }
523 }
524
525 - (void) convertTimer: (NSTimer *) timer
526 {
527     hb_state_t s;
528     hb_get_state( fHandle, &s );
529     switch( s.state )
530     {
531 #define p s.param.working
532         case HB_STATE_WORKING:
533         {
534             float progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
535             NSMutableString * string = [NSMutableString
536                 stringWithFormat: @"Converting: %.1f %%",
537                 100.0 * progress_total];
538             if( p.seconds > -1 )
539             {
540                 [string appendFormat: @" (%.1f fps, ", p.rate_avg];
541                 if( p.hours > 0 )
542                 {
543                     [string appendFormat: @"%d hour%s %d min%s",
544                         p.hours, p.hours == 1 ? "" : "s",
545                         p.minutes, p.minutes == 1 ? "" : "s"];
546                 }
547                 else if( p.minutes > 0 )
548                 {
549                     [string appendFormat: @"%d min%s %d sec%s",
550                         p.minutes, p.minutes == 1 ? "" : "s",
551                         p.seconds, p.seconds == 1 ? "" : "s"];
552                 }
553                 else
554                 {
555                     [string appendFormat: @"%d second%s",
556                         p.seconds, p.seconds == 1 ? "" : "s"];
557                 }
558                 [string appendString: @" left)"];
559             }
560             [fConvertInfoString setStringValue: string];
561             [fConvertIndicator setIndeterminate: NO];
562             [fConvertIndicator setDoubleValue: 100.0 * p.progress];
563             break;
564         }
565 #undef p
566
567         case HB_STATE_WORKDONE:
568             [timer invalidate];
569             [fConvertInfoString setStringValue: @"Done."];
570             [fConvertIndicator setIndeterminate: NO];
571             [fConvertIndicator setDoubleValue: 0.0];
572             [self convertEnable: YES];
573
574             hb_job_t * job;
575             while( ( job = hb_job( fHandle, 0 ) ) )
576             {
577                 hb_rem( fHandle, job );
578             }
579             break;
580
581         default:
582             break;
583     }
584 }
585
586 @end