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];
117 - (void)setQueueArray: (NSMutableArray *)QueueFileArray
119 [fJobGroups setArray:QueueFileArray];
121 /* First stop any timer working now */
122 //[self stopAnimatingCurrentJobGroupInQueue];
123 [fOutlineView reloadData];
127 /* lets get the stats on the status of the queue array */
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
142 NSDictionary *thisQueueDict = nil;
143 for(id tempObject in fJobGroups)
145 thisQueueDict = tempObject;
146 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
150 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
153 /* we have an encoding job so, lets start the animation timer */
154 if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum)
156 fEncodingQueueItem = i;
159 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending
163 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled
170 /* Set the queue status field in the queue window */
171 NSMutableString * string;
172 if (fPendingCount == 1)
174 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
178 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount];
180 [fQueueCountField setStringValue:string];
184 /* This method sets the status string in the queue window
185 * and is called from Controller.mm (fHBController)
186 * instead of running another timer here polling libhb
187 * for encoding status
189 - (void)setQueueStatusString: (NSString *)statusString
192 [fProgressTextField setStringValue:statusString];
196 //------------------------------------------------------------------------------------
198 //------------------------------------------------------------------------------------
201 // clear the delegate so that windowWillClose is not attempted
202 if( [[self window] delegate] == self )
203 [[self window] setDelegate:nil];
205 [fJobGroups release];
207 [fSavedExpandedItems release];
208 [fSavedSelectedItems release];
210 [[NSNotificationCenter defaultCenter] removeObserver:self];
215 //------------------------------------------------------------------------------------
217 //------------------------------------------------------------------------------------
218 - (void)setHandle: (hb_handle_t *)handle
220 fQueueEncodeLibhb = handle;
223 //------------------------------------------------------------------------------------
224 // Receive HBController
225 //------------------------------------------------------------------------------------
226 - (void)setHBController: (HBController *)controller
228 fHBController = controller;
231 - (void)setPidNum: (int)myPidnum
234 [fHBController writeToActivityLog: "HBQueueController : My Pidnum is %d", pidNum];
239 //------------------------------------------------------------------------------------
240 // Displays and brings the queue window to the front
241 //------------------------------------------------------------------------------------
242 - (IBAction) showQueueWindow: (id)sender
244 [self showWindow:sender];
245 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
246 [self startAnimatingCurrentWorkingEncodeInQueue];
251 //------------------------------------------------------------------------------------
253 //------------------------------------------------------------------------------------
258 if( ![[self window] setFrameUsingName:@"Queue"] )
259 [[self window] center];
260 [self setWindowFrameAutosaveName:@"Queue"];
262 /* lets setup our queue list outline view for drag and drop here */
263 [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
264 [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
265 [fOutlineView setVerticalMotionCanBeginDrag: YES];
268 // Don't allow autoresizing of main column, else the "delete" column will get
269 // pushed out of view.
270 [fOutlineView setAutoresizesOutlineColumn: NO];
272 #if HB_OUTLINE_METRIC_CONTROLS
273 [fIndentation setHidden: NO];
274 [fSpacing setHidden: NO];
275 [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]]; // debug
276 [fSpacing setIntegerValue:3]; // debug
279 // Show/hide UI elements
280 fCurrentJobPaneShown = NO; // it's shown in the nib
285 //------------------------------------------------------------------------------------
287 //------------------------------------------------------------------------------------
288 - (void)windowWillClose:(NSNotification *)aNotification
290 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
291 [self stopAnimatingCurrentJobGroupInQueue];
296 //------------------------------------------------------------------------------------
298 //------------------------------------------------------------------------------------
301 // Create a new toolbar instance, and attach it to our window
302 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
304 // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
305 [toolbar setAllowsUserCustomization: YES];
306 [toolbar setAutosavesConfiguration: YES];
307 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
309 // We are the delegate
310 [toolbar setDelegate: self];
312 // Attach the toolbar to our window
313 [[self window] setToolbar:toolbar];
316 //------------------------------------------------------------------------------------
317 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
318 //------------------------------------------------------------------------------------
319 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
320 itemForItemIdentifier:(NSString *)itemIdentifier
321 willBeInsertedIntoToolbar:(BOOL)flag
323 // Required delegate method: Given an item identifier, this method returns an item.
324 // The toolbar will use this method to obtain toolbar items that can be displayed
325 // in the customization sheet, or in the toolbar itself.
327 NSToolbarItem *toolbarItem = nil;
329 if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier])
331 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
333 // Set the text label to be displayed in the toolbar and customization palette
334 [toolbarItem setLabel: @"Start"];
335 [toolbarItem setPaletteLabel: @"Start/Cancel"];
337 // Set up a reasonable tooltip, and image
338 [toolbarItem setToolTip: @"Start Encoding"];
339 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
341 // Tell the item what message to send when it is clicked
342 [toolbarItem setTarget: self];
343 [toolbarItem setAction: @selector(toggleStartCancel:)];
346 if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])
348 toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
350 // Set the text label to be displayed in the toolbar and customization palette
351 [toolbarItem setLabel: @"Pause"];
352 [toolbarItem setPaletteLabel: @"Pause/Resume"];
354 // Set up a reasonable tooltip, and image
355 [toolbarItem setToolTip: @"Pause Encoding"];
356 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
358 // Tell the item what message to send when it is clicked
359 [toolbarItem setTarget: self];
360 [toolbarItem setAction: @selector(togglePauseResume:)];
366 //------------------------------------------------------------------------------------
367 // toolbarDefaultItemIdentifiers:
368 //------------------------------------------------------------------------------------
369 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
371 // Required delegate method: Returns the ordered list of items to be shown in the
372 // toolbar by default.
374 return [NSArray arrayWithObjects:
375 HBQueueStartCancelToolbarIdentifier,
376 HBQueuePauseResumeToolbarIdentifier,
380 //------------------------------------------------------------------------------------
381 // toolbarAllowedItemIdentifiers:
382 //------------------------------------------------------------------------------------
383 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
385 // Required delegate method: Returns the list of all allowed items by identifier.
386 // By default, the toolbar does not assume any items are allowed, even the
387 // separator. So, every allowed item must be explicitly listed.
389 return [NSArray arrayWithObjects:
390 HBQueueStartCancelToolbarIdentifier,
391 HBQueuePauseResumeToolbarIdentifier,
392 NSToolbarCustomizeToolbarItemIdentifier,
393 NSToolbarFlexibleSpaceItemIdentifier,
394 NSToolbarSpaceItemIdentifier,
395 NSToolbarSeparatorItemIdentifier,
399 //------------------------------------------------------------------------------------
400 // validateToolbarItem:
401 //------------------------------------------------------------------------------------
402 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
404 // Optional method: This message is sent to us since we are the target of some
405 // toolbar item actions.
407 if (!fQueueEncodeLibhb) return NO;
412 hb_get_state2 (fQueueEncodeLibhb, &s);
414 if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])
416 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
419 [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];
420 [toolbarItem setLabel: @"Stop"];
421 [toolbarItem setToolTip: @"Stop Encoding"];
424 else if (fPendingCount > 0)
427 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
428 [toolbarItem setLabel: @"Start"];
429 [toolbarItem setToolTip: @"Start Encoding"];
435 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
436 [toolbarItem setLabel: @"Start"];
437 [toolbarItem setToolTip: @"Start Encoding"];
441 if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])
443 if (s.state == HB_STATE_PAUSED)
446 [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
447 [toolbarItem setLabel: @"Resume"];
448 [toolbarItem setToolTip: @"Resume Encoding"];
451 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
454 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
455 [toolbarItem setLabel: @"Pause"];
456 [toolbarItem setToolTip: @"Pause Encoding"];
461 [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
462 [toolbarItem setLabel: @"Pause"];
463 [toolbarItem setToolTip: @"Pause Encoding"];
473 #pragma mark Queue Item Controls
474 //------------------------------------------------------------------------------------
475 // Delete encodes from the queue window and accompanying array
476 // Also handling first cancelling the encode if in fact its currently encoding.
477 //------------------------------------------------------------------------------------
478 - (IBAction)removeSelectedQueueItem: (id)sender
480 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
481 NSUInteger row = [selectedRows firstIndex];
482 if( row == NSNotFound )
484 /* if this is a currently encoding job, we need to be sure to alert the user,
485 * to let them decide to cancel it first, then if they do, we can come back and
488 if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
490 /* We pause the encode here so that it doesn't finish right after and then
491 * screw up the sync while the window is open
493 [fHBController Pause:NULL];
494 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
495 // Which window to attach the sheet to?
496 NSWindow * docWindow = nil;
497 if ([sender respondsToSelector: @selector(window)])
498 docWindow = [sender window];
501 NSBeginCriticalAlertSheet(
503 NSLocalizedString(@"Keep Encoding", nil),
505 NSLocalizedString(@"Stop Encoding and Delete", nil),
507 nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
508 NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
510 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
514 /* since we are not a currently encoding item, we can just be removed */
515 [fHBController removeQueueFileItem:row];
519 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
521 /* We resume encoding and perform the appropriate actions
522 * Note: Pause: is a toggle type method based on hb's current
523 * state, if it paused, it will resume encoding and vice versa.
524 * In this case, we are paused from the calling window, so calling
525 * [fHBController Pause:NULL]; Again will resume encoding
527 [fHBController Pause:NULL];
528 if (returnCode == NSAlertOtherReturn)
530 /* We need to save the currently encoding item number first */
531 int encodingItemToRemove = fEncodingQueueItem;
532 /* Since we are encoding, we need to let fHBController Cancel this job
533 * upon which it will move to the next one if there is one
535 [fHBController doCancelCurrentJob];
536 /* Now, we can go ahead and remove the job we just cancelled since
537 * we have its item number from above
539 [fHBController removeQueueFileItem:encodingItemToRemove];
544 //------------------------------------------------------------------------------------
545 // Show the finished encode in the finder
546 //------------------------------------------------------------------------------------
547 - (IBAction)revealSelectedQueueItem: (id)sender
549 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
550 NSInteger row = [selectedRows firstIndex];
551 if (row != NSNotFound)
553 while (row != NSNotFound)
555 NSMutableDictionary *queueItemToOpen = [fOutlineView itemAtRow: row];
556 [[NSWorkspace sharedWorkspace] selectFile:[queueItemToOpen objectForKey:@"DestinationPath"] inFileViewerRootedAtPath:nil];
558 row = [selectedRows indexGreaterThanIndex: row];
564 //------------------------------------------------------------------------------------
565 // Starts or cancels the processing of jobs depending on the current state
566 //------------------------------------------------------------------------------------
567 - (IBAction)toggleStartCancel: (id)sender
569 if (!fQueueEncodeLibhb) return;
572 hb_get_state2 (fQueueEncodeLibhb, &s);
574 if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
575 [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window
577 else if (fPendingCount > 0)
578 [fHBController Rip: NULL];
581 //------------------------------------------------------------------------------------
582 // Toggles the pause/resume state of libhb
583 //------------------------------------------------------------------------------------
584 - (IBAction)togglePauseResume: (id)sender
586 if (!fQueueEncodeLibhb) return;
589 hb_get_state2 (fQueueEncodeLibhb, &s);
591 if (s.state == HB_STATE_PAUSED)
593 hb_resume (fQueueEncodeLibhb);
594 [self startAnimatingCurrentWorkingEncodeInQueue];
596 else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
598 hb_pause (fQueueEncodeLibhb);
599 [self stopAnimatingCurrentJobGroupInQueue];
604 //------------------------------------------------------------------------------------
605 // Send the selected queue item back to the main window for rescan and possible edit.
606 //------------------------------------------------------------------------------------
607 - (IBAction)editSelectedQueueItem: (id)sender
609 NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
610 NSUInteger row = [selectedRows firstIndex];
611 if( row == NSNotFound )
613 /* if this is a currently encoding job, we need to be sure to alert the user,
614 * to let them decide to cancel it first, then if they do, we can come back and
617 if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
619 /* We pause the encode here so that it doesn't finish right after and then
620 * screw up the sync while the window is open
622 [fHBController Pause:NULL];
623 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
624 // Which window to attach the sheet to?
625 NSWindow * docWindow = nil;
626 if ([sender respondsToSelector: @selector(window)])
627 docWindow = [sender window];
630 NSBeginCriticalAlertSheet(
632 NSLocalizedString(@"Keep Encoding", nil),
634 NSLocalizedString(@"Stop Encoding and Delete", nil),
636 nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
637 NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
642 /* since we are not a currently encoding item, we can just be cancelled */
643 [fHBController rescanQueueItemToMainWindow:[[fJobGroups objectAtIndex:row] objectForKey:@"SourcePath"] scanTitleNum:[[[fJobGroups objectAtIndex:row] objectForKey:@"TitleNumber"] integerValue] selectedQueueItem:row];
650 #pragma mark Animate Endcoding Item
655 //------------------------------------------------------------------------------------
656 // Starts animating the job icon of the currently processing job in the queue outline
658 //------------------------------------------------------------------------------------
659 - (void) startAnimatingCurrentWorkingEncodeInQueue
661 if (!fAnimationTimer)
662 fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle
664 selector:@selector(animateWorkingEncodeInQueue:)
666 repeats:YES] retain];
669 //------------------------------------------------------------------------------------
670 // If a job is currently processing, its job icon in the queue outline view is
671 // animated to its next state.
672 //------------------------------------------------------------------------------------
673 - (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer
675 if (fWorkingCount > 0)
678 fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.
679 [self animateWorkingEncodeIconInQueue];
683 /* We need to make sure we denote only working encodes even for multiple instances */
684 - (void) animateWorkingEncodeIconInQueue
686 NSInteger row = fEncodingQueueItem; /// need to set to fEncodingQueueItem
687 NSInteger col = [fOutlineView columnWithIdentifier: @"icon"];
688 if (row != -1 && col != -1)
690 NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];
691 [fOutlineView setNeedsDisplayInRect: frame];
695 //------------------------------------------------------------------------------------
696 // Stops animating the job icon of the currently processing job in the queue outline
698 //------------------------------------------------------------------------------------
699 - (void) stopAnimatingCurrentJobGroupInQueue
701 if (fAnimationTimer && [fAnimationTimer isValid])
703 [fAnimationTimer invalidate];
704 [fAnimationTimer release];
705 fAnimationTimer = nil;
712 - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
714 NSUInteger index = [indexSet lastIndex];
715 NSUInteger aboveInsertIndexCount = 0;
717 while (index != NSNotFound)
719 NSUInteger removeIndex;
721 if (index >= insertIndex)
723 removeIndex = index + aboveInsertIndexCount;
724 aboveInsertIndexCount++;
732 id object = [[array objectAtIndex:removeIndex] retain];
733 [array removeObjectAtIndex:removeIndex];
734 [array insertObject:object atIndex:insertIndex];
737 index = [indexSet indexLessThanIndex:index];
743 #pragma mark NSOutlineView delegate
746 - (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item
749 return [fJobGroups objectAtIndex:index];
751 // We are only one level deep, so we can't be asked about children
752 NSAssert (NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items.");
756 - (BOOL)outlineView:(NSOutlineView *)fOutlineView isItemExpandable:(id)item
758 // Our outline view has no levels, but we can still expand every item. Doing so
759 // just makes the row taller. See heightOfRowByItem below.
763 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
765 // Our outline view has no levels, but we can still expand every item. Doing so
766 // just makes the row taller. See heightOfRowByItem below.
767 return ![(HBQueueOutlineView*)outlineView isDragging];
770 - (NSInteger)outlineView:(NSOutlineView *)fOutlineView numberOfChildrenOfItem:(id)item
772 // Our outline view has no levels, so number of children will be zero for all
775 return [fJobGroups count];
780 - (void)outlineViewItemDidCollapse:(NSNotification *)notification
782 id item = [[notification userInfo] objectForKey:@"NSObject"];
783 NSInteger row = [fOutlineView rowForItem:item];
784 [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
787 - (void)outlineViewItemDidExpand:(NSNotification *)notification
789 id item = [[notification userInfo] objectForKey:@"NSObject"];
790 NSInteger row = [fOutlineView rowForItem:item];
791 [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
794 - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
796 if ([outlineView isItemExpanded: item])
798 /* Below is the original code to accommodate a live resize,
799 * however as stated in travistex's comments it's very buggy.
800 * For now I will leave it here ... commented out and use
801 * the code below to determine the row height based on each
802 * encodes optional parameters and how they are displayed. */
804 // Short-circuit here if in a live resize primarily to fix a bug but also to
805 // increase resposivness during a resize. There's a bug in NSTableView that
806 // causes row heights to get messed up if you try to change them during a live
807 // resize. So if in a live resize, simply return the previously calculated
808 // height. The row heights will get fixed up after the resize because we have
809 // implemented viewDidEndLiveResize to force all of them to be recalculated.
810 // if ([outlineView inLiveResize] && [item lastDescriptionHeight] > 0)
811 // return [item lastDescriptionHeight];
813 // CGFloat width = [[outlineView tableColumnWithIdentifier: @"desc"] width];
814 // Column width is NOT what is ultimately used. I can't quite figure out what
815 // width to use for calculating text metrics. No matter how I tweak this value,
816 // there are a few conditions in which the drawn text extends below the bounds
817 // of the row cell. In previous versions, which ran under Tiger, I was
818 // reducing width by 47 pixles.
819 // width -= 2; // (?) for intercell spacing
821 // CGFloat height = [item heightOfDescriptionForWidth: width];
824 /* So, we know several rows of text that are in all queue items for display.
825 * These are the title line, Preset, Format, Destination, Picture, and Video Lines
827 CGFloat rowHeightNonTitle = 15.0;
828 /* Add the title line height, then the non title line height for Preset, Format, Destination
831 CGFloat itemHeightForDisplay = HB_ROW_HEIGHT_TITLE_ONLY + (rowHeightNonTitle * 5);
833 /* get our item row number so we an use it to calc how many lines we have to display based
834 * on MP4 Options, Filter Options, X264 Options, Audio Tracks and Subtitles from our queue array */
835 int itemRowNum = [outlineView rowForItem: item];
836 NSMutableDictionary *queueItemToCheck = [outlineView itemAtRow: itemRowNum];
838 /* Check to see if we need to allow for mp4 opts */
839 BOOL mp4OptsPresent = NO;
840 if ([[queueItemToCheck objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
843 if( [[queueItemToCheck objectForKey:@"Mp4LargeFile"] intValue] == 1)
845 mp4OptsPresent = YES;
847 if( [[queueItemToCheck objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
849 mp4OptsPresent = YES;
851 if( [[queueItemToCheck objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
853 mp4OptsPresent = YES;
857 if (mp4OptsPresent == YES)
859 itemHeightForDisplay += rowHeightNonTitle;
862 /* check to see if we need to allow for the Picture Filters row */
863 BOOL pictureFiltersPresent = NO;
864 if( [[queueItemToCheck objectForKey:@"PictureDetelecine"] intValue] > 0)
866 pictureFiltersPresent = YES;
868 if( [[queueItemToCheck objectForKey:@"PictureDecomb"] intValue] > 0)
870 pictureFiltersPresent = YES;
872 if( [[queueItemToCheck objectForKey:@"PictureDeinterlace"] intValue] > 0)
874 pictureFiltersPresent = YES;
876 if( [[queueItemToCheck objectForKey:@"PictureDenoise"] intValue] > 0)
878 pictureFiltersPresent = YES;
880 if( [[queueItemToCheck objectForKey:@"PictureDeblock"] intValue] > 0)
882 pictureFiltersPresent = YES;
884 if( [[queueItemToCheck objectForKey:@"VideoGrayScale"] intValue] > 0)
886 pictureFiltersPresent = YES;
889 if (pictureFiltersPresent == YES)
891 itemHeightForDisplay += rowHeightNonTitle;
894 /* check to see if we need a line to display x264 options */
895 if ([[queueItemToCheck objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
897 itemHeightForDisplay += rowHeightNonTitle;
900 /* check to see how many audio track lines to allow for */
901 unsigned int ourMaximumNumberOfAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
902 int actualCountOfAudioTracks = 0;
903 for (unsigned int i = 1; i <= ourMaximumNumberOfAudioTracks; i++) {
904 if (0 < [[queueItemToCheck objectForKey: [NSString stringWithFormat: @"Audio%dTrack", i]] intValue]) {
905 actualCountOfAudioTracks++;
908 itemHeightForDisplay += (actualCountOfAudioTracks * rowHeightNonTitle);
910 /* add in subtitle lines for each subtitle in the SubtitleList array */
911 itemHeightForDisplay += rowHeightNonTitle * [[queueItemToCheck objectForKey:@"SubtitleList"] count];
913 return itemHeightForDisplay;
918 return HB_ROW_HEIGHT_TITLE_ONLY;
922 - (CGFloat) heightOfDescriptionForWidth:(CGFloat)width
924 // Try to return the cached value if no changes have happened since the last time
925 //if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription)
926 // return fLastDescriptionHeight;
928 //if (fNeedsDescription)
929 // [self updateDescription];
931 // Calculate the height
932 //NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];
933 //fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom
934 //fLastDescriptionWidth = width;
935 return HB_ROW_HEIGHT_FULL_DESCRIPTION;
937 /* supposedly another way to do this, in case boundingRectWithSize isn't working
938 NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)];
939 [[tmpView textStorage] setAttributedString:aString];
940 [tmpView setHorizontallyResizable:NO];
941 [tmpView setVerticallyResizable:YES];
942 // [[tmpView textContainer] setHeightTracksTextView: YES];
943 // [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)];
945 float height = [tmpView frame].size.height;
951 - (CGFloat) lastDescriptionHeight
953 return HB_ROW_HEIGHT_FULL_DESCRIPTION;
956 - (id)outlineView:(NSOutlineView *)fOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
958 if ([[tableColumn identifier] isEqualToString:@"desc"])
962 /* Below should be put into a separate method but I am way too f'ing lazy right now */
963 NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];
965 NSMutableParagraphStyle * ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
966 [ps setHeadIndent: 40.0];
967 [ps setParagraphSpacing: 1.0];
968 [ps setTabStops:[NSArray array]]; // clear all tabs
969 [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];
972 NSDictionary* detailAttr = [NSDictionary dictionaryWithObjectsAndKeys:
973 [NSFont systemFontOfSize:10.0], NSFontAttributeName,
974 ps, NSParagraphStyleAttributeName,
977 NSDictionary* detailBoldAttr = [NSDictionary dictionaryWithObjectsAndKeys:
978 [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName,
979 ps, NSParagraphStyleAttributeName,
982 NSDictionary* titleAttr = [NSDictionary dictionaryWithObjectsAndKeys:
983 [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
984 ps, NSParagraphStyleAttributeName,
987 NSDictionary* shortHeightAttr = [NSDictionary dictionaryWithObjectsAndKeys:
988 [NSFont systemFontOfSize:2.0], NSFontAttributeName,
991 /* First line, we should strip the destination path and just show the file name and add the title num and chapters (if any) */
992 NSString * summaryInfo;
994 NSString * titleString = [NSString stringWithFormat:@"Title %d", [[item objectForKey:@"TitleNumber"] intValue]];
996 NSString * startStopString = @"";
997 if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 0)
999 /* Start Stop is chapters */
1000 startStopString = ([[item objectForKey:@"ChapterStart"] intValue] == [[item objectForKey:@"ChapterEnd"] intValue]) ?
1001 [NSString stringWithFormat:@"Chapter %d", [[item objectForKey:@"ChapterStart"] intValue]] :
1002 [NSString stringWithFormat:@"Chapters %d through %d", [[item objectForKey:@"ChapterStart"] intValue], [[item objectForKey:@"ChapterEnd"] intValue]];
1004 else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 1)
1006 /* Start Stop is seconds */
1007 startStopString = [NSString stringWithFormat:@"Seconds %d through %d", [[item objectForKey:@"StartSeconds"] intValue], [[item objectForKey:@"StartSeconds"] intValue] + [[item objectForKey:@"StopSeconds"] intValue]];
1009 else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 2)
1011 /* Start Stop is Frames */
1012 startStopString = [NSString stringWithFormat:@"Frames %d through %d", [[item objectForKey:@"StartFrame"] intValue], [[item objectForKey:@"StartFrame"] intValue] + [[item objectForKey:@"StopFrame"] intValue]];
1015 NSString * passesString = @"";
1016 /* check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan */
1017 if ([item objectForKey:@"SubtitleList"] && [[[[item objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
1019 passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "];
1021 if ([[item objectForKey:@"VideoTwoPass"] intValue] == 0)
1023 passesString = [passesString stringByAppendingString:@"1 Video Pass"];
1027 if ([[item objectForKey:@"VideoTurboTwoPass"] intValue] == 1)
1029 passesString = [passesString stringByAppendingString:@"2 Video Passes First Turbo"];
1033 passesString = [passesString stringByAppendingString:@"2 Video Passes"];
1037 [finalString appendString:[NSString stringWithFormat:@"%@", [item objectForKey:@"SourceName"]] withAttributes:titleAttr];
1039 /* lets add the output file name to the title string here */
1040 NSString * outputFilenameString = [[item objectForKey:@"DestinationPath"] lastPathComponent];
1042 summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, startStopString, passesString, outputFilenameString];
1044 [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr];
1046 // Insert a short-in-height line to put some white space after the title
1047 [finalString appendString:@"\n" withAttributes:shortHeightAttr];
1048 // End of Title Stuff
1050 /* Second Line (Preset Name)*/
1051 [finalString appendString: @"Preset: " withAttributes:detailBoldAttr];
1052 [finalString appendString:[NSString stringWithFormat:@"%@\n", [item objectForKey:@"PresetName"]] withAttributes:detailAttr];
1054 /* Third Line (Format Summary) */
1055 NSString * audioCodecSummary = @""; // This seems to be set by the last track we have available...
1056 /* Lets also get our audio track detail since we are going through the logic for use later */
1057 unsigned int ourMaximumNumberOfAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
1058 NSMutableArray *audioDetails = [NSMutableArray arrayWithCapacity: ourMaximumNumberOfAudioTracks];
1060 NSString *detailString;
1062 for (unsigned int i = 1; i <= ourMaximumNumberOfAudioTracks; i++) {
1063 base = [NSString stringWithFormat: @"Audio%d", i];
1064 if (0 < [[item objectForKey: [base stringByAppendingString: @"Track"]] intValue]) {
1065 audioCodecSummary = [NSString stringWithFormat: @"%@", [item objectForKey: [base stringByAppendingString: @"Encoder"]]];
1066 drc = [item objectForKey: [base stringByAppendingString: @"TrackDRCSlider"]];
1067 detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@",
1068 [item objectForKey: [base stringByAppendingString: @"TrackDescription"]],
1069 [item objectForKey: [base stringByAppendingString: @"Encoder"]],
1070 [item objectForKey: [base stringByAppendingString: @"Mixdown"]],
1071 [item objectForKey: [base stringByAppendingString: @"Samplerate"]],
1072 [item objectForKey: [base stringByAppendingString: @"Bitrate"]],
1073 (0.0 < [drc floatValue]) ? drc : @"Off"
1075 [audioDetails addObject: detailString];
1080 NSString * jobFormatInfo;
1081 if ([[item objectForKey:@"ChapterMarkers"] intValue] == 1)
1082 jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio, Chapter Markers\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1084 jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1087 [finalString appendString: @"Format: " withAttributes:detailBoldAttr];
1088 [finalString appendString: jobFormatInfo withAttributes:detailAttr];
1090 /* Optional String for mp4 options */
1091 if ([[item objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
1093 NSString * MP4Opts = @"";
1094 BOOL mp4OptsPresent = NO;
1095 if( [[item objectForKey:@"Mp4LargeFile"] intValue] == 1)
1097 mp4OptsPresent = YES;
1098 MP4Opts = [MP4Opts stringByAppendingString:@" - Large file size"];
1100 if( [[item objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
1102 mp4OptsPresent = YES;
1103 MP4Opts = [MP4Opts stringByAppendingString:@" - Web optimized"];
1106 if( [[item objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
1108 mp4OptsPresent = YES;
1109 MP4Opts = [MP4Opts stringByAppendingString:@" - iPod 5G support "];
1111 if (mp4OptsPresent == YES)
1113 [finalString appendString: @"MP4 Options: " withAttributes:detailBoldAttr];
1114 [finalString appendString: MP4Opts withAttributes:detailAttr];
1115 [finalString appendString:@"\n" withAttributes:detailAttr];
1119 /* Fourth Line (Destination Path)*/
1120 [finalString appendString: @"Destination: " withAttributes:detailBoldAttr];
1121 [finalString appendString: [item objectForKey:@"DestinationPath"] withAttributes:detailAttr];
1122 [finalString appendString:@"\n" withAttributes:detailAttr];
1124 /* Fifth Line Picture Details*/
1125 NSString * pictureInfo;
1126 pictureInfo = [NSString stringWithFormat:@"%@", [item objectForKey:@"PictureSizingSummary"]];
1127 if ([[item objectForKey:@"PictureKeepRatio"] intValue] == 1)
1129 pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
1132 if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1134 pictureInfo = [pictureInfo stringByAppendingString:@", Grayscale"];
1137 [finalString appendString: @"Picture: " withAttributes:detailBoldAttr];
1138 [finalString appendString: pictureInfo withAttributes:detailAttr];
1139 [finalString appendString:@"\n" withAttributes:detailAttr];
1141 /* Optional String for Picture Filters */
1143 NSString * pictureFilters = @"";
1144 BOOL pictureFiltersPresent = NO;
1146 if( [[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1148 pictureFiltersPresent = YES;
1149 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[item objectForKey:@"PictureDetelecineCustom"]]];
1151 else if( [[item objectForKey:@"PictureDetelecine"] intValue] == 2)
1153 pictureFiltersPresent = YES;
1154 pictureFilters = [pictureFilters stringByAppendingString:@" - Detelecine (Default)"];
1157 if( [[item objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
1159 if ([[item objectForKey:@"PictureDecomb"] intValue] != 0)
1161 pictureFiltersPresent = YES;
1162 if( [[item objectForKey:@"PictureDecomb"] intValue] == 1)
1164 pictureFiltersPresent = YES;
1165 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[item objectForKey:@"PictureDecombCustom"]]];
1167 else if( [[item objectForKey:@"PictureDecomb"] intValue] == 2)
1169 pictureFiltersPresent = YES;
1170 pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb (Default)"];
1176 if ([[item objectForKey:@"PictureDeinterlace"] intValue] != 0)
1178 pictureFiltersPresent = YES;
1179 if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 1)
1181 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[item objectForKey:@"PictureDeinterlaceCustom"]]];
1183 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 2)
1185 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Fast)"];
1187 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 3)
1189 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slow)"];
1191 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 4)
1193 pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slower)"];
1198 if ([[item objectForKey:@"PictureDenoise"] intValue] != 0)
1200 pictureFiltersPresent = YES;
1201 if ([[item objectForKey:@"PictureDenoise"] intValue] == 1)
1203 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[item objectForKey:@"PictureDenoiseCustom"]]];
1205 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 2)
1207 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Weak)"];
1209 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 3)
1211 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Medium)"];
1213 else if ([[item objectForKey:@"PictureDenoise"] intValue] == 4)
1215 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Strong)"];
1219 if ([[item objectForKey:@"PictureDeblock"] intValue] != 0)
1221 pictureFiltersPresent = YES;
1222 pictureFilters = [pictureFilters stringByAppendingString: [NSString stringWithFormat:@" - Deblock (pp7) (%d)",[[item objectForKey:@"PictureDeblock"] intValue]]];
1225 if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1227 pictureFiltersPresent = YES;
1228 pictureFilters = [pictureFilters stringByAppendingString:@" - Grayscale"];
1231 if (pictureFiltersPresent == YES)
1233 [finalString appendString: @"Filters: " withAttributes:detailBoldAttr];
1234 [finalString appendString: pictureFilters withAttributes:detailAttr];
1235 [finalString appendString:@"\n" withAttributes:detailAttr];
1238 /* Sixth Line Video Details*/
1239 NSString * videoInfo;
1240 videoInfo = [NSString stringWithFormat:@"Encoder: %@", [item objectForKey:@"VideoEncoder"]];
1242 /* for framerate look to see if we are using vfr detelecine */
1243 if ([[item objectForKey:@"JobIndexVideoFramerate"] intValue] == 0)
1245 if ([[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1247 /* we are using same as source with vfr detelecine */
1248 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (vfr detelecine)", videoInfo];
1252 /* we are using a variable framerate without dropping frames */
1253 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (variable)", videoInfo];
1258 /* we have a specified, constant framerate */
1259 if ([[item objectForKey:@"VideoFrameratePFR"] intValue] == 1)
1261 videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (peak framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]];
1265 videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (constant framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]];
1269 if ([[item objectForKey:@"VideoQualityType"] intValue] == 0)// Target Size MB
1271 videoInfo = [NSString stringWithFormat:@"%@ Target Size: %@(MB) (%d(kbps) abr)", videoInfo ,[item objectForKey:@"VideoTargetSize"],[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1273 else if ([[item objectForKey:@"VideoQualityType"] intValue] == 1) // ABR
1275 videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo ,[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1279 videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,[[item objectForKey:@"VideoQualitySlider"] floatValue]];
1282 [finalString appendString: @"Video: " withAttributes:detailBoldAttr];
1283 [finalString appendString: videoInfo withAttributes:detailAttr];
1284 [finalString appendString:@"\n" withAttributes:detailAttr];
1286 if ([[item objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
1288 [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttr];
1289 [finalString appendString: [item objectForKey:@"x264Option"] withAttributes:detailAttr];
1290 [finalString appendString:@"\n" withAttributes:detailAttr];
1295 /* Seventh Line Audio Details*/
1296 NSEnumerator *audioDetailEnumerator = [audioDetails objectEnumerator];
1297 NSString *anAudioDetail;
1298 int audioDetailCount = 0;
1299 while (nil != (anAudioDetail = [audioDetailEnumerator nextObject])) {
1301 if (0 < [anAudioDetail length]) {
1302 [finalString appendString: [NSString stringWithFormat: @"Audio Track %d ", audioDetailCount] withAttributes: detailBoldAttr];
1303 [finalString appendString: anAudioDetail withAttributes: detailAttr];
1304 [finalString appendString: @"\n" withAttributes: detailAttr];
1308 /* Eighth Line Subtitle Details */
1311 NSEnumerator *enumerator = [[item objectForKey:@"SubtitleList"] objectEnumerator];
1313 while (tempObject = [enumerator nextObject])
1315 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
1316 * we want to ignore it for display as well as encoding.
1318 if ([[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue] > 0)
1320 /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/
1321 [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr];
1322 [finalString appendString: [tempObject objectForKey:@"subtitleSourceTrackName"] withAttributes:detailAttr];
1323 if ([[tempObject objectForKey:@"subtitleTrackForced"] intValue] == 1)
1325 [finalString appendString: @" - Forced Only" withAttributes:detailAttr];
1327 if ([[tempObject objectForKey:@"subtitleTrackBurned"] intValue] == 1)
1329 [finalString appendString: @" - Burned In" withAttributes:detailAttr];
1331 if ([[tempObject objectForKey:@"subtitleTrackDefault"] intValue] == 1)
1333 [finalString appendString: @" - Default" withAttributes:detailAttr];
1335 [finalString appendString:@"\n" withAttributes:detailAttr];
1342 else if ([[tableColumn identifier] isEqualToString:@"icon"])
1344 if ([[item objectForKey:@"Status"] intValue] == 0)
1346 return [NSImage imageNamed:@"EncodeComplete"];
1348 else if ([[item objectForKey:@"Status"] intValue] == 1)
1350 return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];
1352 else if ([[item objectForKey:@"Status"] intValue] == 3)
1354 return [NSImage imageNamed:@"EncodeCanceled"];
1358 return [NSImage imageNamed:@"JobSmall"];
1367 /* This method inserts the proper action icons into the far right of the queue window */
1368 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1370 if ([[tableColumn identifier] isEqualToString:@"desc"])
1374 // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer
1375 // using the image portion of the cell so we could switch back to a regular NSTextFieldCell.
1377 // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part
1378 [cell setImage:nil];
1380 else if ([[tableColumn identifier] isEqualToString:@"action"])
1382 [cell setEnabled: YES];
1383 BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
1385 if ([[item objectForKey:@"Status"] intValue] == 0 || ([[item objectForKey:@"Status"] intValue] == 1 && [[item objectForKey:@"EncodingPID"] intValue] != pidNum))
1387 [cell setAction: @selector(revealSelectedQueueItem:)];
1390 [cell setImage:[NSImage imageNamed:@"RevealHighlight"]];
1391 [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]];
1394 [cell setImage:[NSImage imageNamed:@"Reveal"]];
1399 [cell setAction: @selector(removeSelectedQueueItem:)];
1402 [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1403 [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1406 [cell setImage:[NSImage imageNamed:@"Delete"]];
1412 - (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1414 // By default, the disclosure image gets centered vertically in the cell. We want
1415 // always at the top.
1416 if ([outlineView isItemExpanded: item])
1417 [cell setImagePosition: NSImageAbove];
1419 [cell setImagePosition: NSImageOnly];
1423 #pragma mark NSOutlineView delegate (dragging related)
1425 //------------------------------------------------------------------------------------
1426 // NSTableView delegate
1427 //------------------------------------------------------------------------------------
1430 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1432 // Dragging is only allowed of the pending items.
1433 if ([[[items objectAtIndex:0] objectForKey:@"Status"] integerValue] != 2) // 2 is pending
1438 // Don't retain since this is just holding temporaral drag information, and it is
1439 //only used during a drag! We could put this in the pboard actually.
1440 fDraggedNodes = items;
1442 // Provide data for our custom type, and simple NSStrings.
1443 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
1445 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
1446 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
1452 /* This method is used to validate the drops. */
1453 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1455 // Don't allow dropping ONTO an item since they can't really contain any children.
1456 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
1457 if (isOnDropTypeProposal)
1459 return NSDragOperationNone;
1462 // Don't allow dropping INTO an item since they can't really contain any children.
1465 index = [fOutlineView rowForItem: item] + 1;
1469 // NOTE: Should we allow dropping a pending job *above* the
1470 // finished or already encoded jobs ?
1471 // We do not let the user drop a pending job before or *above*
1472 // already finished or currently encoding jobs.
1473 if (index <= fEncodingQueueItem)
1475 return NSDragOperationNone;
1476 index = MAX (index, fEncodingQueueItem);
1479 [outlineView setDropItem:item dropChildIndex:index];
1480 return NSDragOperationGeneric;
1483 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1485 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
1487 for( id obj in fDraggedNodes )
1488 [moveItems addIndex:[fJobGroups indexOfObject:obj]];
1490 // Successful drop, we use moveObjectsInQueueArray:... in fHBController
1491 // to properly rearrange the queue array, save it to plist and then send it back here.
1492 // since Controller.mm is handling all queue array manipulation.
1493 // We *could do this here, but I think we are better served keeping that code together.
1494 [fHBController moveObjectsInQueueArray:fJobGroups fromIndexes:moveItems toIndex: index];