X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=macosx%2FHBQueueController.mm;h=3a53192838e21b359356e9ab806cfa3cc3dc1da6;hb=8b225a770f1eb105989eec793d537ff930a6d3fd;hp=755692a0267de342ba0fa071d883dea548cc8347;hpb=3dd79f6623809ceb16c6d311d51a6c0f4cdbf5a3;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index 755692a0..3a531928 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -1,163 +1,91 @@ /* HBQueueController This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ -#include "HBQueueController.h" -#include "Controller.h" +#import "HBQueueController.h" +#import "Controller.h" +#import "HBImageAndTextCell.h" -#define HB_QUEUE_DRAGGING 0 -#define HBQueueDataType @"HBQueueDataType" +#define HB_ROW_HEIGHT_TITLE_ONLY 17.0 +#define HB_ROW_HEIGHT_FULL_DESCRIPTION 200.0 +// Pasteboard type for or drag operations +#define DragDropSimplePboardType @"MyCustomOutlineViewPboardType" -// UNI_QUEUE turns on the feature where the first item in the queue NSTableView is the -// current job followed by the jobs in hblib's queue. In this scheme, fCurrentJobPane -// disappers. -#define HB_UNI_QUEUE 0 - -#define HB_ROW_HEIGHT_DETAIL 98.0 -#define HB_ROW_HEIGHT_NO_DETAIL 17.0 -#define HB_ROW_HEIGHT_ACTIVE_JOB 60.0 +//------------------------------------------------------------------------------------ +#pragma mark - +//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ -#pragma mark Job group functions +// NSMutableAttributedString (HBAdditions) //------------------------------------------------------------------------------------ -// These could be part of hblib if we think hblib should have knowledge of groups. -// For now, I see groups as a metaphor that HBQueueController provides. -/** - * Returns the number of jobs groups in the queue. - * @param h Handle to hb_handle_t. - * @return Number of job groups. - */ -static int hb_group_count(hb_handle_t * h) -{ - hb_job_t * job; - int count = 0; - int index = 0; - while( ( job = hb_job( h, index++ ) ) ) - { - if (job->sequence_id == 0) - count++; - } - return count; -} +@interface NSMutableAttributedString (HBAdditions) +- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary; +@end -/** - * Returns handle to the first job in the i-th group within the job list. - * @param h Handle to hb_handle_t. - * @param i Index of group. - * @returns Handle to hb_job_t of desired job. - */ -static hb_job_t * hb_group(hb_handle_t * h, int i) +@implementation NSMutableAttributedString (HBAdditions) +- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary { - hb_job_t * job; - int count = 0; - int index = 0; - while( ( job = hb_job( h, index++ ) ) ) - { - if (job->sequence_id == 0) - { - if (count == i) - return job; - count++; - } - } - return NULL; + NSAttributedString * s = [[[NSAttributedString alloc] + initWithString: aString + attributes: aDictionary] autorelease]; + [self appendAttributedString: s]; } +@end -/** - * Removes a groups of jobs from the job list. - * @param h Handle to hb_handle_t. - * @param job Handle to the first job in the group. - */ -static void hb_rem_group( hb_handle_t * h, hb_job_t * job ) -{ - // Find job in list - hb_job_t * j; - int index = 0; - while( ( j = hb_job( h, index ) ) ) - { - if (j == job) - { - // Delete this job plus the following ones in the sequence - hb_rem( h, job ); - while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) ) - hb_rem( h, j ); - return; - } - else - index++; - } -} -/** - * Returns handle to the next job after the given job. - * @param h Handle to hb_handle_t. - * @param job Handle to the a job in the group. - * @returns Handle to hb_job_t of desired job or NULL if no such job. - */ -static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) +@implementation HBQueueOutlineView + +- (void)viewDidEndLiveResize { - hb_job_t * j = NULL; - int index = 0; - while( ( j = hb_job( h, index++ ) ) ) - { - if (j == job) - return hb_job( h, index ); - } - return NULL; + // Since we disabled calculating row heights during a live resize, force them to + // recalculate now. + [self noteHeightOfRowsWithIndexesChanged: + [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [self numberOfRows])]]; + [super viewDidEndLiveResize]; } -#pragma mark - -//------------------------------------------------------------------------------------ -// HBJob -//------------------------------------------------------------------------------------ -#if HB_OUTLINE_QUEUE -@interface HBJob : NSObject +/* This should be for dragging, we take this info from the presets right now */ +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset { - hb_job_t *fJob; -} -+ (HBJob*) jobWithJob: (hb_job_t *) job; -- (id) initWithJob: (hb_job_t *) job; -- (hb_job_t *) job; -@end + fIsDragging = YES; -@implementation HBJob -+ (HBJob*) jobWithJob: (hb_job_t *) job -{ - return [[[HBJob alloc] initWithJob:job] autorelease]; + // By default, NSTableView only drags an image of the first column. Change this to + // drag an image of the queue's icon and desc and action columns. + NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"desc"], [self tableColumnWithIdentifier:@"icon"],[self tableColumnWithIdentifier:@"action"], nil]; + return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset]; } -- (id) initWithJob: (hb_job_t *) job + + +- (void) mouseDown:(NSEvent *)theEvent { - if (self = [super init]) - { - // job is not owned by HBJob. It does not get dealloacted when HBJob is released. - fJob = job; - } - return self; + [super mouseDown:theEvent]; + fIsDragging = NO; } -- (hb_job_t*) job + + +- (BOOL) isDragging; { - return fJob; + return fIsDragging; } -@end -#endif // HB_OUTLINE_QUEUE -#pragma mark - +@end +#pragma mark Toolbar Identifiers // Toolbar identifiers -static NSString* HBQueueToolbar = @"HBQueueToolbar"; -static NSString* HBStartPauseResumeToolbarIdentifier = @"HBStartPauseResumeToolbarIdentifier"; -static NSString* HBShowDetailToolbarIdentifier = @"HBShowDetailToolbarIdentifier"; -static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsToolbarIdentifier"; +static NSString* HBQueueToolbar = @"HBQueueToolbar1"; +static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier"; +static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier"; +#pragma mark - @implementation HBQueueController @@ -166,8 +94,15 @@ static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsTo //------------------------------------------------------------------------------------ - (id)init { - if (self = [super init]) + if (self = [super initWithWindowNibName:@"Queue"]) { + // NSWindowController likes to lazily load its window nib. Since this + // controller tries to touch the outlets before accessing the window, we + // need to force it to load immadiately by invoking its accessor. + // + // If/when we switch to using bindings, this can probably go away. + [self window]; + // Our defaults [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: @"NO", @"QueueWindowIsOpen", @@ -175,18 +110,90 @@ static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsTo @"YES", @"QueueShowsJobsAsGroups", nil]]; - fShowsDetail = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsDetail"]; -#if HB_OUTLINE_QUEUE - fShowsJobsAsGroups = YES; -#else - fShowsJobsAsGroups = [[NSUserDefaults standardUserDefaults] boolForKey:@"QueueShowsJobsAsGroups"]; -#endif + fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; + } + return self; +} -#if HB_OUTLINE_QUEUE - fEncodes = [[NSMutableArray arrayWithCapacity:0] retain]; -#endif +- (void)setQueueArray: (NSMutableArray *)QueueFileArray +{ + [fJobGroups setArray:QueueFileArray]; + fIsDragging = NO; + /* First stop any timer working now */ + [self stopAnimatingCurrentJobGroupInQueue]; + [fOutlineView reloadData]; + + + + /* lets get the stats on the status of the queue array */ + + fEncodingQueueItem = 0; + fPendingCount = 0; + fCompletedCount = 0; + fCanceledCount = 0; + fWorkingCount = 0; + + /* We use a number system to set the encode status of the queue item + * in controller.mm + * 0 == already encoded + * 1 == is being encoded + * 2 == is yet to be encoded + * 3 == cancelled + */ + + int i = 0; + for(id tempObject in fJobGroups) + { + NSDictionary *thisQueueDict = tempObject; + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed + { + fCompletedCount++; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded + { + fWorkingCount++; + fEncodingQueueItem = i; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending + { + fPendingCount++; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled + { + fCanceledCount++; + } + i++; + } + + /* We should fire up the encoding timer here based on fWorkingCount */ + + if (fWorkingCount > 0) + { + /* we have an encoding job so, lets start the animation timer */ + [self startAnimatingCurrentWorkingEncodeInQueue]; + } + + /* Set the queue status field in the queue window */ + NSMutableString * string; + if (fPendingCount == 1) + { + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount]; + } + else + { + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount]; } - return self; + [fQueueCountField setStringValue:string]; + +} +/* This method sets the status string in the queue window + * and is called from Controller.mm (fHBController) + * instead of running another timer here polling libhb + * for encoding status + */ +- (void)setQueueStatusString: (NSString *)statusString +{ +[fProgressTextField setStringValue:statusString]; } //------------------------------------------------------------------------------------ @@ -194,15 +201,16 @@ static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsTo //------------------------------------------------------------------------------------ - (void)dealloc { - [fAnimation release]; - // clear the delegate so that windowWillClose is not attempted - if ([fQueueWindow delegate] == self) - [fQueueWindow setDelegate:nil]; - -#if HB_OUTLINE_QUEUE - [fEncodes release]; -#endif + if( [[self window] delegate] == self ) + [[self window] setDelegate:nil]; + + [fJobGroups release]; + + [fSavedExpandedItems release]; + [fSavedSelectedItems release]; + + [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @@ -212,7 +220,7 @@ static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsTo //------------------------------------------------------------------------------------ - (void)setHandle: (hb_handle_t *)handle { - fHandle = handle; + fQueueEncodeLibhb = handle; } //------------------------------------------------------------------------------------ @@ -223,1332 +231,1283 @@ static NSString* HBShowGroupsToolbarIdentifier = @"HBShowGroupsTo fHBController = controller; } +#pragma mark - + //------------------------------------------------------------------------------------ // Displays and brings the queue window to the front //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender { - if (!fQueueWindow) - { - BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow; - NSAssert(loadSucceeded, @"Could not open Queue nib file"); - } + [self showWindow:sender]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; +} - [self updateQueueUI]; - [self updateCurrentJobUI]; - [fQueueWindow makeKeyAndOrderFront: self]; - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; -} //------------------------------------------------------------------------------------ -// Show or hide the current job pane (fCurrentJobPane). +// awakeFromNib //------------------------------------------------------------------------------------ -- (void) showCurrentJobPane: (BOOL)showPane +- (void)awakeFromNib { - if (showPane != fCurrentJobHidden) - return; - - // Things to keep in mind: - // - When the current job pane is shown, it occupies the upper portion of the - // window with the queue occupying the bottom portion of the window. - // - When the current job pane is hidden, it slides up and out of view. - // NSView setHidden is NOT used. The queue pane is resized to occupy the full - // window. - - NSRect windowFrame = [[fCurrentJobPane superview] frame]; - NSRect queueFrame, jobFrame; - if (showPane) - NSDivideRect(windowFrame, &jobFrame, &queueFrame, NSHeight([fCurrentJobPane frame]), NSMaxYEdge); - else - { - queueFrame = windowFrame; - jobFrame = [fCurrentJobPane frame]; - jobFrame.origin.y = NSHeight(windowFrame); - } - - // Move fCurrentJobPane - NSDictionary * dict1 = [NSDictionary dictionaryWithObjectsAndKeys: - fCurrentJobPane, NSViewAnimationTargetKey, - [NSValue valueWithRect:jobFrame], NSViewAnimationEndFrameKey, - nil]; + [self setupToolbar]; - // Resize fQueuePane - NSDictionary * dict2 = [NSDictionary dictionaryWithObjectsAndKeys: - fQueuePane, NSViewAnimationTargetKey, - [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey, - nil]; + if( ![[self window] setFrameUsingName:@"Queue"] ) + [[self window] center]; + [self setWindowFrameAutosaveName:@"Queue"]; + [[self window] setExcludedFromWindowsMenu:YES]; - if (!fAnimation) - fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil]; + /* lets setup our queue list outline view for drag and drop here */ + [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ]; + [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; + [fOutlineView setVerticalMotionCanBeginDrag: YES]; - [fAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]]; - [fAnimation setDuration:0.25]; - [fAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation - [fAnimation startAnimation]; - fCurrentJobHidden = !showPane; -} -//------------------------------------------------------------------------------------ -// Enables or disables the display of detail information for each job. -//------------------------------------------------------------------------------------ -- (void)setShowsDetail: (BOOL)showsDetail -{ - fShowsDetail = showsDetail; - - [[NSUserDefaults standardUserDefaults] setBool:showsDetail forKey:@"QueueShowsDetail"]; - [[NSUserDefaults standardUserDefaults] synchronize]; + // Don't allow autoresizing of main column, else the "delete" column will get + // pushed out of view. + [fOutlineView setAutoresizesOutlineColumn: NO]; - [fTaskView setRowHeight:showsDetail ? HB_ROW_HEIGHT_DETAIL : HB_ROW_HEIGHT_NO_DETAIL]; -#if HB_UNI_QUEUE - if (hb_count(fHandle)) - [fTaskView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndex:0]]; +#if HB_OUTLINE_METRIC_CONTROLS + [fIndentation setHidden: NO]; + [fSpacing setHidden: NO]; + [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]]; // debug + [fSpacing setIntegerValue:3]; // debug #endif -#if HB_OUTLINE_QUEUE - [fOutlineView noteHeightOfRowsWithIndexesChanged: - [NSIndexSet indexSetWithIndexesInRange: - NSMakeRange(0,[fOutlineView numberOfRows]) - ]]; -#endif + // Show/hide UI elements + fCurrentJobPaneShown = NO; // it's shown in the nib + //[self showCurrentJobPane:NO]; - if ([fTaskView selectedRow] != -1) - [fTaskView scrollRowToVisible:[fTaskView selectedRow]]; + //[self updateQueueCountField]; } + //------------------------------------------------------------------------------------ -// Enables or disables the grouping of job passes into one item in the UI. +// windowWillClose //------------------------------------------------------------------------------------ -- (void)setShowsJobsAsGroups: (BOOL)showsGroups +- (void)windowWillClose:(NSNotification *)aNotification { -#if HB_OUTLINE_QUEUE - return; // Can't modify this value. It's always YES. -#endif - fShowsJobsAsGroups = showsGroups; - - [[NSUserDefaults standardUserDefaults] setBool:showsGroups forKey:@"QueueShowsJobsAsGroups"]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"]; +} + +#pragma mark Toolbar + +//------------------------------------------------------------------------------------ +// setupToolbar +//------------------------------------------------------------------------------------ +- (void)setupToolbar +{ + // Create a new toolbar instance, and attach it to our window + NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease]; + + // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults + [toolbar setAllowsUserCustomization: YES]; + [toolbar setAutosavesConfiguration: YES]; + [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; - [self updateQueueUI]; - if ([fTaskView selectedRow] != -1) - [fTaskView scrollRowToVisible:[fTaskView selectedRow]]; + // We are the delegate + [toolbar setDelegate: self]; + + // Attach the toolbar to our window + [[self window] setToolbar:toolbar]; } //------------------------------------------------------------------------------------ -// Returns a 16x16 image that represents a job pass. +// toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: //------------------------------------------------------------------------------------ -- (NSImage *)smallImageForPass: (int)pass +- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar + itemForItemIdentifier:(NSString *)itemIdentifier + willBeInsertedIntoToolbar:(BOOL)flag { - switch (pass) + // Required delegate method: Given an item identifier, this method returns an item. + // The toolbar will use this method to obtain toolbar items that can be displayed + // in the customization sheet, or in the toolbar itself. + + NSToolbarItem *toolbarItem = nil; + + if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier]) + { + toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; + + // Set the text label to be displayed in the toolbar and customization palette + [toolbarItem setLabel: @"Start"]; + [toolbarItem setPaletteLabel: @"Start/Cancel"]; + + // Set up a reasonable tooltip, and image + [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; + + // Tell the item what message to send when it is clicked + [toolbarItem setTarget: self]; + [toolbarItem setAction: @selector(toggleStartCancel:)]; + } + + if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier]) { - case -1: return [NSImage imageNamed: @"JobPassSubtitleSmall"]; - case 1: return [NSImage imageNamed: @"JobPassFirstSmall"]; - case 2: return [NSImage imageNamed: @"JobPassSecondSmall"]; - default: return [NSImage imageNamed: @"JobPassUnknownSmall"]; + toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; + + // Set the text label to be displayed in the toolbar and customization palette + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setPaletteLabel: @"Pause/Resume"]; + + // Set up a reasonable tooltip, and image + [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setImage: [NSImage imageNamed: @"Pause"]]; + + // Tell the item what message to send when it is clicked + [toolbarItem setTarget: self]; + [toolbarItem setAction: @selector(togglePauseResume:)]; } + + return toolbarItem; } //------------------------------------------------------------------------------------ -// Returns a 64x64 image that represents a job pass. +// toolbarDefaultItemIdentifiers: //------------------------------------------------------------------------------------ -- (NSImage *)largeImageForPass: (int)pass +- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar { - switch (pass) - { - case -1: return [NSImage imageNamed: @"JobPassSubtitleLarge"]; - case 1: return [NSImage imageNamed: @"JobPassFirstLarge"]; - case 2: return [NSImage imageNamed: @"JobPassSecondLarge"]; - default: return [NSImage imageNamed: @"JobPassUnknownLarge"]; - } + // Required delegate method: Returns the ordered list of items to be shown in the + // toolbar by default. + + return [NSArray arrayWithObjects: + HBQueueStartCancelToolbarIdentifier, + HBQueuePauseResumeToolbarIdentifier, + nil]; } -#if HB_OUTLINE_QUEUE //------------------------------------------------------------------------------------ -// Rebuilds the contents of fEncodes which is a array of encodes and HBJobs. +// toolbarAllowedItemIdentifiers: //------------------------------------------------------------------------------------ -- (void)rebuildEncodes +- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar { - [fEncodes removeAllObjects]; + // Required delegate method: Returns the list of all allowed items by identifier. + // By default, the toolbar does not assume any items are allowed, even the + // separator. So, every allowed item must be explicitly listed. - NSMutableArray * aJobGroup = [NSMutableArray arrayWithCapacity:0]; - hb_job_t * nextJob = hb_group( fHandle, 0 ); - while( nextJob ) - { - if (nextJob->sequence_id == 0) - { - // Encountered a new group. Add the current one to fEncodes and then start a new one. - if ([aJobGroup count] > 0) - { - [fEncodes addObject:aJobGroup]; - aJobGroup = [NSMutableArray arrayWithCapacity:0]; - } - } - [aJobGroup addObject: [HBJob jobWithJob:nextJob]]; - nextJob = hb_next_job (fHandle, nextJob); - } - if ([aJobGroup count] > 0) - [fEncodes addObject:aJobGroup]; + return [NSArray arrayWithObjects: + HBQueueStartCancelToolbarIdentifier, + HBQueuePauseResumeToolbarIdentifier, + NSToolbarCustomizeToolbarItemIdentifier, + NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarSpaceItemIdentifier, + NSToolbarSeparatorItemIdentifier, + nil]; } -#endif //------------------------------------------------------------------------------------ -// Generates a multi-line text string that includes the job name on the first line -// followed by details of the job on subsequent lines. If the text is to be drawn as -// part of a highlighted cell, set isHighlighted to true. The returned string may -// contain multiple fonts and paragraph formating. +// validateToolbarItem: //------------------------------------------------------------------------------------ -- (NSAttributedString *)attributedDescriptionForJob: (hb_job_t *)job - withTitle: (BOOL)withTitle - withDetail: (BOOL)withDetail - withHighlighting: (BOOL)highlighted +- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem { - NSMutableAttributedString * finalString; // the return value - NSAttributedString* anAttributedString; // a temp string for building up attributed substrings - NSMutableString* aMutableString; // a temp string for non-attributed substrings - hb_title_t * title = job->title; - - NSMutableParagraphStyle *ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - [ps setLineBreakMode:NSLineBreakByClipping]; - - static NSDictionary* detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:10.0], NSFontAttributeName, - [NSColor darkGrayColor], NSForegroundColorAttributeName, - ps, NSParagraphStyleAttributeName, - nil] retain]; - static NSDictionary* detailHighlightedAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:10.0], NSFontAttributeName, - [NSColor whiteColor], NSForegroundColorAttributeName, - ps, NSParagraphStyleAttributeName, - nil] retain]; - static NSDictionary* titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName, - ps, NSParagraphStyleAttributeName, - nil] retain]; - - finalString = [[[NSMutableAttributedString alloc] init] autorelease]; - - // Title, in bold - // Show the name of the source Note: use title->name instead of title->dvd since - // name is just the chosen folder, instead of dvd which is the full path - if (withTitle) - { - anAttributedString = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:title->name] attributes:titleAttribute] autorelease]; - [finalString appendAttributedString:anAttributedString]; - } - - // Other info in plain - - aMutableString = [NSMutableString stringWithCapacity:200]; + // Optional method: This message is sent to us since we are the target of some + // toolbar item actions. - // The subtitle scan doesn't contain all the stuff we need (like x264opts). - // So grab the next job in the group for display purposes. - if (fShowsJobsAsGroups && job->pass == -1) - { - // When job is the one currently being processed, then the next in its group - // is the the first job in the queue. - hb_job_t * nextjob; - if (job == hb_current_job(fHandle)) - nextjob = hb_job(fHandle, 0); - else - nextjob = hb_next_job(fHandle, job); - if (nextjob) // Overly cautious in case there is no next job! - job = nextjob; - } + if (!fQueueEncodeLibhb) return NO; - if (withTitle) - { - NSString * chapterString = (job->chapter_start == job->chapter_end) ? - [NSString stringWithFormat:@"Chapter %d", job->chapter_start] : - [NSString stringWithFormat:@"Chapters %d through %d", job->chapter_start, job->chapter_end]; - - // Scan pass - if (job->pass == -1) - { - [aMutableString appendString:[NSString stringWithFormat: - @" (Title %d, %@, Subtitle Scan)", title->index, chapterString]]; - } - else - { - if (fShowsJobsAsGroups) - [aMutableString appendString:[NSString stringWithFormat: - @" (Title %d, %@, %d-Pass)", - title->index, chapterString, MIN( 2, job->pass + 1 )]]; - else - [aMutableString appendString:[NSString stringWithFormat: - @" (Title %d, %@, Pass %d of %d)", - title->index, chapterString, MAX( 1, job->pass ), MIN( 2, job->pass + 1 )]]; - } - } - - // End of title stuff - - - // Normal pass - show detail - if (withDetail && job->pass != -1) + BOOL enable = NO; + + hb_state_t s; + hb_get_state2 (fQueueEncodeLibhb, &s); + + if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier]) { - NSString * jobFormat; - NSString * jobPictureDetail; - NSString * jobVideoDetail; - NSString * jobVideoCodec; - NSString * jobVideoQuality; - NSString * jobAudioDetail; - NSString * jobAudioCodec; - - /* Muxer settings (File Format in the gui) */ - if (job->mux == 65536 || job->mux == 131072 || job->mux == 1048576) - jobFormat = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD - else if (job->mux == 262144) - jobFormat = @"AVI"; // HB_MUX_AVI - else if (job->mux == 524288) - jobFormat = @"OGM"; // HB_MUX_OGM - else if (job->mux == 2097152) - jobFormat = @"MKV"; // HB_MUX_MKV - else - jobFormat = @"unknown"; - - // 2097152 - /* Video Codec settings (Encoder in the gui) */ - if (job->vcodec == 1) - jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG - else if (job->vcodec == 2) - jobVideoCodec = @"XviD"; // HB_VCODEC_XVID - else if (job->vcodec == 4) + if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) { - /* Deterimine for sure how we are now setting iPod uuid atom */ - if (job->h264_level) // We are encoding for iPod - jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 - else - jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264 + enable = YES; + [toolbarItem setImage:[NSImage imageNamed: @"Stop"]]; + [toolbarItem setLabel: @"Stop"]; + [toolbarItem setToolTip: @"Stop Encoding"]; } - else - jobVideoCodec = @"unknown"; - - /* Audio Codecs (Second half of Codecs in the gui) */ - if (job->acodec == 256) - jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC - else if (job->acodec == 512) - jobAudioCodec = @"MP3"; // HB_ACODEC_LAME - else if (job->acodec == 1024) - jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS - else if (job->acodec == 2048) - jobAudioCodec = @"AC3"; // HB_ACODEC_AC3 - else - jobAudioCodec = @"unknown"; - /* Show Basic File info */ - if (job->chapter_markers == 1) - [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio, Chapter Markers", jobFormat, jobVideoCodec, jobAudioCodec]]; - else - [aMutableString appendString:[NSString stringWithFormat:@"\nFormat: %@ Container, %@ Video + %@ Audio", jobFormat, jobVideoCodec, jobAudioCodec]]; - - /*Picture info*/ - /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, - maxWidth, maxHeight */ - if (job->pixel_ratio == 1) + + else if (fPendingCount > 0) { - int titlewidth = title->width - job->crop[2] - job->crop[3]; - int displayparwidth = titlewidth * job->pixel_aspect_width / job->pixel_aspect_height; - int displayparheight = title->height - job->crop[0] - job->crop[1]; - jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, job->width, displayparheight]; + enable = YES; + [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } + else - jobPictureDetail = [NSString stringWithFormat:@"Picture: %dx%d", job->width, job->height]; - if (job->keep_ratio == 1) - jobPictureDetail = [jobPictureDetail stringByAppendingString:@" Keep Aspect Ratio"]; - - if (job->grayscale == 1) - jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Grayscale"]; - - if (job->deinterlace == 1) - jobPictureDetail = [jobPictureDetail stringByAppendingString:@", Deinterlace"]; - /* Show Picture info */ - [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobPictureDetail]]; - - /* Detailed Video info */ - if (job->vquality <= 0 || job->vquality >= 1) - jobVideoQuality =[NSString stringWithFormat:@"%d kbps", job->vbitrate]; - else - { - NSNumber * vidQuality; - vidQuality = [NSNumber numberWithInt:job->vquality * 100]; - /* this is screwed up kind of. Needs to be formatted properly */ - if (job->crf == 1) - jobVideoQuality =[NSString stringWithFormat:@"%@%% CRF", vidQuality]; - else - jobVideoQuality =[NSString stringWithFormat:@"%@%% CQP", vidQuality]; - } - - if (job->vrate_base == 1126125) { - /* NTSC FILM 23.976 */ - jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality]; + enable = NO; + [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } - else if (job->vrate_base == 900900) + } + + if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier]) + { + if (s.state == HB_STATE_PAUSED) { - /* NTSC 29.97 */ - jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality]; - } - else + enable = YES; + [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; + [toolbarItem setLabel: @"Resume"]; + [toolbarItem setToolTip: @"Resume Encoding"]; + } + + else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) { - /* Everything else */ - jobVideoDetail = [NSString stringWithFormat:@"Video: %@, %@, %d fps", jobVideoCodec, jobVideoQuality, job->vrate / job->vrate_base]; + enable = YES; + [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } - - /* Add the video detail string to the job filed in the window */ - [aMutableString appendString:[NSString stringWithFormat:@"\n%@", jobVideoDetail]]; - - /* if there is an x264 option string, lets add it here*/ - /*NOTE: Due to size, lets get this in a tool tip*/ - - if (job->x264opts) - [aMutableString appendString:[NSString stringWithFormat:@"\nx264 Options: %@", [NSString stringWithUTF8String:job->x264opts]]]; - - /* Audio Detail */ - if ([jobAudioCodec isEqualToString: @"AC3"]) - jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, Pass-Through", jobAudioCodec]; else - jobAudioDetail = [NSString stringWithFormat:@"Audio: %@, %d kbps, %d Hz", jobAudioCodec, job->abitrate, job->arate]; - - /* we now get the audio mixdown info for each of the two gui audio tracks */ - /* lets do it the long way here to get a handle on things. - Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] */ - int ai; // counter for each audios [] , macgui only allows for two audio tracks currently - for( ai = 0; ai < 2; ai++ ) { - if (job->audio_mixdowns[ai] == HB_AMIXDOWN_MONO) - jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono",ai + 1]]; - if (job->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO) - jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo",ai + 1]]; - if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY) - jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround",ai + 1]]; - if (job->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII) - jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II",ai + 1]]; - if (job->audio_mixdowns[ai] == HB_AMIXDOWN_6CH) - jobAudioDetail = [jobAudioDetail stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet",ai + 1]]; + enable = NO; + [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } - - /* Add the Audio detail string to the job filed in the window */ - [aMutableString appendString:[NSString stringWithFormat: @"\n%@", jobAudioDetail]]; - - /*Destination Field */ - [aMutableString appendString:[NSString stringWithFormat:@"\nDestination: %@", [NSString stringWithUTF8String:job->file]]]; } - - anAttributedString = [[[NSAttributedString alloc] initWithString:aMutableString attributes:highlighted ? detailHighlightedAttribute : detailAttribute] autorelease]; - [finalString appendAttributedString:anAttributedString]; - - return finalString; + return enable; } +#pragma mark - + + +#pragma mark Queue Item Controls //------------------------------------------------------------------------------------ -// Generate string to display in UI. +// Delete encodes from the queue window and accompanying array +// Also handling first cancelling the encode if in fact its currently encoding. //------------------------------------------------------------------------------------ -- (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s +- (IBAction)removeSelectedQueueItem: (id)sender { - if (s->state == HB_STATE_WORKING) + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + NSUInteger row = [selectedRows firstIndex]; + if( row == NSNotFound ) + return; + /* if this is a currently encoding job, we need to be sure to alert the user, + * to let them decide to cancel it first, then if they do, we can come back and + * remove it */ + + if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1) { - NSString * msg; - if (job->pass == -1) - msg = NSLocalizedString( @"Analyzing subtitles", nil ); - else if (job->pass == 1) - msg = NSLocalizedString( @"Analyzing video", nil ); - else if ((job->pass == 0) || (job->pass == 2)) - msg = NSLocalizedString( @"Encoding movie", nil ); - else - return @""; // unknown condition! - - if( s->param.working.seconds > -1 ) - { - return [NSString stringWithFormat: - NSLocalizedString( @"%@ (%.2f fps, avg %.2f fps)", nil ), - msg, s->param.working.rate_cur, s->param.working.rate_avg]; - } - else - return msg; - + /* We pause the encode here so that it doesn't finish right after and then + * screw up the sync while the window is open + */ + [fHBController Pause:NULL]; + NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)]; + // Which window to attach the sheet to? + NSWindow * docWindow = nil; + if ([sender respondsToSelector: @selector(window)]) + docWindow = [sender window]; + + + NSBeginCriticalAlertSheet( + alertTitle, + NSLocalizedString(@"Keep Encoding", nil), + nil, + NSLocalizedString(@"Stop Encoding and Delete", nil), + docWindow, self, + nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil, + NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)); + + // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed } + else + { + /* since we are not a currently encoding item, we can just be cancelled */ + [fHBController removeQueueFileItem:row]; + } +} - else if (s->state == HB_STATE_MUXING) - return NSLocalizedString( @"Muxing", nil ); - - else if (s->state == HB_STATE_PAUSED) - return NSLocalizedString( @"Paused", nil ); - - else if (s->state == HB_STATE_WORKDONE) - return NSLocalizedString( @"Done", nil ); +- (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo +{ + /* We resume encoding and perform the appropriate actions + * Note: Pause: is a toggle type method based on hb's current + * state, if it paused, it will resume encoding and vice versa. + * In this case, we are paused from the calling window, so calling + * [fHBController Pause:NULL]; Again will resume encoding + */ + [fHBController Pause:NULL]; + if (returnCode == NSAlertOtherReturn) + { + /* We need to save the currently encoding item number first */ + int encodingItemToRemove = fEncodingQueueItem; + /* Since we are encoding, we need to let fHBController Cancel this job + * upon which it will move to the next one if there is one + */ + [fHBController doCancelCurrentJob]; + /* Now, we can go ahead and remove the job we just cancelled since + * we have its item number from above + */ + [fHBController removeQueueFileItem:encodingItemToRemove]; + } - return @""; } //------------------------------------------------------------------------------------ -// Generate string to display in UI. +// Show the finished encode in the finder //------------------------------------------------------------------------------------ -- (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s +- (IBAction)revealSelectedQueueItem: (id)sender { - if (s->state == HB_STATE_WORKING) + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + NSInteger row = [selectedRows firstIndex]; + if (row != NSNotFound) { - #define p s->param.working - if (p.seconds < 0) - return @""; - - // Minutes always needed - NSString * minutes; - if (p.minutes > 1) - minutes = [NSString stringWithFormat:NSLocalizedString( @"%d minutes ", nil ), p.minutes]; - else if (p.minutes == 1) - minutes = NSLocalizedString( @"1 minute ", nil ); - else - minutes = @""; - - if (p.hours >= 1) - { - NSString * hours; - if (p.hours > 1) - hours = [NSString stringWithFormat:NSLocalizedString( @"%d hours ", nil ), p.hours]; - else - hours = NSLocalizedString( @"1 hour ", nil ); - - return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), hours, minutes]; - } - - else + while (row != NSNotFound) { - NSString * seconds; - if (p.seconds > 1) - seconds = [NSString stringWithFormat:NSLocalizedString( @"%d seconds ", nil ), p.seconds]; - else - seconds = NSLocalizedString( @"1 second ", nil ); - - return [NSString stringWithFormat:NSLocalizedString( @"%@%@remaining", nil ), minutes, seconds]; - } + NSMutableDictionary *queueItemToOpen = [fOutlineView itemAtRow: row]; + [[NSWorkspace sharedWorkspace] selectFile:[queueItemToOpen objectForKey:@"DestinationPath"] inFileViewerRootedAtPath:nil]; -/* here is code that does it more like the Finder - if( p.seconds > -1 ) - { - float estHours = (p.hours + (p.minutes / 60.0)); - float estMinutes = (p.minutes + (p.seconds / 60.0)); - - if (estHours > 1.5) - return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d hours", nil ), lrintf(estHours)]; - else if (estHours > 0.983) // 59 minutes - return NSLocalizedString( @"Time remaining: About 1 hour", nil ); - else if (estMinutes > 1.5) - return [NSString stringWithFormat:NSLocalizedString( @"Time remaining: About %d minutes", nil ), lrintf(estMinutes)]; - else if (estMinutes > 0.983) // 59 seconds - return NSLocalizedString( @"Time remaining: About 1 minute", nil ); - else if (p.seconds <= 5) - return NSLocalizedString( @"Time remaining: Less than 5 seconds", nil ); - else if (p.seconds <= 10) - return NSLocalizedString( @"Time remaining: Less than 10 seconds", nil ); - else - return NSLocalizedString( @"Time remaining: Less than 1 minute", nil ); + row = [selectedRows indexGreaterThanIndex: row]; } - else - return NSLocalizedString( @"Time remaining: Calculating...", nil ); -*/ - #undef p } - - return @""; } + //------------------------------------------------------------------------------------ -// Refresh progress bar (fProgressBar) from current state. +// Starts or cancels the processing of jobs depending on the current state //------------------------------------------------------------------------------------ -- (void) updateProgressBarWithState: (hb_state_t *)s +- (IBAction)toggleStartCancel: (id)sender { - if (s->state == HB_STATE_WORKING) - { - #define p s->param.working - [fProgressBar setIndeterminate:NO]; + if (!fQueueEncodeLibhb) return; - float progress_total = fShowsJobsAsGroups ? - 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count : - 100.0 * p.progress; + hb_state_t s; + hb_get_state2 (fQueueEncodeLibhb, &s); - [fProgressBar setDoubleValue:progress_total]; - #undef p - } - - else if (s->state == HB_STATE_MUXING) - { - #define p s->param.muxing - [fProgressBar setIndeterminate:YES]; - [fProgressBar startAnimation:nil]; - #undef p - } + if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) + [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window - else if (s->state == HB_STATE_WORKDONE) - { - [fProgressBar setIndeterminate:NO]; - [fProgressBar setDoubleValue:0.0]; - } + else if (fPendingCount > 0) + [fHBController Rip: NULL]; } //------------------------------------------------------------------------------------ -// Refresh queue count text field (fQueueCountField). +// Toggles the pause/resume state of libhb //------------------------------------------------------------------------------------ -- (void)updateQueueCountField +- (IBAction)togglePauseResume: (id)sender { - NSString * msg; - int jobCount; + if (!fQueueEncodeLibhb) return; + + hb_state_t s; + hb_get_state2 (fQueueEncodeLibhb, &s); - if (fShowsJobsAsGroups) + if (s.state == HB_STATE_PAUSED) { - jobCount = fHandle ? hb_group_count(fHandle) : 0; - if (jobCount == 1) - msg = NSLocalizedString(@"1 pending encode", nil); - else - msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), jobCount]; + hb_resume (fQueueEncodeLibhb); + [self startAnimatingCurrentWorkingEncodeInQueue]; } - else + else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) { - jobCount = fHandle ? hb_count(fHandle) : 0; - if (jobCount == 1) - msg = NSLocalizedString(@"1 pending pass", nil); - else - msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending passes", nil), jobCount]; - + hb_pause (fQueueEncodeLibhb); + [self stopAnimatingCurrentJobGroupInQueue]; } - - [fQueueCountField setStringValue:msg]; } -//------------------------------------------------------------------------------------ -// Refresh the UI in the current job pane. Should be called whenever the current job -// being processed has changed or when progress has changed. -//------------------------------------------------------------------------------------ -- (void)updateCurrentJobUI -{ - hb_state_t s; - hb_job_t * job = nil; - - if (fHandle) - { - hb_get_state2( fHandle, &s ); - job = hb_current_job(fHandle); - } +#pragma mark - + + +#pragma mark Animate Endcoding Item - if (job) - { - [fJobDescTextField setAttributedStringValue:[self attributedDescriptionForJob:job withTitle:YES withDetail:YES withHighlighting:NO]]; - [self showCurrentJobPane:YES]; - [fJobIconView setImage: fShowsJobsAsGroups ? [NSImage imageNamed:@"JobLarge"] : [self largeImageForPass: job->pass] ]; - - NSString * statusMsg = [self progressStatusStringForJob:job state:&s]; - NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:&s]; - if ([timeMsg length] > 0) - statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg]; - [fProgressTextField setStringValue:statusMsg]; - [self updateProgressBarWithState:&s]; - } - else - { - [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)]; - [self showCurrentJobPane:NO]; - [fProgressBar stopAnimation:nil]; // just in case in was animating - } -} //------------------------------------------------------------------------------------ -// Refresh the UI in the queue pane. Should be called whenever the content of HB's job -// list has changed so that HBQueueController can sync up. +// Starts animating the job icon of the currently processing job in the queue outline +// view. //------------------------------------------------------------------------------------ -- (void)updateQueueUI +- (void) startAnimatingCurrentWorkingEncodeInQueue { -#if HB_OUTLINE_QUEUE - [self rebuildEncodes]; - [fOutlineView noteNumberOfRowsChanged]; - [fOutlineView reloadData]; -#endif - [fTaskView noteNumberOfRowsChanged]; - [fTaskView reloadData]; - - [self updateQueueCountField]; + if (!fAnimationTimer) + fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle + target:self + selector:@selector(animateWorkingEncodeInQueue:) + userInfo:nil + repeats:YES] retain]; } //------------------------------------------------------------------------------------ -// Deletes the selected job from HB and the queue UI +// If a job is currently processing, its job icon in the queue outline view is +// animated to its next state. //------------------------------------------------------------------------------------ -- (IBAction)removeSelectedJob: (id)sender +- (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer { - if (!fHandle) return; - - int row = [sender selectedRow]; - if (row != -1) + if (fWorkingCount > 0) { -#if HB_UNI_QUEUE - if (row == 0) - { - [self cancelCurrentJob:sender]; - } - else - { - row--; - if (fShowsJobsAsGroups) - hb_rem_group( fHandle, hb_group( fHandle, row ) ); - else - hb_rem( fHandle, hb_job( fHandle, row ) ); - } -#else - if (fShowsJobsAsGroups) - hb_rem_group( fHandle, hb_group( fHandle, row ) ); - else - hb_rem( fHandle, hb_job( fHandle, row ) ); -#endif - [self updateQueueUI]; + fAnimationIndex++; + fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below. + [self animateWorkingEncodeIconInQueue]; } } -//------------------------------------------------------------------------------------ -// Prompts user if the want to cancel encoding of current job. If so, doCancelCurrentJob -// gets called. -//------------------------------------------------------------------------------------ -- (IBAction)cancelCurrentJob: (id)sender -{ - [fHBController Cancel:sender]; -} -//------------------------------------------------------------------------------------ -// Turns on the display of detail information for each job. Does nothing if detail is -// already turned on. -//------------------------------------------------------------------------------------ -- (IBAction)showDetail: (id)sender +- (void) animateWorkingEncodeIconInQueue { - if (!fShowsDetail) - [self setShowsDetail:YES]; + NSInteger row = fEncodingQueueItem; /// need to set to fEncodingQueueItem + NSInteger col = [fOutlineView columnWithIdentifier: @"icon"]; + if (row != -1 && col != -1) + { + NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row]; + [fOutlineView setNeedsDisplayInRect: frame]; + } } //------------------------------------------------------------------------------------ -// Turns off the display of detail information for each job. Does nothing if detail is -// already turned off. +// Stops animating the job icon of the currently processing job in the queue outline +// view. //------------------------------------------------------------------------------------ -- (IBAction)hideDetail: (id)sender +- (void) stopAnimatingCurrentJobGroupInQueue { - if (fShowsDetail) - [self setShowsDetail:NO]; + if (fAnimationTimer && [fAnimationTimer isValid]) + { + [fAnimationTimer invalidate]; + [fAnimationTimer release]; + fAnimationTimer = nil; + } } -//------------------------------------------------------------------------------------ -// Turns on displaying of jobs as groups by calling setShowsJobsAsGroups:YES. Does -// nothing if groups are already turned on. -//------------------------------------------------------------------------------------ -- (IBAction)showJobsAsGroups: (id)sender -{ - if (!fShowsJobsAsGroups) - [self setShowsJobsAsGroups:YES]; -} -//------------------------------------------------------------------------------------ -// Turns on displaying of jobs as individual items by calling setShowsJobsAsGroups:NO. -// Does nothing if groups are already turned off. -//------------------------------------------------------------------------------------ -- (IBAction)showJobsAsPasses: (id)sender -{ - if (fShowsJobsAsGroups) - [self setShowsJobsAsGroups:NO]; -} +#pragma mark - -//------------------------------------------------------------------------------------ -// Toggles the processing of jobs on or off depending on the current state -//------------------------------------------------------------------------------------ -- (IBAction)toggleStartPause: (id)sender +- (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex { - if (!fHandle) return; - - hb_state_t s; - hb_get_state2 (fHandle, &s); + NSUInteger index = [indexSet lastIndex]; + NSUInteger aboveInsertIndexCount = 0; - if (s.state == HB_STATE_PAUSED) - hb_resume (fHandle); - else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) - hb_pause (fHandle); - else + while (index != NSNotFound) { - if (fShowsJobsAsGroups) + NSUInteger removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else { - if (hb_group_count(fHandle) > 0) - [fHBController doRip]; + removeIndex = index; + insertIndex--; } - else if (hb_count(fHandle) > 0) - [fHBController doRip]; - } + + id object = [[array objectAtIndex:removeIndex] retain]; + [array removeObjectAtIndex:removeIndex]; + [array insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + } } + #pragma mark - -#pragma mark Toolbar +#pragma mark NSOutlineView delegate -//------------------------------------------------------------------------------------ -// setupToolbar -//------------------------------------------------------------------------------------ -- (void)setupToolbar -{ - // Create a new toolbar instance, and attach it to our window - NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease]; - - // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults - [toolbar setAllowsUserCustomization: YES]; - [toolbar setAutosavesConfiguration: YES]; - [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; - - // We are the delegate - [toolbar setDelegate: self]; - - // Attach the toolbar to our window - [fQueueWindow setToolbar: toolbar]; -} -//------------------------------------------------------------------------------------ -// toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: -//------------------------------------------------------------------------------------ -- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar - itemForItemIdentifier:(NSString *)itemIdentifier - willBeInsertedIntoToolbar:(BOOL)flag +- (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item { - // Required delegate method: Given an item identifier, this method returns an item. - // The toolbar will use this method to obtain toolbar items that can be displayed - // in the customization sheet, or in the toolbar itself. - - NSToolbarItem *toolbarItem = nil; - - if ([itemIdentifier isEqual: HBStartPauseResumeToolbarIdentifier]) - { - toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - - // Set the text label to be displayed in the toolbar and customization palette - [toolbarItem setLabel: @"Start"]; - [toolbarItem setPaletteLabel: @"Start/Pause"]; - - // Set up a reasonable tooltip, and image - [toolbarItem setToolTip: @"Start Encoding"]; - [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: @selector(toggleStartPause:)]; - } - - else if ([itemIdentifier isEqual: HBShowDetailToolbarIdentifier]) - { - toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - - // Set the text label to be displayed in the toolbar and customization palette - [toolbarItem setLabel: @"Detail"]; - [toolbarItem setPaletteLabel: @"Detail"]; - - // Set up a reasonable tooltip, and image - [toolbarItem setToolTip: @"Displays detailed information in the queue"]; - [toolbarItem setImage: [NSImage imageNamed: @"Detail"]]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: fShowsDetail ? @selector(hideDetail:) : @selector(showDetail:)]; - } - - else if ([itemIdentifier isEqual: HBShowGroupsToolbarIdentifier]) - { - toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - -/* - // Set the text label to be displayed in the toolbar and customization palette - [toolbarItem setLabel: @"Passes"]; - [toolbarItem setPaletteLabel: @"Passes"]; - - // Set up a reasonable tooltip, and image - [toolbarItem setToolTip: @"Displays individual passes in the queue"]; - [toolbarItem setImage: [NSImage imageNamed: @"Passes"]]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: fShowsJobsAsGroups ? @selector(showJobsAsPasses:) : @selector(showJobsAsGroups:)]; -*/ - -// Various attempts at other button types in the toolbar. A matrix worked fine to display -// a button for encodes & passes, but ultimately I decided to go with a single button -// called "Passes" that toggles on or off. All these suffer from the fact taht you need -// to override NSToolbarItem for them in order to validate their state. - [toolbarItem setLabel: @"View"]; - [toolbarItem setPaletteLabel: @"View"]; - - NSButtonCell * buttonCell = [[[NSButtonCell alloc] initImageCell:nil] autorelease]; - [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle]; - [buttonCell setButtonType:NSToggleButton]; - [buttonCell setBordered:NO]; - [buttonCell setImagePosition:NSImageOnly]; - - NSMatrix * matrix = [[[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,54,25) - mode:NSRadioModeMatrix - prototype:buttonCell - numberOfRows:1 - numberOfColumns:2] autorelease]; - [matrix setCellSize:NSMakeSize(27, 25)]; - [matrix setIntercellSpacing:NSMakeSize(0, 0)]; - [matrix selectCellAtRow:0 column:(fShowsJobsAsGroups ? 0 : 1)]; - - buttonCell = [matrix cellAtRow:0 column:0]; - [buttonCell setTitle:@""]; - [buttonCell setImage:[NSImage imageNamed: @"Encodes"]]; - [buttonCell setAlternateImage:[NSImage imageNamed: @"EncodesPressed"]]; - [buttonCell setAction: @selector(showJobsAsGroups:)]; - [matrix setToolTip: @"Displays encodes in the queue" forCell:buttonCell]; - - buttonCell = [matrix cellAtRow:0 column:1]; - [buttonCell setTitle:@""]; - [buttonCell setImage:[NSImage imageNamed: @"Passes"]]; - [buttonCell setAlternateImage:[NSImage imageNamed: @"PassesPressed"]]; - [buttonCell setAction: @selector(showJobsAsPasses:)]; - [matrix setToolTip: @"Displays individual passes in the queue" forCell:buttonCell]; - - [toolbarItem setMinSize: [matrix frame].size]; - [toolbarItem setMaxSize: [matrix frame].size]; - [toolbarItem setView: matrix]; - -/* - NSSegmentedControl * segControl = [[[NSSegmentedControl alloc] initWithFrame:NSMakeRect(0,0,20,20)] autorelease]; - [[segControl cell] setControlSize:NSSmallControlSize]; - [segControl setSegmentCount:2]; - [segControl setLabel:@"Encodes" forSegment:0]; - [segControl setLabel:@"Passes" forSegment:1]; - [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:0]; - [segControl setImage:[NSImage imageNamed:@"Delete"] forSegment:1]; - [segControl setSelectedSegment: (fShowsJobsAsGroups ? 0 : 1)]; - [segControl sizeToFit]; - [toolbarItem setMinSize: [segControl frame].size]; - [toolbarItem setMaxSize: [segControl frame].size]; - [toolbarItem setView: segControl]; -*/ + if (item == nil) + return [fJobGroups objectAtIndex:index]; -/* - NSButton * button = [[[NSButton alloc] initWithFrame:NSMakeRect(0,0,32,32)] autorelease]; - [button setButtonType:NSSwitchButton]; - [button setTitle:@""]; - [button setState: fShowsJobsAsGroups ? NSOnState : NSOffState]; - [toolbarItem setMinSize: NSMakeSize(20,20)]; - [toolbarItem setMaxSize: NSMakeSize(20,20)]; - [toolbarItem setView: button]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: @selector(jobGroupsChanged:)]; -*/ - } - - return toolbarItem; + // We are only one level deep, so we can't be asked about children + NSAssert (NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items."); + return nil; } -//------------------------------------------------------------------------------------ -// toolbarDefaultItemIdentifiers: -//------------------------------------------------------------------------------------ -- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar +- (BOOL)outlineView:(NSOutlineView *)fOutlineView isItemExpandable:(id)item { - // Required delegate method: Returns the ordered list of items to be shown in the - // toolbar by default. - - return [NSArray arrayWithObjects: - HBStartPauseResumeToolbarIdentifier, - NSToolbarSeparatorItemIdentifier, - HBShowGroupsToolbarIdentifier, - HBShowDetailToolbarIdentifier, - nil]; + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. + return YES; } -//------------------------------------------------------------------------------------ -// toolbarAllowedItemIdentifiers: -//------------------------------------------------------------------------------------ -- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item { - // Required delegate method: Returns the list of all allowed items by identifier. - // By default, the toolbar does not assume any items are allowed, even the - // separator. So, every allowed item must be explicitly listed. - - return [NSArray arrayWithObjects: - HBStartPauseResumeToolbarIdentifier, - HBShowGroupsToolbarIdentifier, - HBShowDetailToolbarIdentifier, - NSToolbarCustomizeToolbarItemIdentifier, - NSToolbarFlexibleSpaceItemIdentifier, - NSToolbarSpaceItemIdentifier, - NSToolbarSeparatorItemIdentifier, - nil]; + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. +return ![(HBQueueOutlineView*)outlineView isDragging]; } -//------------------------------------------------------------------------------------ -// validateToolbarItem: -//------------------------------------------------------------------------------------ -- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem +- (NSInteger)outlineView:(NSOutlineView *)fOutlineView numberOfChildrenOfItem:(id)item { - // Optional method: This message is sent to us since we are the target of some - // toolbar item actions. - - if (!fHandle) return NO; + // Our outline view has no levels, so number of children will be zero for all + // top-level items. + if (item == nil) + return [fJobGroups count]; + else + return 0; +} - BOOL enable = NO; +- (void)outlineViewItemDidCollapse:(NSNotification *)notification +{ + id item = [[notification userInfo] objectForKey:@"NSObject"]; + NSInteger row = [fOutlineView rowForItem:item]; + [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]]; +} - hb_state_t s; - hb_get_state2 (fHandle, &s); +- (void)outlineViewItemDidExpand:(NSNotification *)notification +{ + id item = [[notification userInfo] objectForKey:@"NSObject"]; + NSInteger row = [fOutlineView rowForItem:item]; + [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]]; +} - if ([[toolbarItem itemIdentifier] isEqual: HBStartPauseResumeToolbarIdentifier]) +- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item +{ + if ([outlineView isItemExpanded: item]) { - if (s.state == HB_STATE_PAUSED) + /* Below is the original code to accommodate a live resize, + * however as stated in travistex's comments it's very buggy. + * For now I will leave it here ... commented out and use + * the code below to determine the row height based on each + * encodes optional parameters and how they are displayed. */ + + // Short-circuit here if in a live resize primarily to fix a bug but also to + // increase resposivness during a resize. There's a bug in NSTableView that + // causes row heights to get messed up if you try to change them during a live + // resize. So if in a live resize, simply return the previously calculated + // height. The row heights will get fixed up after the resize because we have + // implemented viewDidEndLiveResize to force all of them to be recalculated. + // if ([outlineView inLiveResize] && [item lastDescriptionHeight] > 0) + // return [item lastDescriptionHeight]; + + // CGFloat width = [[outlineView tableColumnWithIdentifier: @"desc"] width]; + // Column width is NOT what is ultimately used. I can't quite figure out what + // width to use for calculating text metrics. No matter how I tweak this value, + // there are a few conditions in which the drawn text extends below the bounds + // of the row cell. In previous versions, which ran under Tiger, I was + // reducing width by 47 pixles. + // width -= 2; // (?) for intercell spacing + + // CGFloat height = [item heightOfDescriptionForWidth: width]; + // return height; + + /* So, we know several rows of text that are in all queue items for display. + * These are the title line, Preset, Format, Destination, Picture, and Video Lines + */ + CGFloat rowHeightNonTitle = 15.0; + /* Add the title line height, then the non title line height for Preset, Format, Destination + * Picture and Video + */ + CGFloat itemHeightForDisplay = HB_ROW_HEIGHT_TITLE_ONLY + (rowHeightNonTitle * 5); + + /* get our item row number so we an use it to calc how many lines we have to display based + * on MP4 Options, Filter Options, X264 Options, Audio Tracks and Subtitles from our queue array */ + int itemRowNum = [outlineView rowForItem: item]; + NSMutableDictionary *queueItemToCheck = [outlineView itemAtRow: itemRowNum]; + + /* Check to see if we need to allow for mp4 opts */ + BOOL mp4OptsPresent = NO; + if ([[queueItemToCheck objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"]) { - enable = YES; - [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Resume"]; - [toolbarItem setPaletteLabel: @"Resume"]; - [toolbarItem setToolTip: @"Resume Encoding"]; - } + + if( [[queueItemToCheck objectForKey:@"Mp4LargeFile"] intValue] == 1) + { + mp4OptsPresent = YES; + } + if( [[queueItemToCheck objectForKey:@"Mp4HttpOptimize"] intValue] == 1) + { + mp4OptsPresent = YES; + } + if( [[queueItemToCheck objectForKey:@"Mp4iPodCompatible"] intValue] == 1) + { + mp4OptsPresent = YES; + } + } - else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) + if (mp4OptsPresent == YES) { - enable = YES; - [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setPaletteLabel: @"Pause"]; - [toolbarItem setToolTip: @"Pause Encoding"]; + itemHeightForDisplay += rowHeightNonTitle; } - - else if (hb_count(fHandle) > 0) + + /* check to see if we need to allow for the Picture Filters row */ + BOOL pictureFiltersPresent = NO; + if( [[queueItemToCheck objectForKey:@"PictureDetelecine"] intValue] > 0) { - enable = YES; - [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setPaletteLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + pictureFiltersPresent = YES; } - - else + if( [[queueItemToCheck objectForKey:@"PictureDecomb"] intValue] > 0) { - enable = NO; - [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setPaletteLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + pictureFiltersPresent = YES; } - } - - else if ([[toolbarItem itemIdentifier] isEqual: HBShowGroupsToolbarIdentifier]) - { - enable = hb_count(fHandle) > 0; - [toolbarItem setAction: fShowsJobsAsGroups ? @selector(showJobsAsPasses:) : @selector(showJobsAsGroups:)]; - if (fShowsJobsAsGroups) + if( [[queueItemToCheck objectForKey:@"PictureDeinterlace"] intValue] > 0) { - [toolbarItem setImage: [NSImage imageNamed: @"Passes"]]; - [toolbarItem setToolTip: @"Displays individual passes in the queue"]; + pictureFiltersPresent = YES; } - else + if( [[queueItemToCheck objectForKey:@"PictureDenoise"] intValue] > 0) { - [toolbarItem setImage: [NSImage imageNamed: @"PassesPressed"]]; - [toolbarItem setToolTip: @"Displays encodes in the queue"]; + pictureFiltersPresent = YES; } - } - - else if ([[toolbarItem itemIdentifier] isEqual: HBShowDetailToolbarIdentifier]) - { - enable = hb_count(fHandle) > 0; - [toolbarItem setAction: fShowsDetail ? @selector(hideDetail:) : @selector(showDetail:)]; - if (fShowsDetail) + if( [[queueItemToCheck objectForKey:@"PictureDeblock"] intValue] > 0) { - [toolbarItem setImage: [NSImage imageNamed: @"DetailPressed"]]; - [toolbarItem setToolTip: @"Hides detailed information in the queue"]; + pictureFiltersPresent = YES; } - else + if( [[queueItemToCheck objectForKey:@"VideoGrayScale"] intValue] > 0) + { + pictureFiltersPresent = YES; + } + + if (pictureFiltersPresent == YES) + { + itemHeightForDisplay += rowHeightNonTitle; + } + + /* check to see if we need a line to display x264 options */ + if ([[queueItemToCheck objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"]) { - [toolbarItem setImage: [NSImage imageNamed: @"Detail"]]; - [toolbarItem setToolTip: @"Displays detailed information in the queue"]; + itemHeightForDisplay += rowHeightNonTitle; } + + /* check to see how many audio track lines to allow for */ + if ([[queueItemToCheck objectForKey:@"Audio1Track"] intValue] > 0) + { + itemHeightForDisplay += rowHeightNonTitle; + } + if ([[queueItemToCheck objectForKey:@"Audio2Track"] intValue] > 0) + { + itemHeightForDisplay += rowHeightNonTitle; + } + if ([[queueItemToCheck objectForKey:@"Audio3Track"] intValue] > 0) + { + itemHeightForDisplay += rowHeightNonTitle; + } + if ([[queueItemToCheck objectForKey:@"Audio4Track"] intValue] > 0) + { + itemHeightForDisplay += rowHeightNonTitle; + } + + /* add in subtitle lines for each subtitle in the SubtitleList array */ + itemHeightForDisplay += rowHeightNonTitle * [[queueItemToCheck objectForKey:@"SubtitleList"] count]; + + return itemHeightForDisplay; + + } + else + { + return HB_ROW_HEIGHT_TITLE_ONLY; } - - return enable; } -#pragma mark - - -//------------------------------------------------------------------------------------ -// awakeFromNib -//------------------------------------------------------------------------------------ -- (void)awakeFromNib -{ - [self setupToolbar]; - - if (![fQueueWindow setFrameUsingName:@"Queue"]) - [fQueueWindow center]; - [fQueueWindow setFrameAutosaveName: @"Queue"]; - [fQueueWindow setExcludedFromWindowsMenu:YES]; - - // Show/hide UI elements - [self setShowsDetail:fShowsDetail]; - [self setShowsJobsAsGroups:fShowsJobsAsGroups]; - [self showCurrentJobPane:NO]; - -#if HB_QUEUE_DRAGGING - [fTaskView registerForDraggedTypes: [NSArray arrayWithObject:HBQueueDataType] ]; -#endif - -#if HB_OUTLINE_QUEUE - // Don't allow autoresizing of main column, else the "delete" column will get - // pushed out of view. - [fOutlineView setAutoresizesOutlineColumn: NO]; -#endif -} - - -//------------------------------------------------------------------------------------ -// windowWillClose -//------------------------------------------------------------------------------------ -- (void)windowWillClose:(NSNotification *)aNotification +- (CGFloat) heightOfDescriptionForWidth:(CGFloat)width { - [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"]; + // Try to return the cached value if no changes have happened since the last time + //if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription) + // return fLastDescriptionHeight; + + //if (fNeedsDescription) + // [self updateDescription]; + + // Calculate the height + //NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin]; + //fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom + //fLastDescriptionWidth = width; + return HB_ROW_HEIGHT_FULL_DESCRIPTION; + +/* supposedly another way to do this, in case boundingRectWithSize isn't working + NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)]; + [[tmpView textStorage] setAttributedString:aString]; + [tmpView setHorizontallyResizable:NO]; + [tmpView setVerticallyResizable:YES]; +// [[tmpView textContainer] setHeightTracksTextView: YES]; +// [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)]; + [tmpView sizeToFit]; + float height = [tmpView frame].size.height; + [tmpView release]; + return height; +*/ } -#pragma mark - -#pragma mark NSTableView delegate - -//------------------------------------------------------------------------------------ -// NSTableView delegate -//------------------------------------------------------------------------------------ -- (int)numberOfRowsInTableView: (NSTableView *)aTableView +- (CGFloat) lastDescriptionHeight { -#if HB_UNI_QUEUE - int numItems = hb_current_job(fHandle) ? 1 : 0; - if (fShowsJobsAsGroups) - return numItems + hb_group_count(fHandle); - else - return numItems + hb_count(fHandle); -#else - if (fShowsJobsAsGroups) - return hb_group_count(fHandle); - else - return hb_count(fHandle); -#endif + return HB_ROW_HEIGHT_FULL_DESCRIPTION; } -//------------------------------------------------------------------------------------ -// NSTableView delegate -//------------------------------------------------------------------------------------ -- (id)tableView: (NSTableView *)aTableView - objectValueForTableColumn: (NSTableColumn *)aTableColumn - row: (int)rowIndex +- (id)outlineView:(NSOutlineView *)fOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { - if (!fHandle) - return @""; // fatal error! - - hb_job_t * job = nil; - -#if HB_UNI_QUEUE - // Looking for the current job? - int jobIndex = rowIndex; - if (hb_current_job(fHandle)) + // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer + // using the image portion of the cell so we could switch back to a regular NSTextFieldCell. + + if ([[tableColumn identifier] isEqualToString:@"desc"]) { - if (rowIndex == 0) - job = hb_current_job(fHandle); + /* This should have caused the description we wanted to show*/ + //return [item objectForKey:@"SourceName"]; + + /* code to build the description as per old queue */ + //return [self formatEncodeItemDescription:item]; + + /* Below should be put into a separate method but I am way too f'ing lazy right now */ + NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease]; + // Attributes + NSMutableParagraphStyle * ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; + [ps setHeadIndent: 40.0]; + [ps setParagraphSpacing: 1.0]; + [ps setTabStops:[NSArray array]]; // clear all tabs + [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]]; + + + NSDictionary* detailAttr = [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont systemFontOfSize:10.0], NSFontAttributeName, + ps, NSParagraphStyleAttributeName, + nil]; + + NSDictionary* detailBoldAttr = [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName, + ps, NSParagraphStyleAttributeName, + nil]; + + NSDictionary* titleAttr = [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName, + ps, NSParagraphStyleAttributeName, + nil]; + + NSDictionary* shortHeightAttr = [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont systemFontOfSize:2.0], NSFontAttributeName, + nil]; + + /* First line, we should strip the destination path and just show the file name and add the title num and chapters (if any) */ + //finalDescription = [finalDescription stringByAppendingString:[NSString stringWithFormat:@"Source: %@ Output: %@\n", [item objectForKey:@"SourceName"],[item objectForKey:@"DestinationPath"]]]; + NSString * summaryInfo; + + NSString * titleString = [NSString stringWithFormat:@"Title %d", [[item objectForKey:@"TitleNumber"] intValue]]; + + NSString * chapterString = ([[item objectForKey:@"ChapterStart"] intValue] == [[item objectForKey:@"ChapterEnd"] intValue]) ? + [NSString stringWithFormat:@"Chapter %d", [[item objectForKey:@"ChapterStart"] intValue]] : + [NSString stringWithFormat:@"Chapters %d through %d", [[item objectForKey:@"ChapterStart"] intValue], [[item objectForKey:@"ChapterEnd"] intValue]]; + + NSString * passesString; + if ([[item objectForKey:@"VideoTwoPass"] intValue] == 0) + { + passesString = [NSString stringWithFormat:@"1 Video Pass"]; + } + else + { + if ([[item objectForKey:@"VideoTurboTwoPass"] intValue] == 1) + { + passesString = [NSString stringWithFormat:@"2 Video Passes Turbo"]; + } + else + { + passesString = [NSString stringWithFormat:@"2 Video Passes"]; + } + } + + [finalString appendString:[NSString stringWithFormat:@"%@", [item objectForKey:@"SourceName"]] withAttributes:titleAttr]; + + /* lets add the output file name to the title string here */ + NSString * outputFilenameString = [[item objectForKey:@"DestinationPath"] lastPathComponent]; + + summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, chapterString, passesString, outputFilenameString]; + + [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr]; + + // Insert a short-in-height line to put some white space after the title + [finalString appendString:@"\n" withAttributes:shortHeightAttr]; + // End of Title Stuff + + /* Second Line (Preset Name)*/ + [finalString appendString: @"Preset: " withAttributes:detailBoldAttr]; + [finalString appendString:[NSString stringWithFormat:@"%@\n", [item objectForKey:@"PresetName"]] withAttributes:detailAttr]; + + /* Third Line (Format Summary) */ + NSString * audioCodecSummary = @""; + /* Lets also get our audio track detail since we are going through the logic for use later */ + NSString * audioDetail1 = @""; + NSString * audioDetail2 = @""; + NSString * audioDetail3 = @""; + NSString * audioDetail4 = @""; + if ([[item objectForKey:@"Audio1Track"] intValue] > 0) + { + audioCodecSummary = [NSString stringWithFormat:@"%@", [item objectForKey:@"Audio1Encoder"]]; + audioDetail1 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)", + [item objectForKey:@"Audio1TrackDescription"] , + [item objectForKey:@"Audio1Encoder"], + [item objectForKey:@"Audio1Mixdown"] , + [item objectForKey:@"Audio1Samplerate"], + [item objectForKey:@"Audio1Bitrate"]]; + + if ([[item objectForKey:@"Audio1TrackDRCSlider"] floatValue] > 1.00) + { + audioDetail1 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail1,[item objectForKey:@"Audio1TrackDRCSlider"]]; + } + else + { + audioDetail1 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail1]; + } + } + + if ([[item objectForKey:@"Audio2Track"] intValue] > 0) + { + audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio2Encoder"]]; + audioDetail2 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)", + [item objectForKey:@"Audio2TrackDescription"] , + [item objectForKey:@"Audio2Encoder"], + [item objectForKey:@"Audio2Mixdown"] , + [item objectForKey:@"Audio2Samplerate"], + [item objectForKey:@"Audio2Bitrate"]]; + + if ([[item objectForKey:@"Audio2TrackDRCSlider"] floatValue] > 1.00) + { + audioDetail2 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail2,[item objectForKey:@"Audio2TrackDRCSlider"]]; + } + else + { + audioDetail2 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail2]; + } + } + + if ([[item objectForKey:@"Audio3Track"] intValue] > 0) + { + audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio3Encoder"]]; + audioDetail3 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)", + [item objectForKey:@"Audio3TrackDescription"] , + [item objectForKey:@"Audio3Encoder"], + [item objectForKey:@"Audio3Mixdown"] , + [item objectForKey:@"Audio3Samplerate"], + [item objectForKey:@"Audio3Bitrate"]]; + + if ([[item objectForKey:@"Audio3TrackDRCSlider"] floatValue] > 1.00) + { + audioDetail3 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail3,[item objectForKey:@"Audio3TrackDRCSlider"]]; + } + else + { + audioDetail3 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail3]; + } + } + + if ([[item objectForKey:@"Audio4Track"] intValue] > 0) + { + audioCodecSummary = [NSString stringWithFormat:@"%@, %@",audioCodecSummary ,[item objectForKey:@"Audio3Encoder"]]; + audioDetail4 = [NSString stringWithFormat:@"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps)", + [item objectForKey:@"Audio4TrackDescription"] , + [item objectForKey:@"Audio4Encoder"], + [item objectForKey:@"Audio4Mixdown"] , + [item objectForKey:@"Audio4Samplerate"], + [item objectForKey:@"Audio4Bitrate"]]; + + if ([[item objectForKey:@"Audio4TrackDRCSlider"] floatValue] > 1.00) + { + audioDetail4 = [NSString stringWithFormat:@"%@, DRC: %@",audioDetail4,[item objectForKey:@"Audio4TrackDRCSlider"]]; + } + else + { + audioDetail4 = [NSString stringWithFormat:@"%@, DRC: Off",audioDetail4]; + } + } + + NSString * jobFormatInfo; + if ([[item objectForKey:@"ChapterMarkers"] intValue] == 1) + jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio, Chapter Markers\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary]; + else + jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary]; + + + [finalString appendString: @"Format: " withAttributes:detailBoldAttr]; + [finalString appendString: jobFormatInfo withAttributes:detailAttr]; + + /* Optional String for mp4 options */ + if ([[item objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"]) + { + NSString * MP4Opts = @""; + BOOL mp4OptsPresent = NO; + if( [[item objectForKey:@"Mp4LargeFile"] intValue] == 1) + { + mp4OptsPresent = YES; + MP4Opts = [MP4Opts stringByAppendingString:@" - Large file size"]; + } + if( [[item objectForKey:@"Mp4HttpOptimize"] intValue] == 1) + { + mp4OptsPresent = YES; + MP4Opts = [MP4Opts stringByAppendingString:@" - Web optimized"]; + } + + if( [[item objectForKey:@"Mp4iPodCompatible"] intValue] == 1) + { + mp4OptsPresent = YES; + MP4Opts = [MP4Opts stringByAppendingString:@" - iPod 5G support "]; + } + if (mp4OptsPresent == YES) + { + [finalString appendString: @"MP4 Options: " withAttributes:detailBoldAttr]; + [finalString appendString: MP4Opts withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + } + + /* Fourth Line (Destination Path)*/ + [finalString appendString: @"Destination: " withAttributes:detailBoldAttr]; + [finalString appendString: [item objectForKey:@"DestinationPath"] withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + /* Fifth Line Picture Details*/ + NSString * pictureInfo; + pictureInfo = [NSString stringWithFormat:@"%@", [item objectForKey:@"PictureSizingSummary"]]; + if ([[item objectForKey:@"PictureKeepRatio"] intValue] == 1) + { + pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"]; + } + if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1) + { + pictureInfo = [pictureInfo stringByAppendingString:@", Grayscale"]; + } + + [finalString appendString: @"Picture: " withAttributes:detailBoldAttr]; + [finalString appendString: pictureInfo withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + + /* Optional String for Picture Filters */ + + NSString * pictureFilters = @""; + BOOL pictureFiltersPresent = NO; + + if( [[item objectForKey:@"PictureDetelecine"] intValue] == 1) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - Detelecine (Default)"]; + } + else if( [[item objectForKey:@"PictureDetelecine"] intValue] == 2) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[item objectForKey:@"PictureDetelecineCustom"]]]; + } + + if( [[item objectForKey:@"PictureDecombDeinterlace"] intValue] == 1) + { + if ([[item objectForKey:@"PictureDecomb"] intValue] != 0) + { + pictureFiltersPresent = YES; + if( [[item objectForKey:@"PictureDecomb"] intValue] == 1) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb (Default)"]; + } + if( [[item objectForKey:@"PictureDecomb"] intValue] == 2) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[item objectForKey:@"PictureDecombCustom"]]]; + } + } + } + else + { + if ([[item objectForKey:@"PictureDeinterlace"] intValue] != 0) + { + pictureFiltersPresent = YES; + if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 1) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Fast)"]; + } + else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 2) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slow)"]; + } + else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 3) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slower)"]; + } + else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 4) + { + pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[item objectForKey:@"PictureDeinterlaceCustom"]]]; + } + + } + } + if ([[item objectForKey:@"PictureDenoise"] intValue] != 0) + { + pictureFiltersPresent = YES; + if ([[item objectForKey:@"PictureDenoise"] intValue] == 1) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Weak)"]; + } + else if ([[item objectForKey:@"PictureDenoise"] intValue] == 2) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Medium)"]; + } + else if ([[item objectForKey:@"PictureDenoise"] intValue] == 3) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Strong)"]; + } + else if ([[item objectForKey:@"PictureDenoise"] intValue] == 4) + { + pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[item objectForKey:@"PictureDenoiseCustom"]]]; + } + + } + if ([[item objectForKey:@"PictureDeblock"] intValue] != 0) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString: [NSString stringWithFormat:@" - Deblock (pp7) (%d)",[[item objectForKey:@"PictureDeblock"] intValue]]]; + } + + if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - Grayscale"]; + } + + if (pictureFiltersPresent == YES) + { + [finalString appendString: @"Filters: " withAttributes:detailBoldAttr]; + [finalString appendString: pictureFilters withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + + /* Sixth Line Video Details*/ + NSString * videoInfo; + videoInfo = [NSString stringWithFormat:@"Encoder: %@", [item objectForKey:@"VideoEncoder"]]; + + /* for framerate look to see if we are using vfr detelecine */ + if ([[item objectForKey:@"JobIndexVideoFramerate"] intValue] == 0) + { + if ([[item objectForKey:@"PictureDetelecine"] intValue] == 1) + { + /* we are using same as source with vfr detelecine */ + videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (vfr detelecine)", videoInfo]; + } + else + { + /* we are using a variable framerate without dropping frames */ + videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (variable)", videoInfo]; + } + } else - jobIndex = rowIndex - 1; + { + /* we have a specified, constant framerate */ + videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (constant framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]]; + } + + if ([[item objectForKey:@"VideoQualityType"] intValue] == 0)// Target Size MB + { + videoInfo = [NSString stringWithFormat:@"%@ Target Size: %@(MB) (%d(kbps) abr)", videoInfo ,[item objectForKey:@"VideoTargetSize"],[[item objectForKey:@"VideoAvgBitrate"] intValue]]; + } + else if ([[item objectForKey:@"VideoQualityType"] intValue] == 1) // ABR + { + videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo ,[[item objectForKey:@"VideoAvgBitrate"] intValue]]; + } + else // CRF + { + videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,[[item objectForKey:@"VideoQualitySlider"] floatValue]]; + } + + [finalString appendString: @"Video: " withAttributes:detailBoldAttr]; + [finalString appendString: videoInfo withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + + if ([[item objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"]) + { + [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttr]; + [finalString appendString: [item objectForKey:@"x264Option"] withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + + + + /* Seventh Line Audio Details*/ + if ([audioDetail1 length] != 0) + { + [finalString appendString: @"Audio Track 1: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail1 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + + if ([audioDetail2 length] != 0) + { + [finalString appendString: @"Audio Track 2: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail2 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + + if ([audioDetail3 length] != 0) + { + [finalString appendString: @"Audio Track 3: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail3 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + + if ([audioDetail4 length] != 0) + { + [finalString appendString: @"Audio Track 4: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail4 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + /* Eighth Line Subtitle Details */ + + int i = 0; + NSEnumerator *enumerator = [[item objectForKey:@"SubtitleList"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups, + * we want to ignore it for display as well as encoding. + */ + if ([[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue] > 0) + { + /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/ + [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr]; + [finalString appendString: [tempObject objectForKey:@"subtitleSourceTrackName"] withAttributes:detailAttr]; + if ([[tempObject objectForKey:@"subtitleTrackForced"] intValue] == 1) + { + [finalString appendString: @" - Forced Only" withAttributes:detailAttr]; + } + if ([[tempObject objectForKey:@"subtitleTrackBurned"] intValue] == 1) + { + [finalString appendString: @" - Burned In" withAttributes:detailAttr]; + } + if ([[tempObject objectForKey:@"subtitleTrackDefault"] intValue] == 1) + { + [finalString appendString: @" - Default" withAttributes:detailAttr]; + } + [finalString appendString:@"\n" withAttributes:detailAttr]; + } + i++; + } + + return finalString; } - - if (!job) + else if ([[tableColumn identifier] isEqualToString:@"icon"]) { - if (fShowsJobsAsGroups) - job = hb_group(fHandle, jobIndex); + if ([[item objectForKey:@"Status"] intValue] == 0) + { + return [NSImage imageNamed:@"EncodeComplete"]; + } + else if ([[item objectForKey:@"Status"] intValue] == 1) + { + return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; + } + else if ([[item objectForKey:@"Status"] intValue] == 3) + { + return [NSImage imageNamed:@"EncodeCanceled"]; + } else - job = hb_job(fHandle, jobIndex); + { + return [NSImage imageNamed:@"JobSmall"]; + } + } -#else - if (fShowsJobsAsGroups) - job = hb_group(fHandle, rowIndex); else - job = hb_job(fHandle, rowIndex); -#endif - - if (!job) - return @""; // fatal error! - - if ([[aTableColumn identifier] isEqualToString:@"desc"]) { - BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView); - return [self attributedDescriptionForJob:job withTitle:YES withDetail:fShowsDetail withHighlighting:highlighted]; - } - - else if ([[aTableColumn identifier] isEqualToString:@"delete"]) return @""; - - else if ([[aTableColumn identifier] isEqualToString:@"icon"]) - return fShowsJobsAsGroups ? [NSImage imageNamed:@"JobSmall"] : [self smallImageForPass: job->pass]; - - return @""; + } } -//------------------------------------------------------------------------------------ -// NSTableView delegate -//------------------------------------------------------------------------------------ -- (void)tableView: (NSTableView *)aTableView - willDisplayCell: (id)aCell - forTableColumn: (NSTableColumn *)aTableColumn - row: (int)rowIndex +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { - if ([[aTableColumn identifier] isEqualToString:@"delete"]) + if ([[tableColumn identifier] isEqualToString:@"desc"]) + { + + + // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer + // using the image portion of the cell so we could switch back to a regular NSTextFieldCell. + + // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part + [cell setImage:nil]; + } + else if ([[tableColumn identifier] isEqualToString:@"action"]) { - BOOL highlighted = [aTableView isRowSelected:rowIndex] && [[aTableView window] isKeyWindow] && ([[aTableView window] firstResponder] == aTableView); - if (highlighted) + [cell setEnabled: YES]; + BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); + if ([[item objectForKey:@"Status"] intValue] == 0) { - [aCell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; - [aCell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + [cell setAction: @selector(revealSelectedQueueItem:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"RevealHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Reveal"]]; } else { - [aCell setImage:[NSImage imageNamed:@"Delete"]]; + [cell setAction: @selector(removeSelectedQueueItem:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Delete"]]; } } } -//------------------------------------------------------------------------------------ -// NSTableView delegate -//------------------------------------------------------------------------------------ -#if HB_UNI_QUEUE -- (float)tableView:(NSTableView *)tableView heightOfRow:(int)row +- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { - if ((row == 0) && hb_current_job(fHandle)) - return HB_ROW_HEIGHT_ACTIVE_JOB; - else - return fShowsDetail ? HB_ROW_HEIGHT_DETAIL : HB_ROW_HEIGHT_NO_DETAIL; + // By default, the discolsure image gets centered vertically in the cell. We want + // always at the top. + if ([outlineView isItemExpanded: item]) + [cell setImagePosition: NSImageAbove]; + else + [cell setImagePosition: NSImageOnly]; } -#endif -#if HB_QUEUE_DRAGGING -- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard -{ - // Copy the row numbers to the pasteboard. - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; - [pboard declareTypes:[NSArray arrayWithObject:HBQueueDataType] owner:self]; - [pboard setData:data forType:HBQueueDataType]; - return YES; -} -#endif +#pragma mark - +#pragma mark NSOutlineView delegate (dragging related) + +//------------------------------------------------------------------------------------ +// NSTableView delegate +//------------------------------------------------------------------------------------ -#if HB_QUEUE_DRAGGING -- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op -{ - // Add code here to validate the drop - NSLog(@"validate Drop"); - return NSDragOperationEvery; -} -#endif -#if HB_QUEUE_DRAGGING -- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id )info - row:(int)row dropOperation:(NSTableViewDropOperation)operation +- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard { - NSPasteboard* pboard = [info draggingPasteboard]; - NSData* rowData = [pboard dataForType:HBQueueDataType]; - NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData]; - int dragRow = [rowIndexes firstIndex]; - - // Move the specified row to its new location... + // Dragging is only allowed of the pending items. + if ([[[items objectAtIndex:0] objectForKey:@"Status"] integerValue] != 2) // 2 is pending + { + return NO; + } + + // Don't retain since this is just holding temporaral drag information, and it is + //only used during a drag! We could put this in the pboard actually. + fDraggedNodes = items; + + // Provide data for our custom type, and simple NSStrings. + [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self]; + + // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!. + [pboard setData:[NSData data] forType:DragDropSimplePboardType]; return YES; } -#endif - - -#if HB_OUTLINE_QUEUE - -- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item -{ - if (item == nil) - return [fEncodes objectAtIndex:index]; - else - return [item objectAtIndex:index]; -} - -- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item -{ - return ! [item isKindOfClass:[HBJob class]]; -} -- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item -{ - if (item == nil) - return [fEncodes count]; - else - return [item count]; -} -- (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item +/* This method is used to validate the drops. */ +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(NSInteger)index { - if (fShowsDetail && [item isKindOfClass:[HBJob class]]) - return HB_ROW_HEIGHT_DETAIL; - else - return HB_ROW_HEIGHT_NO_DETAIL; -} - -- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item -{ - BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); - if ([item isKindOfClass:[HBJob class]]) + // Don't allow dropping ONTO an item since they can't really contain any children. + BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; + if (isOnDropTypeProposal) { - if ([[tableColumn identifier] isEqualToString:@"desc"]) - { - hb_job_t * job = [item job]; -// return [self attributedDescriptionForJob:job withTitle:NO withDetail:fShowsDetail withHighlighting:highlighted]; - if (job->pass == -1) - return @"Subtitle Scan"; - else - { - int passNum = MAX( 1, job->pass ); - if (passNum == 1) - return @"1st Pass"; - if (passNum == 2) - return @"2nd Pass"; - else - return [NSString stringWithFormat: @"Pass %d", passNum]; - } - } + return NSDragOperationNone; } - else + // Don't allow dropping INTO an item since they can't really contain any children. + if (item != nil) { - hb_job_t * job = [[item objectAtIndex:0] job]; - if ([[tableColumn identifier] isEqualToString:@"desc"]) - return [self attributedDescriptionForJob:job withTitle:YES withDetail:NO withHighlighting:highlighted]; + index = [fOutlineView rowForItem: item] + 1; + item = nil; } - - return @""; + + // NOTE: Should we allow dropping a pending job *above* the + // finished or already encoded jobs ? + // We do not let the user drop a pending job before or *above* + // already finished or currently encoding jobs. + if (index <= fEncodingQueueItem) + { + return NSDragOperationNone; + index = MAX (index, fEncodingQueueItem); + } + + [outlineView setDropItem:item dropChildIndex:index]; + return NSDragOperationGeneric; } -- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id )info item:(id)item childIndex:(NSInteger)index { - if ([[tableColumn identifier] isEqualToString:@"desc"]) - { - if ([item isKindOfClass:[HBJob class]]) - [cell setImage:[self smallImageForPass: [item job]->pass]]; - else - [cell setImage:[NSImage imageNamed:@"JobSmall"]]; - } - - else if ([[tableColumn identifier] isEqualToString:@"delete"]) - { - // The Delete action can only be applied for group items, not indivdual jobs. - if ([item isKindOfClass:[HBJob class]]) - { - [cell setEnabled: NO]; - [cell setImage: nil]; - } - else - { - [cell setEnabled: YES]; - BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); - if (highlighted) - { - [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; - [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; - } - else - [cell setImage:[NSImage imageNamed:@"Delete"]]; - } - } + NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet]; + + for( id obj in fDraggedNodes ) + [moveItems addIndex:[fJobGroups indexOfObject:obj]]; + + // Successful drop, we use moveObjectsInQueueArray:... in fHBController + // to properly rearrange the queue array, save it to plist and then send it back here. + // since Controller.mm is handling all queue array manipulation. + // We *could do this here, but I think we are better served keeping that code together. + [fHBController moveObjectsInQueueArray:fJobGroups fromIndexes:moveItems toIndex: index]; + return YES; } -#endif @end