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 ? 98.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 dictionaryWithObjectsAndKeys:
278 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
279 ps, NSParagraphStyleAttributeName,
282 finalString = [[[NSMutableAttributedString alloc] init] autorelease];
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];
290 // Other info in plain
291 aMutableString = [NSMutableString stringWithCapacity:200];
293 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
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)
299 // When job is the one currently being processed, then the next in its group
300 // is the the first job in the queue.
302 if (job == hb_current_job(fHandle))
303 nextjob = hb_job(fHandle, 0);
305 nextjob = hb_next_job(fHandle, job);
306 if (nextjob) // Overly cautious in case there is no next job!
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];
317 [aMutableString appendString:[NSString stringWithFormat:
318 @" (Title %d, %@, Subtitle Scan)", title->index, chapterString]];
325 [aMutableString appendString:[NSString stringWithFormat:
326 @" (Title %d, %@, %d-Pass)",
327 title->index, chapterString, MIN( 2, job->pass + 1 )]];
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 )]];
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]]];
483 anAttributedString = [[[NSAttributedString alloc] initWithString:aMutableString attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease];
484 [finalString appendAttributedString:anAttributedString];
490 //------------------------------------------------------------------------------------
491 // Generate string to display in UI.
492 //------------------------------------------------------------------------------------
493 - (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s
495 if (s->state == HB_STATE_WORKING)
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 );
505 return @""; // unknown condition!
507 if( s->param.working.seconds > -1 )
509 return [NSString stringWithFormat:
510 NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ),
511 msg, s->param.working.rate_cur, s->param.working.rate_avg];
518 else if (s->state == HB_STATE_MUXING)
519 return NSLocalizedString( @"Muxing", nil );
521 else if (s->state == HB_STATE_PAUSED)
522 return NSLocalizedString( @"Paused", nil );
524 else if (s->state == HB_STATE_WORKDONE)
525 return NSLocalizedString( @"Done", nil );
530 //------------------------------------------------------------------------------------
531 // Generate string to display in UI.
532 //------------------------------------------------------------------------------------
533 - (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s
535 if (s->state == HB_STATE_WORKING)
537 #define p s->param.working
541 // Minutes always needed
544 minutes = [NSString stringWithFormat:NSLocalizedString( @"%d minutes ", nil ), p.minutes];
545 else if (p.minutes == 1)
546 minutes = NSLocalizedString( @"1 minute ", nil );
554 hours = [NSString stringWithFormat:NSLocalizedString( @"%d hours ", nil ), p.hours];
556 hours = NSLocalizedString( @"1 hour ", nil );
558 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), hours, minutes];
565 seconds = [NSString stringWithFormat:NSLocalizedString( @"%d seconds ", nil ), p.seconds];
567 seconds = NSLocalizedString( @"1 second ", nil );
569 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), minutes, seconds];
572 /* here is code that does it more like the Finder
575 float estHours = (p.hours + (p.minutes / 60.0));
576 float estMinutes = (p.minutes + (p.seconds / 60.0));
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 );
591 return NSLocalizedString( @"Time remaining: Less than 1 minute", nil );
594 return NSLocalizedString( @"Time remaining: Calculating...", nil );
602 //------------------------------------------------------------------------------------
603 // Refresh progress bar (fProgressBar) from current state.
604 //------------------------------------------------------------------------------------
605 - (void) updateProgressBarWithState: (hb_state_t *)s
607 if (s->state == HB_STATE_WORKING)
609 #define p s->param.working
610 [fProgressBar setIndeterminate:NO];
612 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
613 float progress_total = jobGroups ?
614 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count :
617 [fProgressBar setDoubleValue:progress_total];
621 else if (s->state == HB_STATE_MUXING)
623 #define p s->param.muxing
624 [fProgressBar setIndeterminate:YES];
625 [fProgressBar startAnimation:nil];
629 else if (s->state == HB_STATE_WORKDONE)
631 [fProgressBar setIndeterminate:NO];
632 [fProgressBar setDoubleValue:0.0];
636 //------------------------------------------------------------------------------------
637 // Refresh start/pause button (fStartPauseButton) from current state.
638 //------------------------------------------------------------------------------------
639 - (void) updateStartPauseButton
642 // ************* THIS METHOD CAN DISAPPEAR. THE BUTTON IS NOW HIDDEN AND CAN BE DELETED
643 if (!fHandle) return;
646 hb_get_state2 (fHandle, &s);
648 if (s.state == HB_STATE_PAUSED)
650 [fStartPauseButton setEnabled:YES];
651 // [fStartPauseButton setTitle:NSLocalizedString(@"Resume", nil)];
652 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
655 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
657 [fStartPauseButton setEnabled:YES];
658 // [fStartPauseButton setTitle:NSLocalizedString(@"Pause", nil)];
659 [fStartPauseButton setImage:[NSImage imageNamed: @"Pause"]];
662 else if (hb_count(fHandle) > 0)
664 [fStartPauseButton setEnabled:YES];
665 // [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
666 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
671 [fStartPauseButton setEnabled:NO];
672 // [fStartPauseButton setTitle:NSLocalizedString(@"Start", nil)];
673 [fStartPauseButton setImage:[NSImage imageNamed: @"Play"]];
677 //------------------------------------------------------------------------------------
678 // Refresh queue count text field (fQueueCountField).
679 //------------------------------------------------------------------------------------
680 - (void)updateQueueCountField
684 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
688 jobCount = fHandle ? hb_group_count(fHandle) : 0;
690 msg = NSLocalizedString(@"1 pending encode", nil);
692 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];
696 jobCount = fHandle ? hb_count(fHandle) : 0;
698 msg = NSLocalizedString(@"1 pending pass", nil);
700 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending passes", nil), jobCount];
704 [fQueueCountField setStringValue:msg];
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
714 hb_job_t * job = nil;
718 hb_get_state( fHandle, &s );
719 job = hb_current_job(fHandle);
724 [fJobDescTextField setAttributedStringValue:[self attributedDescriptionForJob:job withDetail:YES withHighlighting:NO]];
726 [self showCurrentJobPane:YES];
727 [fJobIconView setImage: fShowsJobsAsGroups ? [NSImage imageNamed:@"JobLarge"] : [NSImage imageNamed:@"JobPassLarge"] ];
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];
738 [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)];
740 [self showCurrentJobPane:NO];
741 [fProgressBar stopAnimation:nil]; // just in case in was animating
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];
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
755 [fTaskView noteNumberOfRowsChanged];
756 [fTaskView reloadData];
758 [self updateQueueCountField];
759 [self updateStartPauseButton];
762 //------------------------------------------------------------------------------------
763 // Deletes the selected job from HB and the queue UI
764 //------------------------------------------------------------------------------------
765 - (IBAction)removeSelectedJob: (id)sender
767 if (!fHandle) return;
769 int row = [sender selectedRow];
772 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
774 hb_rem_group( fHandle, hb_group( fHandle, row ) );
776 hb_rem( fHandle, hb_job( fHandle, row ) );
777 [self updateQueueUI];
781 //------------------------------------------------------------------------------------
782 // Prompts user if the want to cancel encoding of current job. If so, hb_stop gets
784 //------------------------------------------------------------------------------------
785 - (IBAction)cancelCurrentJob: (id)sender
787 if (!fHandle) return;
789 hb_job_t * job = hb_current_job(fHandle);
792 // If command key is down, don't prompt
793 BOOL hasCmdKeyMask = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) != 0;
798 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop processing of %@?", nil),
799 [NSString stringWithUTF8String:job->title->name]];
801 NSBeginCriticalAlertSheet(
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]);
808 // cancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
812 - (void) cancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
814 if (returnCode == NSAlertDefaultReturn)
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
824 if ([sender isMemberOfClass:[NSButton class]])
826 BOOL detail = [sender state] == NSOnState;
827 [[NSUserDefaults standardUserDefaults] setBool:detail forKey:@"QueueShowsDetail"];
829 [self setShowsDetail:detail];
833 //------------------------------------------------------------------------------------
834 // Enables or disables the display of job groups based on the state of the sender.
835 //------------------------------------------------------------------------------------
836 - (IBAction)jobGroupsChanged: (id)sender
838 if ([sender isMemberOfClass:[NSButton class]])
840 BOOL groups = [sender state] == NSOnState;
841 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
843 [self setShowsJobsAsGroups:groups];
845 else if ([sender isMemberOfClass:[NSSegmentedControl class]])
847 BOOL groups = [sender selectedSegment] == 0;
848 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
850 [self setShowsJobsAsGroups:groups];
852 else if ([sender isMemberOfClass:[NSMatrix class]])
854 BOOL groups = [sender selectedColumn] == 0;
855 [[NSUserDefaults standardUserDefaults] setBool:groups forKey:@"QueueShowsJobsAsGroups"];
857 [self setShowsJobsAsGroups:groups];
861 //------------------------------------------------------------------------------------
862 // Toggles the Shows Detail setting.
863 //------------------------------------------------------------------------------------
864 - (IBAction)toggleShowsDetail: (id)sender
866 [self setShowsDetail:!fShowsDetail];
869 //------------------------------------------------------------------------------------
870 // Toggles the Shows Jobs As Groups setting.
871 //------------------------------------------------------------------------------------
872 - (IBAction)toggleShowsJobsAsGroups: (id)sender
874 [self setShowsJobsAsGroups:!fShowsJobsAsGroups];
877 //------------------------------------------------------------------------------------
878 // Toggles the processing of jobs on or off depending on the current state
879 //------------------------------------------------------------------------------------
880 - (IBAction)toggleStartPause: (id)sender
882 if (!fHandle) return;
885 hb_get_state2 (fHandle, &s);
887 if (s.state == HB_STATE_PAUSED)
889 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
893 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
896 if (hb_group_count(fHandle) > 0)
899 else if (hb_count(fHandle) > 0)
907 //------------------------------------------------------------------------------------
909 //------------------------------------------------------------------------------------
912 // Create a new toolbar instance, and attach it to our window
913 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
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];
920 // We are the delegate
921 [toolbar setDelegate: self];
923 // Attach the toolbar to our window
924 [fQueueWindow setToolbar: toolbar];
927 //------------------------------------------------------------------------------------
928 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
929 //------------------------------------------------------------------------------------
930 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
931 itemForItemIdentifier:(NSString *)itemIdentifier
932 willBeInsertedIntoToolbar:(BOOL)flag
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.
938 NSToolbarItem *toolbarItem = nil;
940 if ([itemIdentifier isEqual: HBStartPauseResumeToolbarIdentifier])
942 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
944 // Set the text label to be displayed in the toolbar and customization palette
945 [toolbarItem setLabel: @"Start"];
946 [toolbarItem setPaletteLabel: @"Start/Pause"];
948 // Set up a reasonable tooltip, and image
949 [toolbarItem setToolTip: @"Start Encoding"];
950 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
952 // Tell the item what message to send when it is clicked
953 [toolbarItem setTarget: self];
954 [toolbarItem setAction: @selector(toggleStartPause:)];
957 else if ([itemIdentifier isEqual: HBShowDetailToolbarIdentifier])
959 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
961 // Set the text label to be displayed in the toolbar and customization palette
962 [toolbarItem setLabel: @"Show Detail"];
963 [toolbarItem setPaletteLabel: @"Show Detail"];
965 // Set up a reasonable tooltip, and image
966 [toolbarItem setToolTip: @"Show Detail"];
967 [toolbarItem setImage: [NSImage imageNamed: @"Info"]];
969 // Tell the item what message to send when it is clicked
970 [toolbarItem setTarget: self];
971 [toolbarItem setAction: @selector(toggleShowsDetail:)];
974 else if ([itemIdentifier isEqual: HBShowGroupsToolbarIdentifier])
976 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
978 // Set the text label to be displayed in the toolbar and customization palette
979 [toolbarItem setLabel: @"View"];
980 [toolbarItem setPaletteLabel: @"View"];
982 // Set up a reasonable tooltip, and image
983 [toolbarItem setToolTip: @"View"];
984 // [toolbarItem setImage: [NSImage imageNamed: @"Disc"]];
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];
993 NSMatrix * matrix = [[[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,54,25)
994 mode:NSRadioModeMatrix
997 numberOfColumns:2] autorelease];
998 [matrix setCellSize:NSMakeSize(27, 25)];
999 [matrix setIntercellSpacing:NSMakeSize(0, 0)];
1000 [matrix selectCellAtRow:0 column:(fShowsJobsAsGroups ? 0 : 1)];
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];
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];
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];
1039 // Tell the item what message to send when it is clicked
1040 [toolbarItem setTarget: self];
1041 [toolbarItem setAction: @selector(jobGroupsChanged:)];
1047 //------------------------------------------------------------------------------------
1048 // toolbarDefaultItemIdentifiers:
1049 //------------------------------------------------------------------------------------
1050 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1052 // Required delegate method: Returns the ordered list of items to be shown in the
1053 // toolbar by default.
1055 return [NSArray arrayWithObjects:
1056 HBStartPauseResumeToolbarIdentifier,
1057 NSToolbarSeparatorItemIdentifier,
1058 HBShowGroupsToolbarIdentifier,
1059 HBShowDetailToolbarIdentifier,
1063 //------------------------------------------------------------------------------------
1064 // toolbarAllowedItemIdentifiers:
1065 //------------------------------------------------------------------------------------
1066 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
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.
1072 return [NSArray arrayWithObjects:
1073 HBStartPauseResumeToolbarIdentifier,
1074 HBShowGroupsToolbarIdentifier,
1075 HBShowDetailToolbarIdentifier,
1076 NSToolbarCustomizeToolbarItemIdentifier,
1077 NSToolbarFlexibleSpaceItemIdentifier,
1078 NSToolbarSpaceItemIdentifier,
1079 NSToolbarSeparatorItemIdentifier,
1083 //------------------------------------------------------------------------------------
1084 // validateToolbarItem:
1085 //------------------------------------------------------------------------------------
1086 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1088 // Optional method: This message is sent to us since we are the target of some
1089 // toolbar item actions.
1091 if (!fHandle) return NO;
1096 hb_get_state2 (fHandle, &s);
1098 if ([[toolbarItem itemIdentifier] isEqual: HBStartPauseResumeToolbarIdentifier])
1100 if (s.state == HB_STATE_PAUSED)
1103 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1104 [toolbarItem setLabel: @"Resume"];
1105 [toolbarItem setPaletteLabel: @"Resume"];
1106 [toolbarItem setToolTip: @"Resume Encoding"];
1109 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1112 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
1113 [toolbarItem setLabel: @"Pause"];
1114 [toolbarItem setPaletteLabel: @"Pause"];
1115 [toolbarItem setToolTip: @"Pause Encoding"];
1118 else if (hb_count(fHandle) > 0)
1121 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1122 [toolbarItem setLabel: @"Start"];
1123 [toolbarItem setPaletteLabel: @"Start"];
1124 [toolbarItem setToolTip: @"Start Encoding"];
1130 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1131 [toolbarItem setLabel: @"Start"];
1132 [toolbarItem setPaletteLabel: @"Start"];
1133 [toolbarItem setToolTip: @"Start Encoding"];
1137 /* not used because HBShowGroupsToolbarIdentifier is now a custom view
1138 else if ([[toolbarItem itemIdentifier] isEqual: HBShowGroupsToolbarIdentifier])
1140 enable = hb_count(fHandle) > 0;
1141 if (fShowsJobsAsGroups)
1143 [toolbarItem setLabel: @"View Passes"];
1144 [toolbarItem setPaletteLabel: @"View Passes"];
1145 [toolbarItem setToolTip: @"Displays items in the queue as individual passes"];
1149 [toolbarItem setLabel: @"View Encodes"];
1150 [toolbarItem setPaletteLabel: @"View Encodes"];
1151 [toolbarItem setToolTip: @"Displays items in the queue as encodes"];
1156 else if ([[toolbarItem itemIdentifier] isEqual: HBShowDetailToolbarIdentifier])
1158 enable = hb_count(fHandle) > 0;
1161 [toolbarItem setLabel: @"Hide Detail"];
1162 [toolbarItem setPaletteLabel: @"Hide Detail"];
1163 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1167 [toolbarItem setLabel: @"Show Detail"];
1168 [toolbarItem setPaletteLabel: @"Show Detail"];
1169 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1178 //------------------------------------------------------------------------------------
1180 //------------------------------------------------------------------------------------
1181 - (void)awakeFromNib
1183 [self setupToolbar];
1185 if (![fQueueWindow setFrameUsingName:@"Queue"])
1186 [fQueueWindow center];
1187 [fQueueWindow setFrameAutosaveName: @"Queue"];
1188 [fQueueWindow setExcludedFromWindowsMenu:YES];
1190 // Show/hide UI elements
1191 [self setShowsDetail:fShowsDetail];
1192 [self setShowsJobsAsGroups:fShowsJobsAsGroups];
1193 [self showCurrentJobPane:NO];
1197 //------------------------------------------------------------------------------------
1199 //------------------------------------------------------------------------------------
1200 - (void)windowWillClose:(NSNotification *)aNotification
1202 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
1206 #pragma mark NSTableView delegate
1208 //------------------------------------------------------------------------------------
1209 // NSTableView delegate
1210 //------------------------------------------------------------------------------------
1211 - (int)numberOfRowsInTableView: (NSTableView *)aTableView
1213 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1215 return hb_group_count(fHandle);
1217 return hb_count(fHandle);
1220 //------------------------------------------------------------------------------------
1221 // NSTableView delegate
1222 //------------------------------------------------------------------------------------
1223 - (id)tableView: (NSTableView *)aTableView
1224 objectValueForTableColumn: (NSTableColumn *)aTableColumn
1228 return @""; // fatal error!
1232 BOOL jobGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
1234 job = hb_group(fHandle, rowIndex);
1236 job = hb_job(fHandle, rowIndex);
1239 return @""; // fatal error!
1241 if ([[aTableColumn identifier] isEqualToString:@"desc"])
1243 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1244 return [self attributedDescriptionForJob:job withDetail:fShowsDetail withHighlighting:highlighted];
1247 else if ([[aTableColumn identifier] isEqualToString:@"delete"])
1250 else if ([[aTableColumn identifier] isEqualToString:@"icon"])
1251 return fShowsJobsAsGroups ? [NSImage imageNamed:@"JobSmall"] : [NSImage imageNamed:@"JobPassSmall"];
1256 //------------------------------------------------------------------------------------
1257 // NSTableView delegate
1258 //------------------------------------------------------------------------------------
1259 - (void)tableView: (NSTableView *)aTableView
1260 willDisplayCell: (id)aCell
1261 forTableColumn: (NSTableColumn *)aTableColumn
1264 if ([[aTableColumn identifier] isEqualToString:@"delete"])
1266 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1269 [aCell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1270 [aCell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1274 [aCell setImage:[NSImage imageNamed:@"Delete"]];