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. */
7 #include "HBQueueController.h"
10 * Returns the number of jobs groups in the queue.
11 * @param h Handle to hb_handle_t.
12 * @return Number of job groups.
14 static int hb_group_count(hb_handle_t * h)
19 while( ( job = hb_job( h, index++ ) ) )
21 if (job->sequence_id == 0)
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.
33 static hb_job_t * hb_group(hb_handle_t * h, int i)
38 while( ( job = hb_job( h, index++ ) ) )
40 if (job->sequence_id == 0)
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.
55 static void hb_rem_group( hb_handle_t * h, hb_job_t * job )
60 while( ( j = hb_job( h, index ) ) )
64 // Delete this job plus the following ones in the sequence
66 while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )
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.
81 static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job )
85 while( ( j = hb_job( h, index++ ) ) )
88 return hb_job( h, index+1 );
95 // Toolbar identifiers
96 static NSString* HBQueueToolbar = @"HBQueueToolbar";
97 static NSString* HBStartPauseResumeToolbarIdentifier = @"HBStartPauseResumeToolbarIdentifier";
98 static NSString* HBShowDetailToolbarIdentifier = @"HBShowDetailToolbarIdentifier";
99 static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsToolbarIdentifier";
102 @implementation HBQueueController
104 //------------------------------------------------------------------------------------
106 //------------------------------------------------------------------------------------
109 if (self = [super init])
112 [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
113 @"NO", @"QueueWindowIsOpen",
114 @"NO", @"QueueShowsDetail",
115 @"YES", @"QueueShowsJobsAsGroups",
118 fShowsDetail = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsDetail"];
119 fShowsJobsAsGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
125 //------------------------------------------------------------------------------------
127 //------------------------------------------------------------------------------------
130 [fAnimation release];
132 // clear the delegate so that windowWillClose is not attempted
133 if ([fQueueWindow delegate] == self)
134 [fQueueWindow setDelegate:nil];
139 //------------------------------------------------------------------------------------
141 //------------------------------------------------------------------------------------
142 - (void)setHandle: (hb_handle_t *)handle
147 //------------------------------------------------------------------------------------
148 // Displays and brings the queue window to the front
149 //------------------------------------------------------------------------------------
150 - (IBAction) showQueueWindow: (id)sender
154 BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow;
155 NSAssert(loadSucceeded, @"Could not open Queue nib file");
158 [self updateQueueUI];
159 [self updateCurrentJobUI];
161 [fQueueWindow makeKeyAndOrderFront: self];
163 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
165 //------------------------------------------------------------------------------------
166 // Show or hide the current job pane (fCurrentJobPane).
167 //------------------------------------------------------------------------------------
168 - (void) showCurrentJobPane: (BOOL)showPane
170 if (showPane != fCurrentJobHidden)
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
180 NSRect windowFrame = [[fCurrentJobPane superview] frame];
181 NSRect queueFrame, jobFrame;
183 NSDivideRect(windowFrame, &jobFrame, &queueFrame, NSHeight([fCurrentJobPane frame]), NSMaxYEdge);
186 queueFrame = windowFrame;
187 jobFrame = [fCurrentJobPane frame];
188 jobFrame.origin.y = NSHeight(windowFrame);
191 // Move fCurrentJobPane
192 NSDictionary * dict1 = [NSDictionary dictionaryWithObjectsAndKeys:
193 fCurrentJobPane, NSViewAnimationTargetKey,
194 [NSValue valueWithRect:jobFrame], NSViewAnimationEndFrameKey,
198 NSDictionary * dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
199 fQueuePane, NSViewAnimationTargetKey,
200 [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey,
204 fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil];
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;
213 //------------------------------------------------------------------------------------
214 // Enables or disables the display of detail information for each job.
215 //------------------------------------------------------------------------------------
216 - (void)setShowsDetail: (BOOL)showsDetail
218 fShowsDetail = showsDetail;
220 [[NSUserDefaults standardUserDefaults] setBool:showsDetail forKey:@"QueueShowsDetail"];
221 [[NSUserDefaults standardUserDefaults] synchronize];
223 // clumsy - have to update UI
224 [fDetailCheckbox setState:showsDetail ? NSOnState : NSOffState];
226 [fTaskView setRowHeight:showsDetail ? 110.0 : 17.0];
227 if ([fTaskView selectedRow] != -1)
228 [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
231 //------------------------------------------------------------------------------------
232 // Enables or disables the grouping of job passes into one item in the UI.
233 //------------------------------------------------------------------------------------
234 - (void)setShowsJobsAsGroups: (BOOL)showsGroups
236 fShowsJobsAsGroups = showsGroups;
238 [[NSUserDefaults standardUserDefaults] setBool:showsGroups forKey:@"QueueShowsJobsAsGroups"];
239 [[NSUserDefaults standardUserDefaults] synchronize];
241 // clumsy - have to update UI
242 [fJobGroupsCheckbox setState:showsGroups ? NSOnState : NSOffState];
244 [self updateQueueUI];
245 if ([fTaskView selectedRow] != -1)
246 [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
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
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;
264 NSMutableParagraphStyle *ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
265 [ps setLineBreakMode:NSLineBreakByClipping];
267 static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
268 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
269 [NSColor darkGrayColor], NSForegroundColorAttributeName,
270 ps, NSParagraphStyleAttributeName,
272 static NSDictionary* detailHighlightedAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
273 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
274 [NSColor whiteColor], NSForegroundColorAttributeName,
275 ps, NSParagraphStyleAttributeName,
277 static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObject:
278 [NSFont systemFontOfSize:[NSFont systemFontSize]] forKey:NSFontAttributeName] retain];
280 finalString = [[[NSMutableAttributedString alloc] init] autorelease];
283 // Show the name of the source Note: use title->name instead of title->dvd since
284 // name is just the chosen folder, instead of dvd which is the full path
285 anAttributedString = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:title->name] attributes:titleAttribute] autorelease];
286 [finalString appendAttributedString:anAttributedString];
291 // Other info in plain
292 aMutableString = [NSMutableString stringWithCapacity:200];
294 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
296 // The subtitle scan doesn't contain all the stuff we need (like x264opts).
297 // So grab the next job in the group for display purposes.
298 if (jobGroups && job->pass == -1)
300 // When job is the one currently being processed, then the next in its group
301 // is the the first job in the queue.
303 if (job == hb_current_job(fHandle))
304 nextjob = hb_job(fHandle, 0);
306 nextjob = hb_next_job(fHandle, job);
307 if (nextjob) // Overly cautious in case there is no next job!
311 NSString * chapterString = (job->chapter_start == job->chapter_end) ?
312 [NSString stringWithFormat:@"Chapter %d", job->chapter_start] :
313 [NSString stringWithFormat:@"Chapters %d through %d", job->chapter_start, job->chapter_end];
318 [aMutableString appendString:[NSString stringWithFormat:
319 @"\nTitle %d, %@, Pass: Scan", title->index, chapterString]];
326 [aMutableString appendString:[NSString stringWithFormat:
327 @"\nTitle %d, %@, %d-Pass",
328 title->index, chapterString, MIN( 2, job->pass + 1 )]];
330 [aMutableString appendString:[NSString stringWithFormat:
331 @"\nTitle %d, %@, Pass %d of %d",
332 title->index, chapterString, MAX( 1, job->pass ), MIN( 2, job->pass + 1 )]];
335 NSString * jobFormat;
336 NSString * jobPictureDetail;
337 NSString * jobVideoDetail;
338 NSString * jobVideoCodec;
339 NSString * jobVideoQuality;
340 NSString * jobAudioDetail;
341 NSString * jobAudioCodec;
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
353 jobFormat = @"unknown";
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)
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
367 jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264
370 jobVideoCodec = @"unknown";
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
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]];
387 [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio", jobFormat, jobVideoCodec, jobAudioCodec]];
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)
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];
400 jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d", job->width, job->height];
401 if (job->keep_ratio == 1)
402 jobPictureDetail = [jobPictureDetail stringByAppendingString:@" Keep Aspect Ratio"];
404 if (job->grayscale == 1)
405 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Grayscale"];
407 if (job->deinterlace == 1)
408 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Deinterlace"];
409 /* Show Picture info */
410 [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobPictureDetail]];
412 /* Detailed Video info */
413 if (job->vquality <= 0 || job->vquality >= 1)
414 jobVideoQuality =[NSString stringWithFormat:@"%d kbps", job->vbitrate];
417 NSNumber * vidQuality;
418 vidQuality = [NSNumber numberWithInt:job->vquality * 100];
419 /* this is screwed up kind of. Needs to be formatted properly */
421 jobVideoQuality =[NSString stringWithFormat:@"%@%% CRF", vidQuality];
423 jobVideoQuality =[NSString stringWithFormat:@"%@%% CQP", vidQuality];
426 if (job->vrate_base == 1126125)
428 /* NTSC FILM 23.976 */
429 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];
431 else if (job->vrate_base == 900900)
434 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];
438 /* Everything else */
439 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, %d fps", jobVideoCodec, jobVideoQuality, job->vrate / job->vrate_base];
442 /* Add the video detail string to the job filed in the window */
443 [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobVideoDetail]];
445 /* if there is an x264 option string, lets add it here*/
446 /*NOTE: Due to size, lets get this in a tool tip*/
449 [aMutableString appendString:[NSString stringWithFormat:@"\nx264 Options: %@", [NSString stringWithUTF8String:job->x264opts]]];
452 if ([jobAudioCodec isEqualToString: @"AC3"])
453 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, Pass-Through", jobAudioCodec];
455 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, %d kbps, %d Hz", jobAudioCodec, job->abitrate, job->arate];
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++ )
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]];
475 /* Add the Audio detail string to the job filed in the window */
476 [aMutableString appendString:[NSString stringWithFormat: @"\n%@", jobAudioDetail]];
478 /*Destination Field */
479 [aMutableString appendString:[NSString stringWithFormat:@"\nDestination: %@", [NSString stringWithUTF8String:job->file]]];
482 anAttributedString = [[[NSAttributedString alloc] initWithString:aMutableString attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease];
483 [finalString appendAttributedString:anAttributedString];
489 //------------------------------------------------------------------------------------
490 // Generate string to display in UI.
491 //------------------------------------------------------------------------------------
492 - (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s
494 if (s->state == HB_STATE_WORKING)
498 msg = NSLocalizedString( @"Analyzing subtitles", nil );
499 else if (job->pass == 1)
500 msg = NSLocalizedString( @"Analyzing video", nil );
501 else if ((job->pass == 0) || (job->pass == 2))
502 msg = NSLocalizedString( @"Encoding movie", nil );
504 return @""; // unknown condition!
506 if( s->param.working.seconds > -1 )
508 return [NSString stringWithFormat:
509 NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ),
510 msg, s->param.working.rate_cur, s->param.working.rate_avg];
517 else if (s->state == HB_STATE_MUXING)
518 return NSLocalizedString( @"Muxing", nil );
520 else if (s->state == HB_STATE_PAUSED)
521 return NSLocalizedString( @"Paused", nil );
523 else if (s->state == HB_STATE_WORKDONE)
524 return NSLocalizedString( @"Done", nil );
529 //------------------------------------------------------------------------------------
530 // Generate string to display in UI.
531 //------------------------------------------------------------------------------------
532 - (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s
534 if (s->state == HB_STATE_WORKING)
536 #define p s->param.working
540 // Minutes always needed
543 minutes = [NSString stringWithFormat:NSLocalizedString( @"%d minutes ", nil ), p.minutes];
544 else if (p.minutes == 1)
545 minutes = NSLocalizedString( @"1 minute ", nil );
553 hours = [NSString stringWithFormat:NSLocalizedString( @"%d hours ", nil ), p.hours];
555 hours = NSLocalizedString( @"1 hour ", nil );
557 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), hours, minutes];
564 seconds = [NSString stringWithFormat:NSLocalizedString( @"%d seconds ", nil ), p.seconds];
566 seconds = NSLocalizedString( @"1 second ", nil );
568 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), minutes, seconds];
571 /* here is code that does it more like the Finder
574 float estHours = (p.hours + (p.minutes / 60.0));
575 float estMinutes = (p.minutes + (p.seconds / 60.0));
578 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d hours", nil ), lrintf(estHours)];
579 else if (estHours > 0.983) // 59 minutes
580 return NSLocalizedString( @"Time remaining: About 1 hour", nil );
581 else if (estMinutes > 1.5)
582 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d minutes", nil ), lrintf(estMinutes)];
583 else if (estMinutes > 0.983) // 59 seconds
584 return NSLocalizedString( @"Time remaining: About 1 minute", nil );
585 else if (p.seconds <= 5)
586 return NSLocalizedString( @"Time remaining: Less than 5 seconds", nil );
587 else if (p.seconds <= 10)
588 return NSLocalizedString( @"Time remaining: Less than 10 seconds", nil );
590 return NSLocalizedString( @"Time remaining: Less than 1 minute", nil );
593 return NSLocalizedString( @"Time remaining: Calculating...", nil );
601 //------------------------------------------------------------------------------------
602 // Refresh progress bar (fProgressBar) from current state.
603 //------------------------------------------------------------------------------------
604 - (void) updateProgressBarWithState: (hb_state_t *)s
606 if (s->state == HB_STATE_WORKING)
608 #define p s->param.working
609 [fProgressBar setIndeterminate:NO];
611 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
612 float progress_total = jobGroups ?
613 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count :
616 [fProgressBar setDoubleValue:progress_total];
620 else if (s->state == HB_STATE_MUXING)
622 #define p s->param.muxing
623 [fProgressBar setIndeterminate:YES];
624 [fProgressBar startAnimation:nil];
628 else if (s->state == HB_STATE_WORKDONE)
630 [fProgressBar setIndeterminate:NO];
631 [fProgressBar setDoubleValue:0.0];
635 //------------------------------------------------------------------------------------
636 // Refresh start/pause button (fStartPauseButton) from current state.
637 //------------------------------------------------------------------------------------
638 - (void) updateStartPauseButton
641 // ************* THIS METHOD CAN DISAPPEAR. THE BUTTON IS NOW HIDDEN AND CAN BE DELETED
642 if (!fHandle) return;
645 hb_get_state2 (fHandle, &s);
647 if (s.state == HB_STATE_PAUSED)
649 [fStartPauseButton setEnabled:YES];
650 // [fStartPauseButton setTitle:NSLocalizedString(@"Resume", nil)];
651 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
654 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
656 [fStartPauseButton setEnabled:YES];
657 // [fStartPauseButton setTitle:NSLocalizedString(@"Pause", nil)];
658 [fStartPauseButton setImage:[NSImage imageNamed: @"Pause"]];
661 else if (hb_count(fHandle) > 0)
663 [fStartPauseButton setEnabled:YES];
664 // [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
665 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
670 [fStartPauseButton setEnabled:NO];
671 // [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
672 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
676 //------------------------------------------------------------------------------------
677 // Refresh queue count text field (fQueueCountField).
678 //------------------------------------------------------------------------------------
679 - (void)updateQueueCountField
683 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
687 jobCount = fHandle ? hb_group_count(fHandle) : 0;
689 msg = NSLocalizedString(@"1 pending encode", nil);
691 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];
695 jobCount = fHandle ? hb_count(fHandle) : 0;
697 msg = NSLocalizedString(@"1 pending pass", nil);
699 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending passes", nil), jobCount];
703 [fQueueCountField setStringValue:msg];
706 //------------------------------------------------------------------------------------
707 // Refresh the UI in the current job pane. Should be called whenever the current job
708 // being processed has changed or when progress has changed.
709 //------------------------------------------------------------------------------------
710 - (void)updateCurrentJobUI
713 hb_job_t * job = nil;
717 hb_get_state( fHandle, &s );
718 job = hb_current_job(fHandle);
723 [fJobDescTextField setAttributedStringValue:[self attributedDescriptionForJob:job withDetail:YES withHighlighting:NO]];
725 [self showCurrentJobPane:YES];
726 [fJobIconView setImage: fShowsJobsAsGroups ? [NSImage imageNamed:@"JobLarge"] : [NSImage imageNamed:@"JobPassLarge"] ];
728 NSString * statusMsg = [self progressStatusStringForJob:job state:&s];
729 NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:&s];
730 if ([timeMsg length] > 0)
731 statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg];
732 [fProgressTextField setStringValue:statusMsg];
733 [self updateProgressBarWithState:&s];
737 [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)];
739 [self showCurrentJobPane:NO];
740 [fProgressBar stopAnimation:nil]; // just in case in was animating
743 // Gross hack. Also update start/pause button. Have to do it here since we don't
744 // have any other periodic chance to update the button.
745 [self updateStartPauseButton];
748 //------------------------------------------------------------------------------------
749 // Refresh the UI in the queue pane. Should be called whenever the content of HB's job
750 // list has changed so that HBQueueController can sync up.
751 //------------------------------------------------------------------------------------
752 - (void)updateQueueUI
754 [fTaskView noteNumberOfRowsChanged];
755 [fTaskView reloadData];
757 [self updateQueueCountField];
758 [self updateStartPauseButton];
761 //------------------------------------------------------------------------------------
762 // Deletes the selected job from HB and the queue UI
763 //------------------------------------------------------------------------------------
764 - (IBAction)removeSelectedJob: (id)sender
766 if (!fHandle) return;
768 int row = [sender selectedRow];
771 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
773 hb_rem_group( fHandle, hb_group( fHandle, row ) );
775 hb_rem( fHandle, hb_job( fHandle, row ) );
776 [self updateQueueUI];
780 //------------------------------------------------------------------------------------
781 // Prompts user if the want to cancel encoding of current job. If so, hb_stop gets
783 //------------------------------------------------------------------------------------
784 - (IBAction)cancelCurrentJob: (id)sender
786 if (!fHandle) return;
788 hb_job_t * job = hb_current_job(fHandle);
791 // If command key is down, don't prompt
792 BOOL hasCmdKeyMask = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0;
797 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop processing of %@?", nil),
798 [NSString stringWithUTF8String:job->title->name]];
800 NSBeginCriticalAlertSheet(
802 NSLocalizedString(@"Stop Processing", nil), NSLocalizedString(@"Keep Processing", nil), nil, fQueueWindow, self,
803 @selector(cancelCurrentJob:returnCode:contextInfo:), nil, nil,
804 NSLocalizedString(@"Your movie will be lost if you don't continue processing.", nil),
805 [NSString stringWithUTF8String:job->title->name]);
807 // cancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
811 - (void) cancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
813 if (returnCode == NSAlertDefaultReturn)
817 //------------------------------------------------------------------------------------
818 // Enables or disables the display of detail information for each job based on the
819 // state of the sender.
820 //------------------------------------------------------------------------------------
821 - (IBAction)detailChanged: (id)sender
823 if ([sender isMemberOfClass:[NSButton class]])
825 BOOL detail = [sender state] == NSOnState;
826 [[NSUserDefaults standardUserDefaults] setBool:detail forKey:@"QueueShowsDetail"];
828 [self setShowsDetail:detail];
832 //------------------------------------------------------------------------------------
833 // Enables or disables the display of job groups based on the state of the sender.
834 //------------------------------------------------------------------------------------
835 - (IBAction)jobGroupsChanged: (id)sender
837 if ([sender isMemberOfClass:[NSButton class]])
839 BOOL groups = [sender state] == NSOnState;
840 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
842 [self setShowsJobsAsGroups:groups];
844 else if ([sender isMemberOfClass:[NSSegmentedControl class]])
846 BOOL groups = [sender selectedSegment] == 0;
847 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
849 [self setShowsJobsAsGroups:groups];
851 else if ([sender isMemberOfClass:[NSMatrix class]])
853 BOOL groups = [sender selectedColumn] == 0;
854 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
856 [self setShowsJobsAsGroups:groups];
860 //------------------------------------------------------------------------------------
861 // Toggles the Shows Detail setting.
862 //------------------------------------------------------------------------------------
863 - (IBAction)toggleShowsDetail: (id)sender
865 [self setShowsDetail:!fShowsDetail];
868 //------------------------------------------------------------------------------------
869 // Toggles the Shows Jobs As Groups setting.
870 //------------------------------------------------------------------------------------
871 - (IBAction)toggleShowsJobsAsGroups: (id)sender
873 [self setShowsJobsAsGroups:!fShowsJobsAsGroups];
876 //------------------------------------------------------------------------------------
877 // Toggles the processing of jobs on or off depending on the current state
878 //------------------------------------------------------------------------------------
879 - (IBAction)toggleStartPause: (id)sender
881 if (!fHandle) return;
884 hb_get_state2 (fHandle, &s);
886 if (s.state == HB_STATE_PAUSED)
888 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
892 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
895 if (hb_group_count(fHandle) > 0)
898 else if (hb_count(fHandle) > 0)
906 //------------------------------------------------------------------------------------
908 //------------------------------------------------------------------------------------
911 // Create a new toolbar instance, and attach it to our window
912 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
914 // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
915 [toolbar setAllowsUserCustomization: YES];
916 [toolbar setAutosavesConfiguration: YES];
917 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
919 // We are the delegate
920 [toolbar setDelegate: self];
922 // Attach the toolbar to our window
923 [fQueueWindow setToolbar: toolbar];
926 //------------------------------------------------------------------------------------
927 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
928 //------------------------------------------------------------------------------------
929 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
930 itemForItemIdentifier:(NSString *)itemIdentifier
931 willBeInsertedIntoToolbar:(BOOL)flag
933 // Required delegate method: Given an item identifier, this method returns an item.
934 // The toolbar will use this method to obtain toolbar items that can be displayed
935 // in the customization sheet, or in the toolbar itself.
937 NSToolbarItem *toolbarItem = nil;
939 if ([itemIdentifier isEqual: HBStartPauseResumeToolbarIdentifier])
941 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
943 // Set the text label to be displayed in the toolbar and customization palette
944 [toolbarItem setLabel: @"Start"];
945 [toolbarItem setPaletteLabel: @"Start/Pause"];
947 // Set up a reasonable tooltip, and image
948 [toolbarItem setToolTip: @"Start Encoding"];
949 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
951 // Tell the item what message to send when it is clicked
952 [toolbarItem setTarget: self];
953 [toolbarItem setAction: @selector(toggleStartPause:)];
956 else if ([itemIdentifier isEqual: HBShowDetailToolbarIdentifier])
958 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
960 // Set the text label to be displayed in the toolbar and customization palette
961 [toolbarItem setLabel: @"Show Detail"];
962 [toolbarItem setPaletteLabel: @"Show Detail"];
964 // Set up a reasonable tooltip, and image
965 [toolbarItem setToolTip: @"Show Detail"];
966 [toolbarItem setImage: [NSImage imageNamed: @"Info"]];
968 // Tell the item what message to send when it is clicked
969 [toolbarItem setTarget: self];
970 [toolbarItem setAction: @selector(toggleShowsDetail:)];
973 else if ([itemIdentifier isEqual: HBShowGroupsToolbarIdentifier])
975 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
977 // Set the text label to be displayed in the toolbar and customization palette
978 [toolbarItem setLabel: @"View"];
979 [toolbarItem setPaletteLabel: @"View"];
981 // Set up a reasonable tooltip, and image
982 [toolbarItem setToolTip: @"View"];
983 // [toolbarItem setImage: [NSImage imageNamed: @"Disc"]];
986 NSButtonCell * buttonCell = [[[NSButtonCell alloc] initImageCell:nil] autorelease];
987 [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle];//NSShadowlessSquareBezelStyle
988 [buttonCell setButtonType:NSToggleButton];
989 [buttonCell setBordered:NO];
990 [buttonCell setImagePosition:NSImageOnly];
992 NSMatrix * matrix = [[[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,54,25)
993 mode:NSRadioModeMatrix
996 numberOfColumns:2] autorelease];
997 [matrix setCellSize:NSMakeSize(27, 25)];
998 [matrix setIntercellSpacing:NSMakeSize(0, 0)];
999 [matrix selectCellAtRow:0 column:(fShowsJobsAsGroups ? 0 : 1)];
1001 buttonCell = [matrix cellAtRow:0 column:0];
1002 [buttonCell setTitle:@""];
1003 [buttonCell setImage:[NSImage imageNamed: @"Encodes"]];
1004 [buttonCell setAlternateImage:[NSImage imageNamed: @"EncodesPressed"]];
1005 buttonCell = [matrix cellAtRow:0 column:1];
1006 [buttonCell setTitle:@""];
1007 [buttonCell setImage:[NSImage imageNamed: @"Passes"]];
1008 [buttonCell setAlternateImage:[NSImage imageNamed: @"PassesPressed"]];
1009 [toolbarItem setMinSize: [matrix frame].size];
1010 [toolbarItem setMaxSize: [matrix frame].size];
1011 [toolbarItem setView: matrix];
1014 NSSegmentedControl * segControl = [[[NSSegmentedControl alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease];
1015 [[segControl cell] setControlSize:NSSmallControlSize];
1016 [segControl setSegmentCount:2];
1017 [segControl setLabel:@"Encodes" forSegment:0];
1018 [segControl setLabel:@"Passes" forSegment:1];
1019 [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:0];
1020 [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:1];
1021 [segControl setSelectedSegment: (fShowsJobsAsGroups ? 0 : 1)];
1022 [segControl sizeToFit];
1023 [toolbarItem setMinSize: [segControl frame].size];
1024 [toolbarItem setMaxSize: [segControl frame].size];
1025 [toolbarItem setView: segControl];
1029 NSButton * button = [[[NSButton alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease];
1030 [button setButtonType:NSSwitchButton];
1031 [button setTitle:@""];
1032 [button setState: fShowsJobsAsGroups ? NSOnState : NSOffState];
1033 [toolbarItem setMinSize: NSMakeSize(20,20)];
1034 [toolbarItem setMaxSize: NSMakeSize(20,20)];
1035 [toolbarItem setView: button];
1038 // Tell the item what message to send when it is clicked
1039 [toolbarItem setTarget: self];
1040 [toolbarItem setAction: @selector(jobGroupsChanged:)];
1046 //------------------------------------------------------------------------------------
1047 // toolbarDefaultItemIdentifiers:
1048 //------------------------------------------------------------------------------------
1049 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1051 // Required delegate method: Returns the ordered list of items to be shown in the
1052 // toolbar by default.
1054 return [NSArray arrayWithObjects:
1055 HBStartPauseResumeToolbarIdentifier,
1056 NSToolbarSeparatorItemIdentifier,
1057 HBShowGroupsToolbarIdentifier,
1058 HBShowDetailToolbarIdentifier,
1062 //------------------------------------------------------------------------------------
1063 // toolbarAllowedItemIdentifiers:
1064 //------------------------------------------------------------------------------------
1065 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1067 // Required delegate method: Returns the list of all allowed items by identifier.
1068 // By default, the toolbar does not assume any items are allowed, even the
1069 // separator. So, every allowed item must be explicitly listed.
1071 return [NSArray arrayWithObjects:
1072 HBStartPauseResumeToolbarIdentifier,
1073 HBShowGroupsToolbarIdentifier,
1074 HBShowDetailToolbarIdentifier,
1075 NSToolbarCustomizeToolbarItemIdentifier,
1076 NSToolbarFlexibleSpaceItemIdentifier,
1077 NSToolbarSpaceItemIdentifier,
1078 NSToolbarSeparatorItemIdentifier,
1082 //------------------------------------------------------------------------------------
1083 // validateToolbarItem:
1084 //------------------------------------------------------------------------------------
1085 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1087 // Optional method: This message is sent to us since we are the target of some
1088 // toolbar item actions.
1090 if (!fHandle) return NO;
1095 hb_get_state2 (fHandle, &s);
1097 if ([[toolbarItem itemIdentifier] isEqual: HBStartPauseResumeToolbarIdentifier])
1099 if (s.state == HB_STATE_PAUSED)
1102 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1103 [toolbarItem setLabel: @"Resume"];
1104 [toolbarItem setPaletteLabel: @"Resume"];
1105 [toolbarItem setToolTip: @"Resume Encoding"];
1108 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1111 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
1112 [toolbarItem setLabel: @"Pause"];
1113 [toolbarItem setPaletteLabel: @"Pause"];
1114 [toolbarItem setToolTip: @"Pause Encoding"];
1117 else if (hb_count(fHandle) > 0)
1120 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1121 [toolbarItem setLabel: @"Start"];
1122 [toolbarItem setPaletteLabel: @"Start"];
1123 [toolbarItem setToolTip: @"Start Encoding"];
1129 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1130 [toolbarItem setLabel: @"Start"];
1131 [toolbarItem setPaletteLabel: @"Start"];
1132 [toolbarItem setToolTip: @"Start Encoding"];
1136 /* not used because HBShowGroupsToolbarIdentifier is now a custom view
1137 else if ([[toolbarItem itemIdentifier] isEqual: HBShowGroupsToolbarIdentifier])
1139 enable = hb_count(fHandle) > 0;
1140 if (fShowsJobsAsGroups)
1142 [toolbarItem setLabel: @"View Passes"];
1143 [toolbarItem setPaletteLabel: @"View Passes"];
1144 [toolbarItem setToolTip: @"Displays items in the queue as individual passes"];
1148 [toolbarItem setLabel: @"View Encodes"];
1149 [toolbarItem setPaletteLabel: @"View Encodes"];
1150 [toolbarItem setToolTip: @"Displays items in the queue as encodes"];
1155 else if ([[toolbarItem itemIdentifier] isEqual: HBShowDetailToolbarIdentifier])
1157 enable = hb_count(fHandle) > 0;
1160 [toolbarItem setLabel: @"Hide Detail"];
1161 [toolbarItem setPaletteLabel: @"Hide Detail"];
1162 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1166 [toolbarItem setLabel: @"Show Detail"];
1167 [toolbarItem setPaletteLabel: @"Show Detail"];
1168 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1177 //------------------------------------------------------------------------------------
1179 //------------------------------------------------------------------------------------
1180 - (void)awakeFromNib
1182 [self setupToolbar];
1184 if (![fQueueWindow setFrameUsingName:@"Queue"])
1185 [fQueueWindow center];
1186 [fQueueWindow setFrameAutosaveName: @"Queue"];
1187 [fQueueWindow setExcludedFromWindowsMenu:YES];
1189 // Show/hide UI elements
1190 [self setShowsDetail:fShowsDetail];
1191 [self setShowsJobsAsGroups:fShowsJobsAsGroups];
1192 [self showCurrentJobPane:NO];
1196 //------------------------------------------------------------------------------------
1198 //------------------------------------------------------------------------------------
1199 - (void)windowWillClose:(NSNotification *)aNotification
1201 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
1205 #pragma mark NSTableView delegate
1207 //------------------------------------------------------------------------------------
1208 // NSTableView delegate
1209 //------------------------------------------------------------------------------------
1210 - (int)numberOfRowsInTableView: (NSTableView *)aTableView
1212 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1214 return hb_group_count(fHandle);
1216 return hb_count(fHandle);
1219 //------------------------------------------------------------------------------------
1220 // NSTableView delegate
1221 //------------------------------------------------------------------------------------
1222 - (id)tableView: (NSTableView *)aTableView
1223 objectValueForTableColumn: (NSTableColumn *)aTableColumn
1227 return @""; // fatal error!
1231 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1233 job = hb_group(fHandle, rowIndex);
1235 job = hb_job(fHandle, rowIndex);
1238 return @""; // fatal error!
1240 if ([[aTableColumn identifier] isEqualToString:@"desc"])
1242 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1243 return [self attributedDescriptionForJob:job withDetail:fShowsDetail withHighlighting:highlighted];
1246 else if ([[aTableColumn identifier] isEqualToString:@"delete"])
1249 else if ([[aTableColumn identifier] isEqualToString:@"icon"])
1250 return fShowsJobsAsGroups ? [NSImage imageNamed:@"JobSmall"] : [NSImage imageNamed:@"JobPassSmall"];
1255 //------------------------------------------------------------------------------------
1256 // NSTableView delegate
1257 //------------------------------------------------------------------------------------
1258 - (void)tableView: (NSTableView *)aTableView
1259 willDisplayCell: (id)aCell
1260 forTableColumn: (NSTableColumn *)aTableColumn
1263 if ([[aTableColumn identifier] isEqualToString:@"delete"])
1265 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1268 [aCell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1269 [aCell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1273 [aCell setImage:[NSImage imageNamed:@"Delete"]];