diff options
author | dynaflash <[email protected]> | 2008-09-16 20:57:13 +0000 |
---|---|---|
committer | dynaflash <[email protected]> | 2008-09-16 20:57:13 +0000 |
commit | ab154ca5555a0b04a5ee4f00bf65b27a57f64471 (patch) | |
tree | 45d7fe81a0382edc928161028fe469142ef47474 /macosx/HBQueueController.mm | |
parent | f1809fae2b817c697ae663167f0ab31c2e0aa676 (diff) |
MacGui: Resilient Queue: Initial Implementation
- Completely overhauls how encodes are setup by the MacGui
- All encodes are now stored in an NSMutableArray (QueueFileArray) and saved in a plist (~/Library/Application Support/HandBrake/Queue.plist)
- A separate instance of libhb (fQueueEncodeLibhb) is used for queue processing (includes single encodes)
- fHandle still takes care of all user intiated scans and encode parameters
- libhb now only stores multiple passes for a single encode instead of storing the entire queue
- If HB crashes, or if you stop encoding while there are still pending encodes in your queue, upon next launch you will be prompted to reload the previous queue from the Queue.plist
- Removed the current encoding information at the top of the queue window to make room for a larger list as I thought it to be redundant
- The queue list is now re-arrangeable via drag-n-drop (pending encodes only)
- Known issues and bugs:
-- If you delete a previously cancelled encode then try to restart with a new encode, nothing happens. Throw off the sync of the currently encoding job.
-- Probably alot more since its a total overhaul of how the MacGui has handled encoding since titer originally wrote it :)
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1703 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'macosx/HBQueueController.mm')
-rw-r--r-- | macosx/HBQueueController.mm | 2503 |
1 files changed, 676 insertions, 1827 deletions
diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index f410e4edf..42674b550 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -9,24 +9,14 @@ #import "HBImageAndTextCell.h" #define HB_ROW_HEIGHT_TITLE_ONLY 17.0 - +#define HB_ROW_HEIGHT_FULL_DESCRIPTION 200.0 // Pasteboard type for or drag operations -#define HBQueuePboardType @"HBQueuePboardType" +#define DragDropSimplePboardType @"MyCustomOutlineViewPboardType" //------------------------------------------------------------------------------------ -// Job ID Utilities +#pragma mark - //------------------------------------------------------------------------------------ -int MakeJobID(int jobGroupID, int sequenceNum) -{ - return jobGroupID<<16 | sequenceNum; -} - -bool IsFirstPass(int jobID) -{ - return LoWord(jobID) == 0; -} - //------------------------------------------------------------------------------------ // NSMutableAttributedString (HBAdditions) //------------------------------------------------------------------------------------ @@ -45,9 +35,6 @@ bool IsFirstPass(int jobID) } @end -//------------------------------------------------------------------------------------ -#pragma mark - -//------------------------------------------------------------------------------------ @implementation HBQueueOutlineView @@ -60,796 +47,39 @@ bool IsFirstPass(int jobID) [super viewDidEndLiveResize]; } -#if HB_QUEUE_DRAGGING + + +/* 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 { - // 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]; + // 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]; } -#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 - -#pragma mark - - -//------------------------------------------------------------------------------------ -// HBJob -//------------------------------------------------------------------------------------ - -static NSMutableParagraphStyle * _descriptionParagraphStyle = nil; -static NSDictionary* _detailAttribute = nil; -static NSDictionary* _detailBoldAttribute = nil; -static NSDictionary* _titleAttribute = nil; -static NSDictionary* _shortHeightAttribute = nil; - -@implementation HBJob - -+ (HBJob*) jobWithLibhbJob: (hb_job_t *) job -{ - return [[[HBJob alloc] initWithLibhbJob:job] autorelease]; -} - -- (id) initWithLibhbJob: (hb_job_t *) job -{ - if (self = [super init]) - { - sequence_id = job->sequence_id; - - chapter_start = job->chapter_start; - chapter_end = job->chapter_end; - chapter_markers = job->chapter_markers; - memcpy(crop, job->crop, sizeof(crop)); - deinterlace = job->deinterlace; - width = job->width; - height = job->height; - keep_ratio = job->keep_ratio; - grayscale = job->grayscale; - pixel_ratio = job->pixel_ratio; - pixel_aspect_width = job->pixel_aspect_width; - pixel_aspect_height = job->pixel_aspect_height; - vcodec = job->vcodec; - vquality = job->vquality; - vbitrate = job->vbitrate; - vrate = job->vrate; - vrate_base = job->vrate_base; - pass = job->pass; - h264_level = job->h264_level; - crf = job->crf; - if (job->x264opts) - x264opts = [[NSString stringWithUTF8String:job->x264opts] retain]; - /* So, with the advent of job->list_audio's I decided why not just use an NSString and concatanate - all of the info we need for all of the audio values to display right into an NSString here ? So I - did. I have no idea why we are reading libhb stuff just to display it in the queue gui. So here we - are with a huge string. But its easy to change and saves alot of messing about. Maybe we move a bunch - of other display stuff into strings for display for each job. It's not like they have to actually do - anything.*/ - hb_audio_config_t * audio; - NSString * thisJobAudioCodecs = [NSString stringWithFormat:@""]; - NSString * thisJobAudioInfo = [NSString stringWithFormat:@""]; - int i; - for( i = 0; i < hb_list_count(job->list_audio); i++ ) - { - audio = (hb_audio_config_t *) hb_list_audio_config_item( job->list_audio, i ); - /* Output Codec */ - NSString *outputCodec; - if (audio->out.codec == HB_ACODEC_AC3) - outputCodec = @"AC3"; - else if (audio->out.codec == HB_ACODEC_FAAC) - outputCodec = @"AAC"; - else if (audio->out.codec == HB_ACODEC_LAME) - outputCodec = @"MP3"; - else if (audio->out.codec == HB_ACODEC_VORBIS) - outputCodec = @"Vorbis"; - else - outputCodec = @"Unknown Codec"; - /* Add the codec to the audio codecs list ( We should check against dupes)*/ - thisJobAudioCodecs = [thisJobAudioCodecs stringByAppendingString:[NSString stringWithFormat:@" %@,",outputCodec]]; - if (i > 0) - { - /* Insert a line break so that we get each track on a separate line */ - /* Wicked HACK alert!!, use 18 whitespaces to align offset in display for list > 2 to offset "Audio" in the queue display - Please Fix Me because this is embarrassing (but it works) */ - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:@"\n "]; - } - /* Detailed Job audio track info*/ - /* Track Number and Mixdown Info */ - if (audio->out.mixdown == HB_ACODEC_AC3)// Remember for ac3 passthru the mixdown uses the source codec - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, Pass-Thru", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else if (audio->out.mixdown == HB_AMIXDOWN_MONO) - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, Mono", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else if (audio->out.mixdown == HB_AMIXDOWN_STEREO) - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, Stereo", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else if (audio->out.mixdown == HB_AMIXDOWN_DOLBY) - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, Dolby Surround", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else if (audio->out.mixdown == HB_AMIXDOWN_DOLBYPLII) - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, Dolby Pro Logic II", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else if (audio->out.mixdown == HB_AMIXDOWN_6CH) - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: %@, 6 Channel Discreet", i + 1, [NSString stringWithUTF8String:audio->lang.description], outputCodec]]; - else - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@"Track %d: Source: %@ Output: Unknown Codec Info", i + 1, [NSString stringWithUTF8String:audio->lang.description]]]; - - thisJobAudioInfo = [thisJobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", %d kbps, %d Hz", audio->out.bitrate, audio->out.samplerate]]; - - } - audioinfo_summary = [[NSString stringWithFormat:@"%@",thisJobAudioInfo]retain]; - audioinfo_codecs = [[NSString stringWithFormat:@"%@",thisJobAudioCodecs]retain]; - - subtitle = job->subtitle; - mux = job->mux; - if (job->file) - file = [[NSString stringWithUTF8String:job->file] retain]; - if (job->title->name) - titleName = [[NSString stringWithUTF8String:job->title->name] retain]; - titleIndex = job->title->index; - titleWidth = job->title->width; - titleHeight = job->title->height; - if (job->subtitle >= 0) - { - hb_subtitle_t * aSubtitle = (hb_subtitle_t *) hb_list_item(job->title->list_subtitle, job->subtitle); - if (aSubtitle) - subtitleLang = [[NSString stringWithUTF8String:aSubtitle->lang] retain]; - } - - // Calculate and store output dimensions and anamorphic dimensions - if (pixel_ratio == 1) // Original PAR Implementation, now called Strict Anamorphic - { - output_width = titleWidth - crop[2] - crop[3]; - output_height = titleHeight - crop[0] - crop[1]; - anamorphic_width = output_width * pixel_aspect_width / pixel_aspect_height; - anamorphic_height = output_height; - } - else if (pixel_ratio == 2) // Loose Anamorphic - { - // call hb_set_anamorphic_size to do a "dry run" to get the values to be - // used by libhb for loose anamorphic. - int par_width, par_height; - hb_set_anamorphic_size(job, &output_width, &output_height, &par_width, &par_height); - anamorphic_width = output_width * par_width / par_height; - anamorphic_height = output_height; - } - else // No Anamorphic - { - output_width = width; - output_height = height; - anamorphic_width = 0; // not needed for this case - anamorphic_height = 0; // not needed for this case - } - - } - return self; -} - -- (void) dealloc -{ - // jobGroup is a weak reference and does not need to be deleted - [x264opts release]; - [file release]; - [titleName release]; - [subtitleLang release]; - [audioinfo_summary release]; - [audioinfo_codecs release]; - [super dealloc]; -} - -- (HBJobGroup *) jobGroup -{ - return jobGroup; -} - -- (void) setJobGroup: (HBJobGroup *)aJobGroup -{ - // This is a weak reference. We don't retain or release it. - jobGroup = aJobGroup; -} - -//------------------------------------------------------------------------------------ -// Generate string to display in UI. -//------------------------------------------------------------------------------------ - -- (NSMutableAttributedString *) attributedDescriptionWithIcon: (BOOL)withIcon - withTitle: (BOOL)withTitle - withPassName: (BOOL)withPassName - withFormatInfo: (BOOL)withFormatInfo - withDestination: (BOOL)withDestination - withPictureInfo: (BOOL)withPictureInfo - withVideoInfo: (BOOL)withVideoInfo - withx264Info: (BOOL)withx264Info - withAudioInfo: (BOOL)withAudioInfo - withSubtitleInfo: (BOOL)withSubtitleInfo - -{ - NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease]; - - // Attributes - NSMutableParagraphStyle * ps = [HBJob descriptionParagraphStyle]; - NSDictionary* detailAttr = [HBJob descriptionDetailAttribute]; - NSDictionary* detailBoldAttr = [HBJob descriptionDetailBoldAttribute]; - NSDictionary* titleAttr = [HBJob descriptionTitleAttribute]; - NSDictionary* shortHeightAttr = [HBJob descriptionShortHeightAttribute]; - - // Title with summary - if (withTitle) - { - if (withIcon) - { - NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: @"JobSmall"]] autorelease]; - NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease]; - - NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName, - imageAttachment, NSAttachmentAttributeName, - ps, NSParagraphStyleAttributeName, - nil]; - - NSAttributedString * imageAsString = [[[NSAttributedString alloc] - initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter] - attributes: imageAttributes] autorelease]; - - [finalString appendAttributedString:imageAsString]; - } - - // Note: use title->name instead of title->dvd since name is just the chosen - // folder, instead of dvd which is the full path - [finalString appendString:titleName withAttributes:titleAttr]; - - NSString * summaryInfo; - - NSString * chapterString = (chapter_start == chapter_end) ? - [NSString stringWithFormat:@"Chapter %d", chapter_start] : - [NSString stringWithFormat:@"Chapters %d through %d", chapter_start, chapter_end]; - - BOOL hasIndepthScan = (pass == -1); - int numVideoPasses = 0; - - // To determine number of video passes, we need to skip past the subtitle scan. - if (hasIndepthScan) - { - // When job is the one currently being processed, then the next in its group - // is the the first job in the queue. - HBJob * nextjob = nil; - NSUInteger index = [jobGroup indexOfJob:self]; - if (index != NSNotFound) - nextjob = [jobGroup jobAtIndex:index+1]; - if (nextjob) // Overly cautious in case there is no next job! - numVideoPasses = MIN( 2, nextjob->pass + 1 ); - } - else - numVideoPasses = MIN( 2, pass + 1 ); - - if (hasIndepthScan && numVideoPasses == 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, Single Video Pass)", titleIndex, chapterString]; - else if (hasIndepthScan && numVideoPasses > 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, %d Video Passes)", titleIndex, chapterString, numVideoPasses]; - else if (numVideoPasses == 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Single Video Pass)", titleIndex, chapterString]; - else - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, %d Video Passes)", titleIndex, chapterString, numVideoPasses]; - - [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 - - - // Pass Name - if (withPassName) - { - if (withIcon) - { - NSString * imageName; - switch (pass) - { - case -1: imageName = @"JobPassSubtitleSmall"; break; - case 0: imageName = @"JobPassFirstSmall"; break; - case 1: imageName = @"JobPassFirstSmall"; break; - case 2: imageName = @"JobPassSecondSmall"; break; - default: imageName = @"JobPassUnknownSmall"; break; - } - - NSFileWrapper * wrapper = [[[NSFileWrapper alloc] initWithPath:[[NSBundle mainBundle] pathForImageResource: imageName]] autorelease]; - NSTextAttachment * imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:wrapper] autorelease]; - - NSDictionary* imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithFloat: -2.0], NSBaselineOffsetAttributeName, - imageAttachment, NSAttachmentAttributeName, - ps, NSParagraphStyleAttributeName, - nil]; - - NSAttributedString * imageAsString = [[[NSAttributedString alloc] - initWithString: [NSString stringWithFormat:@"%C%C", NSAttachmentCharacter, NSTabCharacter] - attributes: imageAttributes] autorelease]; - - [finalString appendAttributedString:imageAsString]; - } - - NSString * jobPassName; - if (pass == -1) - jobPassName = NSLocalizedString (@"Deep Scan", nil); - else - { - int passNum = MAX( 1, pass ); - if (passNum == 0) - jobPassName = NSLocalizedString (@"1st Pass", nil); - else if (passNum == 1) - jobPassName = NSLocalizedString (@"1st Pass", nil); - else if (passNum == 2) - jobPassName = NSLocalizedString (@"2nd Pass", nil); - else - jobPassName = [NSString stringWithFormat: NSLocalizedString(@"Pass %d", nil), passNum]; - } - [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPassName] withAttributes:detailBoldAttr]; - } - - // Video Codec needed by FormatInfo and withVideoInfo - NSString * jobVideoCodec = nil; - if (withFormatInfo || withVideoInfo) - { - // 2097152 - // Video Codec settings (Encoder in the gui) - if (vcodec == HB_VCODEC_FFMPEG) - jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG - else if (vcodec == HB_VCODEC_XVID) - jobVideoCodec = @"XviD"; // HB_VCODEC_XVID - else if (vcodec == HB_VCODEC_X264) - { - // Deterimine for sure how we are now setting iPod uuid atom - if (h264_level) // We are encoding for iPod - jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 - else - jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264 - } - } - if (jobVideoCodec == nil) - jobVideoCodec = @"unknown"; - - if (withFormatInfo) - { - NSString * jobFormatInfo; - // Muxer settings (File Format in the gui) - if (mux == 65536 || mux == 131072 || mux == 1048576) - jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD - else if (mux == 262144) - jobFormatInfo = @"AVI"; // HB_MUX_AVI - else if (mux == 524288) - jobFormatInfo = @"OGM"; // HB_MUX_OGM - else if (mux == 2097152) - jobFormatInfo = @"MKV"; // HB_MUX_MKV - else - jobFormatInfo = @"unknown"; - - if (chapter_markers == 1) - jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio, Chapter Markers\n", jobFormatInfo, jobVideoCodec, audioinfo_codecs]; - else - jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio\n", jobFormatInfo, jobVideoCodec, audioinfo_codecs]; - - [finalString appendString: @"Format: " withAttributes:detailBoldAttr]; - [finalString appendString: jobFormatInfo withAttributes:detailAttr]; - } - - if (withDestination) - { - [finalString appendString: @"Destination: " withAttributes:detailBoldAttr]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", file] withAttributes:detailAttr]; - } - - - if (withPictureInfo) - { - NSString * jobPictureInfo; - if (pixel_ratio == 1) // Original PAR Implementation, now called Strict Anamorphic - jobPictureInfo = [NSString stringWithFormat:@"%d x %d (%d x %d Strict Anamorphic)", output_width, output_height, anamorphic_width, anamorphic_height]; - else if (pixel_ratio == 2) // Loose Anamorphic - jobPictureInfo = [NSString stringWithFormat:@"%d x %d (%d x %d Loose Anamorphic)", output_width, output_height, anamorphic_width, anamorphic_height]; - else - jobPictureInfo = [NSString stringWithFormat:@"%d x %d", output_width, output_height]; - if (keep_ratio == 1) - jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"]; - - if (grayscale == 1) - jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"]; - - if (deinterlace == 1) - jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"]; - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"Picture: " withAttributes:detailBoldAttr]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", jobPictureInfo] withAttributes:detailAttr]; - } - - if (withVideoInfo) - { - NSString * jobVideoQuality; - NSString * jobVideoDetail; - - if (vquality <= 0 || vquality >= 1) - jobVideoQuality = [NSString stringWithFormat:@"%d kbps", vbitrate]; - else - { - NSNumber * vidQuality; - vidQuality = [NSNumber numberWithInt:vquality * 100]; - // this is screwed up kind of. Needs to be formatted properly. - if (crf == 1) - jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality]; - else - jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality]; - } - - if (vrate_base == 1126125) - { - // NTSC FILM 23.976 - jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality]; - } - else if (vrate_base == 900900) - { - // NTSC 29.97 - jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality]; - } - else - { - // Everything else - jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, vrate / vrate_base]; - } - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"Video: " withAttributes:detailBoldAttr]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", jobVideoDetail] withAttributes:detailAttr]; - } - - if (withx264Info) - { - if (vcodec == HB_VCODEC_X264 && x264opts) - { - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttr]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", x264opts] withAttributes:detailAttr]; - } - } - - if (withAudioInfo) - { - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"Audio: " withAttributes:detailBoldAttr]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", audioinfo_summary] withAttributes:detailAttr]; - } - - if (withSubtitleInfo) - { - // subtitle scan == -1 in two cases: - // autoselect: when pass == -1 - // none: when pass != -1 - if ((subtitle == -1) && (pass == -1)) - { - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttr]; - [finalString appendString: @"Autoselect " withAttributes:detailAttr]; - } - else if (subtitle >= 0) - { - if (subtitleLang) - { - if (withIcon) // implies indent the info - [finalString appendString: @"\t" withAttributes:detailBoldAttr]; - [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttr]; - [finalString appendString: subtitleLang withAttributes:detailAttr]; - } - } - } - - - if ([[finalString string] hasSuffix: @"\n"]) - [finalString deleteCharactersInRange: NSMakeRange([[finalString string] length]-1, 1)]; - - return finalString; -} - -+ (NSMutableParagraphStyle *) descriptionParagraphStyle -{ - if (!_descriptionParagraphStyle) - { - _descriptionParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; - [_descriptionParagraphStyle setHeadIndent: 40.0]; - [_descriptionParagraphStyle setParagraphSpacing: 1.0]; - [_descriptionParagraphStyle setTabStops:[NSArray array]]; // clear all tabs - [_descriptionParagraphStyle addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]]; - } - return _descriptionParagraphStyle; -} - -+ (NSDictionary *) descriptionDetailAttribute -{ - if (!_detailAttribute) - _detailAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:10.0], NSFontAttributeName, - _descriptionParagraphStyle, NSParagraphStyleAttributeName, - nil] retain]; - return _detailAttribute; -} - -+ (NSDictionary *) descriptionDetailBoldAttribute -{ - if (!_detailBoldAttribute) - _detailBoldAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName, - _descriptionParagraphStyle, NSParagraphStyleAttributeName, - nil] retain]; - return _detailBoldAttribute; -} - -+ (NSDictionary *) descriptionTitleAttribute -{ - if (!_titleAttribute) - _titleAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName, - _descriptionParagraphStyle, NSParagraphStyleAttributeName, - nil] retain]; - return _titleAttribute; -} - -+ (NSDictionary *) descriptionShortHeightAttribute -{ - if (!_shortHeightAttribute) - _shortHeightAttribute = [[NSDictionary dictionaryWithObjectsAndKeys: - [NSFont systemFontOfSize:2.0], NSFontAttributeName, - nil] retain]; - return _shortHeightAttribute; -} - - -@end - -#pragma mark - - -//------------------------------------------------------------------------------------ -// HBJobGroup -//------------------------------------------------------------------------------------ - -// Notification sent from HBJobGroup setStatus whenever the status changes. -NSString *HBJobGroupStatusNotification = @"HBJobGroupStatusNotification"; - -@implementation HBJobGroup - -+ (HBJobGroup *) jobGroup; -{ - return [[[HBJobGroup alloc] init] autorelease]; -} - -- (id) init -{ - if (self = [super init]) - { - fJobs = [[NSMutableArray arrayWithCapacity:0] retain]; - fDescription = [[NSMutableAttributedString alloc] initWithString: @""]; - [self setNeedsDescription: NO]; - fStatus = HBStatusNone; - } - return self; -} - -- (void) dealloc -{ - [fPresetName release]; - [fJobs release]; - [super dealloc]; -} - -- (unsigned int) count -{ - return [fJobs count]; -} - -- (void) addJob: (HBJob *)aJob -{ - [aJob setJobGroup:self]; - [fJobs addObject: aJob]; - [self setNeedsDescription: YES]; - fLastDescriptionHeight = 0; - fLastDescriptionWidth = 0; -} - -- (HBJob *) jobAtIndex: (unsigned)index -{ - return [fJobs objectAtIndex: index]; -} - -- (unsigned) indexOfJob: (HBJob *)aJob; -{ - return [fJobs indexOfObject: aJob]; -} - -- (NSMutableArray *) fJobs -{ - return fJobs; -} - -- (void) setNeedsDescription: (BOOL)flag -{ - fNeedsDescription = flag; -} - -- (void) updateDescription -{ - fNeedsDescription = NO; - - [fDescription deleteCharactersInRange: NSMakeRange(0, [fDescription length])]; - - if ([self count] == 0) - { - NSAssert(NO, @" jobgroup with no jobs"); - return; - } - - HBJob * job = [self jobAtIndex:0]; - - // append the title - [fDescription appendAttributedString: [job attributedDescriptionWithIcon: NO - withTitle: YES - withPassName: NO - withFormatInfo: NO - withDestination: NO - withPictureInfo: NO - withVideoInfo: NO - withx264Info: NO - withAudioInfo: NO - withSubtitleInfo: NO]]; - - // append the preset name - if ([fPresetName length]) - { - [fDescription appendString:@"Preset: " withAttributes:[HBJob descriptionDetailBoldAttribute]]; - [fDescription appendString:fPresetName withAttributes:[HBJob descriptionDetailAttribute]]; - [fDescription appendString:@"\n" withAttributes:[HBJob descriptionDetailAttribute]]; - } - - // append the format and destinaton - [fDescription appendAttributedString: [job attributedDescriptionWithIcon: NO - withTitle: NO - withPassName: NO - withFormatInfo: YES - withDestination: YES - withPictureInfo: NO - withVideoInfo: NO - withx264Info: NO - withAudioInfo: NO - withSubtitleInfo: NO]]; - - - NSAttributedString * carriageReturn = [[NSAttributedString alloc] initWithString:@"\n"]; - - for( job in fJobs ) - { - int pass = job->pass; - [fDescription appendAttributedString:carriageReturn]; - [fDescription appendAttributedString: - [job attributedDescriptionWithIcon: YES - withTitle: NO - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: pass != -1 - withVideoInfo: pass != -1 - withx264Info: pass != -1 - withAudioInfo: pass == 0 || pass == 2 - withSubtitleInfo: YES]]; - } - -} -- (NSMutableAttributedString *) attributedDescription -{ - if (fNeedsDescription) - [self updateDescription]; - return fDescription; -} -- (CGFloat) heightOfDescriptionForWidth:(CGFloat)width -{ - // 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 fLastDescriptionHeight; -/* 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; -*/ -} - -- (CGFloat) lastDescriptionHeight +- (void) mouseDown:(NSEvent *)theEvent { - return fLastDescriptionHeight; + [super mouseDown:theEvent]; + fIsDragging = NO; } -- (void) setStatus: (HBQueueJobGroupStatus)status -{ - // Create a dictionary with the old status - NSDictionary * userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:self->fStatus] forKey:@"HBOldJobGroupStatus"]; - - self->fStatus = status; - - // Send notification with old status - [[NSNotificationCenter defaultCenter] postNotificationName:HBJobGroupStatusNotification object:self userInfo:userInfo]; -} -- (HBQueueJobGroupStatus) status -{ - return self->fStatus; -} -- (void) setPresetName: (NSString *)name -{ - [name retain]; - [fPresetName release]; - fPresetName = name; -} - -- (NSString *) presetName +- (BOOL) isDragging; { - return fPresetName; + return fIsDragging; } -- (NSString *) name -{ - HBJob * firstJob = [self jobAtIndex:0]; - return firstJob ? firstJob->titleName : nil; -} -- (NSString *) destinationPath -{ - HBJob * firstJob = [self jobAtIndex:0]; - return firstJob ? firstJob->file : nil; -} @end - -#pragma mark - - +#pragma mark Toolbar Identifiers // Toolbar identifiers static NSString* HBQueueToolbar = @"HBQueueToolbar1"; static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier"; @@ -881,1018 +111,177 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe nil]]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; - - // Register for HBJobGroup status changes - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobGroupStatusNotification:) name:HBJobGroupStatusNotification object:nil]; - } - return self; -} - -//------------------------------------------------------------------------------------ -// dealloc -//------------------------------------------------------------------------------------ -- (void)dealloc -{ - // clear the delegate so that windowWillClose is not attempted - if( [[self window] delegate] == self ) - [[self window] setDelegate:nil]; - - [fJobGroups release]; - [fCurrentJobGroup release]; - [fSavedExpandedItems release]; - [fSavedSelectedItems release]; - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super dealloc]; -} - -//------------------------------------------------------------------------------------ -// Receive HB handle -//------------------------------------------------------------------------------------ -- (void)setHandle: (hb_handle_t *)handle -{ - fHandle = handle; -} - -//------------------------------------------------------------------------------------ -// Receive HBController -//------------------------------------------------------------------------------------ -- (void)setHBController: (HBController *)controller -{ - fHBController = controller; -} - -#pragma mark - -#pragma mark - Getting the currently processing job group - -//------------------------------------------------------------------------------------ -// Returns the HBJobGroup that is currently being encoded; nil if no encoding is -// occurring. -//------------------------------------------------------------------------------------ -- (HBJobGroup *) currentJobGroup; -{ - return fCurrentJobGroup; -} - -//------------------------------------------------------------------------------------ -// Returns the HBJob (pass) that is currently being encoded; nil if no encoding is -// occurring. -//------------------------------------------------------------------------------------ -- (HBJob *) currentJob -{ - return fCurrentJob; -} - -#pragma mark - - -//------------------------------------------------------------------------------------ -// Displays and brings the queue window to the front -//------------------------------------------------------------------------------------ -- (IBAction) showQueueWindow: (id)sender -{ - [self showWindow:sender]; - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; -} - -//------------------------------------------------------------------------------------ -// Show or hide the current job pane (fCurrentJobPane). -//------------------------------------------------------------------------------------ -- (void) showCurrentJobPane: (BOOL)showPane -{ - if (showPane == fCurrentJobPaneShown) - 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]; - - // Resize fQueuePane - NSDictionary * dict2 = [NSDictionary dictionaryWithObjectsAndKeys: - fQueuePane, NSViewAnimationTargetKey, - [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey, - nil]; - - 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; -} - -//------------------------------------------------------------------------------------ -// Sets fCurrentJobGroup to a new job group. -//------------------------------------------------------------------------------------ -- (void) setCurrentJobGroup: (HBJobGroup *)aJobGroup -{ - if (aJobGroup) - [aJobGroup setStatus: HBStatusWorking]; - - [aJobGroup retain]; - [fCurrentJobGroup release]; - fCurrentJobGroup = aJobGroup; -} - -#pragma mark - Finding job groups - -//------------------------------------------------------------------------------------ -// Returns the first pending job with a specified destination path or nil if no such -// job exists. -//------------------------------------------------------------------------------------ -- (HBJobGroup *) pendingJobGroupWithDestinationPath: (NSString *)path -{ - for( HBJobGroup * aJobGroup in fJobGroups ) - { - if ([[aJobGroup destinationPath] isEqualToString: path]) - return aJobGroup; - } - return nil; + } + return self; } -//------------------------------------------------------------------------------------ -// Locates and returns a HBJob whose sequence_id matches a specified value. -//------------------------------------------------------------------------------------ -- (HBJob *) findJobWithID: (int)aJobID +- (void)setQueueArray: (NSMutableArray *)QueueFileArray { - for( HBJobGroup * aJobGroup in fJobGroups ) - { - for( HBJob * job in [aJobGroup fJobs] ) - { - if (job->sequence_id == aJobID) - return job; - } - } - return nil; -} + [fJobGroups setArray:QueueFileArray]; + + /* First stop any timer working now */ +[self stopAnimatingCurrentJobGroupInQueue]; + [fOutlineView reloadData]; -//------------------------------------------------------------------------------------ -// Locates and returns a libhb job whose sequence_id matches a specified value. -//------------------------------------------------------------------------------------ -- (hb_job_t *) findLibhbJobWithID: (int)aJobID -{ - hb_job_t * job; - int index = 0; - while( ( job = hb_job( fHandle, index++ ) ) ) - { - if (job->sequence_id == aJobID) - return job; - } - return nil; -} -#pragma mark - -#pragma mark Queue Counts - -//------------------------------------------------------------------------------------ -// Sets a flag indicating that the values for fPendingCount, fCompletedCount, -// fCanceledCount, and fWorkingCount need to be recalculated. -//------------------------------------------------------------------------------------ -- (void) setJobGroupCountsNeedUpdating: (BOOL)flag -{ - fJobGroupCountsNeedUpdating = flag; -} - -//------------------------------------------------------------------------------------ -// Recalculates and stores new values in fPendingCount, fCompletedCount, -// fCanceledCount, and fWorkingCount. -//------------------------------------------------------------------------------------ -- (void) recalculateJobGroupCounts -{ - fPendingCount = 0; - fCompletedCount = 0; - fCanceledCount = 0; - fWorkingCount = 0; - for( HBJobGroup * aJobGroup in fJobGroups ) - { - switch ([aJobGroup status]) - { - case HBStatusNone: - // We don't track these. - break; - case HBStatusPending: - fPendingCount++; - break; - case HBStatusCompleted: - fCompletedCount++; - break; - case HBStatusCanceled: - fCanceledCount++; - break; - case HBStatusWorking: - fWorkingCount++; - break; - } - } - fJobGroupCountsNeedUpdating = NO; -} +/* lets get the stats on the status of the queue array */ -//------------------------------------------------------------------------------------ -// Returns the number of job groups whose status is HBStatusPending. -//------------------------------------------------------------------------------------ -- (unsigned int) pendingCount -{ - if (fJobGroupCountsNeedUpdating) - [self recalculateJobGroupCounts]; - return fPendingCount; -} - -//------------------------------------------------------------------------------------ -// Returns the number of job groups whose status is HBStatusCompleted. -//------------------------------------------------------------------------------------ -- (unsigned int) completedCount -{ - if (fJobGroupCountsNeedUpdating) - [self recalculateJobGroupCounts]; - return fCompletedCount; -} - -//------------------------------------------------------------------------------------ -// Returns the number of job groups whose status is HBStatusCanceled. -//------------------------------------------------------------------------------------ -- (unsigned int) canceledCount -{ - if (fJobGroupCountsNeedUpdating) - [self recalculateJobGroupCounts]; - return fCanceledCount; -} - -//------------------------------------------------------------------------------------ -// Returns the number of job groups whose status is HBStatusWorking. -//------------------------------------------------------------------------------------ -- (unsigned int) workingCount -{ - if (fJobGroupCountsNeedUpdating) - [self recalculateJobGroupCounts]; - return fWorkingCount; -} - -#pragma mark - -#pragma mark UI Updating - -//------------------------------------------------------------------------------------ -// Saves the state of the items that are currently expanded and selected. Calling -// restoreOutlineViewState will restore the state of all items to match what was saved -// by saveOutlineViewState. Nested calls to saveOutlineViewState are not supported. -//------------------------------------------------------------------------------------ -- (void) saveOutlineViewState -{ - if (!fSavedExpandedItems) - fSavedExpandedItems = [[NSMutableIndexSet alloc] init]; - else - [fSavedExpandedItems removeAllIndexes]; - - // This code stores the sequence_id of the first job of each job group into an - // index set. This is sufficient to identify each group uniquely. - - HBJobGroup * aJobGroup; - for( aJobGroup in fJobGroups ) - { - if ([fOutlineView isItemExpanded: aJobGroup]) - [fSavedExpandedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id]; - } - - // Save the selection also. - - if (!fSavedSelectedItems) - fSavedSelectedItems = [[NSMutableIndexSet alloc] init]; - else - [fSavedSelectedItems removeAllIndexes]; - - NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; - NSInteger row = [selectedRows firstIndex]; - while (row != NSNotFound) - { - aJobGroup = [fOutlineView itemAtRow: row]; - [fSavedSelectedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id]; - row = [selectedRows indexGreaterThanIndex: row]; - } - -} - -//------------------------------------------------------------------------------------ -// Restores the expanded state of items in the outline view to match those saved by a -// previous call to saveOutlineViewState. -//------------------------------------------------------------------------------------ -- (void) restoreOutlineViewState -{ - if (fSavedExpandedItems) - { - for( HBJobGroup * aJobGroup in fJobGroups ) - { - HBJob * job = [aJobGroup jobAtIndex:0]; - if (job && [fSavedExpandedItems containsIndex: job->sequence_id]) - [fOutlineView expandItem: aJobGroup]; - } - } - - if (fSavedSelectedItems) - { - NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease]; - NSInteger i = 0; - for( HBJobGroup * aJobGroup in fJobGroups ) - { - HBJob * job = [aJobGroup jobAtIndex:0]; - if (job && [fSavedSelectedItems containsIndex: job->sequence_id]) - [rowsToSelect addIndex: i]; - i++; - } - if ([rowsToSelect count] == 0) - [fOutlineView deselectAll: nil]; - else - [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO]; - } -} - -//------------------------------------------------------------------------------------ -// Marks the icon region of a job group in the queue view as needing display. -//------------------------------------------------------------------------------------ -- (void) updateJobGroupIconInQueue:(HBJobGroup*)aJobGroup -{ - NSInteger row = [fOutlineView rowForItem: aJobGroup]; - NSInteger col = [fOutlineView columnWithIdentifier: @"icon"]; - if (row != -1 && col != -1) - { - NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row]; - [fOutlineView setNeedsDisplayInRect: frame]; - } -} - -//------------------------------------------------------------------------------------ -// Marks the entire region of a job group in the queue view as needing display. -//------------------------------------------------------------------------------------ -- (void) updateJobGroupInQueue:(HBJobGroup*)aJobGroup -{ - NSInteger row = [fOutlineView rowForItem: aJobGroup]; - if (row != -1) - { - NSRect frame = [fOutlineView rectOfRow:row]; - [fOutlineView setNeedsDisplayInRect: frame]; - } -} - -//------------------------------------------------------------------------------------ -// If a job is currently processing, its job icon in the queue outline view is -// animated to its next state. -//------------------------------------------------------------------------------------ -- (void) animateCurrentJobGroupInQueue:(NSTimer*)theTimer -{ - if (fCurrentJobGroup) - { - fAnimationIndex++; - fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below. - [self updateJobGroupIconInQueue: fCurrentJobGroup]; - } -} - -//------------------------------------------------------------------------------------ -// 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]; -} +fEncodingQueueItem = 0; +fPendingCount = 0; +fCompletedCount = 0; +fCanceledCount = 0; +fWorkingCount = 0; -//------------------------------------------------------------------------------------ -// 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; - } -} - -//------------------------------------------------------------------------------------ -// Generate string to display in UI. -//------------------------------------------------------------------------------------ -- (NSString *) progressStatusStringForJob: (HBJob *)job state: (hb_state_t *)s -{ - if (s->state == HB_STATE_WORKING) - { - NSString * msg; - if (job->pass == -1) - msg = NSLocalizedString( @"Deep Scan", 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; - - } - - 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 ); - - return @""; -} - -//------------------------------------------------------------------------------------ -// Generate string to display in UI. -//------------------------------------------------------------------------------------ -- (NSString *) progressTimeRemainingStringForJob: (HBJob *)job state: (hb_state_t *)s -{ - if (s->state == HB_STATE_WORKING) - { - #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]; - } + /* 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 + */ - else + int i = 0; + NSEnumerator *enumerator = [fJobGroups objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + 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 { - 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]; - } - -/* here is code that does it more like the Finder - if( p.seconds > -1 ) + fPendingCount++; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled { - 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 ); - } - else - return NSLocalizedString( @"Time remaining: Calculating...", nil ); -*/ - #undef p - } + fCanceledCount++; + } + i++; + } - return @""; -} +/* We should fire up the encoding timer here based on fWorkingCount */ -//------------------------------------------------------------------------------------ -// Refresh progress bar (fProgressTextField) from current state. -//------------------------------------------------------------------------------------ -- (void) updateProgressTextForJob: (HBJob *)job state: (hb_state_t *)s +if (fWorkingCount > 0) { - 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]; + /* we have an encoding job so, lets start the animation timer */ + [self startAnimatingCurrentWorkingEncodeInQueue]; } -//------------------------------------------------------------------------------------ -// Refresh progress bar (fProgressBar) from current state. -//------------------------------------------------------------------------------------ -- (void) updateProgressBarWithState: (hb_state_t *)s -{ - if (s->state == HB_STATE_WORKING) - { - #define p s->param.working - [fProgressBar setIndeterminate:NO]; - float progress_total = 100.0 * ( p.progress + p.job_cur - 1 ) / p.job_count; - [fProgressBar setDoubleValue:progress_total]; - #undef p - } - - else if (s->state == HB_STATE_MUXING) +/* Set the queue status field in the queue window */ + NSMutableString * string; + if (fPendingCount == 1) { - #define p s->param.muxing - [fProgressBar setIndeterminate:YES]; - [fProgressBar startAnimation:nil]; - #undef p - } - - 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 -} - -//------------------------------------------------------------------------------------ -// Refresh queue count text field (fQueueCountField). -//------------------------------------------------------------------------------------ -- (void)updateQueueCountField -{ - NSString * msg; - int jobCount = [fJobGroups count]; - int pendingCount = [self pendingCount]; - if (jobCount == 0) - msg = NSLocalizedString(@"No encodes", nil); - else if ((jobCount == 1) && (pendingCount == 0)) - msg = NSLocalizedString(@"1 encode", nil); - else if (jobCount == pendingCount) // ie, all jobs listed are pending - { - if (jobCount == 1) - msg = NSLocalizedString(@"1 pending encode", nil); - else - msg = [NSString stringWithFormat:NSLocalizedString(@"%d pending encodes", nil), pendingCount]; - } - else // some completed, some pending - msg = [NSString stringWithFormat:NSLocalizedString(@"%d encodes (%d pending)", nil), jobCount, pendingCount]; - - [fQueueCountField setStringValue:msg]; -} - -//------------------------------------------------------------------------------------ -// Refresh the UI in the current job pane. Should be called whenever the current job -// being processed has changed. -//------------------------------------------------------------------------------------ -- (void)updateCurrentJobDescription -{ - if (fCurrentJob) - { - switch (fCurrentJob->pass) - { - case -1: // Subtitle scan - [fJobDescTextField setAttributedStringValue: - [fCurrentJob attributedDescriptionWithIcon: 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: - [fCurrentJob attributedDescriptionWithIcon: 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: - [fCurrentJob attributedDescriptionWithIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - break; - - default: // unknown - [fJobDescTextField setAttributedStringValue: - [fCurrentJob attributedDescriptionWithIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - } + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount]; } else { - [fJobDescTextField setStringValue: @"No encodes pending"]; - + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount]; } + [fQueueCountField setStringValue:string]; } //------------------------------------------------------------------------------------ -// 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_state_t s; - hb_get_state2( fHandle, &s ); - [self updateProgressTextForJob: fCurrentJob state: &s]; - [self updateProgressBarWithState:&s]; -} - -//------------------------------------------------------------------------------------ -// Notifies HBQueuecontroller that the contents of fJobGroups is about to be modified. -// HBQueuecontroller remembers the state of the UI (selection and expanded items). -//------------------------------------------------------------------------------------ -- (void) beginEditingJobGroupsArray -{ - [self saveOutlineViewState]; -} - -//------------------------------------------------------------------------------------ -// Notifies HBQueuecontroller that modifications to fJobGroups as indicated by a prior -// call to beginEditingJobGroupsArray have been completed. HBQueuecontroller reloads -// the queue view and restores the state of the UI (selection and expanded items). -//------------------------------------------------------------------------------------ -- (void) endEditingJobGroupsArray -{ - [self setJobGroupCountsNeedUpdating:YES]; - [fOutlineView noteNumberOfRowsChanged]; - [fOutlineView reloadData]; - [self restoreOutlineViewState]; - [self updateQueueCountField]; -} - -#pragma mark - -#pragma mark Actions - -//------------------------------------------------------------------------------------ -// Deletes the selected jobs from HB and the queue UI +// dealloc //------------------------------------------------------------------------------------ -- (IBAction)removeSelectedJobGroups: (id)sender +- (void)dealloc { - if (!fHandle) return; - - NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; - NSInteger row = [selectedRows firstIndex]; - if (row != NSNotFound) - { - [self beginEditingJobGroupsArray]; - while (row != NSNotFound) - { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; - switch ([jobGroup status]) - { - case HBStatusCompleted: - case HBStatusCanceled: - [fJobGroups removeObject: jobGroup]; - break; - case HBStatusWorking: - [self cancelCurrentJob: sender]; - break; - case HBStatusPending: - // Remove from libhb - for( HBJob * job in [jobGroup fJobs] ) - { - hb_job_t * libhbJob = [self findLibhbJobWithID:job->sequence_id]; - if (libhbJob) - hb_rem( fHandle, libhbJob ); - } - // Remove from our list - [fJobGroups removeObject: jobGroup]; - break; - case HBStatusNone: - break; - } - - row = [selectedRows indexGreaterThanIndex: row]; - } - [self endEditingJobGroupsArray]; - } -} + // clear the delegate so that windowWillClose is not attempted + if( [[self window] delegate] == self ) + [[self window] setDelegate:nil]; -//------------------------------------------------------------------------------------ -// Reveals the file icons in the Finder of the selected job groups. -//------------------------------------------------------------------------------------ -- (IBAction)revealSelectedJobGroups: (id)sender -{ - if (!fHandle) return; + [fJobGroups release]; - NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; - NSInteger row = [selectedRows firstIndex]; - if (row != NSNotFound) - { - while (row != NSNotFound) - { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; - if ([[jobGroup destinationPath] length]) - [[NSWorkspace sharedWorkspace] selectFile:[jobGroup destinationPath] inFileViewerRootedAtPath:nil]; + [fSavedExpandedItems release]; + [fSavedSelectedItems release]; - row = [selectedRows indexGreaterThanIndex: row]; - } - } -} + [[NSNotificationCenter defaultCenter] removeObserver:self]; -//------------------------------------------------------------------------------------ -// 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 -// libhb to cancel the job. -//------------------------------------------------------------------------------------ -- (IBAction)cancelCurrentJob: (id)sender -{ - [fHBController Cancel:sender]; + [super dealloc]; } //------------------------------------------------------------------------------------ -// Starts or cancels the processing of jobs depending on the current state +// Receive HB handle //------------------------------------------------------------------------------------ -- (IBAction)toggleStartCancel: (id)sender +- (void)setHandle: (hb_handle_t *)handle { - if (!fHandle) return; - - hb_state_t s; - hb_get_state2 (fHandle, &s); - - 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 ([self pendingCount] > 0) - [fHBController doRip]; + fQueueEncodeLibhb = handle; } //------------------------------------------------------------------------------------ -// Toggles the pause/resume state of libhb +// Receive HBController //------------------------------------------------------------------------------------ -- (IBAction)togglePauseResume: (id)sender +- (void)setHBController: (HBController *)controller { - if (!fHandle) return; - - hb_state_t s; - hb_get_state2 (fHandle, &s); - - if (s.state == HB_STATE_PAUSED) - hb_resume (fHandle); - else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) - hb_pause (fHandle); + fHBController = controller; } #pragma mark - -#pragma mark Synchronizing with libhb //------------------------------------------------------------------------------------ -// Queues a job group. The job group's status is set to HBStatusPending. +// Displays and brings the queue window to the front //------------------------------------------------------------------------------------ -- (void) addJobGroup: (HBJobGroup *) aJobGroup +- (IBAction) showQueueWindow: (id)sender { - NSAssert(![fJobGroups containsObject:aJobGroup], @"Duplicate job group"); - [aJobGroup setStatus:HBStatusPending]; - - [self beginEditingJobGroupsArray]; - [fJobGroups addObject:aJobGroup]; - [self endEditingJobGroupsArray]; + [self showWindow:sender]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; } -//------------------------------------------------------------------------------------ -// Notifies HBQueueController that libhb's current job has changed -//------------------------------------------------------------------------------------ -- (void)currentJobChanged: (HBJob *) currentJob -{ - /* if the job has a destination path, lets perform finished job notifications in fHBController - * We call this here so that we pickup the last job in the queue and single encodes before fCurrentJob - * is released. So for the first job and the beginning of single encodes we check for the existence - * of a valid fCurrentJob jobGroup - */ - [currentJob retain]; - /* We need to compare the job group to determine if this is the end of a job group - * or just the end of a job within a group to keep from sending encode done notification - * after the first pass in a two pass encode - */ - HBJobGroup * theJobGroupCheck = [currentJob jobGroup]; - if ((theJobGroupCheck == nil) || (theJobGroupCheck != fCurrentJobGroup)) - { - /* we need to make sure that we are not at the beginning of a queue and also that the job hasn't - * been cancelled - */ - if ([[fCurrentJob jobGroup] destinationPath] && [fCurrentJobGroup status] != HBStatusCanceled) - { - /* send encode messages to fHBController. User prefs are grokked there. */ - [fHBController showGrowlDoneNotification: [[fCurrentJob jobGroup] destinationPath]]; - [fHBController sendToMetaX: [[fCurrentJob jobGroup] destinationPath]]; - } - } - [fCurrentJob release]; - fCurrentJob = currentJob; - - // Log info about the preset name. We do this for each job, since libhb logs each - // job separately. The preset name is found in the job's job group object. - if (fCurrentJob && [fCurrentJob jobGroup] && ([[[fCurrentJob jobGroup] presetName] length] > 0)) - [fHBController writeToActivityLog: "Using preset: %s", [[[fCurrentJob jobGroup] presetName] UTF8String]]; - // Check to see if this is also a change in Job Group - - HBJobGroup * theJobGroup = [currentJob jobGroup]; - if ((theJobGroup == nil) || (theJobGroup != fCurrentJobGroup)) // no more job groups or start of a new group - { - // Previous job has completed - if (fCurrentJobGroup) - { - // Update the status of the job that just finished. If the user canceled, - // the status will have already been set to canceled by libhbWillStop. So - // all other cases are assumed to be a successful encode. BTW, libhb - // doesn't currently report errors back to the GUI. - if ([fCurrentJobGroup status] != HBStatusCanceled) - { - [fCurrentJobGroup setStatus:HBStatusCompleted]; - } - - } - - // Set the new group - [self setCurrentJobGroup: theJobGroup]; - - // Update the UI - [self updateCurrentJobDescription]; - [self updateCurrentJobProgress]; - [self showCurrentJobPane: fCurrentJobGroup != nil]; - if (fCurrentJobGroup) - [self startAnimatingCurrentJobGroupInQueue]; - else - [self stopAnimatingCurrentJobGroupInQueue]; - } - - else // start a new job/pass in the same group - { - // Update the UI - [self updateCurrentJobDescription]; - [self updateCurrentJobProgress]; - } - -} //------------------------------------------------------------------------------------ -// 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)libhbWillStop -{ - if (fCurrentJobGroup) - [fCurrentJobGroup setStatus: HBStatusCanceled]; -} - -//------------------------------------------------------------------------------------ -// Notifies HBQueueController that libhb's state has changed +// awakeFromNib //------------------------------------------------------------------------------------ -- (void)libhbStateChanged: (hb_state_t)state +- (void)awakeFromNib { - switch( state.state ) - { - case HB_STATE_WORKING: - { - //NSLog(@"job = %x; job_cur = %d; job_count = %d", state.param.working.sequence_id, state.param.working.job_cur, state.param.working.job_count); - // First check to see if libhb 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 (fCurrentJobID != state.param.working.sequence_id) - { - fCurrentJobID = state.param.working.sequence_id; - HBJob * currentJob = [self findJobWithID:fCurrentJobID]; - [self currentJobChanged: currentJob]; - } - - if (fCurrentJob) - { - [self updateCurrentJobProgress]; - [self startAnimatingCurrentJobGroupInQueue]; - } - break; - } + [self setupToolbar]; - case HB_STATE_MUXING: - { - [self updateCurrentJobProgress]; - break; - } + if( ![[self window] setFrameUsingName:@"Queue"] ) + [[self window] center]; + [self setWindowFrameAutosaveName:@"Queue"]; + [[self window] setExcludedFromWindowsMenu:YES]; - case HB_STATE_PAUSED: - { - [self updateCurrentJobProgress]; - [self stopAnimatingCurrentJobGroupInQueue]; - break; - } + /* 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]; - case HB_STATE_WORKDONE: - { - // HB_STATE_WORKDONE means that libhb has finished processing all the jobs - // in *its* queue. This message is NOT sent as each individual job is - // completed. - [self currentJobChanged: nil]; - fCurrentJobID = 0; - break; - } + // Don't allow autoresizing of main column, else the "delete" column will get + // pushed out of view. + [fOutlineView setAutoresizesOutlineColumn: NO]; - } +#if HB_OUTLINE_METRIC_CONTROLS + [fIndentation setHidden: NO]; + [fSpacing setHidden: NO]; + [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]]; // debug + [fSpacing setIntegerValue:3]; // debug +#endif -} + // Show/hide UI elements + fCurrentJobPaneShown = NO; // it's shown in the nib + //[self showCurrentJobPane:NO]; -#if HB_OUTLINE_METRIC_CONTROLS -static CGFloat spacingWidth = 3.0; -- (IBAction)imageSpacingChanged: (id)sender; -{ - spacingWidth = [sender floatValue]; - [fOutlineView setNeedsDisplay: YES]; -} -- (IBAction)indentChanged: (id)sender -{ - [fOutlineView setIndentationPerLevel: [sender floatValue]]; - [fOutlineView setNeedsDisplay: YES]; + //[self updateQueueCountField]; } -#endif -#pragma mark - //------------------------------------------------------------------------------------ -// Receives notification whenever an HBJobGroup's status is changed. +// windowWillClose //------------------------------------------------------------------------------------ -- (void) jobGroupStatusNotification:(NSNotification *)notification +- (void)windowWillClose:(NSNotification *)aNotification { - [self setJobGroupCountsNeedUpdating: YES]; -// HBQueueJobGroupStatus oldStatus = (HBQueueJobGroupStatus) [[[notification userInfo] objectForKey:@"HBOldJobGroupStatus"] integerValue]; - HBJobGroup * jobGroup = [notification object]; - if (jobGroup) - [self updateJobGroupInQueue:jobGroup]; - [self updateQueueCountField]; + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"]; } - -#pragma mark - #pragma mark Toolbar //------------------------------------------------------------------------------------ @@ -2006,12 +395,12 @@ static CGFloat spacingWidth = 3.0; // Optional method: This message is sent to us since we are the target of some // toolbar item actions. - if (!fHandle) return NO; + if (!fQueueEncodeLibhb) return NO; BOOL enable = NO; hb_state_t s; - hb_get_state2 (fHandle, &s); + hb_get_state2 (fQueueEncodeLibhb, &s); if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier]) { @@ -2023,7 +412,7 @@ static CGFloat spacingWidth = 3.0; [toolbarItem setToolTip: @"Stop Encoding"]; } - else if ([self pendingCount] > 0) + else if (fPendingCount > 0) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; @@ -2071,51 +460,134 @@ static CGFloat spacingWidth = 3.0; #pragma mark - + +#pragma mark Queue Item Controls //------------------------------------------------------------------------------------ -// awakeFromNib +// Delete pending or cancelled item from the queue window and accompanying array //------------------------------------------------------------------------------------ -- (void)awakeFromNib +- (IBAction)removeSelectedQueueItem: (id)sender { - [self setupToolbar]; + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + [fHBController removeQueueFileItem:row]; +} - if( ![[self window] setFrameUsingName:@"Queue"] ) - [[self window] center]; - [self setWindowFrameAutosaveName:@"Queue"]; - [[self window] setExcludedFromWindowsMenu:YES]; +//------------------------------------------------------------------------------------ +// Show the finished encode in the finder +//------------------------------------------------------------------------------------ +- (IBAction)revealSelectedQueueItem: (id)sender +{ + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + NSInteger row = [selectedRows firstIndex]; + if (row != NSNotFound) + { + while (row != NSNotFound) + { + NSMutableDictionary *queueItemToOpen = [fOutlineView itemAtRow: row]; + [[NSWorkspace sharedWorkspace] selectFile:[queueItemToOpen objectForKey:@"DestinationPath"] inFileViewerRootedAtPath:nil]; -#if HB_QUEUE_DRAGGING - [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:HBQueuePboardType] ]; - [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; - [fOutlineView setVerticalMotionCanBeginDrag: YES]; -#endif + row = [selectedRows indexGreaterThanIndex: row]; + } + } +} - // Don't allow autoresizing of main column, else the "delete" column will get - // pushed out of view. - [fOutlineView setAutoresizesOutlineColumn: NO]; -#if HB_OUTLINE_METRIC_CONTROLS - [fIndentation setHidden: NO]; - [fSpacing setHidden: NO]; - [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]]; // debug - [fSpacing setIntegerValue:3]; // debug -#endif +//------------------------------------------------------------------------------------ +// Starts or cancels the processing of jobs depending on the current state +//------------------------------------------------------------------------------------ +- (IBAction)toggleStartCancel: (id)sender +{ + if (!fQueueEncodeLibhb) return; - // Show/hide UI elements - fCurrentJobPaneShown = YES; // it's shown in the nib - [self showCurrentJobPane:NO]; + hb_state_t s; + hb_get_state2 (fQueueEncodeLibhb, &s); + + 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 - [self updateQueueCountField]; + else if (fPendingCount > 0) + [fHBController Rip: NULL]; } +//------------------------------------------------------------------------------------ +// Toggles the pause/resume state of libhb +//------------------------------------------------------------------------------------ +- (IBAction)togglePauseResume: (id)sender +{ + if (!fQueueEncodeLibhb) return; + + hb_state_t s; + hb_get_state2 (fQueueEncodeLibhb, &s); + + if (s.state == HB_STATE_PAUSED) + hb_resume (fQueueEncodeLibhb); + else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) + hb_pause (fQueueEncodeLibhb); +} + +#pragma mark - + + +#pragma mark Animate Endcoding Item + + + //------------------------------------------------------------------------------------ -// windowWillClose +// Starts animating the job icon of the currently processing job in the queue outline +// view. //------------------------------------------------------------------------------------ -- (void)windowWillClose:(NSNotification *)aNotification +- (void) startAnimatingCurrentWorkingEncodeInQueue { - [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"]; + 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]; } +//------------------------------------------------------------------------------------ +// If a job is currently processing, its job icon in the queue outline view is +// animated to its next state. +//------------------------------------------------------------------------------------ +- (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer +{ + if (fWorkingCount > 0) + { + fAnimationIndex++; + fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below. + [self animateWorkingEncodeIconInQueue]; + } +} + + +- (void) animateWorkingEncodeIconInQueue +{ + 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]; + } +} + +//------------------------------------------------------------------------------------ +// 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; + } +} + + #pragma mark - - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex @@ -2147,10 +619,12 @@ static CGFloat spacingWidth = 3.0; } } + #pragma mark - #pragma mark NSOutlineView delegate -- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item + +- (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item { if (item == nil) return [fJobGroups objectAtIndex:index]; @@ -2160,26 +634,26 @@ static CGFloat spacingWidth = 3.0; return nil; } -- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item +- (BOOL)outlineView:(NSOutlineView *)fOutlineView isItemExpandable:(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. return YES; } -- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item +- (BOOL)outlineView:(NSOutlineView *)fOutlineView 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 + // return ![(HBQueueOutlineView*)fOutlineView isDragging]; + + return YES; //<-- Needs to be YES to allow expanding + } -- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item +- (NSInteger)outlineView:(NSOutlineView *)fOutlineView numberOfChildrenOfItem:(id)item { // Our outline view has no levels, so number of children will be zero for all // top-level items. @@ -2213,62 +687,405 @@ static CGFloat spacingWidth = 3.0; // 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]; + // if ([outlineView inLiveResize] && [item lastDescriptionHeight] > 0) + // return [item lastDescriptionHeight]; - CGFloat width = [[outlineView tableColumnWithIdentifier: @"desc"] width]; + // 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 + // width -= 2; // (?) for intercell spacing - CGFloat height = [item heightOfDescriptionForWidth: width]; - return height; + // CGFloat height = [item heightOfDescriptionForWidth: width]; + // return height; + + return HB_ROW_HEIGHT_FULL_DESCRIPTION; } else return HB_ROW_HEIGHT_TITLE_ONLY; } -- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +- (CGFloat) heightOfDescriptionForWidth:(CGFloat)width +{ + // 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; +*/ +} + +- (CGFloat) lastDescriptionHeight +{ + return HB_ROW_HEIGHT_FULL_DESCRIPTION; +} + +- (id)outlineView:(NSOutlineView *)fOutlineView 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 attributedDescription]; + { + /* 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]; + + summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@)", titleString, chapterString, passesString]; + + [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 = @"None"; + NSString * audioDetail2 = @"None"; + NSString * audioDetail3 = @"None"; + NSString * audioDetail4 = @"None"; + 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:@"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:@"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:@"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"]]; + } + + 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:@" - 64 Bit"]; + } + if( [[item objectForKey:@"Mp4HttpOptimize"] intValue] == 1) + { + mp4OptsPresent = YES; + MP4Opts = [MP4Opts stringByAppendingString:@" - Http Optimized"]; + } + + if( [[item objectForKey:@"Mp4iPodCompatible"] intValue] == 1) + { + mp4OptsPresent = YES; + MP4Opts = [MP4Opts stringByAppendingString:@" - iPod Atom "]; + } + 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 mp4 options */ + + NSString * pictureFilters = @""; + BOOL pictureFiltersPresent = NO; + if( [[item objectForKey:@"VFR"] intValue] == 1) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - VFR"]; + } + if( [[item objectForKey:@"PictureDetelecine"] intValue] == 1 ) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - Detelecine"]; + } + + if( [[item objectForKey:@"PictureDecomb"] intValue] == 1) + { + pictureFiltersPresent = YES; + pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb "]; + } + + if ([[item objectForKey:@"PictureDeinterlace"] intValue] != 0) + { + pictureFiltersPresent = YES; + if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 1) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb: Fast "]; + } + else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 2) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb: Slow "]; + } + else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 3) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb: Slower "]; + } + + } + 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 "]; + } + + } + if ([[item objectForKey:@"PictureDeblock"] intValue] == 1) + { + pictureFilters = [pictureFilters stringByAppendingString:@" - Deblock "]; + } + 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"]]; + videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@", videoInfo ,[item objectForKey:@"VideoFramerate"]]; + + if ([[item objectForKey:@"VideoQualityType"] intValue] == 0)// Target Size MB + { + videoInfo = [NSString stringWithFormat:@"%@ Target Size: %@(MB)", videoInfo ,[item objectForKey:@"VideoTargetSize"]]; + } + 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: %.0f %%", videoInfo ,[[item objectForKey:@"VideoQualitySlider"] floatValue] * 100]; + } + + [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*/ + [finalString appendString: @"Audio Track 1: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail1 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + + [finalString appendString: @"Audio Track 2: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail2 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + + [finalString appendString: @"Audio Track 3: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail3 withAttributes:detailAttr]; + [finalString appendString:@"\n" withAttributes:detailAttr]; + + [finalString appendString: @"Audio Track 4: " withAttributes:detailBoldAttr]; + [finalString appendString: audioDetail4 withAttributes:detailAttr]; + //[finalString appendString:@"\n" withAttributes:detailAttr]; + + + return finalString; + + + + + } else if ([[tableColumn identifier] isEqualToString:@"icon"]) { - switch ([(HBJobGroup*)item status]) + if ([[item objectForKey:@"Status"] intValue] == 0) + { + return [NSImage imageNamed:@"EncodeComplete"]; + } + else if ([[item objectForKey:@"Status"] intValue] == 1) { - case HBStatusCanceled: - return [NSImage imageNamed:@"EncodeCanceled"]; - break; - case HBStatusCompleted: - return [NSImage imageNamed:@"EncodeComplete"]; - break; - case HBStatusWorking: - return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; - break; - default: - return [NSImage imageNamed:@"JobSmall"]; - break; + return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; } + else if ([[item objectForKey:@"Status"] intValue] == 3) + { + return [NSImage imageNamed:@"EncodeCanceled"]; + } + else + { + return [NSImage imageNamed:@"JobSmall"]; + } + } else + { return @""; + } } - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { if ([[tableColumn identifier] isEqualToString:@"desc"]) { -#if HB_OUTLINE_METRIC_CONTROLS - NSSize theSize = [cell imageSpacing]; - theSize.width = spacingWidth; - [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. @@ -2276,14 +1093,13 @@ static CGFloat spacingWidth = 3.0; // 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"]) { [cell setEnabled: YES]; BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); - if ([(HBJobGroup*)item status] == HBStatusCompleted) + if ([[item objectForKey:@"Status"] intValue] == 0) { - [cell setAction: @selector(revealSelectedJobGroups:)]; + [cell setAction: @selector(revealSelectedQueueItem:)]; if (highlighted) { [cell setImage:[NSImage imageNamed:@"RevealHighlight"]]; @@ -2294,7 +1110,7 @@ static CGFloat spacingWidth = 3.0; } else { - [cell setAction: @selector(removeSelectedJobGroups:)]; + [cell setAction: @selector(removeSelectedQueueItem:)]; if (highlighted) { [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; @@ -2323,31 +1139,30 @@ static CGFloat spacingWidth = 3.0; // NSTableView delegate //------------------------------------------------------------------------------------ -#if HB_QUEUE_DRAGGING + - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard { // Dragging is only allowed of the pending items. - for( HBJobGroup * group in items ) + if ([[[fJobGroups objectAtIndex:[outlineView selectedRow]] objectForKey:@"Status"] intValue] != 2) // 2 is pending { - if ([group status] != HBStatusPending) - return NO; + 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]; - + [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:HBQueuePboardType]; - + [pboard setData:[NSData data] forType:DragDropSimplePboardType]; + return YES; } -#endif -#if HB_QUEUE_DRAGGING + +/* This method is used to validate the drops. */ - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index { // Don't allow dropping ONTO an item since they can't really contain any children. @@ -2362,36 +1177,70 @@ static CGFloat spacingWidth = 3.0; item = nil; } - // Prevent dragging into the completed or current job. - int firstPendingIndex = [self completedCount]; - if (fCurrentJobGroup) - firstPendingIndex++; - index = MAX (index, firstPendingIndex); - + // 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; } -#endif -#if HB_QUEUE_DRAGGING + + - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index { - NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet]; - - for( id obj in fDraggedNodes) + NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet]; + + id obj; + NSEnumerator *enumerator = [fDraggedNodes objectEnumerator]; + while (obj = [enumerator nextObject]) { [moveItems addIndex:[fJobGroups indexOfObject:obj]]; } - // Rearrange the data and view - [self saveOutlineViewState]; - [self moveObjectsInArray:fJobGroups fromIndexes:moveItems toIndex: index]; - [fOutlineView reloadData]; - [self restoreOutlineViewState]; - + // 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 +- (void)moveObjectsInQueueArray:(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]; + } +} + @end |