diff options
author | dynaflash <[email protected]> | 2007-10-09 15:15:16 +0000 |
---|---|---|
committer | dynaflash <[email protected]> | 2007-10-09 15:15:16 +0000 |
commit | 7045d521d48d670473d5858c43e4fb54dee14b1c (patch) | |
tree | ce9f16661b37f34ae593dd03f923b4edb9fe689b /macosx/HBQueueController.mm | |
parent | 744d3be393c2dd5752a0ab1b5dd28ce00739333a (diff) |
MacGui: Queue Enhancements courtesy of travistex
- Encodes now stick around in the queue's displayed after they have been completed. They are marked with a check mark icon.
- The "active" encode is shown with a chasing arrows icon.
- Completed encodes have a spotlight icon by them to allow you to show in finder
- Support for reordering encodes in the queue via drag and drop. Currently #define'd out because there's currently no easy way to reorder hblib's job list. But some day maybe...
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1017 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'macosx/HBQueueController.mm')
-rw-r--r-- | macosx/HBQueueController.mm | 898 |
1 files changed, 657 insertions, 241 deletions
diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index dab66d362..fa307e837 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -8,11 +8,6 @@ #include "Controller.h" #import "HBImageAndTextCell.h" -// 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 // <--- NOT COMPLETELY FUNCTIONAL YET - #define HB_ROW_HEIGHT_TITLE_ONLY 17.0 // Pasteboard type for or drag operations @@ -52,6 +47,38 @@ [super viewDidEndLiveResize]; } +#if HB_QUEUE_DRAGGING +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset +{ + // Set the fIsDragging flag so that other's know that a drag operation is being + // performed. + fIsDragging = YES; + + // By default, NSTableView only drags an image of the first column. Change this to + // drag an image of the queue's icon and desc columns. + NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"icon"], [self tableColumnWithIdentifier:@"desc"], nil]; + return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset]; +} +#endif + +#if HB_QUEUE_DRAGGING +- (void) mouseDown:(NSEvent *)theEvent +{ + // After a drag operation, reset fIsDragging back to NO. This is really the only way + // for us to detect when a drag has finished. You can't do it in acceptDrop because + // that won't be called if the dragged item is released outside the view. + [super mouseDown:theEvent]; + fIsDragging = NO; +} +#endif + +#if HB_QUEUE_DRAGGING +- (BOOL) isDragging; +{ + return fIsDragging; +} +#endif + @end //------------------------------------------------------------------------------------ @@ -343,14 +370,14 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withFormatInfo || withVideoInfo) { // 2097152 - /* Video Codec settings (Encoder in the gui) */ + // Video Codec settings (Encoder in the gui) if (hbJob->vcodec == HB_VCODEC_FFMPEG) jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG else if (hbJob->vcodec == HB_VCODEC_XVID) jobVideoCodec = @"XviD"; // HB_VCODEC_XVID else if (hbJob->vcodec == HB_VCODEC_X264) { - /* Deterimine for sure how we are now setting iPod uuid atom */ + // Deterimine for sure how we are now setting iPod uuid atom if (hbJob->h264_level) // We are encoding for iPod jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 else @@ -411,8 +438,8 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withPictureInfo) { NSString * jobPictureInfo; - /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, - maxWidth, maxHeight */ + // integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, + // maxWidth, maxHeight if (hbJob->pixel_ratio == 1) { int titlewidth = title->width - hbJob->crop[2] - hbJob->crop[3]; @@ -456,17 +483,17 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (hbJob->vrate_base == 1126125) { - /* NTSC FILM 23.976 */ + // NTSC FILM 23.976 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality]; } else if (hbJob->vrate_base == 900900) { - /* NTSC 29.97 */ + // NTSC 29.97 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality]; } else { - /* Everything else */ + // Everything else jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base]; } if (withIcon) // implies indent the info @@ -494,9 +521,9 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) else jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->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] */ + // 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++ ) { @@ -571,6 +598,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) fJobs = [[NSMutableArray arrayWithCapacity:0] retain]; fDescription = [[NSMutableAttributedString alloc] initWithString: @""]; [self setNeedsDescription: NO]; + fStatus = HBStatusNone; } return self; } @@ -578,6 +606,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) dealloc { [fJobs release]; + [fPath release]; [super dealloc]; } @@ -594,6 +623,11 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) fLastDescriptionWidth = 0; } +- (void) removeAllJobs +{ + [fJobs removeAllObjects]; +} + - (HBJob *) jobAtIndex: (unsigned)index { return [fJobs objectAtIndex: index]; @@ -616,6 +650,8 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle { + fNeedsDescription = NO; + [fDescription deleteCharactersInRange: NSMakeRange(0, [fDescription length])]; if ([self count] == 0) @@ -659,7 +695,6 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) withSubtitleInfo: YES]]; } - fNeedsDescription = NO; } - (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle @@ -703,16 +738,43 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) return fLastDescriptionHeight; } +- (void) setStatus: (HBQueueJobGroupStatus)status +{ + self->fStatus = status; +} + +- (HBQueueJobGroupStatus) status +{ + return self->fStatus; +} + +- (void) setPath: (NSString *)path +{ + [path retain]; + [fPath release]; + fPath = path; +} + +- (NSString *) path +{ + return fPath; +} + @end #pragma mark - +@interface HBQueueController (Private) +- (void)updateQueueUI; +@end + // Toolbar identifiers static NSString* HBQueueToolbar = @"HBQueueToolbar1"; static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier"; static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier"; +#pragma mark - @implementation HBQueueController @@ -731,6 +793,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe nil]]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; + fCompleted = [[NSMutableArray arrayWithCapacity:0] retain]; BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow; NSAssert(loadSucceeded, @"Could not open Queue nib"); @@ -744,14 +807,15 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)dealloc { - [fAnimation release]; - // clear the delegate so that windowWillClose is not attempted if ([fQueueWindow delegate] == self) [fQueueWindow setDelegate:nil]; [fJobGroups release]; + [fCompleted release]; + [fCurrentJobGroup release]; [fSavedExpandedItems release]; + [fSavedSelectedItems release]; [super dealloc]; } @@ -778,10 +842,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe - (IBAction) showQueueWindow: (id)sender { [self updateQueueUI]; - [self updateCurrentJobUI]; - [fQueueWindow makeKeyAndOrderFront: self]; - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; } //------------------------------------------------------------------------------------ @@ -822,13 +883,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey, nil]; - if (!fAnimation) - fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil]; - - [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]; + NSViewAnimation * anAnimation = [[[NSViewAnimation alloc] initWithViewAnimations:nil] autorelease]; + [anAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]]; + [anAnimation setDuration:0.25]; + [anAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation + [anAnimation startAnimation]; + fCurrentJobPaneShown = showPane; } @@ -837,11 +897,27 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)rebuildJobGroups { + // Currently, job groups are rdered like this: + // Completed job groups + // Current job group + // Pending job groups + [fJobGroups autorelease]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; + // Add all the completed job groups + [fJobGroups addObjectsFromArray: fCompleted]; + + // Add all the completed job groups + if (fCurrentJobGroup) + [fJobGroups addObject: fCurrentJobGroup]; + + // Add all the pending job groups. These come from hblib HBJobGroup * aJobGroup = [HBJobGroup jobGroup]; + // If hblib is currently processing something, hb_group will skip over that group. + // And that's exactly what we want -- fJobGroups contains only pending job groups. + hb_job_t * nextJob = hb_group( fHandle, 0 ); while( nextJob ) { @@ -850,20 +926,83 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // Encountered a new group. Add the current one to fJobGroups and then start a new one. if ([aJobGroup count] > 0) { + [aJobGroup setStatus: HBStatusPending]; [fJobGroups addObject: aJobGroup]; aJobGroup = [HBJobGroup jobGroup]; } } [aJobGroup addJob: [HBJob jobWithJob:nextJob]]; + [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]]; nextJob = hb_next_job (fHandle, nextJob); } if ([aJobGroup count] > 0) { + [aJobGroup setStatus: HBStatusPending]; [fJobGroups addObject:aJobGroup]; } } //------------------------------------------------------------------------------------ +// Adds aJobGroup to the list of completed job groups, marking its status as +// HBStatusComplete. +//------------------------------------------------------------------------------------ +- (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup +{ + // Once hblib has completed its work, the hb_job_t objects will be freed, so we + // can't keep a reference to them. + [aJobGroup removeAllJobs]; + + [aJobGroup setStatus: HBStatusComplete]; + + // Put the group in the completed list for permanent storage, and also rebuild + // the master job group list which contains completed and pending groups. + [fCompleted addObject: aJobGroup]; +} + +- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob +{ + HBJobGroup * aJobGroup = nil; + + // Try to find this new group in our existing job groups. + if (aJob) + { + BOOL found = NO; + NSEnumerator * groupEnum = [fJobGroups objectEnumerator]; + while ( !found && (aJobGroup = [groupEnum nextObject]) ) + { + HBJob * j; + NSEnumerator * jobEnum = [aJobGroup jobEnumerator]; + while ( !found && (j = [jobEnum nextObject]) ) + { + if ([j job] == aJob) + found = YES; + } + } + + // Or create the job group. + if (!aJobGroup) + { + aJobGroup = [HBJobGroup jobGroup]; + [aJobGroup addJob: [HBJob jobWithJob: aJob]]; + [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]]; + while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) ) + [aJobGroup addJob: [HBJob jobWithJob: aJob]]; + + [aJobGroup updateDescriptionWithHBHandle: fHandle]; + } + + [aJobGroup setStatus: HBStatusWorking]; + } + + [aJobGroup retain]; + [fCurrentJobGroup release]; + fCurrentJobGroup = aJobGroup; +} + +#pragma mark - +#pragma mark UI Updating + +//------------------------------------------------------------------------------------ // Saves the state of the items that are currently expanded. Calling restoreOutlineViewState // will restore the state of all items to match what was saved by saveOutlineViewState. //------------------------------------------------------------------------------------ @@ -884,7 +1023,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe while ( (aJobGroup = [e nextObject]) ) { if ([fOutlineView isItemExpanded: aJobGroup]) - [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + { + if ([aJobGroup status] == HBStatusComplete) + [fSavedExpandedItems addIndex: (unsigned int)aJobGroup]; + else + [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + } } // Save the selection also. This is really UGLY code. Since I have to rebuild the @@ -893,15 +1037,23 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // hb_job_t item in each selected group. This is done by saving the object's // address. This could go away if I'd save a unique id in each job object. - int selection = [fOutlineView selectedRow]; - if (selection == -1) - fSavedSelectedItem = 0; + if (!fSavedSelectedItems) + fSavedSelectedItems = [[NSMutableIndexSet alloc] init]; else + [fSavedSelectedItems removeAllIndexes]; + + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + while (row != NSNotFound) { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: selection]; - fSavedSelectedItem = (unsigned int)[[jobGroup jobAtIndex:0] job]; + aJobGroup = [fOutlineView itemAtRow: row]; + if ([aJobGroup status] == HBStatusComplete) + [fSavedSelectedItems addIndex: (unsigned int)aJobGroup]; + else + [fSavedSelectedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + row = [selectedRows indexGreaterThanIndex: row]; } - + } //------------------------------------------------------------------------------------ @@ -916,35 +1068,90 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe NSEnumerator * e = [fJobGroups objectEnumerator]; while ( (aJobGroup = [e nextObject]) ) { - hb_job_t * j = [[aJobGroup jobAtIndex:0] job]; - if ([fSavedExpandedItems containsIndex: (unsigned int)j]) - [fOutlineView expandItem: aJobGroup]; + if ([aJobGroup status] == HBStatusComplete) + { + if ([fSavedExpandedItems containsIndex: (unsigned int)aJobGroup]) + [fOutlineView expandItem: aJobGroup]; + } + else + { + hb_job_t * j = [[aJobGroup jobAtIndex:0] job]; + if ([fSavedExpandedItems containsIndex: (unsigned int)j]) + [fOutlineView expandItem: aJobGroup]; + } } } - if (fSavedSelectedItem) + if (fSavedSelectedItems) { // Ugh. Have to cycle through each row looking for the previously selected job. - // See the explanation in saveExpandedItems about the logic here. - - // Find out what hb_job_t was selected - hb_job_t * j = (hb_job_t *)fSavedSelectedItem; - - int rowToSelect = -1; + // See the explanation in saveOutlineViewState about the logic here. + + NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease]; for (int i = 0; i < [fOutlineView numberOfRows]; i++) { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: i]; - // Test to see if the group's first job is a match - if ([[jobGroup jobAtIndex:0] job] == j) + HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i]; + // Test to see if the group or the group's first job is a match + if ([aJobGroup status] == HBStatusComplete) { - rowToSelect = i; - break; + if ([fSavedSelectedItems containsIndex: (unsigned int)aJobGroup]) + [rowsToSelect addIndex: i]; + } + else + { + if ([fSavedSelectedItems containsIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]) + [rowsToSelect addIndex: i]; } } - if (rowToSelect == -1) + if ([rowsToSelect count] == 0) [fOutlineView deselectAll: nil]; else - [fOutlineView selectRow:rowToSelect byExtendingSelection:NO]; + [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO]; + } +} + +//------------------------------------------------------------------------------------ +// If a job is currently processing, its job icon in the queue outline view is +// animated to its next state. +//------------------------------------------------------------------------------------ +- (void) animateCurrentJobGroupInQueue:(NSTimer*)theTimer +{ + int row = [fOutlineView rowForItem: fCurrentJobGroup]; + int col = [fOutlineView columnWithIdentifier: @"icon"]; + if (row != -1 && col != -1) + { + fAnimationIndex++; + fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below. + NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row]; + [fOutlineView setNeedsDisplayInRect: frame]; + } +} + +//------------------------------------------------------------------------------------ +// Starts animating the job icon of the currently processing job in the queue outline +// view. +//------------------------------------------------------------------------------------ +- (void) startAnimatingCurrentJobGroupInQueue +{ + if (!fAnimationTimer) + fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle + target:self + selector:@selector(animateCurrentJobGroupInQueue:) + userInfo:nil + repeats:YES] retain]; +} + +//------------------------------------------------------------------------------------ +// Stops animating the job icon of the currently processing job in the queue outline +// view. +//------------------------------------------------------------------------------------ +- (void) stopAnimatingCurrentJobGroupInQueue +{ + if (fAnimationTimer && [fAnimationTimer isValid]) + { + [fAnimationTimer invalidate]; + [fAnimationTimer release]; + fAnimationTimer = nil; } } @@ -1061,6 +1268,18 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe } //------------------------------------------------------------------------------------ +// Refresh progress bar (fProgressTextField) from current state. +//------------------------------------------------------------------------------------ +- (void) updateProgressTextForJob: (hb_job_t *)job state: (hb_state_t *)s +{ + 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]; +} + +//------------------------------------------------------------------------------------ // Refresh progress bar (fProgressBar) from current state. //------------------------------------------------------------------------------------ - (void) updateProgressBarWithState: (hb_state_t *)s @@ -1085,8 +1304,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe else if (s->state == HB_STATE_WORKDONE) { [fProgressBar setIndeterminate:NO]; + [fProgressBar stopAnimation:nil]; [fProgressBar setDoubleValue:0.0]; } + + else + [fProgressBar stopAnimation:nil]; // just in case in was animating } //------------------------------------------------------------------------------------ @@ -1108,108 +1331,94 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Refresh the UI in the current job pane. Should be called whenever the current job -// being processed has changed or when progress has changed. +// being processed has changed. //------------------------------------------------------------------------------------ -- (void)updateCurrentJobUI +- (void)updateCurrentJobDescription { - hb_state_t s; - hb_job_t * job = nil; - - if (fHandle) - { - hb_get_state2( fHandle, &s ); - job = hb_current_job(fHandle); - } + hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; if (job) { - if (fLastKnownCurrentJob != job) + HBJob * currentJob = [HBJob jobWithJob: job]; + switch (job->pass) { - HBJob * currentJob = [HBJob jobWithJob: job]; - - switch (job->pass) - { - case -1: // Subtitle scan - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: NO - withVideoInfo: NO - withx264Info: NO - withAudioInfo: NO - withSubtitleInfo: YES]]; - break; - - case 1: // video 1st pass - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: NO - withSubtitleInfo: NO]]; - break; - - case 0: // single pass - case 2: // video 2nd pass + audio - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - break; + case -1: // Subtitle scan + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: NO + withVideoInfo: NO + withx264Info: NO + withAudioInfo: NO + withSubtitleInfo: YES]]; + break; - default: // unknown - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - } - - [self showCurrentJobPane:YES]; - [fJobIconView setImage: [NSImage imageNamed:@"JobLarge"]]; + case 1: // video 1st pass + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: NO + withSubtitleInfo: NO]]; + break; + + case 0: // single pass + case 2: // video 2nd pass + audio + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: YES + withSubtitleInfo: YES]]; + break; + + default: // unknown + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: YES + withSubtitleInfo: YES]]; } - - 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)]; + [fJobDescTextField setStringValue: @"No encodes pending"]; +} - [self showCurrentJobPane:NO]; - [fProgressBar stopAnimation:nil]; // just in case in was animating - } - - fLastKnownCurrentJob = job; +//------------------------------------------------------------------------------------ +// 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)updateCurrentJobProgress +{ + hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; + hb_state_t s; + hb_get_state2( fHandle, &s ); + [self updateProgressTextForJob: job state: &s]; + [self updateProgressBarWithState:&s]; } //------------------------------------------------------------------------------------ @@ -1226,38 +1435,74 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [self updateQueueCountField]; } +#pragma mark - +#pragma mark Actions + //------------------------------------------------------------------------------------ -// Deletes the selected job from HB and the queue UI +// Deletes the selected jobs from HB and the queue UI //------------------------------------------------------------------------------------ -- (IBAction)removeSelectedJob: (id)sender +- (IBAction)removeSelectedJobGroups: (id)sender { if (!fHandle) return; - int row = [sender selectedRow]; - if (row != -1) + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + if (row != NSNotFound) { -#if HB_UNI_QUEUE - if (row == 0) + while (row != NSNotFound) { - [self cancelCurrentJob:sender]; + HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; + switch ([jobGroup status]) + { + case HBStatusComplete: + case HBStatusCanceled: + [fCompleted removeObject: jobGroup]; + break; + case HBStatusWorking: + [self cancelCurrentJob: sender]; + break; + case HBStatusPending: + hb_job_t * job = [[jobGroup jobAtIndex: 0] job]; + hb_rem_group( fHandle, job ); + break; + case HBStatusNone: + break; + } + + row = [selectedRows indexGreaterThanIndex: row]; } - else + + [self hblibJobListChanged]; + } +} + +//------------------------------------------------------------------------------------ +// Reveals the file icons in the Finder of the selected job groups. +//------------------------------------------------------------------------------------ +- (IBAction)revealSelectedJobGroups: (id)sender +{ + if (!fHandle) return; + + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + if (row != NSNotFound) + { + while (row != NSNotFound) { - row--; - hb_rem_group( fHandle, hb_group( fHandle, row ) ); + HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; + if ([[jobGroup path] length]) + [[NSWorkspace sharedWorkspace] selectFile:[jobGroup path] inFileViewerRootedAtPath:nil]; + + row = [selectedRows indexGreaterThanIndex: row]; } -#else - HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; - hb_job_t * job = [[jobGroup jobAtIndex: 0] job]; - hb_rem_group( fHandle, job ); -#endif - [self updateQueueUI]; - } + } } //------------------------------------------------------------------------------------ -// Prompts user if the want to cancel encoding of current job. If so, doCancelCurrentJob -// gets called. +// Calls HBController Cancel: which displays an alert asking user if they want to +// cancel encoding of current job. cancelCurrentJob: returns immediately after posting +// the alert. Later, when the user acknowledges the alert, HBController will call +// hblib to cancel the job. //------------------------------------------------------------------------------------ - (IBAction)cancelCurrentJob: (id)sender { @@ -1297,6 +1542,107 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe hb_pause (fHandle); } +#pragma mark - +#pragma mark Synchronizing with hblib + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's current job has changed +//------------------------------------------------------------------------------------ +- (void)currentJobGroupChanged: (hb_job_t *) currentJob +{ + if (fCurrentJobGroup && [fCurrentJobGroup status] != HBStatusCanceled) + [self addCompletedJobGroup: fCurrentJobGroup]; + [self setCurrentJobGroupFromJob: currentJob]; + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + [self showCurrentJobPane: fCurrentJobGroup != nil]; + if (fCurrentJobGroup) + [self startAnimatingCurrentJobGroupInQueue]; + else + [self stopAnimatingCurrentJobGroupInQueue]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hb_stop is about to be called. This signals us that +// the current job is going to be canceled and deleted. This is somewhat of a hack to +// let HBQueueController know when a job group has been cancelled. Otherwise, we'd +// have no way of knowing if a job was canceled or completed sucessfully. +//------------------------------------------------------------------------------------ +- (void)hblibWillStop +{ + if (fCurrentJobGroup) + [fCurrentJobGroup setStatus: HBStatusCanceled]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's job list has been modified +//------------------------------------------------------------------------------------ +- (void)hblibJobListChanged +{ + // This message is received from HBController after it has added a job group to + // hblib's job list. It is also received from self when a job group is deleted by + // the user. + [self updateQueueUI]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's state has changed +//------------------------------------------------------------------------------------ +- (void)hblibStateChanged: (hb_state_t &)state +{ + // First check to see if hblib has moved on to another job. We get no direct + // message when this happens, so we have to detect it ourself. The new job could + // be either just the next job in the current group, or the start of a new group. + if (fLastKnownCurrentJob != hb_current_job(fHandle)) + { + hb_job_t * currentJob = hb_current_job(fHandle); + if (!currentJob || currentJob->sequence_id == 0) // start of a new group + { + [self currentJobGroupChanged: currentJob]; + [self hblibJobListChanged]; + } + else + { + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + } + + fLastKnownCurrentJob = currentJob; + } + + switch( state.state ) + { + case HB_STATE_WORKING: + { + [self updateCurrentJobProgress]; + [self startAnimatingCurrentJobGroupInQueue]; + break; + } + + case HB_STATE_MUXING: + { + [self updateCurrentJobProgress]; + break; + } + + case HB_STATE_PAUSED: + { + [self updateCurrentJobProgress]; + [self stopAnimatingCurrentJobGroupInQueue]; + break; + } + + case HB_STATE_WORKDONE: + { + // HB_STATE_WORKDONE means that hblib has finished processing all the jobs + // in *its* queue. This message is NOT sent as each individual job is + // completed. + } + + } + +} + #if HB_OUTLINE_METRIC_CONTROLS static float spacingWidth = 3.0; - (IBAction)imageSpacingChanged: (id)sender; @@ -1351,36 +1697,36 @@ static float spacingWidth = 3.0; 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:)]; - } + [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]) { 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:)]; - } + [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; } @@ -1411,10 +1757,10 @@ static float spacingWidth = 3.0; return [NSArray arrayWithObjects: HBQueueStartCancelToolbarIdentifier, HBQueuePauseResumeToolbarIdentifier, - NSToolbarCustomizeToolbarItemIdentifier, - NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarCustomizeToolbarItemIdentifier, + NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, - NSToolbarSeparatorItemIdentifier, + NSToolbarSeparatorItemIdentifier, nil]; } @@ -1439,26 +1785,26 @@ static float spacingWidth = 3.0; { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Stop"]]; - [toolbarItem setLabel: @"Stop"]; - [toolbarItem setToolTip: @"Stop Encoding"]; + [toolbarItem setLabel: @"Stop"]; + [toolbarItem setToolTip: @"Stop Encoding"]; } else if (hb_count(fHandle) > 0) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } else { enable = NO; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } - } + } if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier]) { @@ -1466,27 +1812,27 @@ static float spacingWidth = 3.0; { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Resume"]; - [toolbarItem setToolTip: @"Resume Encoding"]; + [toolbarItem setLabel: @"Resume"]; + [toolbarItem setToolTip: @"Resume Encoding"]; } else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } else { enable = NO; [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } - } + } - return enable; + return enable; } #pragma mark - @@ -1512,7 +1858,6 @@ static float spacingWidth = 3.0; // Don't allow autoresizing of main column, else the "delete" column will get // pushed out of view. [fOutlineView setAutoresizesOutlineColumn: NO]; - [fOutlineView setIndentationPerLevel:21]; #if HB_OUTLINE_METRIC_CONTROLS [fIndentation setHidden: NO]; @@ -1539,31 +1884,31 @@ static float spacingWidth = 3.0; - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex { - unsigned index = [indexSet lastIndex]; - unsigned aboveInsertIndexCount = 0; - - while (index != NSNotFound) - { - unsigned removeIndex; - - if (index >= insertIndex) - { - removeIndex = index + aboveInsertIndexCount; - aboveInsertIndexCount++; - } - else - { - removeIndex = index; - insertIndex--; - } - - id object = [[array objectAtIndex:removeIndex] retain]; - [array removeObjectAtIndex:removeIndex]; - [array insertObject:object atIndex:insertIndex]; - [object release]; - - index = [indexSet indexLessThanIndex:index]; - } + unsigned index = [indexSet lastIndex]; + unsigned aboveInsertIndexCount = 0; + + while (index != NSNotFound) + { + unsigned removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[array objectAtIndex:removeIndex] retain]; + [array removeObjectAtIndex:removeIndex]; + [array insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + } } #pragma mark - @@ -1586,6 +1931,18 @@ static float spacingWidth = 3.0; return YES; } +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item +{ + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. +#if HB_QUEUE_DRAGGING + // Don't autoexpand while dragging, since we can't drop into the items + return ![(HBQueueOutlineView*)outlineView isDragging]; +#else + return YES; +#endif +} + - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { // Our outline view has no levels, so number of children will be zero for all @@ -1636,8 +1993,26 @@ static float spacingWidth = 3.0; - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { + // 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"]) return [item attributedDescriptionWithHBHandle: fHandle]; + else if ([[tableColumn identifier] isEqualToString:@"icon"]) + { + switch ([(HBJobGroup*)item status]) + { + case HBStatusComplete: + return [NSImage imageNamed:@"EncodeComplete"]; + break; + case HBStatusWorking: + return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; + break; + default: + return [NSImage imageNamed:@"JobSmall"]; + break; + } + } else return @""; } @@ -1652,22 +2027,39 @@ static float spacingWidth = 3.0; [cell setImageSpacing: theSize]; #endif + // 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:[NSImage imageNamed:@"JobSmall"]]; + [cell setImage:nil]; } - else if ([[tableColumn identifier] isEqualToString:@"delete"]) + else if ([[tableColumn identifier] isEqualToString:@"action"]) { - // The Delete action can only be applied for group items, not indivdual jobs. [cell setEnabled: YES]; BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); - if (highlighted) + if ([(HBJobGroup*)item status] == HBStatusComplete) { - [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; - [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + [cell setAction: @selector(revealSelectedJobGroups:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"RevealHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Reveal"]]; } else - [cell setImage:[NSImage imageNamed:@"Delete"]]; + { + [cell setAction: @selector(removeSelectedJobGroups:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Delete"]]; + } } } @@ -1691,10 +2083,19 @@ static float spacingWidth = 3.0; #if HB_QUEUE_DRAGGING - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard { + // Dragging is only allowed of the pending items. + NSEnumerator * e = [items objectEnumerator]; + HBJobGroup * group; + while ( (group = [e nextObject]) ) + { + if ([group status] != HBStatusPending) + 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: HBQueuePboardType, nil] owner:self]; @@ -1708,11 +2109,25 @@ static float spacingWidth = 3.0; #if HB_QUEUE_DRAGGING - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index { - // Add code here to validate the drop - BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; + // Don't allow dropping ONTO an item since they can't really contain any children. + BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; if (isOnDropTypeProposal) return NSDragOperationNone; - + + // Don't allow dropping INTO an item since they can't really contain any children. + if (item != nil) + { + index = [fOutlineView rowForItem: item] + 1; + item = nil; + } + + // Prevent dragging into the completed or current job. + int firstPendingIndex = [fCompleted count]; + if (fCurrentJobGroup) + firstPendingIndex++; + index = MAX (index, firstPendingIndex); + + [outlineView setDropItem:item dropChildIndex:index]; return NSDragOperationGeneric; } #endif @@ -1739,4 +2154,5 @@ static float spacingWidth = 3.0; } #endif + @end |