OSDN Git Service

393ab9df80f6d6bccb4cccc7caf99ec161bd9a99
[handbrake-jp/handbrake-jp-git.git] / macosx / InstantHandBrake / ExpressController.m
1 #import "ExpressController.h"
2
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"
8
9 #define p fState->param
10
11 @interface ExpressController (Private)
12
13 - (void) openUpdateDrives: (NSDictionary *) drives;
14 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
15     returnCode contextInfo: (void *) contextInfo;
16 - (void) openEnable: (BOOL) b;
17
18 - (void) convertShow;
19 - (void) convertEnable: (BOOL) b;
20
21 @end
22
23 @implementation ExpressController
24
25 /***********************************************************************
26  * Application delegate methods
27  **********************************************************************/
28 - (void) awakeFromNib
29 {
30     NSEnumerator * enumerator;
31     
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];
39         
40     /* Show the "Open DVD" interface */
41     fDriveDetector = [[DriveDetector alloc] initWithCallback: self
42         selector: @selector( openUpdateDrives: )];
43     [fDriveDetector run];
44     [self openEnable: YES];
45     [fWindow setContentSize: [fOpenView frame].size];
46     [fWindow setContentView: fOpenView];
47     [fWindow center];
48     [fWindow makeKeyAndOrderFront: nil];
49
50     /* NSTableView initializations */
51      NSButtonCell * buttonCell;
52      NSTableColumn * tableColumn;
53      enumerator = [[fConvertTableView tableColumns] objectEnumerator];
54      while( ( tableColumn = [enumerator nextObject] ) )
55      {
56          [tableColumn setEditable: NO];
57      }
58      tableColumn = [fConvertTableView tableColumnWithIdentifier: @"Check"];
59      buttonCell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
60     [buttonCell setEditable: YES];
61     [buttonCell setButtonType: NSSwitchButton];
62     [tableColumn setDataCell: buttonCell];
63
64     /* Preferences */
65     fConvertFolderString = [NSString stringWithFormat:@"%@/Desktop", NSHomeDirectory()];
66     [fConvertFolderString retain];
67 }
68
69 - (void) applicationWillFinishLaunching: (NSNotification *) n
70 {
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 );
76     
77     [[NSNotificationCenter defaultCenter] addObserver:self
78     selector:@selector(scanningSource:)
79     name:@"HBCoreScanningNotification" object:nil];
80     
81     [[NSNotificationCenter defaultCenter] addObserver:self
82     selector:@selector(scanDone:)
83     name:@"HBCoreScanDoneNotification" object:nil];
84     
85     [[NSNotificationCenter defaultCenter] addObserver:self
86     selector:@selector(muxing:)
87     name:@"HBCoreMuxingNotification" object:nil];
88     
89     [[NSNotificationCenter defaultCenter] addObserver:self
90     selector:@selector(working:)
91     name:@"HBCoreWorkingNotification" object:nil];
92     
93     [[NSNotificationCenter defaultCenter] addObserver:self
94     selector:@selector(workDone:)
95     name:@"HBCoreWorkDoneNotification" object:nil];
96 }
97
98 - (void) applicationWillTerminate: (NSNotification *) n
99 {
100     [fCore close];
101 }
102
103 - (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident
104                     willBeInsertedIntoToolbar: (BOOL) flag
105 {
106     NSToolbarItem * item;
107     item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
108
109     if ([ident isEqualToString: TOOLBAR_START])
110     {
111         [item setLabel: NSLocalizedString(@"Start", "Start")];
112         [item setImage: [NSImage imageNamed: @"Play"]];
113         [item setTarget: self];
114         [item setAction: @selector(convertGo:)];
115     }
116     else if ([ident isEqualToString: TOOLBAR_PAUSE])
117     {
118         [item setLabel: NSLocalizedString(@"Pause", "Pause")];
119         [item setImage: [NSImage imageNamed: @"Pause"]];
120         [item setTarget: self];
121         [item setAction: @selector(pauseGo:)];
122     }
123     else if ([ident isEqualToString: TOOLBAR_OPEN])
124     {
125         [item setLabel: NSLocalizedString(@"Open", "Open")];
126         [item setImage: [NSImage imageNamed: @"pref-audio"]];
127         [item setTarget: self];
128         [item setAction: @selector(openShow:)];
129     }
130     else if ([ident isEqualToString: TOOLBAR_ADVANCED])
131     {
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];
137     }
138     else
139     {
140         [item release];
141         return nil;
142     }
143
144     return item;
145 }
146
147 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
148 {
149     return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
150                                         TOOLBAR_OPEN, NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_ADVANCED, nil];
151 }
152
153 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
154 {
155     return [NSArray arrayWithObjects: TOOLBAR_START, TOOLBAR_PAUSE,
156                                         TOOLBAR_OPEN, TOOLBAR_ADVANCED, nil];
157 }
158
159 /***********************************************************************
160  * Tableview datasource methods
161  **********************************************************************/
162 - (int) numberOfRowsInTableView: (NSTableView *) t
163 {
164     if( !fHandle )
165         return 0;
166
167     return hb_list_count( fList );
168 }
169
170 - (id) tableView:(NSTableView *) t objectValueForTableColumn:
171     (NSTableColumn *) col row: (int) row
172 {
173     if( [[col identifier] isEqualToString: @"Check"] )
174     {
175         return [fConvertCheckArray objectAtIndex: row];
176     }
177     else
178     {
179         hb_title_t * title = hb_list_item( fList, row );
180         if( [[col identifier] isEqualToString: @"Title"] )
181         {
182             return [@"Title " stringByAppendingFormat: @"%d",
183                     title->index];
184         }
185         else if( [[col identifier] isEqualToString: @"Duration"] )
186         {
187             if( title->hours > 0 )
188             {
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": ""];
193             }
194             else if( title->minutes > 0 )
195             {
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": ""];
200             }
201             else
202             {
203                 return [NSString stringWithFormat: @"%d seconds",
204                         title->seconds];
205             }
206         }
207     }
208     return nil;
209 }
210
211 - (void) tableView: (NSTableView *) t setObjectValue: (id) object
212     forTableColumn: (NSTableColumn *) col row: (int) row
213 {
214     if( [[col identifier] isEqualToString: @"Check"] )
215     {
216         [fConvertCheckArray replaceObjectAtIndex: row withObject: object];
217     }
218 }
219
220 /***********************************************************************
221  * User events methods
222  **********************************************************************/
223 - (void) openShow: (id) sender
224 {
225     NSRect frame  = [fWindow frame];
226     float  offset = [fConvertView frame].size.height -
227                     [fOpenView frame].size.height;
228
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];
235
236     [fDriveDetector run];
237 }
238
239 - (void) openMatrixChanged: (id) sender
240 {
241     [self openEnable: YES];
242     if( [fOpenMatrix selectedRow] )
243     {
244         [self openBrowse: self];
245     }
246 }
247
248 - (void) openBrowse: (id) sender
249 {
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: )
257         contextInfo: nil];                                                      
258 }
259
260 - (void) openGo: (id) sender
261 {
262     [self openEnable: NO];
263     [fOpenIndicator setIndeterminate: YES];
264     [fOpenIndicator startAnimation: nil];
265     [fOpenProgressField setStringValue: @"Opening..."];
266     [fDriveDetector stop];
267
268     if( [fOpenMatrix selectedRow] )
269     {
270         hb_scan( fHandle, [fOpenFolderString UTF8String], 0 );
271     }
272     else
273     {
274         hb_scan( fHandle, [[fDrives objectForKey: [fOpenPopUp
275                  titleOfSelectedItem]] UTF8String], 0 );
276     }
277 }
278
279 - (void) convertGo: (id) sender
280 {
281     int i, j;
282
283     for( i = 0; i < hb_list_count( fList ); i++ )
284     {
285         if( ![[fConvertCheckArray objectAtIndex: i] boolValue] )
286             continue;
287
288         hb_title_t * title = hb_list_item( fList, i );
289         hb_job_t   * job   = title->job;
290
291         int pixels = 307200;
292                 int aspect = title->aspect;
293                 if( [fConvertAspectPopUp indexOfSelectedItem] == 1)
294                 {
295             aspect = 4 * HB_ASPECT_BASE / 3;
296                 }
297
298                 int maxwidth = 640;
299                 job->vbitrate = 1000;
300                 if( [fConvertMaxWidthPopUp indexOfSelectedItem] == 1)
301                 {
302             maxwidth = 320;
303                         job->vbitrate = 500;
304                 }
305                 //job->deinterlace = 1;
306                 
307                 do
308                 {
309                         hb_set_size( job, aspect, pixels );
310                         pixels -= 10;
311                 } while(job->width > maxwidth);
312                 
313         if( [fConvertFormatPopUp indexOfSelectedItem] == 0 )
314         {
315             /* iPod / H.264 */
316             job->mux      = HB_MUX_IPOD;
317             job->vcodec   = HB_VCODEC_X264;
318                         job->h264_level = 30;
319         }
320         else if( [fConvertFormatPopUp indexOfSelectedItem] == 1 )
321         {
322             /* iPod / MPEG-4 */
323             job->mux      = HB_MUX_MP4;
324             job->vcodec   = HB_VCODEC_FFMPEG;
325         }
326         else
327         {
328             /* PSP / MPEG-4 */
329             job->mux        = HB_MUX_PSP;
330             job->vrate      = 27000000;
331             job->vrate_base = 900900;   /* 29.97 fps */
332             job->vcodec     = HB_VCODEC_FFMPEG;
333             job->vbitrate   = 600;
334             pixels          = 76800;
335             job->arate      = 24000;
336             job->abitrate   = 96;
337             aspect          = 16 * HB_ASPECT_BASE / 9;
338
339                         if( [fConvertAspectPopUp indexOfSelectedItem] )
340                         {
341                                 aspect = -1;
342                         }
343
344                         hb_set_size( job, aspect, pixels );
345         }
346
347         job->vquality = -1.0;
348
349         const char * lang;
350
351         /* Audio selection */
352         hb_audio_t * audio;
353         lang = [[fConvertAudioPopUp titleOfSelectedItem] UTF8String];
354         job->audios[0] = -1;
355         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
356         {
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 ) )
360             {
361                 job->audios[0] = j;
362                 break;
363             }
364         }
365         if( job->audios[0] == -1 )
366         {
367             /* If the language isn't available in this title, choose
368                the first track */
369             job->audios[0] = 0;
370         }
371         job->audios[1] = -1;
372
373         job->audio_mixdowns[0] = HB_AMIXDOWN_DOLBYPLII;
374         
375         /* Subtitle selection */
376         hb_subtitle_t * subtitle;
377         lang = [[fConvertSubtitlePopUp titleOfSelectedItem] UTF8String];
378         job->subtitle = -1;
379         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
380         {
381             /* Choose the first track that matches the language */
382             subtitle = hb_list_item( title->list_subtitle, j );
383             if( !strcmp( lang, subtitle->lang ) )
384             {
385                 job->subtitle = j;
386                 break;
387             }
388         }
389         
390         job->file = strdup( [[NSString stringWithFormat:                 
391                 @"%@/%s - Title %d.m4v", fConvertFolderString,      
392                 title->name, title->index] UTF8String] );
393         hb_add( fHandle, job );
394     }
395
396     hb_start( fHandle );
397
398     [self convertEnable: NO];
399 }
400
401 - (void) convertCancel: (id) sender
402 {
403     hb_stop( fHandle );
404     [self convertEnable: YES];
405 }
406
407 @end
408
409 /***********************************************************************
410  * Private methods
411  **********************************************************************/
412
413 @implementation ExpressController (Private)
414
415 - (void) openUpdateDrives: (NSDictionary *) drives
416 {
417     if( fDrives )
418     {
419         [fDrives release];
420     }
421     fDrives = [[NSDictionary alloc] initWithDictionary: drives];
422
423     NSString * device;
424     NSEnumerator * enumerator = [fDrives keyEnumerator];
425     [fOpenPopUp removeAllItems];
426     while( ( device = [enumerator nextObject] ) )
427     {
428         [fOpenPopUp addItemWithTitle: device];
429     }
430
431     if( ![fOpenPopUp numberOfItems] )
432     {
433         [fOpenPopUp addItemWithTitle: INSERT_STRING];
434     }
435     [fOpenPopUp selectItemAtIndex: 0];
436     if( [fOpenMatrix isEnabled] )
437     {
438         [self openEnable: YES];
439     }
440 }
441
442 - (void) openBrowseDidEnd: (NSOpenPanel *) sheet returnCode: (int)
443     returnCode contextInfo: (void *) contextInfo
444 {
445     if( returnCode != NSOKButton )
446         return;
447
448     if( fOpenFolderString )
449         [fOpenFolderString release];
450     fOpenFolderString = [[[sheet filenames] objectAtIndex: 0] retain];
451     [fOpenFolderField setStringValue: [fOpenFolderString lastPathComponent]];
452     [self openGo: self];
453 }
454
455 - (BOOL)validateToolbarItem: (NSToolbarItem *) toolbarItem
456 {
457     NSString * ident = [toolbarItem itemIdentifier];
458     
459     if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorking isEqualToString:[fCore state]])
460     {
461         [toolbarItem setAction: @selector(convertCancel:)];
462         [toolbarItem setLabel:NSLocalizedString(@"Cancel", @"Cancel")];
463         return YES;
464     }
465     else if ([ident isEqualToString: TOOLBAR_START] && [HBStateWorkDone isEqualToString:[fCore state]])
466     {
467         [toolbarItem setAction: @selector(convertGo:)];
468         [toolbarItem setLabel:NSLocalizedString(@"Convert", @"Convert")];
469         return YES;
470     }
471     else if ([ident isEqualToString: TOOLBAR_OPEN] && [HBStateWorking isEqualToString:[fCore state]])
472     {
473         return NO;
474     }
475     
476     return YES;
477 }
478
479 - (void) openEnable: (BOOL) b
480 {
481     [fOpenMatrix       setEnabled: b];
482     [fOpenPopUp        setEnabled: b];
483     [fOpenFolderField  setEnabled: b];
484     [fOpenBrowseButton setEnabled: b];
485
486     if( b )
487     {
488         if( [fOpenMatrix selectedRow] )
489         {
490             [fOpenPopUp setEnabled: NO];
491         }
492         else
493         {
494             [fOpenFolderField  setEnabled: NO];
495             [fOpenBrowseButton setEnabled: NO];
496             if( [[fOpenPopUp titleOfSelectedItem]
497                     isEqualToString: INSERT_STRING] )
498             {
499             }
500         }
501     }
502 }
503
504 - (void) scanningSource: (NSNotification *) n
505 {
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]];
512 }
513
514 - (void) scanDone: (NSNotification *) n
515 {    
516     [fOpenIndicator setIndeterminate: NO];
517     [fOpenIndicator setDoubleValue: 0.0];
518    
519      [self openEnable: YES];
520
521     if( hb_list_count( fList ) )
522     {
523         [self convertShow];
524     }
525     else
526     {
527         [fDriveDetector run];
528     }
529 }
530
531 - (void) convertShow
532 {
533     int i, j;
534
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++ )
541     {
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 )]];
546
547         /* Update audio popup */
548         hb_audio_t * audio;
549         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
550         {
551             audio = hb_list_item( title->list_audio, j );
552             [fConvertAudioPopUp addItemWithTitle:
553                 [NSString stringWithUTF8String: audio->lang_simple]];
554         }
555                 [fConvertAudioPopUp selectItemWithTitle: @"English"];
556         
557         if ( [fConvertAudioPopUp selectedItem] == nil )
558             [fConvertAudioPopUp selectItemWithTitle: @"Unknown"];
559
560         /* Update subtitle popup */
561         hb_subtitle_t * subtitle;
562         for( j = 0; j < hb_list_count( title->list_subtitle ); j++ )
563         {
564             subtitle = hb_list_item( title->list_subtitle, j );
565             [fConvertSubtitlePopUp addItemWithTitle:
566                 [NSString stringWithUTF8String: subtitle->lang]];
567         }
568     }
569     [fConvertTableView reloadData];
570
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];
583
584     /* Folder popup */
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:
590         NSMakeSize(16,16)];
591     [image16 lockFocus];
592     [[NSGraphicsContext currentContext]
593         setImageInterpolation: NSImageInterpolationHigh];
594     [image32 drawInRect: NSMakeRect(0,0,16,16)
595         fromRect: NSMakeRect(0,0,32,32) operation: NSCompositeCopy
596         fraction: 1.0];
597     [image16 unlockFocus];                                                      
598     [item setImage: image16];
599     [image16 release];
600
601     [self convertEnable: YES];
602 }
603
604 - (void) convertEnable: (BOOL) b
605 {
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];
614 }
615
616 /***********************************************************************
617 * UpdateDockIcon
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
622 * icon.
623 **********************************************************************/
624 - (void) UpdateDockIcon: (float) progress
625 {
626     NSImage * icon;
627     NSData * tiff;
628     NSBitmapImageRep * bmp;
629     uint32_t * pen;
630     uint32_t black = htonl( 0x000000FF );
631     uint32_t red   = htonl( 0xFF0000FF );
632     uint32_t white = htonl( 0xFFFFFFFF );
633     int row_start, row_end;
634     int i, j;
635         
636     /* Get application original icon */
637     icon = [NSImage imageNamed: @"NSApplicationIcon"];
638         
639     if( progress < 0.0 || progress > 1.0 )
640     {
641         [NSApp setApplicationIconImage: icon];
642         return;
643     }
644         
645     /* Get it in a raw bitmap form */
646     tiff = [icon TIFFRepresentationUsingCompression:
647                                            NSTIFFCompressionNone factor: 1.0];
648     bmp = [NSBitmapImageRep imageRepWithData: tiff];
649     
650     /* Draw the progression bar */
651     /* It's pretty simple (ugly?) now, but I'm no designer */
652         
653     row_start = 3 * (int) [bmp size].height / 4;
654     row_end   = 7 * (int) [bmp size].height / 8;
655         
656     for( i = row_start; i < row_start + 2; i++ )
657     {
658         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
659         for( j = 0; j < (int) [bmp size].width; j++ )
660         {
661             pen[j] = black;
662         }
663     }
664     for( i = row_start + 2; i < row_end - 2; i++ )
665     {
666         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
667         pen[0] = black;
668         pen[1] = black;
669         for( j = 2; j < (int) [bmp size].width - 2; j++ )
670         {
671             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
672             {
673                 pen[j] = red;
674             }
675             else
676             {
677                 pen[j] = white;
678             }
679         }
680         pen[j]   = black;
681         pen[j+1] = black;
682     }
683     for( i = row_end - 2; i < row_end; i++ )
684     {
685         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
686         for( j = 0; j < (int) [bmp size].width; j++ )
687         {
688             pen[j] = black;
689         }
690     }
691         
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];
697     [icon release];
698 }
699
700 - (void) working: (NSNotification *) n
701 {
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 )
707             {
708                 [string appendFormat: @" (%.1f fps, ", p.working.rate_avg];
709                 if( p.working.hours > 0 )
710                 {
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"];
714                 }
715                 else if( p.working.minutes > 0 )
716                 {
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"];
720                 }
721                 else
722                 {
723                     [string appendFormat: @"%d second%s",
724                         p.working.seconds, p.working.seconds == 1 ? "" : "s"];
725                 }
726                 [string appendString: @" left)"];
727             }
728             [fConvertInfoString setStringValue: string];
729             [fConvertIndicator setIndeterminate: NO];
730             [fConvertIndicator setDoubleValue: 100.0 * progress_total];
731             [self UpdateDockIcon: progress_total];
732
733 }
734
735 - (void) muxing: (NSNotification *) n
736 {
737     [fConvertInfoString setStringValue: NSLocalizedString(@"Muxing...",@"Muxing...")];
738     [fConvertIndicator setIndeterminate: YES];
739     [fConvertIndicator startAnimation: nil];
740     [self UpdateDockIcon: 1.0];
741 }
742
743 - (void) workDone: (NSNotification *) n
744 {    
745     [fConvertIndicator setIndeterminate: NO];
746     [fConvertIndicator setDoubleValue: 0.0];
747     [self UpdateDockIcon: -1.0];
748     [self convertEnable: YES];
749         
750     [fConvertInfoString setStringValue: NSLocalizedString(@"Done.",@"Done.")];
751
752     hb_job_t * job;
753     while( ( job = hb_job( fHandle, 0 ) ) )
754     {
755         hb_rem( fHandle, job );
756     }
757 }
758
759 @end