From 7045d521d48d670473d5858c43e4fb54dee14b1c Mon Sep 17 00:00:00 2001 From: dynaflash Date: Tue, 9 Oct 2007 15:15:16 +0000 Subject: [PATCH] MacGui: Queue Enhancements courtesy of travistex - Encodes now stick around in the queue's displayed after they have been completed. They are marked with a check mark icon. - The "active" encode is shown with a chasing arrows icon. - Completed encodes have a spotlight icon by them to allow you to show in finder - Support for reordering encodes in the queue via drag and drop. Currently #define'd out because there's currently no easy way to reorder hblib's job list. But some day maybe... git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1017 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- macosx/Controller.h | 2 - macosx/Controller.mm | 24 +- macosx/English.lproj/Queue.nib/classes.nib | 3 +- .../English.lproj/Queue.nib/keyedobjects.nib | Bin 11019 -> 11718 bytes macosx/HBQueueController.h | 73 +- macosx/HBQueueController.mm | 898 +++++++++++++----- macosx/HandBrake.xcodeproj/project.pbxproj | 44 + macosx/icons/EncodeComplete.png | Bin 0 -> 314 bytes macosx/icons/EncodeWorking0.png | Bin 0 -> 350 bytes macosx/icons/EncodeWorking1.png | Bin 0 -> 361 bytes macosx/icons/EncodeWorking2.png | Bin 0 -> 347 bytes macosx/icons/EncodeWorking3.png | Bin 0 -> 350 bytes macosx/icons/EncodeWorking4.png | Bin 0 -> 359 bytes macosx/icons/EncodeWorking5.png | Bin 0 -> 357 bytes macosx/icons/Reveal.png | Bin 0 -> 259 bytes macosx/icons/RevealHighlight.png | Bin 0 -> 229 bytes macosx/icons/RevealHighlightPressed.png | Bin 0 -> 229 bytes macosx/icons/RevealPressed.png | Bin 0 -> 254 bytes 18 files changed, 772 insertions(+), 272 deletions(-) create mode 100644 macosx/icons/EncodeComplete.png create mode 100644 macosx/icons/EncodeWorking0.png create mode 100644 macosx/icons/EncodeWorking1.png create mode 100644 macosx/icons/EncodeWorking2.png create mode 100644 macosx/icons/EncodeWorking3.png create mode 100644 macosx/icons/EncodeWorking4.png create mode 100644 macosx/icons/EncodeWorking5.png create mode 100644 macosx/icons/Reveal.png create mode 100644 macosx/icons/RevealHighlight.png create mode 100644 macosx/icons/RevealHighlightPressed.png create mode 100644 macosx/icons/RevealPressed.png diff --git a/macosx/Controller.h b/macosx/Controller.h index d5770163c..68c8f672e 100644 --- a/macosx/Controller.h +++ b/macosx/Controller.h @@ -179,8 +179,6 @@ int currentSuccessfulScanCount; BOOL SuccessfulScan; NSString * currentSource; - - hb_job_t * fLastKnownCurrentJob; } - (void) TranslateStrings; diff --git a/macosx/Controller.mm b/macosx/Controller.mm index 8279c2873..aa0a5339f 100644 --- a/macosx/Controller.mm +++ b/macosx/Controller.mm @@ -801,12 +801,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // Has current job changed? That means the queue has probably changed as // well so update it - if (fLastKnownCurrentJob != hb_current_job(fHandle)) - { - fLastKnownCurrentJob = hb_current_job(fHandle); - [fQueueController updateQueueUI]; - } - [fQueueController updateCurrentJobUI]; + [fQueueController hblibStateChanged: s]; break; } @@ -830,7 +825,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [self UpdateDockIcon: 1.0]; // Pass along the info to HBQueueController - [fQueueController updateCurrentJobUI]; + [fQueueController hblibStateChanged: s]; break; } @@ -840,7 +835,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fStatusField setStringValue: _( @"Paused" )]; // Pass along the info to HBQueueController - [fQueueController updateCurrentJobUI]; + [fQueueController hblibStateChanged: s]; break; @@ -886,10 +881,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It fRipIndicatorShown = NO; } - // Queue has been modified so update the UI - fLastKnownCurrentJob = nil; - [fQueueController updateQueueUI]; - [fQueueController updateCurrentJobUI]; + // Pass along the info to HBQueueController + [fQueueController hblibStateChanged: s]; /* Check to see if the encode state has not been cancelled to determine if we should check for encode done notifications */ @@ -1652,9 +1645,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; - /* Lets try to update stuff, taken from remove in the queue controller */ - [fQueueController performSelectorOnMainThread: @selector( updateQueueUI ) - withObject: NULL waitUntilDone: NO]; + + // Notify the queue + [fQueueController hblibJobListChanged]; } /* Rip: puts up an alert before ultimately calling doRip @@ -1814,6 +1807,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // remaining passes of the job and then start the queue back up if there are any // remaining jobs. + [fQueueController hblibWillStop]; hb_stop( fHandle ); fEncodeState = 2; // don't alert at end of processing since this was a cancel diff --git a/macosx/English.lproj/Queue.nib/classes.nib b/macosx/English.lproj/Queue.nib/classes.nib index be46492f1..e20a23a7b 100644 --- a/macosx/English.lproj/Queue.nib/classes.nib +++ b/macosx/English.lproj/Queue.nib/classes.nib @@ -221,7 +221,8 @@ cancelCurrentJob = id; imageSpacingChanged = id; indentChanged = id; - removeSelectedJob = id; + removeSelectedJobGroups = id; + revealSelectedJobGroups = id; showQueueWindow = id; togglePauseResume = id; toggleStartCancel = id; diff --git a/macosx/English.lproj/Queue.nib/keyedobjects.nib b/macosx/English.lproj/Queue.nib/keyedobjects.nib index 50285f81048115eb44f056e2e867ed2dc842a6a6..1aa1d0d16b2e02f6c1d14fa973e45725eaebcf12 100644 GIT binary patch delta 7264 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 M)pftG?w)!71O2FCQ2+n{ delta 6715 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 C=gJ5G diff --git a/macosx/HBQueueController.h b/macosx/HBQueueController.h index d223e435f..cab1c201b 100644 --- a/macosx/HBQueueController.h +++ b/macosx/HBQueueController.h @@ -11,18 +11,44 @@ @class HBController; #define HB_QUEUE_DRAGGING 0 // <--- NOT COMPLETELY FUNCTIONAL YET -#define HB_OUTLINE_METRIC_CONTROLS 0 // for tweaking the outline cell spacings +#define HB_OUTLINE_METRIC_CONTROLS 1 // for tweaking the outline cell spacings -//------------------------------------------------------------------------------------ +typedef enum _HBQueueJobGroupStatus +{ + HBStatusNone = 0, + HBStatusPending = 1, + HBStatusWorking = 2, + HBStatusComplete = 3, + HBStatusCanceled = 4 +} HBQueueJobGroupStatus; +//------------------------------------------------------------------------------------ +// As usual, we need to subclass NSOutlineView to handle a few special cases: +// +// (1) variable row heights during live resizes // HBQueueOutlineView exists solely to get around a bug in variable row height outline // views in which row heights get messed up during live resizes. See this discussion: // http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00871.html // However, the recommeneded fix (override drawRect:) does not work. Instead, this // subclass implements viewDidEndLiveResize in order to recalculate all row heights. +// +// (2) prevent expanding of items during drags +// During dragging operations, we don't want outline items to expand, since a queue +// doesn't really have children items. +// +// (3) generate a drag image that incorporates more than just the first column +// 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. + @interface HBQueueOutlineView : NSOutlineView { +#if HB_QUEUE_DRAGGING +BOOL fIsDragging; +#endif } +#if HB_QUEUE_DRAGGING +- (BOOL) isDragging; +#endif @end //------------------------------------------------------------------------------------ @@ -58,14 +84,30 @@ BOOL fNeedsDescription; float fLastDescriptionHeight; float fLastDescriptionWidth; + HBQueueJobGroupStatus fStatus; + NSString *fPath; } +// Creating a job group + (HBJobGroup *) jobGroup; -- (unsigned int) count; + +// Adding jobs - (void) addJob: (HBJob *)aJob; + +// Removing jobs +- (void) removeAllJobs; + +// Querying a job group +- (unsigned int) count; - (HBJob *) jobAtIndex: (unsigned)index; - (unsigned) indexOfJob: (HBJob *)aJob; - (NSEnumerator *) jobEnumerator; +- (void) setStatus: (HBQueueJobGroupStatus)status; +- (HBQueueJobGroupStatus) status; +- (void) setPath: (NSString *)path; +- (NSString *) path; + +// Creating a description - (void) setNeedsDescription: (BOOL)flag; - (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle; - (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle; @@ -77,17 +119,20 @@ @interface HBQueueController : NSObject { - hb_handle_t *fHandle; - HBController *fHBController; + hb_handle_t *fHandle; // reference to hblib + HBController *fHBController; // reference to HBController NSMutableArray *fJobGroups; // hblib's job list organized in a hierarchy of HBJobGroup and HBJob - NSViewAnimation *fAnimation; // for revealing the fCurrentJobPane + HBJobGroup *fCurrentJobGroup; // the HJobGroup current being processed by hblib BOOL fCurrentJobPaneShown; // NO when fCurrentJobPane has been shifted out of view (see showCurrentJobPane) - hb_job_t *fLastKnownCurrentJob; - NSMutableIndexSet *fSavedExpandedItems; - unsigned int fSavedSelectedItem; + hb_job_t *fLastKnownCurrentJob; // this is how we track when hbib has started processing a different job + NSMutableIndexSet *fSavedExpandedItems; // used by save/restoreOutlineViewState to preserve which items are expanded + NSMutableIndexSet *fSavedSelectedItems; // used by save/restoreOutlineViewState to preserve which items are selected #if HB_QUEUE_DRAGGING - NSArray *fDraggedNodes; + NSArray *fDraggedNodes; #endif + NSMutableArray *fCompleted; // HBJobGroups that have been completed. These also appear in fJobGroups. + NSTimer *fAnimationTimer; // animates the icon of the current job in the queue outline view + int fAnimationIndex; // used to generate name of image used to animate the current job in the queue outline view // +---------------fQueueWindow----------------+ // |+-------------fCurrentJobPane-------------+| @@ -128,11 +173,13 @@ - (void)setHandle: (hb_handle_t *)handle; - (void)setHBController: (HBController *)controller; -- (void)updateQueueUI; -- (void)updateCurrentJobUI; +- (void)hblibJobListChanged; +- (void)hblibStateChanged: (hb_state_t &)state; +- (void)hblibWillStop; - (IBAction)showQueueWindow: (id)sender; -- (IBAction)removeSelectedJob: (id)sender; +- (IBAction)removeSelectedJobGroups: (id)sender; +- (IBAction)revealSelectedJobGroups: (id)sender; - (IBAction)cancelCurrentJob: (id)sender; - (IBAction)toggleStartCancel: (id)sender; - (IBAction)togglePauseResume: (id)sender; diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index dab66d362..fa307e837 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -8,11 +8,6 @@ #include "Controller.h" #import "HBImageAndTextCell.h" -// UNI_QUEUE turns on the feature where the first item in the queue NSTableView is the -// current job followed by the jobs in hblib's queue. In this scheme, fCurrentJobPane -// disappers. -#define HB_UNI_QUEUE 0 // <--- NOT COMPLETELY FUNCTIONAL YET - #define HB_ROW_HEIGHT_TITLE_ONLY 17.0 // Pasteboard type for or drag operations @@ -52,6 +47,38 @@ [super viewDidEndLiveResize]; } +#if HB_QUEUE_DRAGGING +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset +{ + // Set the fIsDragging flag so that other's know that a drag operation is being + // performed. + fIsDragging = YES; + + // By default, NSTableView only drags an image of the first column. Change this to + // drag an image of the queue's icon and desc columns. + NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"icon"], [self tableColumnWithIdentifier:@"desc"], nil]; + return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset]; +} +#endif + +#if HB_QUEUE_DRAGGING +- (void) mouseDown:(NSEvent *)theEvent +{ + // After a drag operation, reset fIsDragging back to NO. This is really the only way + // for us to detect when a drag has finished. You can't do it in acceptDrop because + // that won't be called if the dragged item is released outside the view. + [super mouseDown:theEvent]; + fIsDragging = NO; +} +#endif + +#if HB_QUEUE_DRAGGING +- (BOOL) isDragging; +{ + return fIsDragging; +} +#endif + @end //------------------------------------------------------------------------------------ @@ -343,14 +370,14 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withFormatInfo || withVideoInfo) { // 2097152 - /* Video Codec settings (Encoder in the gui) */ + // Video Codec settings (Encoder in the gui) if (hbJob->vcodec == HB_VCODEC_FFMPEG) jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG else if (hbJob->vcodec == HB_VCODEC_XVID) jobVideoCodec = @"XviD"; // HB_VCODEC_XVID else if (hbJob->vcodec == HB_VCODEC_X264) { - /* Deterimine for sure how we are now setting iPod uuid atom */ + // Deterimine for sure how we are now setting iPod uuid atom if (hbJob->h264_level) // We are encoding for iPod jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 else @@ -411,8 +438,8 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withPictureInfo) { NSString * jobPictureInfo; - /*integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, - maxWidth, maxHeight */ + // integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, + // maxWidth, maxHeight if (hbJob->pixel_ratio == 1) { int titlewidth = title->width - hbJob->crop[2] - hbJob->crop[3]; @@ -456,17 +483,17 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (hbJob->vrate_base == 1126125) { - /* NTSC FILM 23.976 */ + // NTSC FILM 23.976 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality]; } else if (hbJob->vrate_base == 900900) { - /* NTSC 29.97 */ + // NTSC 29.97 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality]; } else { - /* Everything else */ + // Everything else jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base]; } if (withIcon) // implies indent the info @@ -494,9 +521,9 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) else jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->arate]; - /* we now get the audio mixdown info for each of the two gui audio tracks */ - /* lets do it the long way here to get a handle on things. - Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] */ + // we now get the audio mixdown info for each of the two gui audio tracks + // lets do it the long way here to get a handle on things. + // Hardcoded for two tracks for gui: audio_mixdowns[i] audio_mixdowns[i] int ai; // counter for each audios [] , macgui only allows for two audio tracks currently for( ai = 0; ai < 2; ai++ ) { @@ -571,6 +598,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) fJobs = [[NSMutableArray arrayWithCapacity:0] retain]; fDescription = [[NSMutableAttributedString alloc] initWithString: @""]; [self setNeedsDescription: NO]; + fStatus = HBStatusNone; } return self; } @@ -578,6 +606,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) dealloc { [fJobs release]; + [fPath release]; [super dealloc]; } @@ -594,6 +623,11 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) fLastDescriptionWidth = 0; } +- (void) removeAllJobs +{ + [fJobs removeAllObjects]; +} + - (HBJob *) jobAtIndex: (unsigned)index { return [fJobs objectAtIndex: index]; @@ -616,6 +650,8 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle { + fNeedsDescription = NO; + [fDescription deleteCharactersInRange: NSMakeRange(0, [fDescription length])]; if ([self count] == 0) @@ -659,7 +695,6 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) withSubtitleInfo: YES]]; } - fNeedsDescription = NO; } - (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle @@ -703,16 +738,43 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) return fLastDescriptionHeight; } +- (void) setStatus: (HBQueueJobGroupStatus)status +{ + self->fStatus = status; +} + +- (HBQueueJobGroupStatus) status +{ + return self->fStatus; +} + +- (void) setPath: (NSString *)path +{ + [path retain]; + [fPath release]; + fPath = path; +} + +- (NSString *) path +{ + return fPath; +} + @end #pragma mark - +@interface HBQueueController (Private) +- (void)updateQueueUI; +@end + // Toolbar identifiers static NSString* HBQueueToolbar = @"HBQueueToolbar1"; static NSString* HBQueueStartCancelToolbarIdentifier = @"HBQueueStartCancelToolbarIdentifier"; static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseResumeToolbarIdentifier"; +#pragma mark - @implementation HBQueueController @@ -731,6 +793,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe nil]]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; + fCompleted = [[NSMutableArray arrayWithCapacity:0] retain]; BOOL loadSucceeded = [NSBundle loadNibNamed:@"Queue" owner:self] && fQueueWindow; NSAssert(loadSucceeded, @"Could not open Queue nib"); @@ -744,14 +807,15 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)dealloc { - [fAnimation release]; - // clear the delegate so that windowWillClose is not attempted if ([fQueueWindow delegate] == self) [fQueueWindow setDelegate:nil]; [fJobGroups release]; + [fCompleted release]; + [fCurrentJobGroup release]; [fSavedExpandedItems release]; + [fSavedSelectedItems release]; [super dealloc]; } @@ -778,10 +842,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe - (IBAction) showQueueWindow: (id)sender { [self updateQueueUI]; - [self updateCurrentJobUI]; - [fQueueWindow makeKeyAndOrderFront: self]; - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; } //------------------------------------------------------------------------------------ @@ -822,13 +883,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [NSValue valueWithRect:queueFrame], NSViewAnimationEndFrameKey, nil]; - if (!fAnimation) - fAnimation = [[NSViewAnimation alloc] initWithViewAnimations:nil]; - - [fAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]]; - [fAnimation setDuration:0.25]; - [fAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation - [fAnimation startAnimation]; + NSViewAnimation * anAnimation = [[[NSViewAnimation alloc] initWithViewAnimations:nil] autorelease]; + [anAnimation setViewAnimations:[NSArray arrayWithObjects:dict1, dict2, nil]]; + [anAnimation setDuration:0.25]; + [anAnimation setAnimationBlockingMode:NSAnimationBlocking]; // prevent user from resizing the window during an animation + [anAnimation startAnimation]; + fCurrentJobPaneShown = showPane; } @@ -837,11 +897,27 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)rebuildJobGroups { + // Currently, job groups are rdered like this: + // Completed job groups + // Current job group + // Pending job groups + [fJobGroups autorelease]; fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain]; + // Add all the completed job groups + [fJobGroups addObjectsFromArray: fCompleted]; + + // Add all the completed job groups + if (fCurrentJobGroup) + [fJobGroups addObject: fCurrentJobGroup]; + + // Add all the pending job groups. These come from hblib HBJobGroup * aJobGroup = [HBJobGroup jobGroup]; + // If hblib is currently processing something, hb_group will skip over that group. + // And that's exactly what we want -- fJobGroups contains only pending job groups. + hb_job_t * nextJob = hb_group( fHandle, 0 ); while( nextJob ) { @@ -850,19 +926,82 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // Encountered a new group. Add the current one to fJobGroups and then start a new one. if ([aJobGroup count] > 0) { + [aJobGroup setStatus: HBStatusPending]; [fJobGroups addObject: aJobGroup]; aJobGroup = [HBJobGroup jobGroup]; } } [aJobGroup addJob: [HBJob jobWithJob:nextJob]]; + [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]]; nextJob = hb_next_job (fHandle, nextJob); } if ([aJobGroup count] > 0) { + [aJobGroup setStatus: HBStatusPending]; [fJobGroups addObject:aJobGroup]; } } +//------------------------------------------------------------------------------------ +// Adds aJobGroup to the list of completed job groups, marking its status as +// HBStatusComplete. +//------------------------------------------------------------------------------------ +- (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup +{ + // Once hblib has completed its work, the hb_job_t objects will be freed, so we + // can't keep a reference to them. + [aJobGroup removeAllJobs]; + + [aJobGroup setStatus: HBStatusComplete]; + + // Put the group in the completed list for permanent storage, and also rebuild + // the master job group list which contains completed and pending groups. + [fCompleted addObject: aJobGroup]; +} + +- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob +{ + HBJobGroup * aJobGroup = nil; + + // Try to find this new group in our existing job groups. + if (aJob) + { + BOOL found = NO; + NSEnumerator * groupEnum = [fJobGroups objectEnumerator]; + while ( !found && (aJobGroup = [groupEnum nextObject]) ) + { + HBJob * j; + NSEnumerator * jobEnum = [aJobGroup jobEnumerator]; + while ( !found && (j = [jobEnum nextObject]) ) + { + if ([j job] == aJob) + found = YES; + } + } + + // Or create the job group. + if (!aJobGroup) + { + aJobGroup = [HBJobGroup jobGroup]; + [aJobGroup addJob: [HBJob jobWithJob: aJob]]; + [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]]; + while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) ) + [aJobGroup addJob: [HBJob jobWithJob: aJob]]; + + [aJobGroup updateDescriptionWithHBHandle: fHandle]; + } + + [aJobGroup setStatus: HBStatusWorking]; + } + + [aJobGroup retain]; + [fCurrentJobGroup release]; + fCurrentJobGroup = aJobGroup; +} + +#pragma mark - +#pragma mark UI Updating + //------------------------------------------------------------------------------------ // Saves the state of the items that are currently expanded. Calling restoreOutlineViewState // will restore the state of all items to match what was saved by saveOutlineViewState. @@ -884,7 +1023,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe while ( (aJobGroup = [e nextObject]) ) { if ([fOutlineView isItemExpanded: aJobGroup]) - [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + { + if ([aJobGroup status] == HBStatusComplete) + [fSavedExpandedItems addIndex: (unsigned int)aJobGroup]; + else + [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + } } // Save the selection also. This is really UGLY code. Since I have to rebuild the @@ -893,15 +1037,23 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // hb_job_t item in each selected group. This is done by saving the object's // address. This could go away if I'd save a unique id in each job object. - int selection = [fOutlineView selectedRow]; - if (selection == -1) - fSavedSelectedItem = 0; + if (!fSavedSelectedItems) + fSavedSelectedItems = [[NSMutableIndexSet alloc] init]; else + [fSavedSelectedItems removeAllIndexes]; + + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + while (row != NSNotFound) { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: selection]; - fSavedSelectedItem = (unsigned int)[[jobGroup jobAtIndex:0] job]; + aJobGroup = [fOutlineView itemAtRow: row]; + if ([aJobGroup status] == HBStatusComplete) + [fSavedSelectedItems addIndex: (unsigned int)aJobGroup]; + else + [fSavedSelectedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + row = [selectedRows indexGreaterThanIndex: row]; } - + } //------------------------------------------------------------------------------------ @@ -916,35 +1068,90 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe NSEnumerator * e = [fJobGroups objectEnumerator]; while ( (aJobGroup = [e nextObject]) ) { - hb_job_t * j = [[aJobGroup jobAtIndex:0] job]; - if ([fSavedExpandedItems containsIndex: (unsigned int)j]) - [fOutlineView expandItem: aJobGroup]; + if ([aJobGroup status] == HBStatusComplete) + { + if ([fSavedExpandedItems containsIndex: (unsigned int)aJobGroup]) + [fOutlineView expandItem: aJobGroup]; + } + else + { + hb_job_t * j = [[aJobGroup jobAtIndex:0] job]; + if ([fSavedExpandedItems containsIndex: (unsigned int)j]) + [fOutlineView expandItem: aJobGroup]; + } } } - if (fSavedSelectedItem) + if (fSavedSelectedItems) { // Ugh. Have to cycle through each row looking for the previously selected job. - // See the explanation in saveExpandedItems about the logic here. - - // Find out what hb_job_t was selected - hb_job_t * j = (hb_job_t *)fSavedSelectedItem; - - int rowToSelect = -1; + // See the explanation in saveOutlineViewState about the logic here. + + NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease]; for (int i = 0; i < [fOutlineView numberOfRows]; i++) { - HBJobGroup * jobGroup = [fOutlineView itemAtRow: i]; - // Test to see if the group's first job is a match - if ([[jobGroup jobAtIndex:0] job] == j) + HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i]; + // Test to see if the group or the group's first job is a match + if ([aJobGroup status] == HBStatusComplete) { - rowToSelect = i; - break; + if ([fSavedSelectedItems containsIndex: (unsigned int)aJobGroup]) + [rowsToSelect addIndex: i]; + } + else + { + if ([fSavedSelectedItems containsIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]) + [rowsToSelect addIndex: i]; } } - if (rowToSelect == -1) + if ([rowsToSelect count] == 0) [fOutlineView deselectAll: nil]; else - [fOutlineView selectRow:rowToSelect byExtendingSelection:NO]; + [fOutlineView selectRowIndexes:rowsToSelect byExtendingSelection:NO]; + } +} + +//------------------------------------------------------------------------------------ +// If a job is currently processing, its job icon in the queue outline view is +// animated to its next state. +//------------------------------------------------------------------------------------ +- (void) animateCurrentJobGroupInQueue:(NSTimer*)theTimer +{ + int row = [fOutlineView rowForItem: fCurrentJobGroup]; + int col = [fOutlineView columnWithIdentifier: @"icon"]; + if (row != -1 && col != -1) + { + fAnimationIndex++; + fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below. + NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row]; + [fOutlineView setNeedsDisplayInRect: frame]; + } +} + +//------------------------------------------------------------------------------------ +// Starts animating the job icon of the currently processing job in the queue outline +// view. +//------------------------------------------------------------------------------------ +- (void) startAnimatingCurrentJobGroupInQueue +{ + if (!fAnimationTimer) + fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle + target:self + selector:@selector(animateCurrentJobGroupInQueue:) + userInfo:nil + repeats:YES] retain]; +} + +//------------------------------------------------------------------------------------ +// Stops animating the job icon of the currently processing job in the queue outline +// view. +//------------------------------------------------------------------------------------ +- (void) stopAnimatingCurrentJobGroupInQueue +{ + if (fAnimationTimer && [fAnimationTimer isValid]) + { + [fAnimationTimer invalidate]; + [fAnimationTimer release]; + fAnimationTimer = nil; } } @@ -1060,6 +1267,18 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe return @""; } +//------------------------------------------------------------------------------------ +// Refresh progress bar (fProgressTextField) from current state. +//------------------------------------------------------------------------------------ +- (void) updateProgressTextForJob: (hb_job_t *)job state: (hb_state_t *)s +{ + NSString * statusMsg = [self progressStatusStringForJob:job state:s]; + NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:s]; + if ([timeMsg length] > 0) + statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg]; + [fProgressTextField setStringValue:statusMsg]; +} + //------------------------------------------------------------------------------------ // Refresh progress bar (fProgressBar) from current state. //------------------------------------------------------------------------------------ @@ -1085,8 +1304,12 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe else if (s->state == HB_STATE_WORKDONE) { [fProgressBar setIndeterminate:NO]; + [fProgressBar stopAnimation:nil]; [fProgressBar setDoubleValue:0.0]; } + + else + [fProgressBar stopAnimation:nil]; // just in case in was animating } //------------------------------------------------------------------------------------ @@ -1108,108 +1331,94 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Refresh the UI in the current job pane. Should be called whenever the current job -// being processed has changed or when progress has changed. +// being processed has changed. //------------------------------------------------------------------------------------ -- (void)updateCurrentJobUI +- (void)updateCurrentJobDescription { - hb_state_t s; - hb_job_t * job = nil; - - if (fHandle) - { - hb_get_state2( fHandle, &s ); - job = hb_current_job(fHandle); - } + hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; if (job) { - if (fLastKnownCurrentJob != job) + HBJob * currentJob = [HBJob jobWithJob: job]; + switch (job->pass) { - HBJob * currentJob = [HBJob jobWithJob: job]; - - switch (job->pass) - { - case -1: // Subtitle scan - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: NO - withVideoInfo: NO - withx264Info: NO - withAudioInfo: NO - withSubtitleInfo: YES]]; - break; - - case 1: // video 1st pass - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: NO - withSubtitleInfo: NO]]; - break; - - case 0: // single pass - case 2: // video 2nd pass + audio - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - break; + case -1: // Subtitle scan + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: NO + withVideoInfo: NO + withx264Info: NO + withAudioInfo: NO + withSubtitleInfo: YES]]; + break; - default: // unknown - [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO - withTitle: YES - withPassName: YES - withFormatInfo: NO - withDestination: NO - withPictureInfo: YES - withVideoInfo: YES - withx264Info: YES - withAudioInfo: YES - withSubtitleInfo: YES]]; - } - - [self showCurrentJobPane:YES]; - [fJobIconView setImage: [NSImage imageNamed:@"JobLarge"]]; + case 1: // video 1st pass + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: NO + withSubtitleInfo: NO]]; + break; + + case 0: // single pass + case 2: // video 2nd pass + audio + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: YES + withSubtitleInfo: YES]]; + break; + + default: // unknown + [fJobDescTextField setAttributedStringValue: + [currentJob attributedDescriptionWithHBHandle:fHandle + withIcon: NO + withTitle: YES + withPassName: YES + withFormatInfo: NO + withDestination: NO + withPictureInfo: YES + withVideoInfo: YES + withx264Info: YES + withAudioInfo: YES + withSubtitleInfo: YES]]; } - - NSString * statusMsg = [self progressStatusStringForJob:job state:&s]; - NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:&s]; - if ([timeMsg length] > 0) - statusMsg = [NSString stringWithFormat:@"%@ - %@", statusMsg, timeMsg]; - [fProgressTextField setStringValue:statusMsg]; - [self updateProgressBarWithState:&s]; } + else - { - [fJobDescTextField setStringValue:NSLocalizedString(@"No job processing", nil)]; + [fJobDescTextField setStringValue: @"No encodes pending"]; +} - [self showCurrentJobPane:NO]; - [fProgressBar stopAnimation:nil]; // just in case in was animating - } - - fLastKnownCurrentJob = job; +//------------------------------------------------------------------------------------ +// Refresh the UI in the current job pane. Should be called whenever the current job +// being processed has changed or when progress has changed. +//------------------------------------------------------------------------------------ +- (void)updateCurrentJobProgress +{ + hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; + hb_state_t s; + hb_get_state2( fHandle, &s ); + [self updateProgressTextForJob: job state: &s]; + [self updateProgressBarWithState:&s]; } //------------------------------------------------------------------------------------ @@ -1226,38 +1435,74 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [self updateQueueCountField]; } +#pragma mark - +#pragma mark Actions + //------------------------------------------------------------------------------------ -// Deletes the selected job from HB and the queue UI +// Deletes the selected jobs from HB and the queue UI //------------------------------------------------------------------------------------ -- (IBAction)removeSelectedJob: (id)sender +- (IBAction)removeSelectedJobGroups: (id)sender { if (!fHandle) return; - int row = [sender selectedRow]; - if (row != -1) + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + if (row != NSNotFound) { -#if HB_UNI_QUEUE - if (row == 0) + while (row != NSNotFound) { - [self cancelCurrentJob:sender]; + HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; + switch ([jobGroup status]) + { + case HBStatusComplete: + case HBStatusCanceled: + [fCompleted removeObject: jobGroup]; + break; + case HBStatusWorking: + [self cancelCurrentJob: sender]; + break; + case HBStatusPending: + hb_job_t * job = [[jobGroup jobAtIndex: 0] job]; + hb_rem_group( fHandle, job ); + break; + case HBStatusNone: + break; + } + + row = [selectedRows indexGreaterThanIndex: row]; } - else + + [self hblibJobListChanged]; + } +} + +//------------------------------------------------------------------------------------ +// Reveals the file icons in the Finder of the selected job groups. +//------------------------------------------------------------------------------------ +- (IBAction)revealSelectedJobGroups: (id)sender +{ + if (!fHandle) return; + + NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes]; + int row = [selectedRows firstIndex]; + if (row != NSNotFound) + { + while (row != NSNotFound) { - row--; - hb_rem_group( fHandle, hb_group( fHandle, row ) ); + HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; + if ([[jobGroup path] length]) + [[NSWorkspace sharedWorkspace] selectFile:[jobGroup path] inFileViewerRootedAtPath:nil]; + + row = [selectedRows indexGreaterThanIndex: row]; } -#else - HBJobGroup * jobGroup = [fOutlineView itemAtRow: row]; - hb_job_t * job = [[jobGroup jobAtIndex: 0] job]; - hb_rem_group( fHandle, job ); -#endif - [self updateQueueUI]; - } + } } //------------------------------------------------------------------------------------ -// Prompts user if the want to cancel encoding of current job. If so, doCancelCurrentJob -// gets called. +// Calls HBController Cancel: which displays an alert asking user if they want to +// cancel encoding of current job. cancelCurrentJob: returns immediately after posting +// the alert. Later, when the user acknowledges the alert, HBController will call +// hblib to cancel the job. //------------------------------------------------------------------------------------ - (IBAction)cancelCurrentJob: (id)sender { @@ -1297,6 +1542,107 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe hb_pause (fHandle); } +#pragma mark - +#pragma mark Synchronizing with hblib + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's current job has changed +//------------------------------------------------------------------------------------ +- (void)currentJobGroupChanged: (hb_job_t *) currentJob +{ + if (fCurrentJobGroup && [fCurrentJobGroup status] != HBStatusCanceled) + [self addCompletedJobGroup: fCurrentJobGroup]; + [self setCurrentJobGroupFromJob: currentJob]; + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + [self showCurrentJobPane: fCurrentJobGroup != nil]; + if (fCurrentJobGroup) + [self startAnimatingCurrentJobGroupInQueue]; + else + [self stopAnimatingCurrentJobGroupInQueue]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hb_stop is about to be called. This signals us that +// the current job is going to be canceled and deleted. This is somewhat of a hack to +// let HBQueueController know when a job group has been cancelled. Otherwise, we'd +// have no way of knowing if a job was canceled or completed sucessfully. +//------------------------------------------------------------------------------------ +- (void)hblibWillStop +{ + if (fCurrentJobGroup) + [fCurrentJobGroup setStatus: HBStatusCanceled]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's job list has been modified +//------------------------------------------------------------------------------------ +- (void)hblibJobListChanged +{ + // This message is received from HBController after it has added a job group to + // hblib's job list. It is also received from self when a job group is deleted by + // the user. + [self updateQueueUI]; +} + +//------------------------------------------------------------------------------------ +// Notifies HBQueueController that hblib's state has changed +//------------------------------------------------------------------------------------ +- (void)hblibStateChanged: (hb_state_t &)state +{ + // First check to see if hblib has moved on to another job. We get no direct + // message when this happens, so we have to detect it ourself. The new job could + // be either just the next job in the current group, or the start of a new group. + if (fLastKnownCurrentJob != hb_current_job(fHandle)) + { + hb_job_t * currentJob = hb_current_job(fHandle); + if (!currentJob || currentJob->sequence_id == 0) // start of a new group + { + [self currentJobGroupChanged: currentJob]; + [self hblibJobListChanged]; + } + else + { + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + } + + fLastKnownCurrentJob = currentJob; + } + + switch( state.state ) + { + case HB_STATE_WORKING: + { + [self updateCurrentJobProgress]; + [self startAnimatingCurrentJobGroupInQueue]; + break; + } + + case HB_STATE_MUXING: + { + [self updateCurrentJobProgress]; + break; + } + + case HB_STATE_PAUSED: + { + [self updateCurrentJobProgress]; + [self stopAnimatingCurrentJobGroupInQueue]; + break; + } + + case HB_STATE_WORKDONE: + { + // HB_STATE_WORKDONE means that hblib has finished processing all the jobs + // in *its* queue. This message is NOT sent as each individual job is + // completed. + } + + } + +} + #if HB_OUTLINE_METRIC_CONTROLS static float spacingWidth = 3.0; - (IBAction)imageSpacingChanged: (id)sender; @@ -1351,36 +1697,36 @@ static float spacingWidth = 3.0; if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier]) { toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - + // Set the text label to be displayed in the toolbar and customization palette - [toolbarItem setLabel: @"Start"]; - [toolbarItem setPaletteLabel: @"Start/Cancel"]; - - // Set up a reasonable tooltip, and image - [toolbarItem setToolTip: @"Start Encoding"]; - [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: @selector(toggleStartCancel:)]; - } + [toolbarItem setLabel: @"Start"]; + [toolbarItem setPaletteLabel: @"Start/Cancel"]; + + // Set up a reasonable tooltip, and image + [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; + + // Tell the item what message to send when it is clicked + [toolbarItem setTarget: self]; + [toolbarItem setAction: @selector(toggleStartCancel:)]; + } if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier]) { toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease]; - + // Set the text label to be displayed in the toolbar and customization palette - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setPaletteLabel: @"Pause/Resume"]; - - // Set up a reasonable tooltip, and image - [toolbarItem setToolTip: @"Pause Encoding"]; - [toolbarItem setImage: [NSImage imageNamed: @"Pause"]]; - - // Tell the item what message to send when it is clicked - [toolbarItem setTarget: self]; - [toolbarItem setAction: @selector(togglePauseResume:)]; - } + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setPaletteLabel: @"Pause/Resume"]; + + // Set up a reasonable tooltip, and image + [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setImage: [NSImage imageNamed: @"Pause"]]; + + // Tell the item what message to send when it is clicked + [toolbarItem setTarget: self]; + [toolbarItem setAction: @selector(togglePauseResume:)]; + } return toolbarItem; } @@ -1411,10 +1757,10 @@ static float spacingWidth = 3.0; return [NSArray arrayWithObjects: HBQueueStartCancelToolbarIdentifier, HBQueuePauseResumeToolbarIdentifier, - NSToolbarCustomizeToolbarItemIdentifier, - NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarCustomizeToolbarItemIdentifier, + NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, - NSToolbarSeparatorItemIdentifier, + NSToolbarSeparatorItemIdentifier, nil]; } @@ -1439,26 +1785,26 @@ static float spacingWidth = 3.0; { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Stop"]]; - [toolbarItem setLabel: @"Stop"]; - [toolbarItem setToolTip: @"Stop Encoding"]; + [toolbarItem setLabel: @"Stop"]; + [toolbarItem setToolTip: @"Stop Encoding"]; } else if (hb_count(fHandle) > 0) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } else { enable = NO; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Start"]; - [toolbarItem setToolTip: @"Start Encoding"]; + [toolbarItem setLabel: @"Start"]; + [toolbarItem setToolTip: @"Start Encoding"]; } - } + } if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier]) { @@ -1466,27 +1812,27 @@ static float spacingWidth = 3.0; { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Play"]]; - [toolbarItem setLabel: @"Resume"]; - [toolbarItem setToolTip: @"Resume Encoding"]; + [toolbarItem setLabel: @"Resume"]; + [toolbarItem setToolTip: @"Resume Encoding"]; } else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING)) { enable = YES; [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } else { enable = NO; [toolbarItem setImage:[NSImage imageNamed: @"Pause"]]; - [toolbarItem setLabel: @"Pause"]; - [toolbarItem setToolTip: @"Pause Encoding"]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setToolTip: @"Pause Encoding"]; } - } + } - return enable; + return enable; } #pragma mark - @@ -1512,7 +1858,6 @@ static float spacingWidth = 3.0; // Don't allow autoresizing of main column, else the "delete" column will get // pushed out of view. [fOutlineView setAutoresizesOutlineColumn: NO]; - [fOutlineView setIndentationPerLevel:21]; #if HB_OUTLINE_METRIC_CONTROLS [fIndentation setHidden: NO]; @@ -1539,31 +1884,31 @@ static float spacingWidth = 3.0; - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex { - unsigned index = [indexSet lastIndex]; - unsigned aboveInsertIndexCount = 0; - - while (index != NSNotFound) - { - unsigned removeIndex; - - if (index >= insertIndex) - { - removeIndex = index + aboveInsertIndexCount; - aboveInsertIndexCount++; - } - else - { - removeIndex = index; - insertIndex--; - } - - id object = [[array objectAtIndex:removeIndex] retain]; - [array removeObjectAtIndex:removeIndex]; - [array insertObject:object atIndex:insertIndex]; - [object release]; - - index = [indexSet indexLessThanIndex:index]; - } + unsigned index = [indexSet lastIndex]; + unsigned aboveInsertIndexCount = 0; + + while (index != NSNotFound) + { + unsigned removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[array objectAtIndex:removeIndex] retain]; + [array removeObjectAtIndex:removeIndex]; + [array insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + } } #pragma mark - @@ -1586,6 +1931,18 @@ static float spacingWidth = 3.0; return YES; } +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item +{ + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. +#if HB_QUEUE_DRAGGING + // Don't autoexpand while dragging, since we can't drop into the items + return ![(HBQueueOutlineView*)outlineView isDragging]; +#else + return YES; +#endif +} + - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { // Our outline view has no levels, so number of children will be zero for all @@ -1636,8 +1993,26 @@ static float spacingWidth = 3.0; - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { + // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer + // using the image portion of the cell so we could switch back to a regular NSTextFieldCell. + if ([[tableColumn identifier] isEqualToString:@"desc"]) return [item attributedDescriptionWithHBHandle: fHandle]; + else if ([[tableColumn identifier] isEqualToString:@"icon"]) + { + switch ([(HBJobGroup*)item status]) + { + case HBStatusComplete: + return [NSImage imageNamed:@"EncodeComplete"]; + break; + case HBStatusWorking: + return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]]; + break; + default: + return [NSImage imageNamed:@"JobSmall"]; + break; + } + } else return @""; } @@ -1652,22 +2027,39 @@ static float spacingWidth = 3.0; [cell setImageSpacing: theSize]; #endif + // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer + // using the image portion of the cell so we could switch back to a regular NSTextFieldCell. + // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part - [cell setImage:[NSImage imageNamed:@"JobSmall"]]; + [cell setImage:nil]; } - else if ([[tableColumn identifier] isEqualToString:@"delete"]) + else if ([[tableColumn identifier] isEqualToString:@"action"]) { - // The Delete action can only be applied for group items, not indivdual jobs. [cell setEnabled: YES]; BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView); - if (highlighted) + if ([(HBJobGroup*)item status] == HBStatusComplete) { - [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; - [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + [cell setAction: @selector(revealSelectedJobGroups:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"RevealHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Reveal"]]; } else - [cell setImage:[NSImage imageNamed:@"Delete"]]; + { + [cell setAction: @selector(removeSelectedJobGroups:)]; + if (highlighted) + { + [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]]; + [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]]; + } + else + [cell setImage:[NSImage imageNamed:@"Delete"]]; + } } } @@ -1691,10 +2083,19 @@ static float spacingWidth = 3.0; #if HB_QUEUE_DRAGGING - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard { + // Dragging is only allowed of the pending items. + NSEnumerator * e = [items objectEnumerator]; + HBJobGroup * group; + while ( (group = [e nextObject]) ) + { + if ([group status] != HBStatusPending) + return NO; + } + // Don't retain since this is just holding temporaral drag information, and it is //only used during a drag! We could put this in the pboard actually. fDraggedNodes = items; - + // Provide data for our custom type, and simple NSStrings. [pboard declareTypes:[NSArray arrayWithObjects: HBQueuePboardType, nil] owner:self]; @@ -1708,11 +2109,25 @@ static float spacingWidth = 3.0; #if HB_QUEUE_DRAGGING - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(int)index { - // Add code here to validate the drop - BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; + // Don't allow dropping ONTO an item since they can't really contain any children. + BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; if (isOnDropTypeProposal) return NSDragOperationNone; - + + // Don't allow dropping INTO an item since they can't really contain any children. + if (item != nil) + { + index = [fOutlineView rowForItem: item] + 1; + item = nil; + } + + // Prevent dragging into the completed or current job. + int firstPendingIndex = [fCompleted count]; + if (fCurrentJobGroup) + firstPendingIndex++; + index = MAX (index, firstPendingIndex); + + [outlineView setDropItem:item dropChildIndex:index]; return NSDragOperationGeneric; } #endif @@ -1739,4 +2154,5 @@ static float spacingWidth = 3.0; } #endif + @end diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj index 2cf6bc8c4..5c1b84be3 100644 --- a/macosx/HandBrake.xcodeproj/project.pbxproj +++ b/macosx/HandBrake.xcodeproj/project.pbxproj @@ -152,6 +152,17 @@ E37C89470C83989F00C1B919 /* HBQueueController.mm in Sources */ = {isa = PBXBuildFile; fileRef = E37C89450C83989F00C1B919 /* HBQueueController.mm */; }; E37C89480C83989F00C1B919 /* HBQueueController.h in Headers */ = {isa = PBXBuildFile; fileRef = E37C89460C83989F00C1B919 /* HBQueueController.h */; }; E37C894F0C8398CF00C1B919 /* Queue.nib in Resources */ = {isa = PBXBuildFile; fileRef = E37C894D0C8398CF00C1B919 /* Queue.nib */; }; + E3997A2C0CAB58BC00287239 /* EncodeWorking3.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A260CAB58BC00287239 /* EncodeWorking3.png */; }; + E3997A2D0CAB58BC00287239 /* EncodeWorking4.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A270CAB58BC00287239 /* EncodeWorking4.png */; }; + E3997A2E0CAB58BC00287239 /* EncodeWorking1.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A280CAB58BC00287239 /* EncodeWorking1.png */; }; + E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A290CAB58BC00287239 /* EncodeWorking0.png */; }; + E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A2A0CAB58BC00287239 /* EncodeWorking2.png */; }; + E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */ = {isa = PBXBuildFile; fileRef = E3997A2B0CAB58BC00287239 /* EncodeWorking5.png */; }; + E3C844F60CA6B3F90013B683 /* RevealPressed.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F20CA6B3F90013B683 /* RevealPressed.png */; }; + E3C844F70CA6B3F90013B683 /* RevealHighlightPressed.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F30CA6B3F90013B683 /* RevealHighlightPressed.png */; }; + E3C844F80CA6B3F90013B683 /* RevealHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F40CA6B3F90013B683 /* RevealHighlight.png */; }; + E3C844F90CA6B3F90013B683 /* Reveal.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F50CA6B3F90013B683 /* Reveal.png */; }; + E3C845870CA6E9080013B683 /* EncodeComplete.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C845860CA6E9080013B683 /* EncodeComplete.png */; }; EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; FC8519500C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; }; @@ -317,6 +328,17 @@ E37C89450C83989F00C1B919 /* HBQueueController.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = HBQueueController.mm; sourceTree = ""; }; E37C89460C83989F00C1B919 /* HBQueueController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HBQueueController.h; sourceTree = ""; }; E37C894E0C8398CF00C1B919 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/Queue.nib; sourceTree = ""; }; + E3997A260CAB58BC00287239 /* EncodeWorking3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking3.png; sourceTree = ""; }; + E3997A270CAB58BC00287239 /* EncodeWorking4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking4.png; sourceTree = ""; }; + E3997A280CAB58BC00287239 /* EncodeWorking1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking1.png; sourceTree = ""; }; + E3997A290CAB58BC00287239 /* EncodeWorking0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking0.png; sourceTree = ""; }; + E3997A2A0CAB58BC00287239 /* EncodeWorking2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking2.png; sourceTree = ""; }; + E3997A2B0CAB58BC00287239 /* EncodeWorking5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeWorking5.png; sourceTree = ""; }; + E3C844F20CA6B3F90013B683 /* RevealPressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealPressed.png; sourceTree = ""; }; + E3C844F30CA6B3F90013B683 /* RevealHighlightPressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealHighlightPressed.png; sourceTree = ""; }; + E3C844F40CA6B3F90013B683 /* RevealHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealHighlight.png; sourceTree = ""; }; + E3C844F50CA6B3F90013B683 /* Reveal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Reveal.png; sourceTree = ""; }; + E3C845860CA6E9080013B683 /* EncodeComplete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeComplete.png; sourceTree = ""; }; EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; }; FC85194C0C59A02C0073812C /* denoise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = denoise.c; path = ../libhb/denoise.c; sourceTree = SOURCE_ROOT; }; FC85194D0C59A02C0073812C /* deinterlace.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deinterlace.c; path = ../libhb/deinterlace.c; sourceTree = SOURCE_ROOT; }; @@ -567,6 +589,17 @@ E37167830C92F6180072B384 /* JobPassSecondSmall.png */, E37167840C92F6180072B384 /* JobPassSubtitleSmall.png */, E37167A70C92FAA50072B384 /* JobPassUnknownSmall.png */, + E3C844F50CA6B3F90013B683 /* Reveal.png */, + E3C844F20CA6B3F90013B683 /* RevealPressed.png */, + E3C844F40CA6B3F90013B683 /* RevealHighlight.png */, + E3C844F30CA6B3F90013B683 /* RevealHighlightPressed.png */, + E3C845860CA6E9080013B683 /* EncodeComplete.png */, + E3997A290CAB58BC00287239 /* EncodeWorking0.png */, + E3997A280CAB58BC00287239 /* EncodeWorking1.png */, + E3997A2A0CAB58BC00287239 /* EncodeWorking2.png */, + E3997A260CAB58BC00287239 /* EncodeWorking3.png */, + E3997A270CAB58BC00287239 /* EncodeWorking4.png */, + E3997A2B0CAB58BC00287239 /* EncodeWorking5.png */, ); path = icons; sourceTree = ""; @@ -770,6 +803,17 @@ A2D7AD6D0C998AD30082CA33 /* pref-picture.tiff in Resources */, A2D7AD6E0C998AD30082CA33 /* Queue.tiff in Resources */, A2D7AD6F0C998AD30082CA33 /* Source.tiff in Resources */, + E3C844F60CA6B3F90013B683 /* RevealPressed.png in Resources */, + E3C844F70CA6B3F90013B683 /* RevealHighlightPressed.png in Resources */, + E3C844F80CA6B3F90013B683 /* RevealHighlight.png in Resources */, + E3C844F90CA6B3F90013B683 /* Reveal.png in Resources */, + E3C845870CA6E9080013B683 /* EncodeComplete.png in Resources */, + E3997A2C0CAB58BC00287239 /* EncodeWorking3.png in Resources */, + E3997A2D0CAB58BC00287239 /* EncodeWorking4.png in Resources */, + E3997A2E0CAB58BC00287239 /* EncodeWorking1.png in Resources */, + E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */, + E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */, + E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macosx/icons/EncodeComplete.png b/macosx/icons/EncodeComplete.png new file mode 100644 index 0000000000000000000000000000000000000000..503e52c8e781c64e0b1d281f273a8c1c6b866218 GIT binary patch literal 314 zcmV-A0mc4_P)q=X8Gu)Fz&Y#xTVJigFa+5Eyf*KBv-SUz?+=m1Pz=E4XE=_y;PM{?I`36u zgb7i$A9%a-|EJ#{{u>>WLYKpCK;7;9|BIeZ2eYFudVp=8{ICt1;n)qh{q;InQ}X2i zpzS;UfBWA8<5;_@E$K=eOn_5b(3UtrCzem?(id0Y{j zW|Tn0a7F)vrvER0KKoyCBL%1JBqbzVsgbHlnJSryWDEcR0RR6300t_ed;lF14*&oF M07*qoM6N<$f+s(VcK`qY literal 0 HcmV?d00001 diff --git a/macosx/icons/EncodeWorking0.png b/macosx/icons/EncodeWorking0.png new file mode 100644 index 0000000000000000000000000000000000000000..200606b54fbb7c320a645ff6472ce7992f541c5c GIT binary patch literal 350 zcmV-k0iphhP)| z!j6GKOiUmVh#4dT!2u8ma7<1jCqW{^K1#N>D+pNL``_B+&!05yA`yxHLEl-Ii5%vc z6qB>$@%;tX_=tTKxD=_Z#+59ZZ3x8R1O*6Wqlc4_#|E|!jwRBy?N_c#Y3+6u+0NWf z-p4TloKGu4UMD7j?`A4e?TnBuLQX5dkZ>xI>VE6Q5Z669tpJnWr{dUY*^!S;u}fk6 ztl^T=6Cj8|W~~B<+13-#BLLc@UH>+8s2X#QphE- literal 0 HcmV?d00001 diff --git a/macosx/icons/EncodeWorking1.png b/macosx/icons/EncodeWorking1.png new file mode 100644 index 0000000000000000000000000000000000000000..b3e0749bcf9f571306ee093455388a936cd62b3c GIT binary patch literal 361 zcmV-v0ha!WP)+72)BpUXDqjF?sc2diF|2d?|lexmZ*g%IOF-m;++9Qsn0 zN=aBGU}+*9rd1`catZ+TkxZc3JuZS#ay=G%E`*#CK$A=^x0|`rfpCGX$teMJ+77VO zOp1dl03|MzoKAo>&>R@hPJoqwBNMqUz>M;2DwPswhy~Lq42UVAp=Ml=Ps?qhjnReJ zL|8LYZfC?KH^x|hNOebvNpF2fe|gG(`I)}(2LJ#7|NjF3QW@zDE-MF#00000NkvXX Hu0mjfrlXDG literal 0 HcmV?d00001 diff --git a/macosx/icons/EncodeWorking2.png b/macosx/icons/EncodeWorking2.png new file mode 100644 index 0000000000000000000000000000000000000000..11fef318842e10c3c9b4854d5c301cebbea6a5ba GIT binary patch literal 347 zcmV-h0i^zkP)zsfi zID*6h9Dzb2DjJDyl{z{l6~z`h%B|E}Zu!}tF>5eZ!oK7s@9#JBy?GNz3jWde;3aBk z+aD@y?lrC^H^nAKt`H!acQvb@G{{_^lB328Kw?x$HLXc$BE{QGchX_<3_^_q^0>Bp z@-b8{*nlqE&o{Z#F#zzhNeW$|dDAnt3qns0z>sVriKX*OT6YW$8z)B2`t;-gOxh-p zMa$TnZsWud>(lcUzy&D;@KLZnG@w&R!8b$($LX$o9OCOUQb_abbAEhAcahwsVTo8K tXmEW%y^B=JDwpz^e$Xob009600|3zo>B%7MOF94m002ovPDHLkV1mx}k1hZJ literal 0 HcmV?d00001 diff --git a/macosx/icons/EncodeWorking3.png b/macosx/icons/EncodeWorking3.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2187d019a6c74edfc863573741f863656b1b41 GIT binary patch literal 350 zcmV-k0iphhP)3^qe$c3Mf3nu~G9BYDgA zJ1@J^?X1A?eI>7P90BwB#n$zZy6@{jN}3AQJ!^6TmShP!QEVgZW==lD5ZO6uasoDO z32e~`y8&XD`P7t0;E#YzHXsF;XRp1 zyjn{%y~u8um(Dk5+;tKp;ZpXas|2rY)3*aD?}+gdaIxtZULc-lW+lcV`IK#EKF$q5 zt;o$92j-sGcRPlF{%kOIJBfs2k@KQXL>0t=sR$5B3CIjs_i1Fnk5nX!O#;rrR0Jrk z0PHYxMQl14FF~6_ig^jN1%fD(axuzR0BzBp?rSZmhzO*LV+aT-U`+dWM6{ogr?Q_8 zBeM;5p3xp8duV)Gcn7ID%5C7zQU1%%^o2hG009600{}`P=?!*u&&&V-002ovPDHLk FV1h~*j%NS> literal 0 HcmV?d00001 diff --git a/macosx/icons/EncodeWorking5.png b/macosx/icons/EncodeWorking5.png new file mode 100644 index 0000000000000000000000000000000000000000..33fae2c28a0b50e5c3f2d5ebe3774948784f7cec GIT binary patch literal 357 zcmV-r0h<1aP)YMw;cn({&X_W;{fDt0oSOk2w~A zO4E5=8y13SKqgs$v+*;>0bo!w6@ds&5(hw9&`c~_x52hFV6FubkphuLM5KUPM45ym zqKr`(s>Ub_l^bK#A{DK4k!o4xT7IS<>;(V-|NjF3@i^(>PQ0K;00000NkvXXu0mjf DMrw&g literal 0 HcmV?d00001 diff --git a/macosx/icons/Reveal.png b/macosx/icons/Reveal.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc11758904ad5b1081e03f6254fd35fba489df2 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSNH#i3J&% z$MgMG z@H~UA2Z5ea9Z?em1$tzgh1PV~bDr{cyx635ZhgOjWYeqA1^R`DcG!eYcp3HUppwcv zS(i`u?@x+}bNuF5Y%%Gq-H)ZhyZ;|jHv_ux|9@tN&(45$isJURhTj4@?*D&ghIgd_navBoeg$$FJYD@<);T3K0RY5^ BWJ>@5 literal 0 HcmV?d00001 -- 2.40.0