3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
7 #import "HBQueueController.h"
9 #import "HBImageAndTextCell.h"
11 #define HB_ROW_HEIGHT_TITLE_ONLY 17.0
12 #define HB_ROW_HEIGHT_FULL_DESCRIPTION 200.0
13 // Pasteboard type for or drag operations
14 #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType"
16 //------------------------------------------------------------------------------------
18 //------------------------------------------------------------------------------------
20 //------------------------------------------------------------------------------------
21 // NSMutableAttributedString (HBAdditions)
22 //------------------------------------------------------------------------------------
24 @interface NSMutableAttributedString (HBAdditions)
25 - (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary;
28 @implementation NSMutableAttributedString (HBAdditions)
29 - (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary
31 NSAttributedString * s = [[[NSAttributedString alloc]
32 initWithString: aString
33 attributes: aDictionary] autorelease];
34 [self appendAttributedString: s];
39 @implementation HBQueueOutlineView
41 - (void)viewDidEndLiveResize
43 // Since we disabled calculating row heights during a live resize, force them to
45 [self noteHeightOfRowsWithIndexesChanged:
46 [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [self numberOfRows])]];
47 [super viewDidEndLiveResize];
52 /* This should be for dragging, we take this info from the presets right now */
53 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
57 // By default, NSTableView only drags an image of the first column. Change this to
58 // drag an image of the queue's icon and desc and action columns.
59 NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"desc"], [self tableColumnWithIdentifier:@"icon"],[self tableColumnWithIdentifier:@"action"], nil];
60 return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
65 - (void) mouseDown:(NSEvent *)theEvent
67 [super mouseDown:theEvent];
80 #pragma mark Toolbar Identifiers
81 // Toolbar identifiers
82 static NSString* HBQueueToolbar = @"HBQueueToolbar1";
83 static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier";
84 static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier";
88 @implementation HBQueueController
90 //------------------------------------------------------------------------------------
92 //------------------------------------------------------------------------------------
95 if (self = [super initWithWindowNibName:@"Queue"])
97 // NSWindowController likes to lazily load its window nib. Since this
98 // controller tries to touch the outlets before accessing the window, we
99 // need to force it to load immadiately by invoking its accessor.
101 // If/when we switch to using bindings, this can probably go away.
105 [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
106 @"NO", @"QueueWindowIsOpen",
107 @"NO", @"QueueShowsDetail",
108 @"YES", @"QueueShowsJobsAsGroups",
111 fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];
116 - (void)setQueueArray: (NSMutableArray *)QueueFileArray
118 [fJobGroups setArray:QueueFileArray];
120 /* First stop any timer working now */
121 [self stopAnimatingCurrentJobGroupInQueue];
122 [fOutlineView reloadData];
126 /* lets get the stats on the status of the queue array */
128 fEncodingQueueItem = 0;
134 /* We use a number system to set the encode status of the queue item
136 * 0 == already encoded
137 * 1 == is being encoded
138 * 2 == is yet to be encoded
143 for(id tempObject in fJobGroups)
145 NSDictionary *thisQueueDict = tempObject;
146 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
150 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
153 fEncodingQueueItem = i;
155 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending
159 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled
166 /* We should fire up the encoding timer here based on fWorkingCount */
168 if (fWorkingCount > 0)
170 /* we have an encoding job so, lets start the animation timer */
171 [self startAnimatingCurrentWorkingEncodeInQueue];
174 /* Set the queue status field in the queue window */
175 NSMutableString * string;
176 if (fPendingCount == 1)
178 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
182 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount];
184 [fQueueCountField setStringValue:string];
187 /* This method sets the status string in the queue window
188 * and is called from Controller.mm (fHBController)
189 * instead of running another timer here polling libhb
190 * for encoding status
192 - (void)setQueueStatusString: (NSString *)statusString
194 [fProgressTextField setStringValue:statusString];
197 //------------------------------------------------------------------------------------
199 //------------------------------------------------------------------------------------
202 // clear the delegate so that windowWillClose is not attempted
203 if( [[self window] delegate] == self )
204 [[self window] setDelegate:nil];
206 [fJobGroups release];
208 [fSavedExpandedItems release];
209 [fSavedSelectedItems release];
211 [[NSNotificationCenter defaultCenter] removeObserver:self];
216 //------------------------------------------------------------------------------------
218 //------------------------------------------------------------------------------------
219 - (void)setHandle: (hb_handle_t *)handle
221 fQueueEncodeLibhb = handle;
224 //------------------------------------------------------------------------------------
225 // Receive HBController
226 //------------------------------------------------------------------------------------
227 - (void)setHBController: (HBController *)controller
229 fHBController = controller;
234 //------------------------------------------------------------------------------------
235 // Displays and brings the queue window to the front
236 //------------------------------------------------------------------------------------
237 - (IBAction) showQueueWindow: (id)sender
239 [self showWindow:sender];
240 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
245 //------------------------------------------------------------------------------------
247 //------------------------------------------------------------------------------------
252 if( ![[self window] setFrameUsingName:@"Queue"] )
253 [[self window] center];
254 [self setWindowFrameAutosaveName:@"Queue"];
255 //[[self window] setExcludedFromWindowsMenu:YES];
257 /* lets setup our queue list outline view for drag and drop here */
258 [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
259 [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
260 [fOutlineView setVerticalMotionCanBeginDrag: YES];
263 // Don't allow autoresizing of main column, else the "delete" column will get
264 // pushed out of view.
265 [fOutlineView setAutoresizesOutlineColumn: NO];
267 #if HB_OUTLINE_METRIC_CONTROLS
268 [fIndentation setHidden: NO];
269 [fSpacing setHidden: NO];
270 [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]]; // debug
271 [fSpacing setIntegerValue:3]; // debug
274 // Show/hide UI elements
275 fCurrentJobPaneShown = NO; // it's shown in the nib
276 //[self showCurrentJobPane:NO];
278 //[self updateQueueCountField];
282 //------------------------------------------------------------------------------------
284 //------------------------------------------------------------------------------------
285 - (void)windowWillClose:(NSNotification *)aNotification
287 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
292 //------------------------------------------------------------------------------------
294 //------------------------------------------------------------------------------------
297 // Create a new toolbar instance, and attach it to our window
298 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
300 // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
301 [toolbar setAllowsUserCustomization: YES];
302 [toolbar setAutosavesConfiguration: YES];
303 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
305 // We are the delegate
306 [toolbar setDelegate: self];
308 // Attach the toolbar to our window
309 [[self window] setToolbar:toolbar];
312 //------------------------------------------------------------------------------------
313 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
314 //------------------------------------------------------------------------------------
315 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
316 itemForItemIdentifier:(NSString *)itemIdentifier
317 willBeInsertedIntoToolbar:(BOOL)flag
319 // Required delegate method: Given an item identifier, this method returns an item.
320 // The toolbar will use this method to obtain toolbar items that can be displayed
321 // in the customization sheet, or in the toolbar itself.
323 NSToolbarItem *toolbarItem = nil;
325 if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier])
327 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
329 // Set the text label to be displayed in the toolbar and customization palette
330 [toolbarItem setLabel: @"Start"];
331 [toolbarItem setPaletteLabel: @"Start/Cancel"];
333 // Set up a reasonable tooltip, and image
334 [toolbarItem setToolTip: @"Start Encoding"];
335 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
337 // Tell the item what message to send when it is clicked
338 [toolbarItem setTarget: self];
339 [toolbarItem setAction: @selector(toggleStartCancel:)];
342 if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])
344 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
346 // Set the text label to be displayed in the toolbar and customization palette
347 [toolbarItem setLabel: @"Pause"];
348 [toolbarItem setPaletteLabel: @"Pause/Resume"];
350 // Set up a reasonable tooltip, and image
351 [toolbarItem setToolTip: @"Pause Encoding"];
352 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
354 // Tell the item what message to send when it is clicked
355 [toolbarItem setTarget: self];
356 [toolbarItem setAction: @selector(togglePauseResume:)];
362 //------------------------------------------------------------------------------------
363 // toolbarDefaultItemIdentifiers:
364 //------------------------------------------------------------------------------------
365 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
367 // Required delegate method: Returns the ordered list of items to be shown in the
368 // toolbar by default.
370 return [NSArray arrayWithObjects:
371 HBQueueStartCancelToolbarIdentifier,
372 HBQueuePauseResumeToolbarIdentifier,
376 //------------------------------------------------------------------------------------
377 // toolbarAllowedItemIdentifiers:
378 //------------------------------------------------------------------------------------
379 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
381 // Required delegate method: Returns the list of all allowed items by identifier.
382 // By default, the toolbar does not assume any items are allowed, even the
383 // separator. So, every allowed item must be explicitly listed.
385 return [NSArray arrayWithObjects:
386 HBQueueStartCancelToolbarIdentifier,
387 HBQueuePauseResumeToolbarIdentifier,
388 NSToolbarCustomizeToolbarItemIdentifier,
389 NSToolbarFlexibleSpaceItemIdentifier,
390 NSToolbarSpaceItemIdentifier,
391 NSToolbarSeparatorItemIdentifier,
395 //------------------------------------------------------------------------------------
396 // validateToolbarItem:
397 //------------------------------------------------------------------------------------
398 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
400 // Optional method: This message is sent to us since we are the target of some
401 // toolbar item actions.
403 if (!fQueueEncodeLibhb) return NO;
408 hb_get_state2 (fQueueEncodeLibhb, &s);
410 if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])
412 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
415 [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];
416 [toolbarItem setLabel: @"Stop"];
417 [toolbarItem setToolTip: @"Stop Encoding"];
420 else if (fPendingCount > 0)
423 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
424 [toolbarItem setLabel: @"Start"];
425 [toolbarItem setToolTip: @"Start Encoding"];
431 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
432 [toolbarItem setLabel: @"Start"];
433 [toolbarItem setToolTip: @"Start Encoding"];
437 if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])
439 if (s.state == HB_STATE_PAUSED)
442 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
443 [toolbarItem setLabel: @"Resume"];
444 [toolbarItem setToolTip: @"Resume Encoding"];
447 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
450 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
451 [toolbarItem setLabel: @"Pause"];
452 [toolbarItem setToolTip: @"Pause Encoding"];
457 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
458 [toolbarItem setLabel: @"Pause"];
459 [toolbarItem setToolTip: @"Pause Encoding"];
469 #pragma mark Queue Item Controls
470 //------------------------------------------------------------------------------------
471 // Delete encodes from the queue window and accompanying array
472 // Also handling first cancelling the encode if in fact its currently encoding.
473 //------------------------------------------------------------------------------------
474 - (IBAction)removeSelectedQueueItem: (id)sender
476 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
477 NSUInteger row = [selectedRows firstIndex];
478 if( row == NSNotFound )
480 /* if this is a currently encoding job, we need to be sure to alert the user,
481 * to let them decide to cancel it first, then if they do, we can come back and
484 if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
486 /* We pause the encode here so that it doesn't finish right after and then
487 * screw up the sync while the window is open
489 [fHBController Pause:NULL];
490 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
491 // Which window to attach the sheet to?
492 NSWindow * docWindow = nil;
493 if ([sender respondsToSelector: @selector(window)])
494 docWindow = [sender window];
497 NSBeginCriticalAlertSheet(
499 NSLocalizedString(@"Keep Encoding", nil),
501 NSLocalizedString(@"Stop Encoding and Delete", nil),
503 nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
504 NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
506 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
510 /* since we are not a currently encoding item, we can just be cancelled */
511 [fHBController removeQueueFileItem:row];
515 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
517 /* We resume encoding and perform the appropriate actions
518 * Note: Pause: is a toggle type method based on hb's current
519 * state, if it paused, it will resume encoding and vice versa.
520 * In this case, we are paused from the calling window, so calling
521 * [fHBController Pause:NULL]; Again will resume encoding
523 [fHBController Pause:NULL];
524 if (returnCode == NSAlertOtherReturn)
526 /* We need to save the currently encoding item number first */
527 int encodingItemToRemove = fEncodingQueueItem;
528 /* Since we are encoding, we need to let fHBController Cancel this job
529 * upon which it will move to the next one if there is one
531 [fHBController doCancelCurrentJob];
532 /* Now, we can go ahead and remove the job we just cancelled since
533 * we have its item number from above
535 [fHBController removeQueueFileItem:encodingItemToRemove];
540 //------------------------------------------------------------------------------------
541 // Show the finished encode in the finder
542 //------------------------------------------------------------------------------------
543 - (IBAction)revealSelectedQueueItem: (id)sender
545 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
546 NSInteger row = [selectedRows firstIndex];
547 if (row != NSNotFound)
549 while (row != NSNotFound)
551 NSMutableDictionary *queueItemToOpen = [fOutlineView itemAtRow: row];
552 [[NSWorkspace sharedWorkspace] selectFile:[queueItemToOpen objectForKey:@"DestinationPath"] inFileViewerRootedAtPath:nil];
554 row = [selectedRows indexGreaterThanIndex: row];
560 //------------------------------------------------------------------------------------
561 // Starts or cancels the processing of jobs depending on the current state
562 //------------------------------------------------------------------------------------
563 - (IBAction)toggleStartCancel: (id)sender
565 if (!fQueueEncodeLibhb) return;
568 hb_get_state2 (fQueueEncodeLibhb, &s);
570 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
571 [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window
573 else if (fPendingCount > 0)
574 [fHBController Rip: NULL];
577 //------------------------------------------------------------------------------------
578 // Toggles the pause/resume state of libhb
579 //------------------------------------------------------------------------------------
580 - (IBAction)togglePauseResume: (id)sender
582 if (!fQueueEncodeLibhb) return;
585 hb_get_state2 (fQueueEncodeLibhb, &s);
587 if (s.state == HB_STATE_PAUSED)
589 hb_resume (fQueueEncodeLibhb);
590 [self startAnimatingCurrentWorkingEncodeInQueue];
592 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
594 hb_pause (fQueueEncodeLibhb);
595 [self stopAnimatingCurrentJobGroupInQueue];
600 //------------------------------------------------------------------------------------
601 // Send the selected queue item back to the main window for rescan and possible edit.
602 //------------------------------------------------------------------------------------
603 - (IBAction)editSelectedQueueItem: (id)sender
605 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
606 NSUInteger row = [selectedRows firstIndex];
607 if( row == NSNotFound )
609 /* if this is a currently encoding job, we need to be sure to alert the user,
610 * to let them decide to cancel it first, then if they do, we can come back and
613 if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
615 /* We pause the encode here so that it doesn't finish right after and then
616 * screw up the sync while the window is open
618 [fHBController Pause:NULL];
619 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
620 // Which window to attach the sheet to?
621 NSWindow * docWindow = nil;
622 if ([sender respondsToSelector: @selector(window)])
623 docWindow = [sender window];
626 NSBeginCriticalAlertSheet(
628 NSLocalizedString(@"Keep Encoding", nil),
630 NSLocalizedString(@"Stop Encoding and Delete", nil),
632 nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
633 NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
638 /* since we are not a currently encoding item, we can just be cancelled */
639 [fHBController rescanQueueItemToMainWindow:[[fJobGroups objectAtIndex:row] objectForKey:@"SourcePath"] scanTitleNum:[[[fJobGroups objectAtIndex:row] objectForKey:@"TitleNumber"] integerValue] selectedQueueItem:row];
646 #pragma mark Animate Endcoding Item
651 //------------------------------------------------------------------------------------
652 // Starts animating the job icon of the currently processing job in the queue outline
654 //------------------------------------------------------------------------------------
655 - (void) startAnimatingCurrentWorkingEncodeInQueue
657 if (!fAnimationTimer)
658 fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle
660 selector:@selector(animateWorkingEncodeInQueue:)
662 repeats:YES] retain];
665 //------------------------------------------------------------------------------------
666 // If a job is currently processing, its job icon in the queue outline view is
667 // animated to its next state.
668 //------------------------------------------------------------------------------------
669 - (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer
671 if (fWorkingCount > 0)
674 fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.
675 [self animateWorkingEncodeIconInQueue];
680 - (void) animateWorkingEncodeIconInQueue
682 NSInteger row = fEncodingQueueItem; /// need to set to fEncodingQueueItem
683 NSInteger col = [fOutlineView columnWithIdentifier: @"icon"];
684 if (row != -1 && col != -1)
686 NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];
687 [fOutlineView setNeedsDisplayInRect: frame];
691 //------------------------------------------------------------------------------------
692 // Stops animating the job icon of the currently processing job in the queue outline
694 //------------------------------------------------------------------------------------
695 - (void) stopAnimatingCurrentJobGroupInQueue
697 if (fAnimationTimer && [fAnimationTimer isValid])
699 [fAnimationTimer invalidate];
700 [fAnimationTimer release];
701 fAnimationTimer = nil;
708 - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
710 NSUInteger index = [indexSet lastIndex];
711 NSUInteger aboveInsertIndexCount = 0;
713 while (index != NSNotFound)
715 NSUInteger removeIndex;
717 if (index >= insertIndex)
719 removeIndex = index + aboveInsertIndexCount;
720 aboveInsertIndexCount++;
728 id object = [[array objectAtIndex:removeIndex] retain];
729 [array removeObjectAtIndex:removeIndex];
730 [array insertObject:object atIndex:insertIndex];
733 index = [indexSet indexLessThanIndex:index];
739 #pragma mark NSOutlineView delegate
742 - (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item
745 return [fJobGroups objectAtIndex:index];
747 // We are only one level deep, so we can't be asked about children
748 NSAssert (NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items.");
752 - (BOOL)outlineView:(NSOutlineView *)fOutlineView isItemExpandable:(id)item
754 // Our outline view has no levels, but we can still expand every item. Doing so
755 // just makes the row taller. See heightOfRowByItem below.
759 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
761 // Our outline view has no levels, but we can still expand every item. Doing so
762 // just makes the row taller. See heightOfRowByItem below.
763 return ![(HBQueueOutlineView*)outlineView isDragging];
766 - (NSInteger)outlineView:(NSOutlineView *)fOutlineView numberOfChildrenOfItem:(id)item
768 // Our outline view has no levels, so number of children will be zero for all
771 return [fJobGroups count];
776 - (void)outlineViewItemDidCollapse:(NSNotification *)notification
778 id item = [[notification userInfo] objectForKey:@"NSObject"];
779 NSInteger row = [fOutlineView rowForItem:item];
780 [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
783 - (void)outlineViewItemDidExpand:(NSNotification *)notification
785 id item = [[notification userInfo] objectForKey:@"NSObject"];
786 NSInteger row = [fOutlineView rowForItem:item];
787 [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
790 - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
792 if ([outlineView isItemExpanded: item])
794 /* Below is the original code to accommodate a live resize,
795 * however as stated in travistex's comments it's very buggy.
796 * For now I will leave it here ... commented out and use
797 * the code below to determine the row height based on each
798 * encodes optional parameters and how they are displayed. */
800 // Short-circuit here if in a live resize primarily to fix a bug but also to
801 // increase resposivness during a resize. There's a bug in NSTableView that
802 // causes row heights to get messed up if you try to change them during a live
803 // resize. So if in a live resize, simply return the previously calculated
804 // height. The row heights will get fixed up after the resize because we have
805 // implemented viewDidEndLiveResize to force all of them to be recalculated.
806 // if ([outlineView inLiveResize] && [item lastDescriptionHeight] > 0)
807 // return [item lastDescriptionHeight];
809 // CGFloat width = [[outlineView tableColumnWithIdentifier: @"desc"] width];
810 // Column width is NOT what is ultimately used. I can't quite figure out what
811 // width to use for calculating text metrics. No matter how I tweak this value,
812 // there are a few conditions in which the drawn text extends below the bounds
813 // of the row cell. In previous versions, which ran under Tiger, I was
814 // reducing width by 47 pixles.
815 // width -= 2; // (?) for intercell spacing
817 // CGFloat height = [item heightOfDescriptionForWidth: width];
820 /* So, we know several rows of text that are in all queue items for display.
821 * These are the title line, Preset, Format, Destination, Picture, and Video Lines
823 CGFloat rowHeightNonTitle = 15.0;
824 /* Add the title line height, then the non title line height for Preset, Format, Destination
827 CGFloat itemHeightForDisplay = HB_ROW_HEIGHT_TITLE_ONLY + (rowHeightNonTitle * 5);
829 /* get our item row number so we an use it to calc how many lines we have to display based
830 * on MP4 Options, Filter Options, X264 Options, Audio Tracks and Subtitles from our queue array */
831 int itemRowNum = [outlineView rowForItem: item];
832 NSMutableDictionary *queueItemToCheck = [outlineView itemAtRow: itemRowNum];
834 /* Check to see if we need to allow for mp4 opts */
835 BOOL mp4OptsPresent = NO;
836 if ([[queueItemToCheck objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
839 if( [[queueItemToCheck objectForKey:@"Mp4LargeFile"] intValue] == 1)
841 mp4OptsPresent = YES;
843 if( [[queueItemToCheck objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
845 mp4OptsPresent = YES;
847 if( [[queueItemToCheck objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
849 mp4OptsPresent = YES;
853 if (mp4OptsPresent == YES)
855 itemHeightForDisplay += rowHeightNonTitle;
858 /* check to see if we need to allow for the Picture Filters row */
859 BOOL pictureFiltersPresent = NO;
860 if( [[queueItemToCheck objectForKey:@"PictureDetelecine"] intValue] > 0)
862 pictureFiltersPresent = YES;
864 if( [[queueItemToCheck objectForKey:@"PictureDecomb"] intValue] > 0)
866 pictureFiltersPresent = YES;
868 if( [[queueItemToCheck objectForKey:@"PictureDeinterlace"] intValue] > 0)
870 pictureFiltersPresent = YES;
872 if( [[queueItemToCheck objectForKey:@"PictureDenoise"] intValue] > 0)
874 pictureFiltersPresent = YES;
876 if( [[queueItemToCheck objectForKey:@"PictureDeblock"] intValue] > 0)
878 pictureFiltersPresent = YES;
880 if( [[queueItemToCheck objectForKey:@"VideoGrayScale"] intValue] > 0)
882 pictureFiltersPresent = YES;
885 if (pictureFiltersPresent == YES)
887 itemHeightForDisplay += rowHeightNonTitle;
890 /* check to see if we need a line to display x264 options */
891 if ([[queueItemToCheck objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
893 itemHeightForDisplay += rowHeightNonTitle;
896 /* check to see how many audio track lines to allow for */
897 if ([[queueItemToCheck objectForKey:@"Audio1Track"] intValue] > 0)
899 itemHeightForDisplay += rowHeightNonTitle;
901 if ([[queueItemToCheck objectForKey:@"Audio2Track"] intValue] > 0)
903 itemHeightForDisplay += rowHeightNonTitle;
905 if ([[queueItemToCheck objectForKey:@"Audio3Track"] intValue] > 0)
907 itemHeightForDisplay += rowHeightNonTitle;
909 if ([[queueItemToCheck objectForKey:@"Audio4Track"] intValue] > 0)
911 itemHeightForDisplay += rowHeightNonTitle;
914 /* add in subtitle lines for each subtitle in the SubtitleList array */
915 itemHeightForDisplay += rowHeightNonTitle * [[queueItemToCheck objectForKey:@"SubtitleList"] count];
917 return itemHeightForDisplay;
922 return HB_ROW_HEIGHT_TITLE_ONLY;
926 - (CGFloat) heightOfDescriptionForWidth:(CGFloat)width
928 // Try to return the cached value if no changes have happened since the last time
929 //if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription)
930 // return fLastDescriptionHeight;
932 //if (fNeedsDescription)
933 // [self updateDescription];
935 // Calculate the height
936 //NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];
937 //fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom
938 //fLastDescriptionWidth = width;
939 return HB_ROW_HEIGHT_FULL_DESCRIPTION;
941 /* supposedly another way to do this, in case boundingRectWithSize isn't working
942 NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)];
943 [[tmpView textStorage] setAttributedString:aString];
944 [tmpView setHorizontallyResizable:NO];
945 [tmpView setVerticallyResizable:YES];
946 // [[tmpView textContainer] setHeightTracksTextView: YES];
947 // [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)];
949 float height = [tmpView frame].size.height;
955 - (CGFloat) lastDescriptionHeight
957 return HB_ROW_HEIGHT_FULL_DESCRIPTION;
960 - (id)outlineView:(NSOutlineView *)fOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
962 // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer
963 // using the image portion of the cell so we could switch back to a regular NSTextFieldCell.
965 if ([[tableColumn identifier] isEqualToString:@"desc"])
969 /* Below should be put into a separate method but I am way too f'ing lazy right now */
970 NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];
972 NSMutableParagraphStyle * ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
973 [ps setHeadIndent: 40.0];
974 [ps setParagraphSpacing: 1.0];
975 [ps setTabStops:[NSArray array]]; // clear all tabs
976 [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];
979 NSDictionary* detailAttr = [NSDictionary dictionaryWithObjectsAndKeys:
980 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
981 ps, NSParagraphStyleAttributeName,
984 NSDictionary* detailBoldAttr = [NSDictionary dictionaryWithObjectsAndKeys:
985 [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName,
986 ps, NSParagraphStyleAttributeName,
989 NSDictionary* titleAttr = [NSDictionary dictionaryWithObjectsAndKeys:
990 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
991 ps, NSParagraphStyleAttributeName,
994 NSDictionary* shortHeightAttr = [NSDictionary dictionaryWithObjectsAndKeys:
995 [NSFont systemFontOfSize:2.0], NSFontAttributeName,
998 /* First line, we should strip the destination path and just show the file name and add the title num and chapters (if any) */
999 //finalDescription = [finalDescription stringByAppendingString:[NSString stringWithFormat:@"Source: %@ Output: %@\n", [item objectForKey:@"SourceName"],[item objectForKey:@"DestinationPath"]]];
1000 NSString * summaryInfo;
1002 NSString * titleString = [NSString stringWithFormat:@"Title %d", [[item objectForKey:@"TitleNumber"] intValue]];
1004 NSString * startStopString = @"";
1005 if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 0)
1007 /* Start Stop is chapters */
1008 startStopString = ([[item objectForKey:@"ChapterStart"] intValue] == [[item objectForKey:@"ChapterEnd"] intValue]) ?
1009 [NSString stringWithFormat:@"Chapter %d", [[item objectForKey:@"ChapterStart"] intValue]] :
1010 [NSString stringWithFormat:@"Chapters %d through %d", [[item objectForKey:@"ChapterStart"] intValue], [[item objectForKey:@"ChapterEnd"] intValue]];
1012 else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 1)
1014 /* Start Stop is seconds */
1015 startStopString = [NSString stringWithFormat:@"Seconds %d through %d", [[item objectForKey:@"StartSeconds"] intValue], [[item objectForKey:@"StartSeconds"] intValue] + [[item objectForKey:@"StopSeconds"] intValue]];
1017 else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 2)
1019 /* Start Stop is Frames */
1020 startStopString = [NSString stringWithFormat:@"Frames %d through %d", [[item objectForKey:@"StartFrame"] intValue], [[item objectForKey:@"StartFrame"] intValue] + [[item objectForKey:@"StopFrame"] intValue]];
1023 NSString * passesString = @"";
1024 /* check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan */
1025 if ([item objectForKey:@"SubtitleList"] && [[[[item objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
1027 passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "];
1029 if ([[item objectForKey:@"VideoTwoPass"] intValue] == 0)
1031 passesString = [passesString stringByAppendingString:@"1 Video Pass"];
1035 if ([[item objectForKey:@"VideoTurboTwoPass"] intValue] == 1)
1037 passesString = [passesString stringByAppendingString:@"2 Video Passes First Turbo"];
1041 passesString = [passesString stringByAppendingString:@"2 Video Passes"];
1045 [finalString appendString:[NSString stringWithFormat:@"%@", [item objectForKey:@"SourceName"]] withAttributes:titleAttr];
1047 /* lets add the output file name to the title string here */
1048 NSString * outputFilenameString = [[item objectForKey:@"DestinationPath"] lastPathComponent];
1050 summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, startStopString, passesString, outputFilenameString];
1052 [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr];
1054 // Insert a short-in-height line to put some white space after the title
1055 [finalString appendString:@"\n" withAttributes:shortHeightAttr];
1056 // End of Title Stuff
1058 /* Second Line (Preset Name)*/
1059 [finalString appendString: @"Preset: " withAttributes:detailBoldAttr];
1060 [finalString appendString:[NSString stringWithFormat:@"%@\n", [item objectForKey:@"PresetName"]] withAttributes:detailAttr];
1062 /* Third Line (Format Summary) */
1063 NSString * audioCodecSummary = @"";
1064 /* Lets also get our audio track detail since we are going through the logic for use later */
1065 NSString * audioDetail1 = @"";
1066 NSString * audioDetail2 = @"";
1067 NSString * audioDetail3 = @"";
1068 NSString * audioDetail4 = @"";
1069 if ([[item objectForKey:@"Audio1Track"] intValue] > 0)
1071 audioCodecSummary = [NSString stringWithFormat:@"%@", [item objectForKey:@"Audio1Encoder"]];
1072 audioDetail1 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)",
1073 [item objectForKey:@"Audio1TrackDescription"] ,
1074 [item objectForKey:@"Audio1Encoder"],
1075 [item objectForKey:@"Audio1Mixdown"] ,
1076 [item objectForKey:@"Audio1Samplerate"],
1077 [item objectForKey:@"Audio1Bitrate"]];
1079 if ([[item objectForKey:@"Audio1TrackDRCSlider"] floatValue] > 0.00)
1081 audioDetail1 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail1,[item objectForKey:@"Audio1TrackDRCSlider"]];
1085 audioDetail1 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail1];
1089 if ([[item objectForKey:@"Audio2Track"] intValue] > 0)
1091 audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio2Encoder"]];
1092 audioDetail2 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)",
1093 [item objectForKey:@"Audio2TrackDescription"] ,
1094 [item objectForKey:@"Audio2Encoder"],
1095 [item objectForKey:@"Audio2Mixdown"] ,
1096 [item objectForKey:@"Audio2Samplerate"],
1097 [item objectForKey:@"Audio2Bitrate"]];
1099 if ([[item objectForKey:@"Audio2TrackDRCSlider"] floatValue] > 0.00)
1101 audioDetail2 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail2,[item objectForKey:@"Audio2TrackDRCSlider"]];
1105 audioDetail2 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail2];
1109 if ([[item objectForKey:@"Audio3Track"] intValue] > 0)
1111 audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio3Encoder"]];
1112 audioDetail3 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)",
1113 [item objectForKey:@"Audio3TrackDescription"] ,
1114 [item objectForKey:@"Audio3Encoder"],
1115 [item objectForKey:@"Audio3Mixdown"] ,
1116 [item objectForKey:@"Audio3Samplerate"],
1117 [item objectForKey:@"Audio3Bitrate"]];
1119 if ([[item objectForKey:@"Audio3TrackDRCSlider"] floatValue] > 0.00)
1121 audioDetail3 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail3,[item objectForKey:@"Audio3TrackDRCSlider"]];
1125 audioDetail3 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail3];
1129 if ([[item objectForKey:@"Audio4Track"] intValue] > 0)
1131 audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio3Encoder"]];
1132 audioDetail4 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)",
1133 [item objectForKey:@"Audio4TrackDescription"] ,
1134 [item objectForKey:@"Audio4Encoder"],
1135 [item objectForKey:@"Audio4Mixdown"] ,
1136 [item objectForKey:@"Audio4Samplerate"],
1137 [item objectForKey:@"Audio4Bitrate"]];
1139 if ([[item objectForKey:@"Audio4TrackDRCSlider"] floatValue] > 0.00)
1141 audioDetail4 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail4,[item objectForKey:@"Audio4TrackDRCSlider"]];
1145 audioDetail4 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail4];
1149 NSString * jobFormatInfo;
1150 if ([[item objectForKey:@"ChapterMarkers"] intValue] == 1)
1151 jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio, Chapter Markers\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1153 jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1156 [finalString appendString: @"Format: " withAttributes:detailBoldAttr];
1157 [finalString appendString: jobFormatInfo withAttributes:detailAttr];
1159 /* Optional String for mp4 options */
1160 if ([[item objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
1162 NSString * MP4Opts = @"";
1163 BOOL mp4OptsPresent = NO;
1164 if( [[item objectForKey:@"Mp4LargeFile"] intValue] == 1)
1166 mp4OptsPresent = YES;
1167 MP4Opts = [MP4Opts stringByAppendingString:@" - Large file size"];
1169 if( [[item objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
1171 mp4OptsPresent = YES;
1172 MP4Opts = [MP4Opts stringByAppendingString:@" - Web optimized"];
1175 if( [[item objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
1177 mp4OptsPresent = YES;
1178 MP4Opts = [MP4Opts stringByAppendingString:@" - iPod 5G support "];
1180 if (mp4OptsPresent == YES)
1182 [finalString appendString: @"MP4 Options: " withAttributes:detailBoldAttr];
1183 [finalString appendString: MP4Opts withAttributes:detailAttr];
1184 [finalString appendString:@"\n" withAttributes:detailAttr];
1188 /* Fourth Line (Destination Path)*/
1189 [finalString appendString: @"Destination: " withAttributes:detailBoldAttr];
1190 [finalString appendString: [item objectForKey:@"DestinationPath"] withAttributes:detailAttr];
1191 [finalString appendString:@"\n" withAttributes:detailAttr];
1193 /* Fifth Line Picture Details*/
1194 NSString * pictureInfo;
1195 pictureInfo = [NSString stringWithFormat:@"%@", [item objectForKey:@"PictureSizingSummary"]];
1196 if ([[item objectForKey:@"PictureKeepRatio"] intValue] == 1)
1198 pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
1201 if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1203 pictureInfo = [pictureInfo stringByAppendingString:@", Grayscale"];
1206 [finalString appendString: @"Picture: " withAttributes:detailBoldAttr];
1207 [finalString appendString: pictureInfo withAttributes:detailAttr];
1208 [finalString appendString:@"\n" withAttributes:detailAttr];
1210 /* Optional String for Picture Filters */
1212 NSString * pictureFilters = @"";
1213 BOOL pictureFiltersPresent = NO;
1215 if( [[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1217 pictureFiltersPresent = YES;
1218 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[item objectForKey:@"PictureDetelecineCustom"]]];
1220 else if( [[item objectForKey:@"PictureDetelecine"] intValue] == 2)
1222 pictureFiltersPresent = YES;
1223 pictureFilters = [pictureFilters stringByAppendingString:@" - Detelecine (Default)"];
1226 if( [[item objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
1228 if ([[item objectForKey:@"PictureDecomb"] intValue] != 0)
1230 pictureFiltersPresent = YES;
1231 if( [[item objectForKey:@"PictureDecomb"] intValue] == 1)
1233 pictureFiltersPresent = YES;
1234 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[item objectForKey:@"PictureDecombCustom"]]];
1236 else if( [[item objectForKey:@"PictureDecomb"] intValue] == 2)
1238 pictureFiltersPresent = YES;
1239 pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb (Default)"];
1245 if ([[item objectForKey:@"PictureDeinterlace"] intValue] != 0)
1247 pictureFiltersPresent = YES;
1248 if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 1)
1250 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[item objectForKey:@"PictureDeinterlaceCustom"]]];
1252 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 2)
1254 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Fast)"];
1256 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 3)
1258 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slow)"];
1260 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 4)
1262 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slower)"];
1267 if ([[item objectForKey:@"PictureDenoise"] intValue] != 0)
1269 pictureFiltersPresent = YES;
1270 if ([[item objectForKey:@"PictureDenoise"] intValue] == 1)
1272 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[item objectForKey:@"PictureDenoiseCustom"]]];
1274 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 2)
1276 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Weak)"];
1278 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 3)
1280 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Medium)"];
1282 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 4)
1284 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Strong)"];
1288 if ([[item objectForKey:@"PictureDeblock"] intValue] != 0)
1290 pictureFiltersPresent = YES;
1291 pictureFilters = [pictureFilters stringByAppendingString: [NSString stringWithFormat:@" - Deblock (pp7) (%d)",[[item objectForKey:@"PictureDeblock"] intValue]]];
1294 if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1296 pictureFiltersPresent = YES;
1297 pictureFilters = [pictureFilters stringByAppendingString:@" - Grayscale"];
1300 if (pictureFiltersPresent == YES)
1302 [finalString appendString: @"Filters: " withAttributes:detailBoldAttr];
1303 [finalString appendString: pictureFilters withAttributes:detailAttr];
1304 [finalString appendString:@"\n" withAttributes:detailAttr];
1307 /* Sixth Line Video Details*/
1308 NSString * videoInfo;
1309 videoInfo = [NSString stringWithFormat:@"Encoder: %@", [item objectForKey:@"VideoEncoder"]];
1311 /* for framerate look to see if we are using vfr detelecine */
1312 if ([[item objectForKey:@"JobIndexVideoFramerate"] intValue] == 0)
1314 if ([[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1316 /* we are using same as source with vfr detelecine */
1317 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (vfr detelecine)", videoInfo];
1321 /* we are using a variable framerate without dropping frames */
1322 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (variable)", videoInfo];
1327 /* we have a specified, constant framerate */
1328 videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (constant framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]];
1331 if ([[item objectForKey:@"VideoQualityType"] intValue] == 0)// Target Size MB
1333 videoInfo = [NSString stringWithFormat:@"%@ Target Size: %@(MB) (%d(kbps) abr)", videoInfo ,[item objectForKey:@"VideoTargetSize"],[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1335 else if ([[item objectForKey:@"VideoQualityType"] intValue] == 1) // ABR
1337 videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo ,[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1341 videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,[[item objectForKey:@"VideoQualitySlider"] floatValue]];
1344 [finalString appendString: @"Video: " withAttributes:detailBoldAttr];
1345 [finalString appendString: videoInfo withAttributes:detailAttr];
1346 [finalString appendString:@"\n" withAttributes:detailAttr];
1348 if ([[item objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
1350 [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttr];
1351 [finalString appendString: [item objectForKey:@"x264Option"] withAttributes:detailAttr];
1352 [finalString appendString:@"\n" withAttributes:detailAttr];
1357 /* Seventh Line Audio Details*/
1358 if ([audioDetail1 length] != 0)
1360 [finalString appendString: @"Audio Track 1: " withAttributes:detailBoldAttr];
1361 [finalString appendString: audioDetail1 withAttributes:detailAttr];
1362 [finalString appendString:@"\n" withAttributes:detailAttr];
1365 if ([audioDetail2 length] != 0)
1367 [finalString appendString: @"Audio Track 2: " withAttributes:detailBoldAttr];
1368 [finalString appendString: audioDetail2 withAttributes:detailAttr];
1369 [finalString appendString:@"\n" withAttributes:detailAttr];
1372 if ([audioDetail3 length] != 0)
1374 [finalString appendString: @"Audio Track 3: " withAttributes:detailBoldAttr];
1375 [finalString appendString: audioDetail3 withAttributes:detailAttr];
1376 [finalString appendString:@"\n" withAttributes:detailAttr];
1379 if ([audioDetail4 length] != 0)
1381 [finalString appendString: @"Audio Track 4: " withAttributes:detailBoldAttr];
1382 [finalString appendString: audioDetail4 withAttributes:detailAttr];
1383 [finalString appendString:@"\n" withAttributes:detailAttr];
1385 /* Eighth Line Subtitle Details */
1388 NSEnumerator *enumerator = [[item objectForKey:@"SubtitleList"] objectEnumerator];
1390 while (tempObject = [enumerator nextObject])
1392 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
1393 * we want to ignore it for display as well as encoding.
1395 if ([[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue] > 0)
1397 /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/
1398 [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr];
1399 [finalString appendString: [tempObject objectForKey:@"subtitleSourceTrackName"] withAttributes:detailAttr];
1400 if ([[tempObject objectForKey:@"subtitleTrackForced"] intValue] == 1)
1402 [finalString appendString: @" - Forced Only" withAttributes:detailAttr];
1404 if ([[tempObject objectForKey:@"subtitleTrackBurned"] intValue] == 1)
1406 [finalString appendString: @" - Burned In" withAttributes:detailAttr];
1408 if ([[tempObject objectForKey:@"subtitleTrackDefault"] intValue] == 1)
1410 [finalString appendString: @" - Default" withAttributes:detailAttr];
1412 [finalString appendString:@"\n" withAttributes:detailAttr];
1419 else if ([[tableColumn identifier] isEqualToString:@"icon"])
1421 if ([[item objectForKey:@"Status"] intValue] == 0)
1423 return [NSImage imageNamed:@"EncodeComplete"];
1425 else if ([[item objectForKey:@"Status"] intValue] == 1)
1427 return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];
1429 else if ([[item objectForKey:@"Status"] intValue] == 3)
1431 return [NSImage imageNamed:@"EncodeCanceled"];
1435 return [NSImage imageNamed:@"JobSmall"];
1445 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1447 if ([[tableColumn identifier] isEqualToString:@"desc"])
1451 // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer
1452 // using the image portion of the cell so we could switch back to a regular NSTextFieldCell.
1454 // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part
1455 [cell setImage:nil];
1457 else if ([[tableColumn identifier] isEqualToString:@"action"])
1459 [cell setEnabled: YES];
1460 BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
1461 if ([[item objectForKey:@"Status"] intValue] == 0)
1463 [cell setAction: @selector(revealSelectedQueueItem:)];
1466 [cell setImage:[NSImage imageNamed:@"RevealHighlight"]];
1467 [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]];
1470 [cell setImage:[NSImage imageNamed:@"Reveal"]];
1474 [cell setAction: @selector(removeSelectedQueueItem:)];
1477 [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1478 [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1481 [cell setImage:[NSImage imageNamed:@"Delete"]];
1486 - (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1488 // By default, the discolsure image gets centered vertically in the cell. We want
1489 // always at the top.
1490 if ([outlineView isItemExpanded: item])
1491 [cell setImagePosition: NSImageAbove];
1493 [cell setImagePosition: NSImageOnly];
1497 #pragma mark NSOutlineView delegate (dragging related)
1499 //------------------------------------------------------------------------------------
1500 // NSTableView delegate
1501 //------------------------------------------------------------------------------------
1504 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1506 // Dragging is only allowed of the pending items.
1507 if ([[[items objectAtIndex:0] objectForKey:@"Status"] integerValue] != 2) // 2 is pending
1512 // Don't retain since this is just holding temporaral drag information, and it is
1513 //only used during a drag! We could put this in the pboard actually.
1514 fDraggedNodes = items;
1516 // Provide data for our custom type, and simple NSStrings.
1517 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
1519 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
1520 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
1526 /* This method is used to validate the drops. */
1527 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1529 // Don't allow dropping ONTO an item since they can't really contain any children.
1530 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
1531 if (isOnDropTypeProposal)
1533 return NSDragOperationNone;
1536 // Don't allow dropping INTO an item since they can't really contain any children.
1539 index = [fOutlineView rowForItem: item] + 1;
1543 // NOTE: Should we allow dropping a pending job *above* the
1544 // finished or already encoded jobs ?
1545 // We do not let the user drop a pending job before or *above*
1546 // already finished or currently encoding jobs.
1547 if (index <= fEncodingQueueItem)
1549 return NSDragOperationNone;
1550 index = MAX (index, fEncodingQueueItem);
1553 [outlineView setDropItem:item dropChildIndex:index];
1554 return NSDragOperationGeneric;
1557 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1559 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
1561 for( id obj in fDraggedNodes )
1562 [moveItems addIndex:[fJobGroups indexOfObject:obj]];
1564 // Successful drop, we use moveObjectsInQueueArray:... in fHBController
1565 // to properly rearrange the queue array, save it to plist and then send it back here.
1566 // since Controller.mm is handling all queue array manipulation.
1567 // We *could do this here, but I think we are better served keeping that code together.
1568 [fHBController moveObjectsInQueueArray:fJobGroups fromIndexes:moveItems toIndex: index];