OSDN Git Service

x264 bump to r1338-19977e9
[handbrake-jp/handbrake-jp-git.git] / macosx / InstantHandBrake / ExpressController.m
1 /* This file is part of the HandBrake source code.
2    Homepage: <http://handbrake.fr/>.
3    It may be used under the terms of the GNU General Public License. */
4
5 #import "ExpressController.h"
6
7 #define INSERT_STRING       @"Insert a DVD"
8 #define TOOLBAR_START       @"TOOLBAR_START"
9 #define TOOLBAR_PAUSE       @"TOOLBAR_PAUSE"
10 #define TOOLBAR_OPEN        @"TOOLBAR_OPEN"
11 #define TOOLBAR_ADVANCED    @"TOOLBAR_ADVANCED"
12
13 #define p fState->param
14
15 #import "Device.h"
16
17 @interface ExpressController (Private)
18
19 - (void) openUpdateDrives: (NSDictionary *) drives;
20 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
21     returnCode contextInfo: (void *) contextInfo;
22 - (void) openEnable: (BOOL) b;
23
24 - (id) updatePopUpIcon: (id) value;
25 - (void) convertShow;
26 - (void) convertEnable: (BOOL) b;
27
28 @end
29
30 @implementation ExpressController
31
32 /***********************************************************************
33  * Application delegate methods
34  **********************************************************************/
35 - (void) awakeFromNib
36 {
37     NSEnumerator * enumerator;
38     
39     /* NSToolbar initializations */
40     fToolbar = [[NSToolbar alloc] initWithIdentifier: @"InstantHandBrake Toolbar"];
41     [fToolbar setDelegate: self];
42     [fToolbar setAllowsUserCustomization: NO];
43     [fToolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
44     [fToolbar setVisible:NO];
45     [fWindow setShowsToolbarButton:NO];
46     [fWindow setToolbar: fToolbar];
47         
48     /* Show the "Open DVD" interface */
49     fDriveDetector = [[DriveDetector alloc] initWithCallback: self
50         selector: @selector( openUpdateDrives: )];
51     [fDriveDetector run];
52     [self openEnable: YES];
53     [fWindow setContentSize: [fOpenView frame].size];
54     [fWindow setContentView: fOpenView];
55     [fWindow center];
56     [fWindow makeKeyAndOrderFront: nil];
57
58     /* NSTableView initializations */
59      NSButtonCell * buttonCell;
60      NSTableColumn * tableColumn;
61      enumerator = [[fConvertTableView tableColumns] objectEnumerator];
62      while( ( tableColumn = [enumerator nextObject] ) )
63      {
64          [tableColumn setEditable: NO];
65      }
66      tableColumn = [fConvertTableView tableColumnWithIdentifier: @"Check"];
67      buttonCell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
68     [buttonCell setEditable: YES];
69     [buttonCell setButtonType: NSSwitchButton];
70     [tableColumn setDataCell: buttonCell];
71
72     /* Preferences */
73     fConvertFolderString = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"];
74     [fConvertFolderString retain];
75 }
76
77 - (void) applicationWillFinishLaunching: (NSNotification *) n
78 {
79     fCore = [[HBCore alloc] init];
80     [fCore openInDebugMode:NO checkForUpdates:NO];
81     fHandle = [fCore hb_handle];
82     fState = [fCore hb_state];
83     fList   = hb_get_titles( fHandle );
84     
85     fDevice = [[DeviceController alloc] init];
86     
87     [[NSNotificationCenter defaultCenter] addObserver:self
88     selector:@selector(scanningSource:)
89     name:@"HBCoreScanningNotification" object:nil];
90     
91     [[NSNotificationCenter defaultCenter] addObserver:self
92     selector:@selector(scanDone:)
93     name:@"HBCoreScanDoneNotification" object:nil];
94     
95     [[NSNotificationCenter defaultCenter] addObserver:self
96     selector:@selector(muxing:)
97     name:@"HBCoreMuxingNotification" object:nil];
98     
99     [[NSNotificationCenter defaultCenter] addObserver:self
100     selector:@selector(working:)
101     name:@"HBCoreWorkingNotification" object:nil];
102     
103     [[NSNotificationCenter defaultCenter] addObserver:self
104     selector:@selector(workDone:)
105     name:@"HBCoreWorkDoneNotification" object:nil];
106     
107     [GrowlApplicationBridge setGrowlDelegate: self];
108 }
109
110 - (void) applicationWillTerminate: (NSNotification *) n
111 {
112     [fCore close];
113 }
114
115 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
116 {
117     if( !flag ) {
118         [fWindow  makeKeyAndOrderFront:nil];
119         return YES;
120     }
121     return NO;
122 }
123
124 - (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident
125                     willBeInsertedIntoToolbar: (BOOL) flag
126 {
127     NSToolbarItem * item;
128     item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
129
130     if ([ident isEqualToString: TOOLBAR_START])
131     {
132         [item setLabel: NSLocalizedString(@"Convert", "Convert")];
133         [item setPaletteLabel: NSLocalizedString(@"Convert/Cancel", @"Convert/Cancel")];
134         [item setImage: [NSImage imageNamed: @"Play"]];
135         [item setTarget: self];
136         [item setAction: @selector(convertGo:)];
137     }
138     else if ([ident isEqualToString: TOOLBAR_PAUSE])
139     {
140         [item setLabel: NSLocalizedString(@"Pause", "Pause")];
141         [item setPaletteLabel: NSLocalizedString(@"Pause/Resume", @"Pause/Resume")];
142         [item setImage: [NSImage imageNamed: @"Pause"]];
143         [item setTarget: self];
144         [item setAction: @selector(pauseGo:)];
145     }
146     else if ([ident isEqualToString: TOOLBAR_OPEN])
147     {
148         [item setLabel: NSLocalizedString(@"Open...", "Open...")];
149         [item setPaletteLabel: NSLocalizedString(@"Open Another Source", "Open Another Source")];
150         [item setImage: [NSImage imageNamed: @"Open"]];
151         [item setTarget: self];
152         [item setAction: @selector(openShow:)];
153     }
154     else
155     {
156         [item release];
157         return nil;
158     }
159
160     return item;
161 }
162
163 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
164 {
165     return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
166                                     NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_OPEN, nil];
167 }
168
169 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
170 {
171     return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
172                                         TOOLBAR_OPEN, NSToolbarFlexibleSpaceItemIdentifier, nil];
173 }
174
175 /***********************************************************************
176  * Tableview datasource methods
177  **********************************************************************/
178 - (int) numberOfRowsInTableView: (NSTableView *) t
179 {
180     if( !fHandle )
181         return 0;
182
183     return hb_list_count( fList );
184 }
185
186 - (id) tableView:(NSTableView *) t objectValueForTableColumn:
187     (NSTableColumn *) col row: (int) row
188 {
189     if( [[col identifier] isEqualToString: @"Check"] )
190     {
191         return [fConvertCheckArray objectAtIndex: row];
192     }
193     else
194     {
195         hb_title_t * title = hb_list_item( fList, row );
196         if( [[col identifier] isEqualToString: @"Title"] )
197         {
198             return [@"Title " stringByAppendingFormat: @"%d",
199                     title->index];
200         }
201         else if( [[col identifier] isEqualToString: @"Duration"] )
202         {
203             if( title->hours > 0 )
204             {
205                 return [NSString stringWithFormat:
206                     @"%d hour%s %d min%s", title->hours,
207                     title->hours > 1 ? "s" : "", title->minutes,
208                     title->minutes > 1 ? "s": ""];
209             }
210             else if( title->minutes > 0 )
211             {
212                 return [NSString stringWithFormat:
213                     @"%d min%s %d sec%s", title->minutes,
214                     title->minutes > 1 ? "s" : "", title->seconds,
215                     title->seconds > 1 ? "s": ""];
216             }
217             else
218             {
219                 return [NSString stringWithFormat: @"%d seconds",
220                         title->seconds];
221             }
222         }
223         else if( [[col identifier] isEqualToString: @"Size"] )
224         {
225             return [NSString stringWithFormat:@"-"];
226         }
227     }
228     return nil;
229 }
230
231 - (void) tableView: (NSTableView *) t setObjectValue: (id) object
232     forTableColumn: (NSTableColumn *) col row: (int) row
233 {
234     if( [[col identifier] isEqualToString: @"Check"] )
235     {
236         [fConvertCheckArray replaceObjectAtIndex: row withObject: object];
237     }
238 }
239
240 /***********************************************************************
241  * User events methods
242  **********************************************************************/
243 - (void) openShow: (id) sender
244 {
245     NSRect frame  = [fWindow frame];
246     float  offset = ( [fConvertView frame].size.height -
247                     [fOpenView frame].size.height ) * [fWindow userSpaceScaleFactor];
248
249     frame.origin.y    += offset;
250     frame.size.height -= offset;
251     [fWindow setContentView: fEmptyView];
252     [fWindow setFrame: frame display: YES animate: YES];
253     [fToolbar setVisible:NO];
254     [fOpenProgressField setStringValue: @""];
255     [fWindow setContentView: fOpenView];
256
257     [fDriveDetector run];
258 }
259
260 - (void) openMatrixChanged: (id) sender
261 {
262     [self openEnable: YES];
263     if( [fOpenMatrix selectedRow] )
264     {
265         [self openBrowse: self];
266     }
267 }
268
269 - (void) openBrowse: (id) sender
270 {
271     NSOpenPanel * panel = [NSOpenPanel openPanel];
272     [panel setAllowsMultipleSelection: NO];
273     [panel setCanChooseFiles: YES];
274     [panel setCanChooseDirectories: YES ];
275     [panel beginSheetForDirectory: nil file: nil types: nil
276         modalForWindow: fWindow modalDelegate: self
277         didEndSelector: @selector( openBrowseDidEnd:returnCode:contextInfo: )
278         contextInfo: nil];                                                      
279 }
280
281 - (void) openGo: (id) sender
282 {
283     [self openEnable: NO];
284     [fOpenIndicator setIndeterminate: YES];
285     [fOpenIndicator startAnimation: nil];
286     [fOpenProgressField setStringValue: NSLocalizedString( @"Opening...", @"Opening...") ];
287     [fDriveDetector stop];
288
289     if( [fOpenMatrix selectedRow] )
290     {
291         hb_scan( fHandle, [fOpenFolderString UTF8String], 0 );
292     }
293     else
294     {
295         hb_scan( fHandle, [[fDrives objectForKey: [fOpenPopUp
296                  titleOfSelectedItem]] UTF8String], 0 );
297     }
298 }
299
300 - (void) selectFolderSheetShow: (id) sender
301 {
302     NSOpenPanel * panel = [NSOpenPanel openPanel];
303
304     [panel setPrompt: NSLocalizedString(@"Select", @"Convert -> Save panel prompt")];
305     [panel setAllowsMultipleSelection: NO];
306     [panel setCanChooseFiles: NO];
307     [panel setCanChooseDirectories: YES];
308     [panel setCanCreateDirectories: YES];
309
310     [panel beginSheetForDirectory: nil file: nil types: nil
311         modalForWindow: fWindow modalDelegate: self didEndSelector:
312         @selector(selectFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
313 }
314
315 - (void) selectFolderSheetClosed: (NSOpenPanel *) sheet returnCode: (int)
316     returnCode contextInfo: (void *) contextInfo
317 {
318     if( returnCode != NSOKButton )
319         return;
320
321     if( fConvertFolderString )
322         [fConvertFolderString release];
323     fConvertFolderString = [[[sheet filenames] objectAtIndex: 0] retain];
324     [[fConvertFolderPopUp itemAtIndex: 0] setTitle: [fConvertFolderString lastPathComponent]];
325     [fConvertFolderPopUp selectItemAtIndex:0];
326     
327     NSMenuItem * item = [fConvertFolderPopUp itemAtIndex: 0];
328     [item setImage: [self updatePopUpIcon:fConvertFolderString]];
329     
330 }
331
332 - (void) convertGo: (id) sender
333 {
334     int i, j;
335     Preset * currentPreset = [[[fDevice devicesList] objectAtIndex:[fConvertFormatPopUp indexOfSelectedItem]] firstPreset];
336
337     for( i = 0; i < hb_list_count( fList ); i++ )
338     {
339         if( ![[fConvertCheckArray objectAtIndex: i] boolValue] )
340             continue;
341
342         hb_title_t * title = hb_list_item( fList, i );
343         hb_job_t   * job   = title->job;
344
345                 int maxwidth = [currentPreset maxWidth];
346         int maxheight = [currentPreset maxHeight];
347         int pixels = maxwidth * maxheight;
348                 int aspect = title->aspect;
349         
350                 if( [fConvertAspectPopUp indexOfSelectedItem] == 1 )
351                 {
352             aspect = 4 * HB_ASPECT_BASE / 3;
353                 }
354         else if ( [fConvertAspectPopUp indexOfSelectedItem] == 2 )
355         {
356             aspect = 16 * HB_ASPECT_BASE / 9;
357         }
358
359                 job->vbitrate = [currentPreset videoBitRate];
360                 
361         if( [fConvertMaxWidthPopUp indexOfSelectedItem] == 2 )
362                 {
363             maxwidth = 480;
364                         job->vbitrate /= 1.5;
365         }
366         else if ( [fConvertMaxWidthPopUp indexOfSelectedItem] == 3 )
367         {
368             maxwidth = 320;
369                         job->vbitrate /= 2;
370         }
371         
372         if ( [fConvertAspectPopUp indexOfSelectedItem] > 0 )
373         {
374             do
375             {
376                 hb_set_size( job, aspect, pixels );
377                 pixels -= 10;
378             } while(job->width > maxwidth || job->height > maxheight);
379         }
380         else
381         {
382             /* Reset job->crop values */
383             memcpy( job->crop, job->title->crop, 4 * sizeof( int ) );
384             job->width = maxwidth;
385             hb_fix_aspect( job, HB_KEEP_WIDTH );
386         }
387                 
388         job->mux        = [currentPreset muxer];
389         job->vcodec     = [currentPreset videoCodec];
390         job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
391         strcpy(job->x264opts, [[currentPreset videoCodecOptions] UTF8String]);
392         job->chapter_markers = 1;
393         job->vquality = -1.0;
394
395         const char * lang;
396
397         /* Audio selection */
398         hb_audio_t * audio;
399         lang = [[fConvertAudioPopUp titleOfSelectedItem] UTF8String];
400         job->audios[0] = -1;
401         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
402         {
403             /* Choose the first track that matches the language */
404             audio = hb_list_item( title->list_audio, j );
405             if( !strcmp( lang, audio->lang_simple ) )
406             {
407                 job->audios[0] = j;
408                 break;
409             }
410         }
411         if( job->audios[0] == -1 )
412         {
413             /* If the language isn't available in this title, choose
414                the first track */
415             job->audios[0] = 0;
416         }
417         job->audios[1] = -1;
418
419         job->audio_mixdowns[0] = HB_AMIXDOWN_DOLBYPLII;
420         
421         /* Subtitle selection */
422         hb_subtitle_t * subtitle;
423         lang = [[fConvertSubtitlePopUp titleOfSelectedItem] UTF8String];
424         job->subtitle = -1;
425         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
426         {
427             /* Choose the first track that matches the language */
428             subtitle = hb_list_item( title->list_subtitle, j );
429             if( !strcmp( lang, subtitle->lang ) )
430             {
431                 job->subtitle = j;
432                 break;
433             }
434         }
435         
436         job->file = strdup( [[NSString stringWithFormat:                 
437                 @"%@/%s - Title %d.m4v", fConvertFolderString,      
438                 title->name, title->index] UTF8String] );
439         hb_add( fHandle, job );
440     }
441
442     hb_start( fHandle );
443
444     [self convertEnable: NO];
445 }
446
447 - (void) convertCancel: (id) sender
448 {
449     hb_stop( fHandle );
450     [self convertEnable: YES];
451 }
452
453 @end
454
455 /***********************************************************************
456  * Private methods
457  **********************************************************************/
458
459 @implementation ExpressController (Private)
460
461 - (void) openUpdateDrives: (NSDictionary *) drives
462 {
463     if( fDrives )
464     {
465         [fDrives release];
466     }
467     fDrives = [[NSDictionary alloc] initWithDictionary: drives];
468
469     NSString * device;
470     NSEnumerator * enumerator = [fDrives keyEnumerator];
471     [fOpenPopUp removeAllItems];
472     while( ( device = [enumerator nextObject] ) )
473     {
474         [fOpenPopUp addItemWithTitle: device];
475     }
476
477     if( ![fOpenPopUp numberOfItems] )
478     {
479         [fOpenPopUp addItemWithTitle: INSERT_STRING];
480     }
481     [fOpenPopUp selectItemAtIndex: 0];
482     if( [fOpenMatrix isEnabled] )
483     {
484         [self openEnable: YES];
485     }
486 }
487
488 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
489     returnCode contextInfo: (void *) contextInfo
490 {
491     if( returnCode != NSOKButton )
492         return;
493
494     if( fOpenFolderString )
495         [fOpenFolderString release];
496     fOpenFolderString = [[[sheet filenames] objectAtIndex: 0] retain];
497     [fOpenFolderField setStringValue: [fOpenFolderString lastPathComponent]];
498     [self openGo: self];
499 }
500
501 - (BOOL)validateToolbarItem: (NSToolbarItem *) toolbarItem
502 {
503     NSString * ident = [toolbarItem itemIdentifier];
504     
505     if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorking isEqualToString:[fCore state]])
506     {
507         [toolbarItem setAction: @selector(convertCancel:)];
508         [toolbarItem setLabel:NSLocalizedString(@"Cancel", @"Cancel")];
509         return YES;
510     }
511     else if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorkDone isEqualToString:[fCore state]])
512     {
513         [toolbarItem setAction: @selector(convertGo:)];
514         [toolbarItem setLabel:NSLocalizedString(@"Convert", @"Convert")];
515         return YES;
516     }
517     else if ([ident isEqualToString: TOOLBAR_OPEN] && [HBStateWorking isEqualToString:[fCore state]])
518     {
519         return NO;
520     }
521     
522     return YES;
523 }
524
525 - (void) openEnable: (BOOL) b
526 {
527     [fOpenMatrix       setEnabled: b];
528     [fOpenPopUp        setEnabled: b];
529     [fOpenFolderField  setEnabled: b];
530     [fOpenBrowseButton setEnabled: b];
531     [fOpenGoButton     setEnabled: b]; 
532
533     if( b )
534     {
535         if( [fOpenMatrix selectedRow] )
536         {
537             [fOpenPopUp setEnabled: NO];
538         }
539         else
540         {
541             [fOpenFolderField  setEnabled: NO];
542             [fOpenBrowseButton setEnabled: NO];
543             if( [[fOpenPopUp titleOfSelectedItem]
544                     isEqualToString: INSERT_STRING] )
545             {
546                 [fOpenGoButton setEnabled: NO];
547             }
548         }
549     }
550 }
551
552 - (void) scanningSource: (NSNotification *) n
553 {
554     [fOpenIndicator setIndeterminate: NO];
555     [fOpenIndicator setDoubleValue: 100.0 *
556             ( (float) p.scanning.title_cur - 0.5 ) / p.scanning.title_count];
557     [fOpenProgressField setStringValue: [NSString
558             stringWithFormat: @"Scanning title %d of %d...",
559             p.scanning.title_cur, p.scanning.title_count]];
560 }
561
562 - (void) scanDone: (NSNotification *) n
563 {    
564     [fOpenIndicator setIndeterminate: NO];
565     [fOpenIndicator setDoubleValue: 0.0];
566    
567      [self openEnable: YES];
568
569     if( hb_list_count( fList ) )
570     {
571         [self convertShow];
572     }
573     else
574     {
575         [fDriveDetector run];
576         [fOpenProgressField setStringValue: NSLocalizedString(@"No Title Found...",@"No Title Found...")];
577     }
578 }
579
580 - (void) convertShow
581 {
582     int i, j;
583
584     fConvertCheckArray = [[NSMutableArray alloc] initWithCapacity:
585         hb_list_count( fList )];
586     [fConvertAudioPopUp removeAllItems];
587     [fConvertSubtitlePopUp removeAllItems];
588     [fConvertSubtitlePopUp addItemWithTitle: @"None"];
589     for( i = 0; i < hb_list_count( fList ); i++ )
590     {
591         /* Default is to convert titles longer than 30 minutes. */
592         hb_title_t * title = hb_list_item( fList, i );
593         [fConvertCheckArray addObject: [NSNumber numberWithBool:
594             ( 60 * title->hours + title->minutes > 30 )]];
595
596         /* Update audio popup */
597         hb_audio_t * audio;
598         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
599         {
600             audio = hb_list_item( title->list_audio, j );
601             [fConvertAudioPopUp addItemWithTitle:
602                 [NSString stringWithUTF8String: audio->lang_simple]];
603         }
604                 [fConvertAudioPopUp selectItemWithTitle: @"English"];
605         
606         if ( [fConvertAudioPopUp selectedItem] == nil )
607             [fConvertAudioPopUp selectItemAtIndex:0];
608
609         /* Update subtitle popup */
610         hb_subtitle_t * subtitle;
611         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
612         {
613             subtitle = hb_list_item( title->list_subtitle, j );
614             [fConvertSubtitlePopUp addItemWithTitle:
615                 [NSString stringWithUTF8String: subtitle->lang]];
616         }
617     }
618     [fConvertTableView reloadData];
619     
620     NSEnumerator * enumerator;
621     Device * device;
622     enumerator = [[fDevice devicesList] objectEnumerator];
623     
624     while( ( device = [enumerator nextObject] ) )
625         [fConvertFormatPopUp addItemWithTitle:[device name]];
626
627     NSRect frame  = [fWindow frame];
628     float  offset = ( [fConvertView frame].size.height -
629                     [fOpenView frame].size.height ) * [fWindow userSpaceScaleFactor];;
630     frame.origin.y    -= offset;
631     frame.size.height += offset;
632     [fWindow setContentView: fEmptyView];
633     [fWindow setFrame: frame display: YES animate: YES];
634     [fToolbar setVisible:YES];
635     [fWindow setContentView: fConvertView];
636
637     NSMenuItem * item = [fConvertFolderPopUp itemAtIndex: 0];
638     [item setTitle: [fConvertFolderString lastPathComponent]];
639     [item setImage: [self updatePopUpIcon:fConvertFolderString]];
640     
641     [self convertEnable: YES];
642 }
643
644 - (void) convertEnable: (BOOL) b
645 {
646     [fConvertTableView setEnabled: b];
647     [fConvertFolderPopUp setEnabled: b];
648     [fConvertFormatPopUp setEnabled: b];
649     [fConvertAspectPopUp setEnabled: b];
650     [fConvertMaxWidthPopUp setEnabled: b];
651     [fConvertAudioPopUp setEnabled: b];
652     [fConvertSubtitlePopUp setEnabled: b];
653 }
654
655 /***********************************************************************
656 * UpdateDockIcon
657 ***********************************************************************
658 * Shows a progression bar on the dock icon, filled according to
659 * 'progress' (0.0 <= progress <= 1.0).
660 * Called with progress < 0.0 or progress > 1.0, restores the original
661 * icon.
662 **********************************************************************/
663 - (void) UpdateDockIcon: (float) progress
664 {
665     NSImage * icon;
666     NSData * tiff;
667     NSBitmapImageRep * bmp;
668     uint32_t * pen;
669     uint32_t black = htonl( 0x000000FF );
670     uint32_t red   = htonl( 0xFF0000FF );
671     uint32_t white = htonl( 0xFFFFFFFF );
672     int row_start, row_end;
673     int i, j;
674         
675     /* Get application original icon */
676     icon = [NSImage imageNamed: @"NSApplicationIcon"];
677         
678     if( progress < 0.0 || progress > 1.0 )
679     {
680         [NSApp setApplicationIconImage: icon];
681         return;
682     }
683         
684     /* Get it in a raw bitmap form */
685     tiff = [icon TIFFRepresentationUsingCompression:
686                                            NSTIFFCompressionNone factor: 1.0];
687     bmp = [NSBitmapImageRep imageRepWithData: tiff];
688     
689     /* Draw the progression bar */
690     /* It's pretty simple (ugly?) now, but I'm no designer */
691         
692     row_start = 3 * (int) [bmp size].height / 4;
693     row_end   = 7 * (int) [bmp size].height / 8;
694         
695     for( i = row_start; i < row_start + 2; i++ )
696     {
697         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
698         for( j = 0; j < (int) [bmp size].width; j++ )
699         {
700             pen[j] = black;
701         }
702     }
703     for( i = row_start + 2; i < row_end - 2; i++ )
704     {
705         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
706         pen[0] = black;
707         pen[1] = black;
708         for( j = 2; j < (int) [bmp size].width - 2; j++ )
709         {
710             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
711             {
712                 pen[j] = red;
713             }
714             else
715             {
716                 pen[j] = white;
717             }
718         }
719         pen[j]   = black;
720         pen[j+1] = black;
721     }
722     for( i = row_end - 2; i < row_end; i++ )
723     {
724         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
725         for( j = 0; j < (int) [bmp size].width; j++ )
726         {
727             pen[j] = black;
728         }
729     }
730         
731     /* Now update the dock icon */
732     tiff = [bmp TIFFRepresentationUsingCompression:
733                                           NSTIFFCompressionNone factor: 1.0];
734     icon = [[NSImage alloc] initWithData: tiff];
735     [NSApp setApplicationIconImage: icon];
736     [icon release];
737 }
738
739 - (id) updatePopUpIcon: (id) value
740 {
741     if (!value)
742         return nil;
743     
744     NSImage * icon;
745     
746     icon = [[NSWorkspace sharedWorkspace] iconForFile: value];
747     
748     [icon setScalesWhenResized: YES];
749     [icon setSize: NSMakeSize(16.0 , 16.0)];
750     
751     return icon;
752 }
753
754 - (void) working: (NSNotification *) n
755 {
756    float progress_total = ( p.working.progress + p.working.job_cur - 1 ) / p.working.job_count;
757     NSMutableString * string = [NSMutableString stringWithFormat: @"Converting: %.1f %%", 100.0 * progress_total];
758     
759     if( p.working.seconds > -1 )
760     {
761         [string appendFormat: @" (%.1f fps, ", p.working.rate_avg];
762         if( p.working.hours > 0 )
763         {
764         [string appendFormat: @"%d hour%s %d min%s",
765             p.working.hours, p.working.hours == 1 ? "" : "s",
766             p.working.minutes, p.working.minutes == 1 ? "" : "s"];
767         }
768         else if( p.working.minutes > 0 )
769         {
770             [string appendFormat: @"%d min%s %d sec%s",
771                 p.working.minutes, p.working.minutes == 1 ? "" : "s",
772                 p.working.seconds, p.working.seconds == 1 ? "" : "s"];
773         }
774         else
775         {
776             [string appendFormat: @"%d second%s",
777                 p.working.seconds, p.working.seconds == 1 ? "" : "s"];
778         }
779             [string appendString: @" left)"];
780     }
781     
782     [fConvertInfoString setStringValue: string];
783     [fConvertIndicator setIndeterminate: NO];
784     [fConvertIndicator setDoubleValue: 100.0 * progress_total];
785     [self UpdateDockIcon: progress_total];
786 }
787
788 - (void) muxing: (NSNotification *) n
789 {
790     [fConvertInfoString setStringValue: NSLocalizedString(@"Muxing...",@"Muxing...")];
791     [fConvertIndicator setIndeterminate: YES];
792     [fConvertIndicator startAnimation: nil];
793     [self UpdateDockIcon: 1.0];
794 }
795
796 - (void) workDone: (NSNotification *) n
797 {    
798     [fConvertIndicator setIndeterminate: NO];
799     [fConvertIndicator setDoubleValue: 0.0];
800     [self UpdateDockIcon: -1.0];
801     [self convertEnable: YES];
802         
803     [fConvertInfoString setStringValue: NSLocalizedString(@"Done.",@"Done.")];
804
805     [fCore removeAllJobs];
806 }
807
808 @end