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"
8 #include "Controller.h"
9 #import "HBImageAndTextCell.h"
11 #define HB_QUEUE_DRAGGING 0 // <--- NOT COMPLETELY FUNCTIONAL YET
12 #define HBQueueDataType @"HBQueueDataType"
14 // UNI_QUEUE turns on the feature where the first item in the queue NSTableView is the
15 // current job followed by the jobs in hblib's queue. In this scheme, fCurrentJobPane
17 #define HB_UNI_QUEUE 0 // <--- NOT COMPLETELY FUNCTIONAL YET
19 #define HB_ROW_HEIGHT_TITLE_ONLY 17.0
20 #define HB_ROW_HEIGHT_TITLE_WITH_SUMMARY 45.0
21 #define HB_ROW_HEIGHT_INDEPTH_PASS 17.0
22 #define HB_ROW_HEIGHT_1ST_PASS 54.0
23 #define HB_ROW_HEIGHT_2ND_PASS 67.0
25 //#define HB_ROW_HEIGHT_NO_DETAIL 17.0
26 //#define HB_ROW_HEIGHT_ACTIVE_JOB 60.0
28 //------------------------------------------------------------------------------------
29 #pragma mark Job group functions
30 //------------------------------------------------------------------------------------
31 // These could be part of hblib if we think hblib should have knowledge of groups.
32 // For now, I see groups as a metaphor that HBQueueController provides.
35 * Returns the number of jobs groups in the queue.
36 * @param h Handle to hb_handle_t.
37 * @return Number of job groups.
39 static int hb_group_count(hb_handle_t * h)
44 while( ( job = hb_job( h, index++ ) ) )
46 if (job->sequence_id == 0)
53 * Returns handle to the first job in the i-th group within the job list.
54 * @param h Handle to hb_handle_t.
55 * @param i Index of group.
56 * @returns Handle to hb_job_t of desired job.
58 static hb_job_t * hb_group(hb_handle_t * h, int i)
63 while( ( job = hb_job( h, index++ ) ) )
65 if (job->sequence_id == 0)
76 * Removes a groups of jobs from the job list.
77 * @param h Handle to hb_handle_t.
78 * @param job Handle to the first job in the group.
80 static void hb_rem_group( hb_handle_t * h, hb_job_t * job )
85 while( ( j = hb_job( h, index ) ) )
89 // Delete this job plus the following ones in the sequence
91 while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) )
102 * Returns handle to the next job after the given job.
103 * @param h Handle to hb_handle_t.
104 * @param job Handle to the a job in the group.
105 * @returns Handle to hb_job_t of desired job or NULL if no such job.
107 static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job )
111 while( ( j = hb_job( h, index++ ) ) )
114 return hb_job( h, index );
121 //------------------------------------------------------------------------------------
123 //------------------------------------------------------------------------------------
127 @interface HBJob : NSObject
131 + (HBJob*) jobWithJob: (hb_job_t *) job;
132 - (id) initWithJob: (hb_job_t *) job;
136 @implementation HBJob
137 + (HBJob*) jobWithJob: (hb_job_t *) job
139 return [[[HBJob alloc] initWithJob:job] autorelease];
142 - (id) initWithJob: (hb_job_t *) job
144 if (self = [super init])
146 // job is not owned by HBJob. It does not get dealloacted when HBJob is released.
163 #endif // HB_OUTLINE_QUEUE
167 // Toolbar identifiers
168 static NSString* HBQueueToolbar = @"HBQueueToolbar1";
169 static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier";
170 static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier";
171 #if !HB_OUTLINE_QUEUE
172 static NSString* HBShowDetailToolbarIdentifier = @"HBQueueShowDetailToolbarIdentifier";
173 static NSString* HBShowGroupsToolbarIdentifier = @"HBQueueShowGroupsToolbarIdentifier";
177 @implementation HBQueueController
179 //------------------------------------------------------------------------------------
181 //------------------------------------------------------------------------------------
184 if (self = [super init])
187 [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
188 @"NO", @"QueueWindowIsOpen",
189 @"NO", @"QueueShowsDetail",
190 @"YES", @"QueueShowsJobsAsGroups",
195 fShowsJobsAsGroups = YES;
197 fShowsDetail = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsDetail"];
198 fShowsJobsAsGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"];
202 fEncodes = [[NSMutableArray arrayWithCapacity:0] retain];
208 //------------------------------------------------------------------------------------
210 //------------------------------------------------------------------------------------
213 [fAnimation release];
215 // clear the delegate so that windowWillClose is not attempted
216 if ([fQueueWindow delegate] == self)
217 [fQueueWindow setDelegate:nil];
221 [fSavedExpandedItems release];
227 //------------------------------------------------------------------------------------
229 //------------------------------------------------------------------------------------
230 - (void)setHandle: (hb_handle_t *)handle
235 //------------------------------------------------------------------------------------
236 // Receive HBController
237 //------------------------------------------------------------------------------------
238 - (void)setHBController: (HBController *)controller
240 fHBController = controller;
243 //------------------------------------------------------------------------------------
244 // Displays and brings the queue window to the front
245 //------------------------------------------------------------------------------------
246 - (IBAction) showQueueWindow: (id)sender
250 BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow;
251 NSAssert(loadSucceeded, @"Could not open Queue nib file");
254 [self updateQueueUI];
255 [self updateCurrentJobUI];
257 [fQueueWindow makeKeyAndOrderFront: self];
259 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
261 //------------------------------------------------------------------------------------
262 // Show or hide the current job pane (fCurrentJobPane).
263 //------------------------------------------------------------------------------------
264 - (void) showCurrentJobPane: (BOOL)showPane
266 if (showPane != fCurrentJobHidden)
269 // Things to keep in mind:
270 // - When the current job pane is shown, it occupies the upper portion of the
271 // window with the queue occupying the bottom portion of the window.
272 // - When the current job pane is hidden, it slides up and out of view.
273 // NSView setHidden is NOT used. The queue pane is resized to occupy the full
276 NSRect windowFrame = [[fCurrentJobPane superview] frame];
277 NSRect queueFrame, jobFrame;
279 NSDivideRect(windowFrame, &jobFrame, &queueFrame, NSHeight([fCurrentJobPane frame]), NSMaxYEdge);
282 queueFrame = windowFrame;
283 jobFrame = [fCurrentJobPane frame];
284 jobFrame.origin.y = NSHeight(windowFrame);
287 // Move fCurrentJobPane
288 NSDictionary * dict1 = [NSDictionary dictionaryWithObjectsAndKeys:
289 fCurrentJobPane, NSViewAnimationTargetKey,
290 [NSValue valueWithRect:jobFrame], NSViewAnimationEndFrameKey,
294 NSDictionary * dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
295 fQueuePane, NSViewAnimationTargetKey,
296 [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey,
300 fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil];
302 [fAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]];
303 [fAnimation setDuration:0.25];
304 [fAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation
305 [fAnimation startAnimation];
306 fCurrentJobHidden = !showPane;
309 //------------------------------------------------------------------------------------
310 // Enables or disables the display of detail information for each job.
311 //------------------------------------------------------------------------------------
312 - (void)setShowsDetail: (BOOL)showsDetail
315 return; // Can't modify this value. It's always YES.
317 fShowsDetail = showsDetail;
319 [[NSUserDefaults standardUserDefaults] setBool:showsDetail forKey:@"QueueShowsDetail"];
320 [[NSUserDefaults standardUserDefaults] synchronize];
322 [fTaskView setRowHeight:showsDetail ? HB_ROW_HEIGHT_DETAIL : HB_ROW_HEIGHT_NO_DETAIL];
324 if (hb_count(fHandle))
325 [fTaskView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:0]];
327 if ([fTaskView selectedRow] != -1)
328 [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
332 //------------------------------------------------------------------------------------
333 // Enables or disables the grouping of job passes into one item in the UI.
334 //------------------------------------------------------------------------------------
335 - (void)setShowsJobsAsGroups: (BOOL)showsGroups
338 return; // Can't modify this value. It's always YES.
340 fShowsJobsAsGroups = showsGroups;
342 [[NSUserDefaults standardUserDefaults] setBool:showsGroups forKey:@"QueueShowsJobsAsGroups"];
343 [[NSUserDefaults standardUserDefaults] synchronize];
345 [self updateQueueUI];
346 if ([fTaskView selectedRow] != -1)
347 [fTaskView scrollRowToVisible:[fTaskView selectedRow]];
351 //------------------------------------------------------------------------------------
352 // Returns a 16x16 image that represents a job pass.
353 //------------------------------------------------------------------------------------
354 - (NSImage *)smallImageForPass: (int)pass
358 case -1: return [NSImage imageNamed: @"JobPassSubtitleSmall"];
359 case 0: return [NSImage imageNamed: @"JobPassFirstSmall"];
360 case 1: return [NSImage imageNamed: @"JobPassFirstSmall"];
361 case 2: return [NSImage imageNamed: @"JobPassSecondSmall"];
362 default: return [NSImage imageNamed: @"JobPassUnknownSmall"];
366 //------------------------------------------------------------------------------------
367 // Returns a 64x64 image that represents a job pass.
368 //------------------------------------------------------------------------------------
369 - (NSImage *)largeImageForPass: (int)pass
373 case -1: return [NSImage imageNamed: @"JobPassSubtitleLarge"];
374 case 0: return [NSImage imageNamed: @"JobPassFirstLarge"];
375 case 1: return [NSImage imageNamed: @"JobPassFirstLarge"];
376 case 2: return [NSImage imageNamed: @"JobPassSecondLarge"];
377 default: return [NSImage imageNamed: @"JobPassUnknownLarge"];
382 //------------------------------------------------------------------------------------
383 // Rebuilds the contents of fEncodes which is a array of encodes and HBJobs.
384 //------------------------------------------------------------------------------------
385 - (void)rebuildEncodes
387 [fEncodes autorelease];
388 fEncodes = [[NSMutableArray arrayWithCapacity:0] retain];
390 NSMutableArray * aJobGroup = [NSMutableArray arrayWithCapacity:0];
392 hb_job_t * nextJob = hb_group( fHandle, 0 );
395 if (nextJob->sequence_id == 0)
397 // Encountered a new group. Add the current one to fEncodes and then start a new one.
398 if ([aJobGroup count] > 0)
400 [fEncodes addObject:aJobGroup];
401 aJobGroup = [NSMutableArray arrayWithCapacity:0];
404 [aJobGroup addObject: [HBJob jobWithJob:nextJob]];
405 nextJob = hb_next_job (fHandle, nextJob);
407 if ([aJobGroup count] > 0)
409 [fEncodes addObject:aJobGroup];
415 //------------------------------------------------------------------------------------
416 // Saves the state of the items that are currently expanded. Calling restoreOutlineViewState
417 // will restore the state of all items to match what was saved by saveOutlineViewState.
418 //------------------------------------------------------------------------------------
419 - (void) saveOutlineViewState
421 if (!fSavedExpandedItems)
422 fSavedExpandedItems = [[NSMutableIndexSet alloc] init];
424 [fSavedExpandedItems removeAllIndexes];
426 // NB: This code is stuffing the address of each job into an index set. While it
427 // works 99.9% of the time, it's not the ideal solution. We need unique ids in
428 // each job, possibly using the existing sequence_id field. Could use the high
429 // word as a unique encode id and the low word the sequence number.
432 NSEnumerator * e = [fEncodes objectEnumerator];
433 while ( (anEncode = [e nextObject]) )
435 if ([fOutlineView isItemExpanded: anEncode])
436 [fSavedExpandedItems addIndex: (unsigned int)[[anEncode objectAtIndex:0] job]];
439 // Save the selection also. This is really UGLY code. Since I have to rebuild the
440 // entire outline hierachy every time hblib changes its job list, there's no easy
441 // way for me to remember the selection state. So here's the strategy. If a *group*
442 // object is selected, then the first hb_job_t item in the group is saved in
443 // fSavedSelectedItem. If a job is selected, then its hb_job_t item is saved. To
444 // distinguish between a group being selected vs an actual job, the high bit of
445 // fSavedSelectedItem is set when it refers to a job object. I know, not pretty.
446 // This could go away if I'd save a unique id in each job object.
448 int selection = [fOutlineView selectedRow];
450 fSavedSelectedItem = 0;
453 id obj = [fOutlineView itemAtRow: selection];
454 if ([obj isKindOfClass:[HBJob class]])
455 fSavedSelectedItem = (unsigned int)[obj job] | 0x80000000; // set high bit!
457 fSavedSelectedItem = (unsigned int)[[obj objectAtIndex:0] job];
464 //------------------------------------------------------------------------------------
465 // Restores the expanded state of items in the outline view to match those saved by a
466 // previous call to saveOutlineViewState.
467 //------------------------------------------------------------------------------------
468 - (void) restoreOutlineViewState
470 if (fSavedExpandedItems)
473 NSEnumerator * e = [fEncodes objectEnumerator];
474 while ( (anEncode = [e nextObject]) )
476 hb_job_t * j = [[anEncode objectAtIndex:0] job];
477 if ([fSavedExpandedItems containsIndex: (unsigned int)j])
478 [fOutlineView expandItem: anEncode];
482 if (fSavedSelectedItem)
484 // Ugh. Have to cycle through each row looking for the previously selected job.
485 // See the explanation in saveExpandedItems about the logic here.
487 // Find out if the selection was a job or a group.
488 BOOL isJob = (fSavedSelectedItem & 0x80000000) == 0x80000000;
489 // Find out what hb_job_t was selected
490 hb_job_t * j = (hb_job_t *)(fSavedSelectedItem & ~0x80000000); // strip high bit
492 int rowToSelect = -1;
493 for (int i = 0; i < [fOutlineView numberOfRows]; i++)
495 id obj = [fOutlineView itemAtRow: i];
496 if (isJob && [obj isKindOfClass:[HBJob class]])
498 // For a job in the outline view, test to see if it is a match
505 else if (!isJob && ![obj isKindOfClass:[HBJob class]])
507 // For a group, test to see if the group's first job is a match
508 if ([[obj objectAtIndex:0] job] == j)
515 if (rowToSelect == -1)
516 [fOutlineView deselectAll: nil];
518 [fOutlineView selectRow:rowToSelect byExtendingSelection:NO];
523 //------------------------------------------------------------------------------------
524 // Generates a multi-line text string that includes the job name on the first line
525 // followed by details of the job on subsequent lines. If the text is to be drawn as
526 // part of a highlighted cell, set isHighlighted to true. The returned string may
527 // contain multiple fonts and paragraph formating.
528 //------------------------------------------------------------------------------------
529 - (NSAttributedString *)attributedDescriptionForJob: (hb_job_t *)job
530 withTitle: (BOOL)withTitle
531 withDetail: (BOOL)withDetail
532 withHighlighting: (BOOL)highlighted
534 NSMutableAttributedString * finalString; // the return value
535 NSAttributedString* anAttributedString; // a temp string for building up attributed substrings
536 NSMutableString* aMutableString; // a temp string for non-attributed substrings
537 hb_title_t * title = job->title;
539 NSMutableParagraphStyle *ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
540 [ps setLineBreakMode:NSLineBreakByClipping];
542 static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
543 [NSFont systemFontOfSize:11.0], NSFontAttributeName,
544 [NSColor darkGrayColor], NSForegroundColorAttributeName,
545 ps, NSParagraphStyleAttributeName,
547 static NSDictionary* detailHighlightedAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
548 [NSFont systemFontOfSize:11.0], NSFontAttributeName,
549 [NSColor whiteColor], NSForegroundColorAttributeName,
550 ps, NSParagraphStyleAttributeName,
552 static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
553 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
554 ps, NSParagraphStyleAttributeName,
557 finalString = [[[NSMutableAttributedString alloc] init] autorelease];
560 // Show the name of the source Note: use title->name instead of title->dvd since
561 // name is just the chosen folder, instead of dvd which is the full path
564 anAttributedString = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:title->name] attributes:titleAttribute] autorelease];
565 [finalString appendAttributedString:anAttributedString];
568 // Other info in plain
570 aMutableString = [NSMutableString stringWithCapacity:200];
572 // The subtitle scan doesn't contain all the stuff we need (like x264opts).
573 // So grab the next job in the group for display purposes.
575 if (fShowsJobsAsGroups && job->pass == -1)
577 // When job is the one currently being processed, then the next in its group
578 // is the the first job in the queue.
580 if (job == hb_current_job(fHandle))
581 nextjob = hb_job(fHandle, 0);
583 nextjob = hb_next_job(fHandle, job);
584 if (nextjob) // Overly cautious in case there is no next job!
591 NSString * chapterString = (job->chapter_start == job->chapter_end) ?
592 [NSString stringWithFormat:@"Chapter %d", job->chapter_start] :
593 [NSString stringWithFormat:@"Chapters %d through %d", job->chapter_start, job->chapter_end];
598 [aMutableString appendString:[NSString stringWithFormat:
599 @" (Title %d, %@, In-depth Scan)", title->index, chapterString]];
603 int numPasses = MIN( 2, job->pass + 1 );
604 if (fShowsJobsAsGroups)
607 [aMutableString appendString:[NSString stringWithFormat:
608 @" (Title %d, %@, 1 Passes)",
609 title->index, chapterString]];
611 [aMutableString appendString:[NSString stringWithFormat:
612 @" (Title %d, %@, %d Passes)",
613 title->index, chapterString, numPasses]];
616 [aMutableString appendString:[NSString stringWithFormat:
617 @" (Title %d, %@, Pass %d of %d)",
618 title->index, chapterString, MAX( 1, job->pass ), numPasses]];
622 // End of title stuff
625 // Normal pass - show detail
626 if (withDetail && job->pass == -1)
628 [aMutableString appendString:@"Subtitle Pass"];
631 else if (withDetail && job->pass != -1)
633 NSString * jobFormat;
634 NSString * jobPictureDetail;
635 NSString * jobVideoDetail;
636 NSString * jobVideoCodec;
637 NSString * jobVideoQuality;
638 NSString * jobAudioDetail;
639 NSString * jobAudioCodec;
641 if ([aMutableString length] != 0)
642 [aMutableString appendString:@"\n"];
644 // if (job->pass == -1)
645 // [aMutableString appendString:@"Subtitle Pass"];
648 int passNum = MAX( 1, job->pass );
650 [aMutableString appendString:@"1st Pass"];
651 else if (passNum == 2)
652 [aMutableString appendString:@"2nd Pass"];
654 [aMutableString appendString:[NSString stringWithFormat: @"Pass %d", passNum]];
657 /* Muxer settings (File Format in the gui) */
658 if (job->mux == 65536 || job->mux == 131072 || job->mux == 1048576)
659 jobFormat = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD
660 else if (job->mux == 262144)
661 jobFormat = @"AVI"; // HB_MUX_AVI
662 else if (job->mux == 524288)
663 jobFormat = @"OGM"; // HB_MUX_OGM
664 else if (job->mux == 2097152)
665 jobFormat = @"MKV"; // HB_MUX_MKV
667 jobFormat = @"unknown";
670 /* Video Codec settings (Encoder in the gui) */
671 if (job->vcodec == 1)
672 jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG
673 else if (job->vcodec == 2)
674 jobVideoCodec = @"XviD"; // HB_VCODEC_XVID
675 else if (job->vcodec == 4)
677 /* Deterimine for sure how we are now setting iPod uuid atom */
678 if (job->h264_level) // We are encoding for iPod
679 jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264
681 jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264
684 jobVideoCodec = @"unknown";
686 /* Audio Codecs (Second half of Codecs in the gui) */
687 if (job->acodec == 256)
688 jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC
689 else if (job->acodec == 512)
690 jobAudioCodec = @"MP3"; // HB_ACODEC_LAME
691 else if (job->acodec == 1024)
692 jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS
693 else if (job->acodec == 2048)
694 jobAudioCodec = @"AC3"; // HB_ACODEC_AC3
696 jobAudioCodec = @"unknown";
697 /* Show Basic File info */
698 if (job->chapter_markers == 1)
699 [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio, Chapter Markers", jobFormat, jobVideoCodec, jobAudioCodec]];
701 [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio", jobFormat, jobVideoCodec, jobAudioCodec]];
704 /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height,
705 maxWidth, maxHeight */
706 if (job->pixel_ratio == 1)
708 int titlewidth = title->width - job->crop[2] - job->crop[3];
709 int displayparwidth = titlewidth * job->pixel_aspect_width / job->pixel_aspect_height;
710 int displayparheight = title->height - job->crop[0] - job->crop[1];
711 jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, job->width, displayparheight];
714 jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d", job->width, job->height];
715 if (job->keep_ratio == 1)
716 jobPictureDetail = [jobPictureDetail stringByAppendingString:@" Keep Aspect Ratio"];
718 if (job->grayscale == 1)
719 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Grayscale"];
721 if (job->deinterlace == 1)
722 jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Deinterlace"];
723 /* Show Picture info */
724 [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobPictureDetail]];
726 /* Detailed Video info */
727 if (job->vquality <= 0 || job->vquality >= 1)
728 jobVideoQuality =[NSString stringWithFormat:@"%d kbps", job->vbitrate];
731 NSNumber * vidQuality;
732 vidQuality = [NSNumber numberWithInt:job->vquality * 100];
733 /* this is screwed up kind of. Needs to be formatted properly */
735 jobVideoQuality =[NSString stringWithFormat:@"%@%% CRF", vidQuality];
737 jobVideoQuality =[NSString stringWithFormat:@"%@%% CQP", vidQuality];
740 if (job->vrate_base == 1126125)
742 /* NTSC FILM 23.976 */
743 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];
745 else if (job->vrate_base == 900900)
748 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];
752 /* Everything else */
753 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, %d fps", jobVideoCodec, jobVideoQuality, job->vrate / job->vrate_base];
756 /* Add the video detail string to the job filed in the window */
757 [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobVideoDetail]];
759 /* if there is an x264 option string, lets add it here*/
760 /*NOTE: Due to size, lets get this in a tool tip*/
763 [aMutableString appendString:[NSString stringWithFormat:@"\nx264 Options: %@", [NSString stringWithUTF8String:job->x264opts]]];
766 if ([jobAudioCodec isEqualToString: @"AC3"])
767 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, Pass-Through", jobAudioCodec];
769 jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, %d kbps, %d Hz", jobAudioCodec, job->abitrate, job->arate];
771 /* we now get the audio mixdown info for each of the two gui audio tracks */
772 /* lets do it the long way here to get a handle on things.
773 Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] */
774 int ai; // counter for each audios [] , macgui only allows for two audio tracks currently
775 for( ai = 0; ai < 2; ai++ )
777 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_MONO)
778 jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono",ai + 1]];
779 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)
780 jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo",ai + 1]];
781 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)
782 jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround",ai + 1]];
783 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII)
784 jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II",ai + 1]];
785 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_6CH)
786 jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet",ai + 1]];
789 /* Add the Audio detail string to the job field in the window */
790 [aMutableString appendString:[NSString stringWithFormat: @"\n%@", jobAudioDetail]];
792 /*Destination Field */
793 [aMutableString appendString:[NSString stringWithFormat:@"\nDestination: %@", [NSString stringWithUTF8String:job->file]]];
796 anAttributedString = [[[NSAttributedString alloc] initWithString:aMutableString attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease];
797 [finalString appendAttributedString:anAttributedString];
805 - (NSAttributedString *)attributedDescriptionForJob: (hb_job_t *)job
806 withTitle: (BOOL)withTitle
807 withPassName: (BOOL)withPassName
808 withFormatInfo: (BOOL)withFormatInfo
809 withDestination: (BOOL)withDestination
810 withPictureInfo: (BOOL)withPictureInfo
811 withVideoInfo: (BOOL)withVideoInfo
812 withx264Info: (BOOL)withx264Info
813 withAudioInfo: (BOOL)withAudioInfo
814 withHighlighting: (BOOL)highlighted
816 NSMutableArray * stringParts = [NSMutableArray arrayWithCapacity:0];
818 hb_title_t * title = job->title;
821 static NSMutableParagraphStyle *ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
822 [ps setLineBreakMode:NSLineBreakByClipping];
824 static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
825 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
826 [NSColor darkGrayColor], NSForegroundColorAttributeName,
827 ps, NSParagraphStyleAttributeName,
829 static NSDictionary* detailHighlightedAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
830 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
831 [NSColor whiteColor], NSForegroundColorAttributeName,
832 ps, NSParagraphStyleAttributeName,
834 static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys:
835 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
836 ps, NSParagraphStyleAttributeName,
840 // Title with summary
841 NSString * titleString;
844 // Note: use title->name instead of title->dvd since name is just the chosen
845 // folder, instead of dvd which is the full path
846 titleString = [NSString stringWithUTF8String:title->name];
848 NSString * summaryInfo;
850 NSString * chapterString = (job->chapter_start == job->chapter_end) ?
851 [NSString stringWithFormat:@"Chapter %d", job->chapter_start] :
852 [NSString stringWithFormat:@"Chapters %d through %d", job->chapter_start, job->chapter_end];
854 BOOL hasIndepthScan = (job->pass == -1);
855 int numVideoPasses = 0;
857 // To determine number of video passes, we need to skip past the in-depth scan.
858 if (fShowsJobsAsGroups && hasIndepthScan)
860 // When job is the one currently being processed, then the next in its group
861 // is the the first job in the queue.
863 if (job == hb_current_job(fHandle))
864 nextjob = hb_job(fHandle, 0);
866 nextjob = hb_next_job(fHandle, job);
867 if (nextjob) // Overly cautious in case there is no next job!
868 numVideoPasses = MIN( 2, nextjob->pass + 1 );
871 numVideoPasses = MIN( 2, job->pass + 1 );
873 if (hasIndepthScan && numVideoPasses == 1)
874 summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, In-depth Scan, Single Video Pass)", title->index, chapterString];
875 else if (hasIndepthScan && numVideoPasses > 1)
876 summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, In-depth Scan, %d Video Passes)", title->index, chapterString, numVideoPasses];
877 else if (numVideoPasses == 1)
878 summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Single Video Pass)", title->index, chapterString];
880 summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, %d Video Passes)", title->index, chapterString, numVideoPasses];
882 [stringParts addObject: [NSString stringWithFormat:@"%@%@", titleString, summaryInfo]];
885 // End of title stuff
891 NSString * jobPassName;
893 jobPassName = NSLocalizedString (@"In-depth Scan", nil);
896 int passNum = MAX( 1, job->pass );
898 jobPassName = NSLocalizedString (@"Encode Pass", nil);
899 else if (passNum == 1)
900 jobPassName = NSLocalizedString (@"1st Pass", nil);
901 else if (passNum == 2)
902 jobPassName = NSLocalizedString (@"2nd Pass", nil);
904 jobPassName = [NSString stringWithFormat: NSLocalizedString(@"Pass %d", nil), passNum];
906 [stringParts addObject: jobPassName];
909 // Video Codec needed by FormatInfo and withVideoInfo
910 NSString * jobVideoCodec;
911 if (withFormatInfo || withVideoInfo)
914 /* Video Codec settings (Encoder in the gui) */
915 if (job->vcodec == 1)
916 jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG
917 else if (job->vcodec == 2)
918 jobVideoCodec = @"XviD"; // HB_VCODEC_XVID
919 else if (job->vcodec == 4)
921 /* Deterimine for sure how we are now setting iPod uuid atom */
922 if (job->h264_level) // We are encoding for iPod
923 jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264
925 jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264
928 jobVideoCodec = @"unknown";
931 // Audio Codec needed by FormatInfo and AudioInfo
932 NSString * jobAudioCodec;
933 if (withFormatInfo || withAudioInfo)
935 if (job->acodec == 256)
936 jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC
937 else if (job->acodec == 512)
938 jobAudioCodec = @"MP3"; // HB_ACODEC_LAME
939 else if (job->acodec == 1024)
940 jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS
941 else if (job->acodec == 2048)
942 jobAudioCodec = @"AC3"; // HB_ACODEC_AC3
944 jobAudioCodec = @"unknown";
950 NSString * jobFormatInfo;
951 // Muxer settings (File Format in the gui)
952 if (job->mux == 65536 || job->mux == 131072 || job->mux == 1048576)
953 jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD
954 else if (job->mux == 262144)
955 jobFormatInfo = @"AVI"; // HB_MUX_AVI
956 else if (job->mux == 524288)
957 jobFormatInfo = @"OGM"; // HB_MUX_OGM
958 else if (job->mux == 2097152)
959 jobFormatInfo = @"MKV"; // HB_MUX_MKV
961 jobFormatInfo = @"unknown";
963 if (job->chapter_markers == 1)
964 jobFormatInfo = [NSString stringWithFormat:@"Format: %@ Container, %@ Video + %@ Audio, Chapter Markers", jobFormatInfo, jobVideoCodec, jobAudioCodec];
966 jobFormatInfo = [NSString stringWithFormat:@"Format: %@ Container, %@ Video + %@ Audio", jobFormatInfo, jobVideoCodec, jobAudioCodec];
968 [stringParts addObject: jobFormatInfo];
973 NSString * jobDestinationInfo = [NSString stringWithFormat:@"Destination: %@", [NSString stringWithUTF8String:job->file]];
974 [stringParts addObject: jobDestinationInfo];
980 NSString * jobPictureInfo;
981 /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height,
982 maxWidth, maxHeight */
983 if (job->pixel_ratio == 1)
985 int titlewidth = title->width - job->crop[2] - job->crop[3];
986 int displayparwidth = titlewidth * job->pixel_aspect_width / job->pixel_aspect_height;
987 int displayparheight = title->height - job->crop[0] - job->crop[1];
988 jobPictureInfo = [NSString stringWithFormat:@"Picture: %dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, job->width, displayparheight];
991 jobPictureInfo = [NSString stringWithFormat:@"Picture: %dx%d", job->width, job->height];
992 if (job->keep_ratio == 1)
993 jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
995 if (job->grayscale == 1)
996 jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"];
998 if (job->deinterlace == 1)
999 jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"];
1000 [stringParts addObject: jobPictureInfo];
1005 NSString * jobVideoQuality;
1006 NSString * jobVideoDetail;
1008 if (job->vquality <= 0 || job->vquality >= 1)
1009 jobVideoQuality = [NSString stringWithFormat:@"%d kbps", job->vbitrate];
1012 NSNumber * vidQuality;
1013 vidQuality = [NSNumber numberWithInt:job->vquality * 100];
1014 // this is screwed up kind of. Needs to be formatted properly.
1016 jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality];
1018 jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality];
1021 if (job->vrate_base == 1126125)
1023 /* NTSC FILM 23.976 */
1024 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality];
1026 else if (job->vrate_base == 900900)
1029 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality];
1033 /* Everything else */
1034 jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, %d fps", jobVideoCodec, jobVideoQuality, job->vrate / job->vrate_base];
1036 [stringParts addObject: jobVideoDetail];
1041 NSString * jobx264Info;
1044 jobx264Info = [NSString stringWithFormat:@"x264 Options: %@", [NSString stringWithUTF8String:job->x264opts]];
1045 [stringParts addObject: jobx264Info];
1051 NSString * jobAudioInfo;
1052 if ([jobAudioCodec isEqualToString: @"AC3"])
1053 jobAudioInfo = [NSString stringWithFormat:@"Audio: %@, Pass-Through", jobAudioCodec];
1055 jobAudioInfo = [NSString stringWithFormat:@"Audio: %@, %d kbps, %d Hz", jobAudioCodec, job->abitrate, job->arate];
1057 /* we now get the audio mixdown info for each of the two gui audio tracks */
1058 /* lets do it the long way here to get a handle on things.
1059 Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] */
1060 int ai; // counter for each audios [] , macgui only allows for two audio tracks currently
1061 for( ai = 0; ai < 2; ai++ )
1063 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_MONO)
1064 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono", ai + 1]];
1065 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO)
1066 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo", ai + 1]];
1067 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY)
1068 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround", ai + 1]];
1069 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII)
1070 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II", ai + 1]];
1071 if (job->audio_mixdowns[ai] == HB_AMIXDOWN_6CH)
1072 jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet", ai + 1]];
1074 [stringParts addObject: jobAudioInfo];
1078 NSMutableAttributedString * anAttributedString = [[[NSMutableAttributedString alloc]
1079 initWithString:[stringParts componentsJoinedByString:@"\n"]
1080 attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease];
1083 [anAttributedString setAttributes: titleAttribute range: NSMakeRange(0, [titleString length])];
1085 return anAttributedString;
1088 //------------------------------------------------------------------------------------
1089 // Generate string to display in UI.
1090 //------------------------------------------------------------------------------------
1091 - (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s
1093 if (s->state == HB_STATE_WORKING)
1096 if (job->pass == -1)
1097 msg = NSLocalizedString( @"Analyzing subtitles", nil );
1098 else if (job->pass == 1)
1099 msg = NSLocalizedString( @"Analyzing video", nil );
1100 else if ((job->pass == 0) || (job->pass == 2))
1101 msg = NSLocalizedString( @"Encoding movie", nil );
1103 return @""; // unknown condition!
1105 if( s->param.working.seconds > -1 )
1107 return [NSString stringWithFormat:
1108 NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ),
1109 msg, s->param.working.rate_cur, s->param.working.rate_avg];
1116 else if (s->state == HB_STATE_MUXING)
1117 return NSLocalizedString( @"Muxing", nil );
1119 else if (s->state == HB_STATE_PAUSED)
1120 return NSLocalizedString( @"Paused", nil );
1122 else if (s->state == HB_STATE_WORKDONE)
1123 return NSLocalizedString( @"Done", nil );
1128 //------------------------------------------------------------------------------------
1129 // Generate string to display in UI.
1130 //------------------------------------------------------------------------------------
1131 - (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s
1133 if (s->state == HB_STATE_WORKING)
1135 #define p s->param.working
1139 // Minutes always needed
1142 minutes = [NSString stringWithFormat:NSLocalizedString( @"%d minutes ", nil ), p.minutes];
1143 else if (p.minutes == 1)
1144 minutes = NSLocalizedString( @"1 minute ", nil );
1152 hours = [NSString stringWithFormat:NSLocalizedString( @"%d hours ", nil ), p.hours];
1154 hours = NSLocalizedString( @"1 hour ", nil );
1156 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), hours, minutes];
1163 seconds = [NSString stringWithFormat:NSLocalizedString( @"%d seconds ", nil ), p.seconds];
1165 seconds = NSLocalizedString( @"1 second ", nil );
1167 return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), minutes, seconds];
1170 /* here is code that does it more like the Finder
1171 if( p.seconds > -1 )
1173 float estHours = (p.hours + (p.minutes / 60.0));
1174 float estMinutes = (p.minutes + (p.seconds / 60.0));
1177 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d hours", nil ), lrintf(estHours)];
1178 else if (estHours > 0.983) // 59 minutes
1179 return NSLocalizedString( @"Time remaining: About 1 hour", nil );
1180 else if (estMinutes > 1.5)
1181 return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d minutes", nil ), lrintf(estMinutes)];
1182 else if (estMinutes > 0.983) // 59 seconds
1183 return NSLocalizedString( @"Time remaining: About 1 minute", nil );
1184 else if (p.seconds <= 5)
1185 return NSLocalizedString( @"Time remaining: Less than 5 seconds", nil );
1186 else if (p.seconds <= 10)
1187 return NSLocalizedString( @"Time remaining: Less than 10 seconds", nil );
1189 return NSLocalizedString( @"Time remaining: Less than 1 minute", nil );
1192 return NSLocalizedString( @"Time remaining: Calculating...", nil );
1200 //------------------------------------------------------------------------------------
1201 // Refresh progress bar (fProgressBar) from current state.
1202 //------------------------------------------------------------------------------------
1203 - (void) updateProgressBarWithState: (hb_state_t *)s
1205 if (s->state == HB_STATE_WORKING)
1207 #define p s->param.working
1208 [fProgressBar setIndeterminate:NO];
1210 float progress_total = fShowsJobsAsGroups ?
1211 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count :
1214 [fProgressBar setDoubleValue:progress_total];
1218 else if (s->state == HB_STATE_MUXING)
1220 #define p s->param.muxing
1221 [fProgressBar setIndeterminate:YES];
1222 [fProgressBar startAnimation:nil];
1226 else if (s->state == HB_STATE_WORKDONE)
1228 [fProgressBar setIndeterminate:NO];
1229 [fProgressBar setDoubleValue:0.0];
1233 //------------------------------------------------------------------------------------
1234 // Refresh queue count text field (fQueueCountField).
1235 //------------------------------------------------------------------------------------
1236 - (void)updateQueueCountField
1241 if (fShowsJobsAsGroups)
1243 jobCount = fHandle ? hb_group_count(fHandle) : 0;
1245 msg = NSLocalizedString(@"1 pending encode", nil);
1247 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount];
1251 jobCount = fHandle ? hb_count(fHandle) : 0;
1253 msg = NSLocalizedString(@"1 pending pass", nil);
1255 msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending passes", nil), jobCount];
1259 [fQueueCountField setStringValue:msg];
1262 //------------------------------------------------------------------------------------
1263 // Refresh the UI in the current job pane. Should be called whenever the current job
1264 // being processed has changed or when progress has changed.
1265 //------------------------------------------------------------------------------------
1266 - (void)updateCurrentJobUI
1269 hb_job_t * job = nil;
1273 hb_get_state2( fHandle, &s );
1274 job = hb_current_job(fHandle);
1279 if (fLastKnownCurrentJob != job)
1283 case -1: // in-depth scan
1284 [fJobDescTextField setAttributedStringValue:
1285 [self attributedDescriptionForJob:job
1294 withHighlighting: NO]];
1297 case 1: // video 1st pass
1298 [fJobDescTextField setAttributedStringValue:
1299 [self attributedDescriptionForJob:job
1304 withPictureInfo: YES
1308 withHighlighting: NO]];
1311 case 0: // single pass
1312 case 2: // video 2nd pass + audio
1313 [fJobDescTextField setAttributedStringValue:
1314 [self attributedDescriptionForJob:job
1319 withPictureInfo: YES
1323 withHighlighting: NO]];
1327 [fJobDescTextField setAttributedStringValue:
1328 [self attributedDescriptionForJob:job
1333 withPictureInfo: YES
1337 withHighlighting: NO]];
1340 [self showCurrentJobPane:YES];
1341 [fJobIconView setImage: fShowsJobsAsGroups ? [NSImage imageNamed:@"JobLarge"] : [self largeImageForPass: job->pass] ];
1344 NSString * statusMsg = [self progressStatusStringForJob:job state:&s];
1345 NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:&s];
1346 if ([timeMsg length] > 0)
1347 statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg];
1348 [fProgressTextField setStringValue:statusMsg];
1349 [self updateProgressBarWithState:&s];
1353 [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)];
1355 [self showCurrentJobPane:NO];
1356 [fProgressBar stopAnimation:nil]; // just in case in was animating
1359 fLastKnownCurrentJob = job;
1362 //------------------------------------------------------------------------------------
1363 // Refresh the UI in the queue pane. Should be called whenever the content of HB's job
1364 // list has changed so that HBQueueController can sync up.
1365 //------------------------------------------------------------------------------------
1366 - (void)updateQueueUI
1368 #if HB_OUTLINE_QUEUE
1369 [self saveOutlineViewState];
1370 [self rebuildEncodes];
1371 [fOutlineView noteNumberOfRowsChanged];
1372 [fOutlineView reloadData];
1373 [self restoreOutlineViewState];
1375 [fTaskView noteNumberOfRowsChanged];
1376 [fTaskView reloadData];
1379 [self updateQueueCountField];
1382 //------------------------------------------------------------------------------------
1383 // Deletes the selected job from HB and the queue UI
1384 //------------------------------------------------------------------------------------
1385 - (IBAction)removeSelectedJob: (id)sender
1387 if (!fHandle) return;
1389 int row = [sender selectedRow];
1395 [self cancelCurrentJob:sender];
1400 if (fShowsJobsAsGroups)
1401 hb_rem_group( fHandle, hb_group( fHandle, row ) );
1403 hb_rem( fHandle, hb_job( fHandle, row ) );
1406 #if HB_OUTLINE_QUEUE
1407 hb_job_t * job = [[[fOutlineView itemAtRow: row] objectAtIndex: 0] job];
1408 hb_rem_group( fHandle, job );
1410 if (fShowsJobsAsGroups)
1411 hb_rem_group( fHandle, hb_group( fHandle, row ) );
1413 hb_rem( fHandle, hb_job( fHandle, row ) );
1416 [self updateQueueUI];
1420 //------------------------------------------------------------------------------------
1421 // Prompts user if the want to cancel encoding of current job. If so, doCancelCurrentJob
1423 //------------------------------------------------------------------------------------
1424 - (IBAction)cancelCurrentJob: (id)sender
1426 [fHBController Cancel:sender];
1429 //------------------------------------------------------------------------------------
1430 // Turns on the display of detail information for each job. Does nothing if detail is
1431 // already turned on.
1432 //------------------------------------------------------------------------------------
1433 - (IBAction)showDetail: (id)sender
1436 [self setShowsDetail:YES];
1439 //------------------------------------------------------------------------------------
1440 // Turns off the display of detail information for each job. Does nothing if detail is
1441 // already turned off.
1442 //------------------------------------------------------------------------------------
1443 - (IBAction)hideDetail: (id)sender
1446 [self setShowsDetail:NO];
1449 //------------------------------------------------------------------------------------
1450 // Turns on displaying of jobs as groups by calling setShowsJobsAsGroups:YES. Does
1451 // nothing if groups are already turned on.
1452 //------------------------------------------------------------------------------------
1453 - (IBAction)showJobsAsGroups: (id)sender
1455 if (!fShowsJobsAsGroups)
1456 [self setShowsJobsAsGroups:YES];
1459 //------------------------------------------------------------------------------------
1460 // Turns on displaying of jobs as individual items by calling setShowsJobsAsGroups:NO.
1461 // Does nothing if groups are already turned off.
1462 //------------------------------------------------------------------------------------
1463 - (IBAction)showJobsAsPasses: (id)sender
1465 if (fShowsJobsAsGroups)
1466 [self setShowsJobsAsGroups:NO];
1469 //------------------------------------------------------------------------------------
1470 // Starts or cancels the processing of jobs depending on the current state
1471 //------------------------------------------------------------------------------------
1472 - (IBAction)toggleStartCancel: (id)sender
1474 if (!fHandle) return;
1477 hb_get_state2 (fHandle, &s);
1479 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1480 [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window
1484 if (fShowsJobsAsGroups)
1486 if (hb_group_count(fHandle) > 0)
1487 [fHBController doRip];
1489 else if (hb_count(fHandle) > 0)
1490 [fHBController doRip];
1494 //------------------------------------------------------------------------------------
1495 // Toggles the pause/resume state of hblib
1496 //------------------------------------------------------------------------------------
1497 - (IBAction)togglePauseResume: (id)sender
1499 if (!fHandle) return;
1502 hb_get_state2 (fHandle, &s);
1504 if (s.state == HB_STATE_PAUSED)
1505 hb_resume (fHandle);
1506 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1510 #if HB_OUTLINE_METRIC_CONTROLS
1511 static float spacingWidth = 3.0;
1512 - (IBAction)imageSpacingChanged: (id)sender;
1514 spacingWidth = [sender floatValue];
1515 [fOutlineView setNeedsDisplay: YES];
1517 - (IBAction)indentChanged: (id)sender
1519 [fOutlineView setIndentationPerLevel: [sender floatValue]];
1520 [fOutlineView setNeedsDisplay: YES];
1526 #pragma mark Toolbar
1528 //------------------------------------------------------------------------------------
1530 //------------------------------------------------------------------------------------
1531 - (void)setupToolbar
1533 // Create a new toolbar instance, and attach it to our window
1534 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
1536 // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
1537 [toolbar setAllowsUserCustomization: YES];
1538 [toolbar setAutosavesConfiguration: YES];
1539 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1541 // We are the delegate
1542 [toolbar setDelegate: self];
1544 // Attach the toolbar to our window
1545 [fQueueWindow setToolbar: toolbar];
1548 //------------------------------------------------------------------------------------
1549 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
1550 //------------------------------------------------------------------------------------
1551 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
1552 itemForItemIdentifier:(NSString *)itemIdentifier
1553 willBeInsertedIntoToolbar:(BOOL)flag
1555 // Required delegate method: Given an item identifier, this method returns an item.
1556 // The toolbar will use this method to obtain toolbar items that can be displayed
1557 // in the customization sheet, or in the toolbar itself.
1559 NSToolbarItem *toolbarItem = nil;
1561 if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier])
1563 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
1565 // Set the text label to be displayed in the toolbar and customization palette
1566 [toolbarItem setLabel: @"Start"];
1567 [toolbarItem setPaletteLabel: @"Start/Cancel"];
1569 // Set up a reasonable tooltip, and image
1570 [toolbarItem setToolTip: @"Start Encoding"];
1571 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1573 // Tell the item what message to send when it is clicked
1574 [toolbarItem setTarget: self];
1575 [toolbarItem setAction: @selector(toggleStartCancel:)];
1578 if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])
1580 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
1582 // Set the text label to be displayed in the toolbar and customization palette
1583 [toolbarItem setLabel: @"Pause"];
1584 [toolbarItem setPaletteLabel: @"Pause/Resume"];
1586 // Set up a reasonable tooltip, and image
1587 [toolbarItem setToolTip: @"Pause Encoding"];
1588 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1590 // Tell the item what message to send when it is clicked
1591 [toolbarItem setTarget: self];
1592 [toolbarItem setAction: @selector(togglePauseResume:)];
1595 #if !HB_OUTLINE_QUEUE
1596 else if ([itemIdentifier isEqual: HBShowDetailToolbarIdentifier])
1598 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
1600 // Set the text label to be displayed in the toolbar and customization palette
1601 [toolbarItem setLabel: @"Detail"];
1602 [toolbarItem setPaletteLabel: @"Detail"];
1604 // Set up a reasonable tooltip, and image
1605 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1606 [toolbarItem setImage: [NSImage imageNamed: @"Detail"]];
1608 // Tell the item what message to send when it is clicked
1609 [toolbarItem setTarget: self];
1610 [toolbarItem setAction: fShowsDetail ? @selector(hideDetail:) : @selector(showDetail:)];
1613 else if ([itemIdentifier isEqual: HBShowGroupsToolbarIdentifier])
1615 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
1618 // Set the text label to be displayed in the toolbar and customization palette
1619 [toolbarItem setLabel: @"Passes"];
1620 [toolbarItem setPaletteLabel: @"Passes"];
1622 // Set up a reasonable tooltip, and image
1623 [toolbarItem setToolTip: @"Displays individual passes in the queue"];
1624 [toolbarItem setImage: [NSImage imageNamed: @"Passes"]];
1626 // Tell the item what message to send when it is clicked
1627 [toolbarItem setTarget: self];
1628 [toolbarItem setAction: fShowsJobsAsGroups ? @selector(showJobsAsPasses:) : @selector(showJobsAsGroups:)];
1631 // Various attempts at other button types in the toolbar. A matrix worked fine to display
1632 // a button for encodes & passes, but ultimately I decided to go with a single button
1633 // called "Passes" that toggles on or off. All these suffer from the fact taht you need
1634 // to override NSToolbarItem for them in order to validate their state.
1635 [toolbarItem setLabel: @"View"];
1636 [toolbarItem setPaletteLabel: @"View"];
1638 NSButtonCell * buttonCell = [[[NSButtonCell alloc] initImageCell:nil] autorelease];
1639 [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle];
1640 [buttonCell setButtonType:NSToggleButton];
1641 [buttonCell setBordered:NO];
1642 [buttonCell setImagePosition:NSImageOnly];
1644 NSMatrix * matrix = [[[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,54,25)
1645 mode:NSRadioModeMatrix
1646 prototype:buttonCell
1648 numberOfColumns:2] autorelease];
1649 [matrix setCellSize:NSMakeSize(27, 25)];
1650 [matrix setIntercellSpacing:NSMakeSize(0, 0)];
1651 [matrix selectCellAtRow:0 column:(fShowsJobsAsGroups ? 0 : 1)];
1653 buttonCell = [matrix cellAtRow:0 column:0];
1654 [buttonCell setTitle:@""];
1655 [buttonCell setImage:[NSImage imageNamed: @"Encodes"]];
1656 [buttonCell setAlternateImage:[NSImage imageNamed: @"EncodesPressed"]];
1657 [buttonCell setAction: @selector(showJobsAsGroups:)];
1658 [matrix setToolTip: @"Displays encodes in the queue" forCell:buttonCell];
1660 buttonCell = [matrix cellAtRow:0 column:1];
1661 [buttonCell setTitle:@""];
1662 [buttonCell setImage:[NSImage imageNamed: @"Passes"]];
1663 [buttonCell setAlternateImage:[NSImage imageNamed: @"PassesPressed"]];
1664 [buttonCell setAction: @selector(showJobsAsPasses:)];
1665 [matrix setToolTip: @"Displays individual passes in the queue" forCell:buttonCell];
1667 [toolbarItem setMinSize: [matrix frame].size];
1668 [toolbarItem setMaxSize: [matrix frame].size];
1669 [toolbarItem setView: matrix];
1672 NSSegmentedControl * segControl = [[[NSSegmentedControl alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease];
1673 [[segControl cell] setControlSize:NSSmallControlSize];
1674 [segControl setSegmentCount:2];
1675 [segControl setLabel:@"Encodes" forSegment:0];
1676 [segControl setLabel:@"Passes" forSegment:1];
1677 [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:0];
1678 [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:1];
1679 [segControl setSelectedSegment: (fShowsJobsAsGroups ? 0 : 1)];
1680 [segControl sizeToFit];
1681 [toolbarItem setMinSize: [segControl frame].size];
1682 [toolbarItem setMaxSize: [segControl frame].size];
1683 [toolbarItem setView: segControl];
1687 NSButton * button = [[[NSButton alloc] initWithFrame:NSMakeRect(0,0,32,32)] autorelease];
1688 [button setButtonType:NSSwitchButton];
1689 [button setTitle:@""];
1690 [button setState: fShowsJobsAsGroups ? NSOnState : NSOffState];
1691 [toolbarItem setMinSize: NSMakeSize(20,20)];
1692 [toolbarItem setMaxSize: NSMakeSize(20,20)];
1693 [toolbarItem setView: button];
1695 // Tell the item what message to send when it is clicked
1696 [toolbarItem setTarget: self];
1697 [toolbarItem setAction: @selector(jobGroupsChanged:)];
1705 //------------------------------------------------------------------------------------
1706 // toolbarDefaultItemIdentifiers:
1707 //------------------------------------------------------------------------------------
1708 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1710 // Required delegate method: Returns the ordered list of items to be shown in the
1711 // toolbar by default.
1713 return [NSArray arrayWithObjects:
1714 HBQueueStartCancelToolbarIdentifier,
1715 HBQueuePauseResumeToolbarIdentifier,
1716 #if !HB_OUTLINE_QUEUE
1717 NSToolbarSeparatorItemIdentifier,
1718 HBShowGroupsToolbarIdentifier,
1719 HBShowDetailToolbarIdentifier,
1724 //------------------------------------------------------------------------------------
1725 // toolbarAllowedItemIdentifiers:
1726 //------------------------------------------------------------------------------------
1727 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1729 // Required delegate method: Returns the list of all allowed items by identifier.
1730 // By default, the toolbar does not assume any items are allowed, even the
1731 // separator. So, every allowed item must be explicitly listed.
1733 return [NSArray arrayWithObjects:
1734 HBQueueStartCancelToolbarIdentifier,
1735 HBQueuePauseResumeToolbarIdentifier,
1736 #if !HB_OUTLINE_QUEUE
1737 HBShowGroupsToolbarIdentifier,
1738 HBShowDetailToolbarIdentifier,
1740 NSToolbarCustomizeToolbarItemIdentifier,
1741 NSToolbarFlexibleSpaceItemIdentifier,
1742 NSToolbarSpaceItemIdentifier,
1743 NSToolbarSeparatorItemIdentifier,
1747 //------------------------------------------------------------------------------------
1748 // validateToolbarItem:
1749 //------------------------------------------------------------------------------------
1750 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1752 // Optional method: This message is sent to us since we are the target of some
1753 // toolbar item actions.
1755 if (!fHandle) return NO;
1760 hb_get_state2 (fHandle, &s);
1762 if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])
1764 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1767 [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];
1768 [toolbarItem setLabel: @"Stop"];
1769 [toolbarItem setToolTip: @"Stop Encoding"];
1772 else if (hb_count(fHandle) > 0)
1775 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1776 [toolbarItem setLabel: @"Start"];
1777 [toolbarItem setToolTip: @"Start Encoding"];
1783 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1784 [toolbarItem setLabel: @"Start"];
1785 [toolbarItem setToolTip: @"Start Encoding"];
1789 if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])
1791 if (s.state == HB_STATE_PAUSED)
1794 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
1795 [toolbarItem setLabel: @"Resume"];
1796 [toolbarItem setToolTip: @"Resume Encoding"];
1799 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
1802 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
1803 [toolbarItem setLabel: @"Pause"];
1804 [toolbarItem setToolTip: @"Pause Encoding"];
1809 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
1810 [toolbarItem setLabel: @"Pause"];
1811 [toolbarItem setToolTip: @"Pause Encoding"];
1815 #if !HB_OUTLINE_QUEUE
1816 else if ([[toolbarItem itemIdentifier] isEqual: HBShowGroupsToolbarIdentifier])
1818 enable = hb_count(fHandle) > 0;
1819 [toolbarItem setAction: fShowsJobsAsGroups ? @selector(showJobsAsPasses:) : @selector(showJobsAsGroups:)];
1820 if (fShowsJobsAsGroups)
1822 [toolbarItem setImage: [NSImage imageNamed: @"Passes"]];
1823 [toolbarItem setToolTip: @"Displays individual passes in the queue"];
1827 [toolbarItem setImage: [NSImage imageNamed: @"PassesPressed"]];
1828 [toolbarItem setToolTip: @"Displays encodes in the queue"];
1832 else if ([[toolbarItem itemIdentifier] isEqual: HBShowDetailToolbarIdentifier])
1834 enable = hb_count(fHandle) > 0;
1835 [toolbarItem setAction: fShowsDetail ? @selector(hideDetail:) : @selector(showDetail:)];
1838 [toolbarItem setImage: [NSImage imageNamed: @"DetailPressed"]];
1839 [toolbarItem setToolTip: @"Hides detailed information in the queue"];
1843 [toolbarItem setImage: [NSImage imageNamed: @"Detail"]];
1844 [toolbarItem setToolTip: @"Displays detailed information in the queue"];
1854 //------------------------------------------------------------------------------------
1856 //------------------------------------------------------------------------------------
1857 - (void)awakeFromNib
1859 [self setupToolbar];
1861 if (![fQueueWindow setFrameUsingName:@"Queue"])
1862 [fQueueWindow center];
1863 [fQueueWindow setFrameAutosaveName: @"Queue"];
1864 [fQueueWindow setExcludedFromWindowsMenu:YES];
1866 #if HB_OUTLINE_QUEUE
1867 [[fOutlineView enclosingScrollView] setHidden: NO];
1869 [[fTaskView enclosingScrollView] setHidden: NO];
1872 #if HB_QUEUE_DRAGGING
1873 #if HB_OUTLINE_QUEUE
1874 [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:HBQueueDataType] ];
1876 [fTaskView registerForDraggedTypes: [NSArray arrayWithObject:HBQueueDataType] ];
1880 #if HB_OUTLINE_QUEUE
1881 // Don't allow autoresizing of main column, else the "delete" column will get
1882 // pushed out of view.
1883 [fOutlineView setAutoresizesOutlineColumn: NO];
1884 [fOutlineView setIndentationPerLevel:21];
1886 #if HB_OUTLINE_METRIC_CONTROLS
1887 [fIndentation setHidden: NO];
1888 [fSpacing setHidden: NO];
1889 [fIndentation setIntValue:[fOutlineView indentationPerLevel]]; // debug
1890 [fSpacing setIntValue:3]; // debug
1893 // Show/hide UI elements
1894 [self setShowsDetail:fShowsDetail];
1895 [self setShowsJobsAsGroups:fShowsJobsAsGroups];
1896 [self showCurrentJobPane:NO];
1900 //------------------------------------------------------------------------------------
1902 //------------------------------------------------------------------------------------
1903 - (void)windowWillClose:(NSNotification *)aNotification
1905 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
1909 #pragma mark NSTableView delegate
1911 //------------------------------------------------------------------------------------
1912 // NSTableView delegate
1913 //------------------------------------------------------------------------------------
1914 - (int)numberOfRowsInTableView: (NSTableView *)aTableView
1917 int numItems = hb_current_job(fHandle) ? 1 : 0;
1918 if (fShowsJobsAsGroups)
1919 return numItems + hb_group_count(fHandle);
1921 return numItems + hb_count(fHandle);
1923 if (fShowsJobsAsGroups)
1924 return hb_group_count(fHandle);
1926 return hb_count(fHandle);
1930 //------------------------------------------------------------------------------------
1931 // NSTableView delegate
1932 //------------------------------------------------------------------------------------
1933 - (id)tableView: (NSTableView *)aTableView
1934 objectValueForTableColumn: (NSTableColumn *)aTableColumn
1938 return @""; // fatal error!
1940 hb_job_t * job = nil;
1943 // Looking for the current job?
1944 int jobIndex = rowIndex;
1945 if (hb_current_job(fHandle))
1948 job = hb_current_job(fHandle);
1950 jobIndex = rowIndex - 1;
1955 if (fShowsJobsAsGroups)
1956 job = hb_group(fHandle, jobIndex);
1958 job = hb_job(fHandle, jobIndex);
1961 if (fShowsJobsAsGroups)
1962 job = hb_group(fHandle, rowIndex);
1964 job = hb_job(fHandle, rowIndex);
1968 return @""; // fatal error!
1970 if ([[aTableColumn identifier] isEqualToString:@"desc"])
1972 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1973 return [self attributedDescriptionForJob:job withTitle:YES withDetail:fShowsDetail withHighlighting:highlighted];
1976 else if ([[aTableColumn identifier] isEqualToString:@"delete"])
1979 else if ([[aTableColumn identifier] isEqualToString:@"icon"])
1980 return fShowsJobsAsGroups ? [NSImage imageNamed:@"JobSmall"] : [self smallImageForPass: job->pass];
1985 //------------------------------------------------------------------------------------
1986 // NSTableView delegate
1987 //------------------------------------------------------------------------------------
1988 - (void)tableView: (NSTableView *)aTableView
1989 willDisplayCell: (id)aCell
1990 forTableColumn: (NSTableColumn *)aTableColumn
1993 if ([[aTableColumn identifier] isEqualToString:@"delete"])
1995 BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView);
1998 [aCell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1999 [aCell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
2003 [aCell setImage:[NSImage imageNamed:@"Delete"]];
2008 //------------------------------------------------------------------------------------
2009 // NSTableView delegate
2010 //------------------------------------------------------------------------------------
2012 - (float)tableView:(NSTableView *)tableView heightOfRow:(int)row
2014 if ((row == 0) && hb_current_job(fHandle))
2015 return HB_ROW_HEIGHT_ACTIVE_JOB;
2017 return fShowsDetail ? HB_ROW_HEIGHT_DETAIL : HB_ROW_HEIGHT_NO_DETAIL;
2021 #if HB_QUEUE_DRAGGING
2022 - (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
2024 // Copy the row numbers to the pasteboard.
2025 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
2026 [pboard declareTypes:[NSArray arrayWithObject:HBQueueDataType] owner:self];
2027 [pboard setData:data forType:HBQueueDataType];
2032 #if HB_QUEUE_DRAGGING
2033 - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
2035 // Add code here to validate the drop
2036 NSLog(@"validate Drop");
2037 return NSDragOperationEvery;
2041 #if HB_QUEUE_DRAGGING
2042 - (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
2043 row:(int)row dropOperation:(NSTableViewDropOperation)operation
2045 NSPasteboard* pboard = [info draggingPasteboard];
2046 NSData* rowData = [pboard dataForType:HBQueueDataType];
2047 NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
2048 int dragRow = [rowIndexes firstIndex];
2050 // Move the specified row to its new location...
2057 #pragma mark NSOutlineView delegate
2059 #if HB_OUTLINE_QUEUE
2061 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
2064 return [fEncodes objectAtIndex:index];
2066 return [item objectAtIndex:index];
2069 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
2071 return ! [item isKindOfClass:[HBJob class]];
2074 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
2077 return [fEncodes count];
2079 return [item count];
2082 - (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
2085 if (fShowsDetail && [item isKindOfClass:[HBJob class]] && ([item job]->pass != -1))
2086 return HB_ROW_HEIGHT_DETAIL;
2088 return HB_ROW_HEIGHT_NO_DETAIL;
2091 if (fShowsDetail && [item isKindOfClass:[HBJob class]])
2093 hb_job_t * j = [item job];
2094 NSAssert (j != nil, @"job is nil");
2096 return HB_ROW_HEIGHT_INDEPTH_PASS;
2097 else if (j->pass == 1)
2098 return HB_ROW_HEIGHT_1ST_PASS;
2099 else if ((j->pass == 2) || (j->pass == 0))
2100 return HB_ROW_HEIGHT_2ND_PASS;
2102 return HB_ROW_HEIGHT_TITLE_ONLY; // unknown!
2106 if ([outlineView isItemExpanded: item])
2107 return HB_ROW_HEIGHT_TITLE_WITH_SUMMARY;
2109 return HB_ROW_HEIGHT_TITLE_ONLY;
2113 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
2115 BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
2116 if ([item isKindOfClass:[HBJob class]])
2118 if ([[tableColumn identifier] isEqualToString:@"desc"])
2120 hb_job_t * job = [item job];
2123 case -1: // in-depth scan
2124 return [self attributedDescriptionForJob:job
2133 withHighlighting: highlighted];
2136 case 1: // video 1st pass
2137 return [self attributedDescriptionForJob:job
2142 withPictureInfo: YES
2146 withHighlighting: highlighted];
2149 case 0: // single pass
2150 case 2: // video 2nd pass + audio
2151 return [self attributedDescriptionForJob:job
2156 withPictureInfo: YES
2160 withHighlighting: highlighted];
2172 hb_job_t * job = [[item objectAtIndex:0] job];
2173 if ([[tableColumn identifier] isEqualToString:@"desc"])
2175 if ([fOutlineView isItemExpanded: item])
2176 return [self attributedDescriptionForJob:job
2180 withDestination: YES
2185 withHighlighting: highlighted];
2187 return [self attributedDescriptionForJob:job
2196 withHighlighting: highlighted];
2204 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
2206 if ([[tableColumn identifier] isEqualToString:@"desc"])
2208 #if HB_OUTLINE_METRIC_CONTROLS
2209 NSSize theSize = [cell imageSpacing];
2210 theSize.width = spacingWidth;
2211 [cell setImageSpacing: theSize];
2214 // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part
2215 if ([item isKindOfClass:[HBJob class]])
2216 [cell setImage:[self smallImageForPass: [item job]->pass]];
2218 [cell setImage:[NSImage imageNamed:@"JobSmall"]];
2221 else if ([[tableColumn identifier] isEqualToString:@"delete"])
2223 // The Delete action can only be applied for group items, not indivdual jobs.
2224 if ([item isKindOfClass:[HBJob class]])
2226 [cell setEnabled: NO];
2227 [cell setImage: nil];
2231 [cell setEnabled: YES];
2232 BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
2235 [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
2236 [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
2239 [cell setImage:[NSImage imageNamed:@"Delete"]];
2244 - (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
2246 // By default, the discolsure image gets centered vertically in the cell. We want
2247 // always at the top.
2248 if ([outlineView isItemExpanded: item])
2249 [cell setImagePosition: NSImageAbove];
2251 [cell setImagePosition: NSImageOnly];