OSDN Git Service

657178c7467b97186fc50af8274e4747752696bb
[handbrake-jp/handbrake-jp-git.git] / macosx / HBQueueController.mm
1 /* HBQueueController
2
3     This file is part of the HandBrake source code.
4     Homepage: <http://handbrake.m0k.org/>.
5     It may be used under the terms of the GNU General Public License. */
6
7 #include "HBQueueController.h"
8
9 /**
10  * Returns the number of jobs groups in the queue.
11  * @param h Handle to hb_handle_t.
12  * @return Number of job groups.
13  */
14 static int hb_group_count(hb_handle_t * h)    
15 {
16     hb_job_t * job;
17     int count = 0;
18     int index = 0;
19     while( ( job = hb_job( h, index++ ) ) )
20     {
21         if (job->sequence_id == 0)
22             count++;
23     }
24     return count;
25 }
26
27 /**
28  * Returns handle to the first job in the i-th group within the job list.
29  * @param h Handle to hb_handle_t.
30  * @param i Index of group.
31  * @returns Handle to hb_job_t of desired job.
32  */
33 static hb_job_t * hb_group(hb_handle_t * h, int i)    
34 {
35     hb_job_t * job;
36     int count = 0;
37     int index = 0;
38     while( ( job = hb_job( h, index++ ) ) )
39     {
40         if (job->sequence_id == 0)
41         {
42             if (count == i)
43                 return job;
44             count++;
45         }
46     }
47     return NULL;
48 }
49
50 /**
51  * Removes a groups of jobs from the job list.
52  * @param h Handle to hb_handle_t.
53  * @param job Handle to the first job in the group.
54  */
55 static void hb_rem_group( hb_handle_t * h, hb_job_t * job )
56 {
57     // Find job in list
58     hb_job_t * j;
59     int index = 0;
60     while( ( j = hb_job( h, index ) ) )
61     {
62         if (j == job)
63         {
64             // Delete this job plus the following ones in the sequence
65             hb_rem( h, job );
66             while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )
67                 hb_rem( h, j );
68             return;
69         }
70         else
71             index++;
72     }
73 }
74
75 /**
76  * Returns handle to the next job after the given job.
77  * @param h Handle to hb_handle_t.
78  * @param job Handle to the a job in the group.
79  * @returns Handle to hb_job_t of desired job or NULL if no such job.
80  */
81 static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job )
82 {
83     hb_job_t * j = NULL;
84     int index = 0;
85     while( ( j = hb_job( h, index++ ) ) )
86     {
87         if (j == job)
88             return hb_job( h, index+1 );
89     }
90     return NULL;
91 }
92
93 #pragma mark -
94
95 // Toolbar identifiers
96 static NSString*    HBQueueToolbar                            = @"HBQueueToolbar";
97 static NSString*    HBStartPauseResumeToolbarIdentifier       = @"HBStartPauseResumeToolbarIdentifier";
98 static NSString*    HBShowDetailToolbarIdentifier             = @"HBShowDetailToolbarIdentifier";
99 static NSString*    HBShowGroupsToolbarIdentifier             = @"HBShowGroupsToolbarIdentifier";
100
101
102 @implementation HBQueueController
103
104 //------------------------------------------------------------------------------------
105 // init
106 //------------------------------------------------------------------------------------
107 - (id)init
108 {
109     if (self = [super init])
110     {
111         // Our defaults
112         [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
113             @"NO",      @"QueueWindowIsOpen",
114             @"NO",      @"QueueShowsDetail",
115             @"YES",     @"QueueShowsJobsAsGroups",
116             nil]];
117
118         fShowsDetail = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsDetail"];
119         fShowsJobsAsGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
120
121     }
122     return self; 
123 }
124
125 //------------------------------------------------------------------------------------
126 // dealloc
127 //------------------------------------------------------------------------------------
128 - (void)dealloc
129 {
130     [fAnimation release];
131     
132     // clear the delegate so that windowWillClose is not attempted
133     if ([fQueueWindow delegate] == self)
134         [fQueueWindow setDelegate:nil];
135     
136     [super dealloc];
137 }
138
139 //------------------------------------------------------------------------------------
140 // Receive HB handle
141 //------------------------------------------------------------------------------------
142 - (void)setHandle: (hb_handle_t *)handle
143 {
144     fHandle = handle;
145 }
146
147 //------------------------------------------------------------------------------------
148 // Displays and brings the queue window to the front
149 //------------------------------------------------------------------------------------
150 - (IBAction) showQueueWindow: (id)sender
151 {
152     if (!fQueueWindow)
153     {
154         BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow;
155         NSAssert(loadSucceeded, @"Could not open Queue nib file");
156     }
157
158     [self updateQueueUI];
159     [self updateCurrentJobUI];
160
161     [fQueueWindow makeKeyAndOrderFront: self];
162
163     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
164 }
165 //------------------------------------------------------------------------------------
166 // Show or hide the current job pane (fCurrentJobPane).
167 //------------------------------------------------------------------------------------
168 - (void) showCurrentJobPane: (BOOL)showPane
169 {
170     if (showPane != fCurrentJobHidden)
171         return;
172     
173     // Things to keep in mind:
174     // - When the current job pane is shown, it occupies the upper portion of the
175     //   window with the queue occupying the bottom portion of the window.
176     // - When the current job pane is hidden, it slides up and out of view.
177     //   NSView setHidden is NOT used. The queue pane is resized to occupy the full
178     //   window.
179     
180     NSRect windowFrame = [[fCurrentJobPane superview] frame];
181     NSRect queueFrame, jobFrame;
182     if (showPane)
183         NSDivideRect(windowFrame, &jobFrame, &queueFrame, NSHeight([fCurrentJobPane frame]), NSMaxYEdge);
184     else
185     {
186         queueFrame = windowFrame;
187         jobFrame = [fCurrentJobPane frame];
188         jobFrame.origin.y = NSHeight(windowFrame);
189     }
190     
191     // Move fCurrentJobPane
192     NSDictionary * dict1 = [NSDictionary dictionaryWithObjectsAndKeys:
193         fCurrentJobPane, NSViewAnimationTargetKey,
194         [NSValue valueWithRect:jobFrame], NSViewAnimationEndFrameKey,
195         nil];
196
197     // Resize fQueuePane
198     NSDictionary * dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
199         fQueuePane, NSViewAnimationTargetKey,
200         [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey,
201         nil];
202
203     if (!fAnimation)
204         fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil];
205
206     [fAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]];
207     [fAnimation setDuration:0.25];
208     [fAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation
209     [fAnimation startAnimation];
210     fCurrentJobHidden = !showPane;
211 }
212
213 //------------------------------------------------------------------------------------
214 // Enables or disables the display of detail information for each job.
215 //------------------------------------------------------------------------------------
216 - (void)setShowsDetail: (BOOL)showsDetail
217 {
218     fShowsDetail = showsDetail;
219     
220     [[NSUserDefaults standardUserDefaults] setBool:showsDetail forKey:@"QueueShowsDetail"];
221     [[NSUserDefaults standardUserDefaults] synchronize];
222
223     // clumsy - have to update UI
224     [fDetailCheckbox setState:showsDetail ? NSOnState : NSOffState];
225     
226     [fTaskView setRowHeight:showsDetail ? 98.0 : 17.0];
227     if ([fTaskView selectedRow] != -1)
228         [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
229 }
230
231 //------------------------------------------------------------------------------------
232 // Enables or disables the grouping of job passes into one item in the UI.
233 //------------------------------------------------------------------------------------
234 - (void)setShowsJobsAsGroups: (BOOL)showsGroups
235 {
236     fShowsJobsAsGroups = showsGroups;
237     
238     [[NSUserDefaults standardUserDefaults] setBool:showsGroups forKey:@"QueueShowsJobsAsGroups"];
239     [[NSUserDefaults standardUserDefaults] synchronize];
240
241     // clumsy - have to update UI
242     [fJobGroupsCheckbox setState:showsGroups ? NSOnState : NSOffState];
243     
244     [self updateQueueUI];
245     if ([fTaskView selectedRow] != -1)
246         [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
247 }
248
249 //------------------------------------------------------------------------------------
250 // Generates a multi-line text string that includes the job name on the first line
251 // followed by details of the job on subsequent lines. If the text is to be drawn as
252 // part of a highlighted cell, set isHighlighted to true. The returned string may
253 // contain multiple fonts and paragraph formating.
254 //------------------------------------------------------------------------------------
255 - (NSAttributedString *)attributedDescriptionForJob: (hb_job_t *)job
256                                          withDetail: (BOOL)detail
257                                    withHighlighting: (BOOL)highlighted
258 {
259     NSMutableAttributedString * finalString;   // the return value
260     NSAttributedString* anAttributedString;    // a temp string for building up attributed substrings
261     NSMutableString* aMutableString;           // a temp string for non-attributed substrings
262     hb_title_t * title = job->title;
263     
264     NSMutableParagraphStyle *ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
265     [ps setLineBreakMode:NSLineBreakByClipping];
266
267     static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
268                 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
269                 [NSColor darkGrayColor], NSForegroundColorAttributeName,
270                 ps, NSParagraphStyleAttributeName,
271                 nil] retain];
272     static NSDictionary* detailHighlightedAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
273                 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
274                 [NSColor whiteColor], NSForegroundColorAttributeName,
275                 ps, NSParagraphStyleAttributeName,
276                 nil] retain];
277     static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
278                 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
279                 ps, NSParagraphStyleAttributeName,
280                 nil] retain];
281
282     finalString = [[[NSMutableAttributedString alloc] init] autorelease];
283
284     // Title, in bold
285     // Show the name of the source Note: use title->name instead of title->dvd since
286     // name is just the chosen folder, instead of dvd which is the full path
287     anAttributedString = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:title->name] attributes:titleAttribute] autorelease];
288     [finalString appendAttributedString:anAttributedString];
289
290     // Other info in plain
291     aMutableString = [NSMutableString stringWithCapacity:200];
292     
293     BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
294
295     // The subtitle scan doesn't contain all the stuff we need (like x264opts).
296     // So grab the next job in the group for display purposes.
297     if (jobGroups && job->pass == -1)
298     {
299         // When job is the one currently being processed, then the next in its group
300         // is the the first job in the queue.
301         hb_job_t * nextjob;
302         if (job == hb_current_job(fHandle))
303             nextjob = hb_job(fHandle, 0);
304         else
305             nextjob = hb_next_job(fHandle, job);
306         if (nextjob)    // Overly cautious in case there is no next job!
307             job = nextjob;
308     }
309
310     NSString * chapterString = (job->chapter_start == job->chapter_end) ?
311             [NSString stringWithFormat:@"Chapter %d", job->chapter_start] :
312             [NSString stringWithFormat:@"Chapters %d through %d", job->chapter_start, job->chapter_end];
313     
314     // Scan pass
315     if (job->pass == -1)
316     {
317         [aMutableString appendString:[NSString stringWithFormat:
318                 @"  (Title %d, %@, Subtitle Scan)", title->index, chapterString]];
319     }
320
321     // Normal pass
322     else
323     {
324         if (jobGroups)
325             [aMutableString appendString:[NSString stringWithFormat:
326                     @"  (Title %d, %@, %d-Pass)",
327                     title->index, chapterString, MIN( 2, job->pass + 1 )]];
328         else
329             [aMutableString appendString:[NSString stringWithFormat:
330                     @"  (Title %d, %@, Pass %d of %d)",
331                     title->index, chapterString, MAX( 1, job->pass ), MIN( 2, job->pass + 1 )]];
332         
333         if (detail)
334         {
335             NSString * jobFormat;
336             NSString * jobPictureDetail;
337             NSString * jobVideoDetail;
338             NSString * jobVideoCodec;
339             NSString * jobVideoQuality;
340             NSString * jobAudioDetail;
341             NSString * jobAudioCodec;
342
343             /* Muxer settings (File Format in the gui) */
344             if (job->mux == 65536 || job->mux == 131072 || job->mux == 1048576)
345                 jobFormat = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD
346             else if (job->mux == 262144)
347                 jobFormat = @"AVI"; // HB_MUX_AVI
348             else if (job->mux == 524288)
349                 jobFormat = @"OGM"; // HB_MUX_OGM
350             else if (job->mux == 2097152)
351                 jobFormat = @"MKV"; // HB_MUX_MKV
352             else
353                 jobFormat = @"unknown";
354             
355             // 2097152
356             /* Video Codec settings (Encoder in the gui) */
357             if (job->vcodec == 1)
358                 jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG
359             else if (job->vcodec == 2)
360                 jobVideoCodec = @"XviD"; // HB_VCODEC_XVID
361             else if (job->vcodec == 4)
362             {
363                 /* Deterimine for sure how we are now setting iPod uuid atom */
364                 if (job->h264_level) // We are encoding for iPod
365                     jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264    
366                 else
367                     jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264
368             }
369             else
370                 jobVideoCodec = @"unknown";
371             
372             /* Audio Codecs (Second half of Codecs in the gui) */
373             if (job->acodec == 256)
374                 jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC
375             else if (job->acodec == 512)
376                 jobAudioCodec = @"MP3"; // HB_ACODEC_LAME
377             else if (job->acodec == 1024)
378                 jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS
379             else if (job->acodec == 2048)
380                 jobAudioCodec = @"AC3"; // HB_ACODEC_AC3
381             else
382                 jobAudioCodec = @"unknown";
383             /* Show Basic File info */
384             if (job->chapter_markers == 1)
385                 [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio, Chapter Markers", jobFormat, jobVideoCodec, jobAudioCodec]];
386             else
387                 [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio", jobFormat, jobVideoCodec, jobAudioCodec]];
388                 
389             /*Picture info*/
390             /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height,
391              maxWidth, maxHeight */
392             if (job->pixel_ratio == 1)
393             {
394                 int titlewidth = title->width - job->crop[2] - job->crop[3];
395                 int displayparwidth = titlewidth * job->pixel_aspect_width / job->pixel_aspect_height;
396                 int displayparheight = title->height - job->crop[0] - job->crop[1];
397                 jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, job->width, displayparheight];
398             }
399             else
400                 jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d", job->width, job->height];
401             if (job->keep_ratio == 1)
402                 jobPictureDetail = [jobPictureDetail stringByAppendingString:@" Keep Aspect Ratio"];
403             
404             if (job->grayscale == 1)
405                 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Grayscale"];
406             
407             if (job->deinterlace == 1)
408                 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Deinterlace"];
409             /* Show Picture info */    
410             [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobPictureDetail]];
411             
412             /* Detailed Video info */
413             if (job->vquality <= 0 || job->vquality >= 1)
414                 jobVideoQuality =[NSString stringWithFormat:@"%d kbps", job->vbitrate];
415             else
416             {
417                 NSNumber * vidQuality;
418                 vidQuality = [NSNumber numberWithInt:job->vquality * 100];
419                 /* this is screwed up kind of. Needs to be formatted properly */
420                 if (job->crf == 1)
421                     jobVideoQuality =[NSString stringWithFormat:@"%@%% CRF", vidQuality];            
422                 else
423                     jobVideoQuality =[NSString stringWithFormat:@"%@%% CQP", vidQuality];
424             }
425             
426             if (job->vrate_base == 1126125)
427             {
428                 /* NTSC FILM 23.976 */
429                 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];
430             }
431             else if (job->vrate_base == 900900)
432             {
433                 /* NTSC 29.97 */
434                 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];
435             }
436             else
437             {
438                 /* Everything else */
439                 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, %d fps", jobVideoCodec, jobVideoQuality, job->vrate / job->vrate_base];
440             }
441             
442             /* Add the video detail string to the job filed in the window */
443             [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobVideoDetail]];
444             
445             /* if there is an x264 option string, lets add it here*/
446             /*NOTE: Due to size, lets get this in a tool tip*/
447             
448             if (job->x264opts)
449                 [aMutableString appendString:[NSString stringWithFormat:@"\nx264 Options: %@", [NSString stringWithUTF8String:job->x264opts]]];
450             
451             /* Audio Detail */
452             if ([jobAudioCodec isEqualToString: @"AC3"])
453                 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, Pass-Through", jobAudioCodec];
454             else
455                 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, %d kbps, %d Hz", jobAudioCodec, job->abitrate, job->arate];
456             
457             /* we now get the audio mixdown info for each of the two gui audio tracks */
458             /* lets do it the long way here to get a handle on things.
459                 Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] */
460             int ai; // counter for each audios [] , macgui only allows for two audio tracks currently
461             for( ai = 0; ai < 2; ai++ )
462             {
463                 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_MONO)
464                     jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono",ai + 1]];
465                 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)
466                     jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo",ai + 1]];
467                 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)
468                     jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround",ai + 1]];
469                 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII)
470                     jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II",ai + 1]];
471                 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_6CH)
472                     jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet",ai + 1]];
473             }
474             
475             /* Add the Audio detail string to the job filed in the window */
476             [aMutableString appendString:[NSString stringWithFormat: @"\n%@", jobAudioDetail]];
477             
478             /*Destination Field */
479             [aMutableString appendString:[NSString stringWithFormat:@"\nDestination: %@", [NSString stringWithUTF8String:job->file]]];
480         }
481     }
482     
483     anAttributedString = [[[NSAttributedString alloc] initWithString:aMutableString attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease];
484     [finalString appendAttributedString:anAttributedString];
485
486             
487     return finalString;
488 }
489
490 //------------------------------------------------------------------------------------
491 // Generate string to display in UI.
492 //------------------------------------------------------------------------------------
493 - (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s
494 {
495     if (s->state == HB_STATE_WORKING)
496     {
497         NSString * msg;
498         if (job->pass == -1)
499             msg = NSLocalizedString( @"Analyzing subtitles", nil );
500         else if (job->pass == 1)
501             msg = NSLocalizedString( @"Analyzing video", nil );
502         else if ((job->pass == 0) ||  (job->pass == 2))
503             msg = NSLocalizedString( @"Encoding movie", nil );
504         else
505             return @""; // unknown condition!
506             
507         if( s->param.working.seconds > -1 )
508         {
509             return [NSString stringWithFormat:
510                 NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ),
511                 msg, s->param.working.rate_cur, s->param.working.rate_avg];
512         }
513         else
514             return msg;
515
516     }
517
518     else if (s->state == HB_STATE_MUXING)
519         return NSLocalizedString( @"Muxing", nil );
520
521     else if (s->state == HB_STATE_PAUSED)
522         return NSLocalizedString( @"Paused", nil );
523
524     else if (s->state == HB_STATE_WORKDONE)
525         return NSLocalizedString( @"Done", nil );
526     
527     return @"";
528 }
529
530 //------------------------------------------------------------------------------------
531 // Generate string to display in UI.
532 //------------------------------------------------------------------------------------
533 - (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s
534 {
535     if (s->state == HB_STATE_WORKING)
536     {
537         #define p s->param.working
538         if (p.seconds < 0)
539             return @"";
540         
541         // Minutes always needed
542         NSString * minutes;
543         if (p.minutes > 1)
544           minutes = [NSString stringWithFormat:NSLocalizedString( @"%d minutes ", nil ), p.minutes];
545         else if (p.minutes == 1)
546           minutes = NSLocalizedString( @"1 minute ", nil );
547         else
548           minutes = @"";
549         
550         if (p.hours >= 1)
551         {
552             NSString * hours;
553             if (p.hours > 1)
554               hours = [NSString stringWithFormat:NSLocalizedString( @"%d hours ", nil ), p.hours];
555             else
556               hours = NSLocalizedString( @"1 hour ", nil );
557
558             return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), hours, minutes];
559         }
560         
561         else
562         {
563             NSString * seconds;
564             if (p.seconds > 1)
565               seconds = [NSString stringWithFormat:NSLocalizedString( @"%d seconds ", nil ), p.seconds];
566             else
567               seconds = NSLocalizedString( @"1 second ", nil );
568
569             return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), minutes, seconds];
570         }
571
572 /* here is code that does it more like the Finder
573         if( p.seconds > -1 )
574         {
575             float estHours = (p.hours + (p.minutes / 60.0));
576             float estMinutes = (p.minutes + (p.seconds / 60.0));
577
578             if (estHours > 1.5)
579                 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d hours", nil ), lrintf(estHours)];
580             else if (estHours > 0.983)    // 59 minutes
581                 return NSLocalizedString( @"Time remaining: About 1 hour", nil );
582             else if (estMinutes > 1.5)
583                 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d minutes", nil ), lrintf(estMinutes)];
584             else if (estMinutes > 0.983)    // 59 seconds
585                 return NSLocalizedString( @"Time remaining: About 1 minute", nil );
586             else if (p.seconds <= 5)
587                 return NSLocalizedString( @"Time remaining: Less than 5 seconds", nil );
588             else if (p.seconds <= 10)
589                 return NSLocalizedString( @"Time remaining: Less than 10 seconds", nil );
590             else
591                 return NSLocalizedString( @"Time remaining: Less than 1 minute", nil );
592         }
593         else
594             return NSLocalizedString( @"Time remaining: Calculating...", nil );
595 */
596         #undef p
597     }
598     
599     return @"";
600 }
601
602 //------------------------------------------------------------------------------------
603 // Refresh progress bar (fProgressBar) from current state.
604 //------------------------------------------------------------------------------------
605 - (void) updateProgressBarWithState: (hb_state_t *)s
606 {
607     if (s->state == HB_STATE_WORKING)
608     {
609         #define p s->param.working
610         [fProgressBar setIndeterminate:NO];
611
612         BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
613         float progress_total = jobGroups ?
614                 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count :
615                 100.0 * p.progress;
616
617         [fProgressBar setDoubleValue:progress_total];
618         #undef p
619     }
620     
621     else if (s->state == HB_STATE_MUXING)
622     {
623         #define p s->param.muxing
624         [fProgressBar setIndeterminate:YES];
625         [fProgressBar startAnimation:nil];
626         #undef p
627     }
628
629     else if (s->state == HB_STATE_WORKDONE)
630     {
631         [fProgressBar setIndeterminate:NO];
632         [fProgressBar setDoubleValue:0.0];
633     }
634 }
635
636 //------------------------------------------------------------------------------------
637 // Refresh start/pause button (fStartPauseButton) from current state.
638 //------------------------------------------------------------------------------------
639 - (void) updateStartPauseButton
640 {
641
642 // ************* THIS METHOD CAN DISAPPEAR. THE BUTTON IS NOW HIDDEN AND CAN BE DELETED
643     if (!fHandle) return;
644
645     hb_state_t s;
646     hb_get_state2 (fHandle, &s);
647
648     if (s.state == HB_STATE_PAUSED)
649     {
650         [fStartPauseButton setEnabled:YES];
651 //        [fStartPauseButton setTitle:NSLocalizedString(@"Resume", nil)];
652         [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
653    }
654     
655     else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
656     {
657         [fStartPauseButton setEnabled:YES];
658 //        [fStartPauseButton setTitle:NSLocalizedString(@"Pause", nil)];
659         [fStartPauseButton setImage:[NSImage imageNamed: @"Pause"]];
660     }
661
662     else if (hb_count(fHandle) > 0)
663     {
664         [fStartPauseButton setEnabled:YES];
665 //        [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
666         [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
667     }
668
669     else
670     {
671         [fStartPauseButton setEnabled:NO];
672 //        [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
673         [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
674     }
675 }
676
677 //------------------------------------------------------------------------------------
678 // Refresh queue count text field (fQueueCountField).
679 //------------------------------------------------------------------------------------
680 - (void)updateQueueCountField
681 {
682     NSString * msg;
683     int jobCount;
684     BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
685     
686     if (jobGroups)
687     {
688         jobCount = fHandle ? hb_group_count(fHandle) : 0;
689         if (jobCount == 1)
690             msg = NSLocalizedString(@"1 pending encode", nil);
691         else
692             msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];
693     }
694     else
695     {
696         jobCount = fHandle ? hb_count(fHandle) : 0;
697         if (jobCount == 1)
698             msg = NSLocalizedString(@"1 pending pass", nil);
699         else
700             msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending passes", nil), jobCount];
701
702     }
703
704     [fQueueCountField setStringValue:msg];
705 }
706
707 //------------------------------------------------------------------------------------
708 // Refresh the UI in the current job pane. Should be called whenever the current job
709 // being processed has changed or when progress has changed.
710 //------------------------------------------------------------------------------------
711 - (void)updateCurrentJobUI
712 {
713     hb_state_t s;
714     hb_job_t * job = nil;
715     
716     if (fHandle)
717     {
718         hb_get_state( fHandle, &s );
719         job = hb_current_job(fHandle);
720     }
721
722     if (job)
723     {
724         [fJobDescTextField setAttributedStringValue:[self attributedDescriptionForJob:job withDetail:YES withHighlighting:NO]];
725
726         [self showCurrentJobPane:YES];
727         [fJobIconView setImage: fShowsJobsAsGroups ? [NSImage imageNamed:@"JobLarge"] : [NSImage imageNamed:@"JobPassLarge"] ];
728         
729         NSString * statusMsg = [self progressStatusStringForJob:job state:&s];
730         NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:&s];
731         if ([timeMsg length] > 0)
732             statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg];
733         [fProgressTextField setStringValue:statusMsg];
734         [self updateProgressBarWithState:&s];
735     }
736     else
737     {
738         [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)];
739
740         [self showCurrentJobPane:NO];
741         [fProgressBar stopAnimation:nil];    // just in case in was animating
742     }
743
744     // Gross hack. Also update start/pause button. Have to do it here since we don't
745     // have any other periodic chance to update the button.
746     [self updateStartPauseButton];
747 }
748
749 //------------------------------------------------------------------------------------
750 // Refresh the UI in the queue pane. Should be called whenever the content of HB's job
751 // list has changed so that HBQueueController can sync up.
752 //------------------------------------------------------------------------------------
753 - (void)updateQueueUI
754 {
755     [fTaskView noteNumberOfRowsChanged];
756     [fTaskView reloadData];
757     
758     [self updateQueueCountField];
759     [self updateStartPauseButton];
760 }
761
762 //------------------------------------------------------------------------------------
763 // Deletes the selected job from HB and the queue UI
764 //------------------------------------------------------------------------------------
765 - (IBAction)removeSelectedJob: (id)sender
766 {
767     if (!fHandle) return;
768     
769     int row = [sender selectedRow];
770     if (row != -1)
771     {
772         BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
773         if (jobGroups)
774             hb_rem_group( fHandle, hb_group( fHandle, row ) );
775         else
776             hb_rem( fHandle, hb_job( fHandle, row ) );
777         [self updateQueueUI];
778     }
779 }
780
781 //------------------------------------------------------------------------------------
782 // Prompts user if the want to cancel encoding of current job. If so, hb_stop gets
783 // called.
784 //------------------------------------------------------------------------------------
785 - (IBAction)cancelCurrentJob: (id)sender
786 {
787     if (!fHandle) return;
788     
789     hb_job_t * job = hb_current_job(fHandle);
790     if (!job) return;
791
792     // If command key is down, don't prompt
793     BOOL hasCmdKeyMask = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0;
794     if (hasCmdKeyMask)
795         hb_stop(fHandle);
796     else
797     {
798         NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop processing of %@?", nil),
799                 [NSString stringWithUTF8String:job->title->name]];
800         
801         NSBeginCriticalAlertSheet(
802                 alertTitle,
803                 NSLocalizedString(@"Stop Processing", nil), NSLocalizedString(@"Keep Processing", nil), nil, fQueueWindow, self,
804                 @selector(cancelCurrentJob:returnCode:contextInfo:), nil, nil,
805                 NSLocalizedString(@"Your movie will be lost if you don't continue processing.", nil),
806                 [NSString stringWithUTF8String:job->title->name]);
807
808         // cancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
809     }
810 }
811
812 - (void) cancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
813 {
814     if (returnCode == NSAlertDefaultReturn)
815         hb_stop(fHandle);
816 }
817
818 //------------------------------------------------------------------------------------
819 // Enables or disables the display of detail information for each job based on the 
820 // state of the sender.
821 //------------------------------------------------------------------------------------
822 - (IBAction)detailChanged: (id)sender
823 {
824     if ([sender isMemberOfClass:[NSButton class]])
825     {
826         BOOL detail = [sender state] == NSOnState;
827         [[NSUserDefaults standardUserDefaults] setBool:detail forKey:@"QueueShowsDetail"];
828
829         [self setShowsDetail:detail];
830     }
831 }
832
833 //------------------------------------------------------------------------------------
834 // Enables or disables the display of job groups based on the state of the sender.
835 //------------------------------------------------------------------------------------
836 - (IBAction)jobGroupsChanged: (id)sender
837 {
838     if ([sender isMemberOfClass:[NSButton class]])
839     {
840         BOOL groups = [sender state] == NSOnState;
841         [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
842
843         [self setShowsJobsAsGroups:groups];
844     }
845     else if ([sender isMemberOfClass:[NSSegmentedControl class]])
846     {
847         BOOL groups = [sender selectedSegment] == 0;
848         [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
849
850         [self setShowsJobsAsGroups:groups];
851     }
852     else if ([sender isMemberOfClass:[NSMatrix class]])
853     {
854         BOOL groups = [sender selectedColumn] == 0;
855         [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
856
857         [self setShowsJobsAsGroups:groups];
858     }
859 }
860
861 //------------------------------------------------------------------------------------
862 // Toggles the Shows Detail setting.
863 //------------------------------------------------------------------------------------
864 - (IBAction)toggleShowsDetail: (id)sender
865 {
866     [self setShowsDetail:!fShowsDetail];
867 }
868
869 //------------------------------------------------------------------------------------
870 // Toggles the Shows Jobs As Groups setting.
871 //------------------------------------------------------------------------------------
872 - (IBAction)toggleShowsJobsAsGroups: (id)sender
873 {
874     [self setShowsJobsAsGroups:!fShowsJobsAsGroups];
875 }
876
877 //------------------------------------------------------------------------------------
878 // Toggles the processing of jobs on or off depending on the current state
879 //------------------------------------------------------------------------------------
880 - (IBAction)toggleStartPause: (id)sender
881 {
882     if (!fHandle) return;
883     
884     hb_state_t s;
885     hb_get_state2 (fHandle, &s);
886
887     if (s.state == HB_STATE_PAUSED)
888         hb_resume (fHandle);
889     else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
890         hb_pause (fHandle);
891     else
892     {
893         BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
894         if (jobGroups)
895         {
896             if (hb_group_count(fHandle) > 0)
897                 hb_start (fHandle);
898         }
899         else if (hb_count(fHandle) > 0)
900             hb_start (fHandle);
901     }    
902 }
903
904 #pragma mark -
905 #pragma mark Toolbar
906
907 //------------------------------------------------------------------------------------
908 // setupToolbar
909 //------------------------------------------------------------------------------------
910 - (void)setupToolbar
911 {
912     // Create a new toolbar instance, and attach it to our window 
913     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
914     
915     // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults 
916     [toolbar setAllowsUserCustomization: YES];
917     [toolbar setAutosavesConfiguration: YES];
918     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
919     
920     // We are the delegate
921     [toolbar setDelegate: self];
922     
923     // Attach the toolbar to our window 
924     [fQueueWindow setToolbar: toolbar];
925 }
926
927 //------------------------------------------------------------------------------------
928 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
929 //------------------------------------------------------------------------------------
930 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
931         itemForItemIdentifier:(NSString *)itemIdentifier
932         willBeInsertedIntoToolbar:(BOOL)flag
933 {
934     // Required delegate method: Given an item identifier, this method returns an item.
935     // The toolbar will use this method to obtain toolbar items that can be displayed
936     // in the customization sheet, or in the toolbar itself.
937     
938     NSToolbarItem *toolbarItem = nil;
939     
940     if ([itemIdentifier isEqual: HBStartPauseResumeToolbarIdentifier])
941     {
942         toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
943                 
944         // Set the text label to be displayed in the toolbar and customization palette 
945                 [toolbarItem setLabel: @"Start"];
946                 [toolbarItem setPaletteLabel: @"Start/Pause"];
947                 
948                 // Set up a reasonable tooltip, and image
949                 [toolbarItem setToolTip: @"Start Encoding"];
950                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
951                 
952                 // Tell the item what message to send when it is clicked 
953                 [toolbarItem setTarget: self];
954                 [toolbarItem setAction: @selector(toggleStartPause:)];
955         }
956     
957     else if ([itemIdentifier isEqual: HBShowDetailToolbarIdentifier])
958     {
959         toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
960                 
961         // Set the text label to be displayed in the toolbar and customization palette 
962                 [toolbarItem setLabel: @"Show Detail"];
963                 [toolbarItem setPaletteLabel: @"Show Detail"];
964                 
965                 // Set up a reasonable tooltip, and image
966                 [toolbarItem setToolTip: @"Show Detail"];
967                 [toolbarItem setImage: [NSImage imageNamed: @"Info"]];
968                 
969                 // Tell the item what message to send when it is clicked 
970                 [toolbarItem setTarget: self];
971                 [toolbarItem setAction: @selector(toggleShowsDetail:)];
972         }
973     
974     else if ([itemIdentifier isEqual: HBShowGroupsToolbarIdentifier])
975     {
976         toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
977                 
978         // Set the text label to be displayed in the toolbar and customization palette 
979                 [toolbarItem setLabel: @"View"];
980                 [toolbarItem setPaletteLabel: @"View"];
981                 
982                 // Set up a reasonable tooltip, and image
983                 [toolbarItem setToolTip: @"View"];
984 //              [toolbarItem setImage: [NSImage imageNamed: @"Disc"]];
985         
986         
987         NSButtonCell * buttonCell = [[[NSButtonCell alloc] initImageCell:nil] autorelease];
988         [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle];//NSShadowlessSquareBezelStyle
989         [buttonCell setButtonType:NSToggleButton];
990         [buttonCell setBordered:NO];
991         [buttonCell setImagePosition:NSImageOnly];
992
993         NSMatrix * matrix = [[[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,54,25)
994                 mode:NSRadioModeMatrix
995                 prototype:buttonCell
996                 numberOfRows:1
997                 numberOfColumns:2] autorelease];
998         [matrix setCellSize:NSMakeSize(27, 25)];
999         [matrix setIntercellSpacing:NSMakeSize(0, 0)];
1000         [matrix selectCellAtRow:0 column:(fShowsJobsAsGroups ? 0 : 1)];
1001
1002         buttonCell = [matrix cellAtRow:0 column:0];
1003         [buttonCell setTitle:@""];
1004         [buttonCell setImage:[NSImage imageNamed: @"Encodes"]];
1005         [buttonCell setAlternateImage:[NSImage imageNamed: @"EncodesPressed"]];
1006         buttonCell = [matrix cellAtRow:0 column:1];
1007         [buttonCell setTitle:@""];
1008         [buttonCell setImage:[NSImage imageNamed: @"Passes"]];
1009         [buttonCell setAlternateImage:[NSImage imageNamed: @"PassesPressed"]];
1010         [toolbarItem setMinSize: [matrix frame].size];
1011         [toolbarItem setMaxSize: [matrix frame].size];
1012                 [toolbarItem setView: matrix];
1013
1014 /*
1015         NSSegmentedControl * segControl = [[[NSSegmentedControl alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease];
1016         [[segControl cell] setControlSize:NSSmallControlSize];
1017         [segControl setSegmentCount:2];
1018         [segControl setLabel:@"Encodes" forSegment:0];
1019         [segControl setLabel:@"Passes" forSegment:1];
1020         [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:0];
1021         [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:1];
1022         [segControl setSelectedSegment: (fShowsJobsAsGroups ? 0 : 1)];
1023         [segControl sizeToFit];
1024         [toolbarItem setMinSize: [segControl frame].size];
1025         [toolbarItem setMaxSize: [segControl frame].size];
1026                 [toolbarItem setView: segControl];
1027 */
1028
1029 /*
1030         NSButton * button = [[[NSButton alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease];
1031         [button setButtonType:NSSwitchButton];
1032         [button setTitle:@""];
1033         [button setState: fShowsJobsAsGroups ? NSOnState : NSOffState];
1034         [toolbarItem setMinSize: NSMakeSize(20,20)];
1035         [toolbarItem setMaxSize: NSMakeSize(20,20)];
1036                 [toolbarItem setView: button];
1037 */
1038                 
1039                 // Tell the item what message to send when it is clicked 
1040                 [toolbarItem setTarget: self];
1041                 [toolbarItem setAction: @selector(jobGroupsChanged:)];
1042         }
1043     
1044     return toolbarItem;
1045 }
1046
1047 //------------------------------------------------------------------------------------
1048 // toolbarDefaultItemIdentifiers:
1049 //------------------------------------------------------------------------------------
1050 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1051 {
1052     // Required delegate method: Returns the ordered list of items to be shown in the
1053     // toolbar by default.
1054     
1055     return [NSArray arrayWithObjects:
1056         HBStartPauseResumeToolbarIdentifier,
1057                 NSToolbarSeparatorItemIdentifier,
1058                 HBShowGroupsToolbarIdentifier,
1059         HBShowDetailToolbarIdentifier,
1060         nil];
1061 }
1062
1063 //------------------------------------------------------------------------------------
1064 // toolbarAllowedItemIdentifiers:
1065 //------------------------------------------------------------------------------------
1066 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1067 {
1068     // Required delegate method: Returns the list of all allowed items by identifier.
1069     // By default, the toolbar does not assume any items are allowed, even the
1070     // separator. So, every allowed item must be explicitly listed.
1071
1072     return [NSArray arrayWithObjects:
1073         HBStartPauseResumeToolbarIdentifier,
1074                 HBShowGroupsToolbarIdentifier,
1075         HBShowDetailToolbarIdentifier,
1076                 NSToolbarCustomizeToolbarItemIdentifier,
1077                 NSToolbarFlexibleSpaceItemIdentifier,
1078         NSToolbarSpaceItemIdentifier,
1079                 NSToolbarSeparatorItemIdentifier,
1080         nil];
1081 }
1082
1083 //------------------------------------------------------------------------------------
1084 // validateToolbarItem:
1085 //------------------------------------------------------------------------------------
1086 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1087 {
1088     // Optional method: This message is sent to us since we are the target of some
1089     // toolbar item actions.
1090
1091     if (!fHandle) return NO;
1092
1093     BOOL enable = NO;
1094
1095     hb_state_t s;
1096     hb_get_state2 (fHandle, &s);
1097
1098     if ([[toolbarItem itemIdentifier] isEqual: HBStartPauseResumeToolbarIdentifier])
1099     {
1100         if (s.state == HB_STATE_PAUSED)
1101         {
1102             enable = YES;
1103             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1104                         [toolbarItem setLabel: @"Resume"];
1105                         [toolbarItem setPaletteLabel: @"Resume"];
1106                         [toolbarItem setToolTip: @"Resume Encoding"];
1107        }
1108         
1109         else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1110         {
1111             enable = YES;
1112             [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
1113                         [toolbarItem setLabel: @"Pause"];
1114                         [toolbarItem setPaletteLabel: @"Pause"];
1115                         [toolbarItem setToolTip: @"Pause Encoding"];
1116         }
1117
1118         else if (hb_count(fHandle) > 0)
1119         {
1120             enable = YES;
1121             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1122                         [toolbarItem setLabel: @"Start"];
1123                         [toolbarItem setPaletteLabel: @"Start"];
1124                         [toolbarItem setToolTip: @"Start Encoding"];
1125         }
1126
1127         else
1128         {
1129             enable = NO;
1130             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1131                         [toolbarItem setLabel: @"Start"];
1132                         [toolbarItem setPaletteLabel: @"Start"];
1133                         [toolbarItem setToolTip: @"Start Encoding"];
1134         }
1135         }
1136     
1137 /* not used because HBShowGroupsToolbarIdentifier is now a custom view
1138     else if ([[toolbarItem itemIdentifier] isEqual: HBShowGroupsToolbarIdentifier])
1139     {
1140         enable = hb_count(fHandle) > 0;
1141         if (fShowsJobsAsGroups)
1142         {
1143             [toolbarItem setLabel: @"View Passes"];
1144             [toolbarItem setPaletteLabel: @"View Passes"];
1145             [toolbarItem setToolTip: @"Displays items in the queue as individual passes"];
1146         }
1147         else
1148         {
1149             [toolbarItem setLabel: @"View Encodes"];
1150             [toolbarItem setPaletteLabel: @"View Encodes"];
1151             [toolbarItem setToolTip: @"Displays items in the queue as encodes"];
1152         }
1153     }
1154 */
1155     
1156     else if ([[toolbarItem itemIdentifier] isEqual: HBShowDetailToolbarIdentifier])
1157     {
1158         enable = hb_count(fHandle) > 0;
1159         if (fShowsDetail)
1160         {
1161             [toolbarItem setLabel: @"Hide Detail"];
1162             [toolbarItem setPaletteLabel: @"Hide Detail"];
1163             [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1164         }
1165         else
1166         {
1167             [toolbarItem setLabel: @"Show Detail"];
1168             [toolbarItem setPaletteLabel: @"Show Detail"];
1169             [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1170         }
1171     }
1172
1173         return enable;
1174 }
1175
1176 #pragma mark -
1177
1178 //------------------------------------------------------------------------------------
1179 // awakeFromNib
1180 //------------------------------------------------------------------------------------
1181 - (void)awakeFromNib
1182 {
1183     [self setupToolbar];
1184     
1185     if (![fQueueWindow setFrameUsingName:@"Queue"])
1186         [fQueueWindow center];
1187     [fQueueWindow setFrameAutosaveName: @"Queue"];
1188     [fQueueWindow setExcludedFromWindowsMenu:YES];
1189     
1190     // Show/hide UI elements
1191     [self setShowsDetail:fShowsDetail];
1192     [self setShowsJobsAsGroups:fShowsJobsAsGroups];
1193     [self showCurrentJobPane:NO];
1194 }
1195
1196
1197 //------------------------------------------------------------------------------------
1198 // windowWillClose
1199 //------------------------------------------------------------------------------------
1200 - (void)windowWillClose:(NSNotification *)aNotification
1201 {
1202     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
1203 }
1204
1205 #pragma mark -
1206 #pragma mark NSTableView delegate
1207
1208 //------------------------------------------------------------------------------------
1209 // NSTableView delegate
1210 //------------------------------------------------------------------------------------
1211 - (int)numberOfRowsInTableView: (NSTableView *)aTableView
1212 {
1213     BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1214     if (jobGroups)
1215         return hb_group_count(fHandle);
1216     else
1217         return hb_count(fHandle);
1218 }
1219
1220 //------------------------------------------------------------------------------------
1221 // NSTableView delegate
1222 //------------------------------------------------------------------------------------
1223 - (id)tableView: (NSTableView *)aTableView
1224       objectValueForTableColumn: (NSTableColumn *)aTableColumn
1225                             row: (int)rowIndex
1226 {
1227     if (!fHandle)
1228         return @"";    // fatal error!
1229         
1230     hb_job_t * job;
1231
1232     BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1233     if (jobGroups)
1234         job = hb_group(fHandle, rowIndex);
1235     else
1236         job = hb_job(fHandle, rowIndex);
1237
1238     if (!job)
1239         return @"";    // fatal error!
1240
1241     if ([[aTableColumn identifier] isEqualToString:@"desc"])
1242     {
1243         BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1244         return [self attributedDescriptionForJob:job withDetail:fShowsDetail withHighlighting:highlighted];    
1245     }
1246     
1247     else if ([[aTableColumn identifier] isEqualToString:@"delete"])
1248         return @"";
1249
1250     else if ([[aTableColumn identifier] isEqualToString:@"icon"])
1251         return fShowsJobsAsGroups ? [NSImage imageNamed:@"JobSmall"] : [NSImage imageNamed:@"JobPassSmall"];
1252
1253     return @"";
1254 }
1255
1256 //------------------------------------------------------------------------------------
1257 // NSTableView delegate
1258 //------------------------------------------------------------------------------------
1259 - (void)tableView: (NSTableView *)aTableView
1260         willDisplayCell: (id)aCell
1261          forTableColumn: (NSTableColumn *)aTableColumn
1262                     row: (int)rowIndex
1263 {
1264     if ([[aTableColumn identifier] isEqualToString:@"delete"])
1265     {
1266         BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1267         if (highlighted)
1268         {
1269             [aCell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1270             [aCell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1271         }
1272         else
1273         {
1274             [aCell setImage:[NSImage imageNamed:@"Delete"]];
1275         }
1276     }
1277 }
1278
1279 @end