diff options
author | Damiano Galassi <[email protected]> | 2015-10-20 18:55:08 +0200 |
---|---|---|
committer | Damiano Galassi <[email protected]> | 2015-10-20 18:55:08 +0200 |
commit | 7f50c27989a63e87d2f17c6495d29e136ccfd837 (patch) | |
tree | 280036131fa6d7dd1a11ef84a55c4dfad3e96aca /macosx | |
parent | c32d5236135c6be0b4987fb74de511e3332d7396 (diff) |
MacGui: added undo/redo support to the video, picture, filters, chapters and range parts of HBJob.
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/English.lproj/ChaptersTitles.xib | 103 | ||||
-rw-r--r-- | macosx/HBChapter.h | 5 | ||||
-rw-r--r-- | macosx/HBChapter.m | 18 | ||||
-rw-r--r-- | macosx/HBChapterTitlesController.m | 45 | ||||
-rw-r--r-- | macosx/HBController.m | 25 | ||||
-rw-r--r-- | macosx/HBFilters.h | 2 | ||||
-rw-r--r-- | macosx/HBFilters.m | 55 | ||||
-rw-r--r-- | macosx/HBJob.h | 2 | ||||
-rw-r--r-- | macosx/HBJob.m | 62 | ||||
-rw-r--r-- | macosx/HBPicture.h | 2 | ||||
-rw-r--r-- | macosx/HBPicture.m | 162 | ||||
-rw-r--r-- | macosx/HBRange.h | 2 | ||||
-rw-r--r-- | macosx/HBRange.m | 55 | ||||
-rw-r--r-- | macosx/HBTitle.m | 4 | ||||
-rw-r--r-- | macosx/HBVideo.h | 2 | ||||
-rw-r--r-- | macosx/HBVideo.m | 70 |
16 files changed, 508 insertions, 106 deletions
diff --git a/macosx/English.lproj/ChaptersTitles.xib b/macosx/English.lproj/ChaptersTitles.xib index c7a0c3b03..95a7ecd87 100644 --- a/macosx/English.lproj/ChaptersTitles.xib +++ b/macosx/English.lproj/ChaptersTitles.xib @@ -69,14 +69,14 @@ </binding> </connections> </button> - <scrollView horizontalLineScroll="19" horizontalPageScroll="0.0" verticalLineScroll="19" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="lqY-aE-MZi"> + <scrollView horizontalLineScroll="18" horizontalPageScroll="0.0" verticalLineScroll="18" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="lqY-aE-MZi"> <rect key="frame" x="20" y="20" width="886" height="266"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <clipView key="contentView" id="3Pi-zz-fFg"> <rect key="frame" x="1" y="0.0" width="884" height="265"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" headerView="0rK-Rs-NTb" id="InF-gR-Lia"> + <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="16" headerView="0rK-Rs-NTb" viewBased="YES" id="InF-gR-Lia"> <rect key="frame" x="0.0" y="0.0" width="884" height="248"/> <autoresizingMask key="autoresizingMask"/> <animations/> @@ -96,6 +96,38 @@ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> </textFieldCell> <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + <prototypeCellViews> + <tableCellView id="xTN-ym-UFK"> + <rect key="frame" x="1" y="1" width="76" height="17"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="9mL-8S-7Nl"> + <rect key="frame" x="0.0" y="3" width="84" height="14"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <animations/> + <textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="G9p-Cg-wyC"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <binding destination="xTN-ym-UFK" name="value" keyPath="objectValue.index" id="4K5-re-kOW"/> + </connections> + </textField> + </subviews> + <animations/> + <connections> + <outlet property="textField" destination="9mL-8S-7Nl" id="eLt-na-ejF"/> + </connections> + </tableCellView> + </prototypeCellViews> + <connections> + <binding destination="Gv0-qM-nu4" name="value" keyPath="arrangedObjects.index" id="ehv-7Z-dS4"> + <dictionary key="options"> + <bool key="NSConditionallySetsEditable" value="YES"/> + </dictionary> + </binding> + </connections> </tableColumn> <tableColumn identifier="duration" editable="NO" width="84" minWidth="10" maxWidth="3.4028234663852886e+38" id="QVB-Cw-DJD" userLabel="Timestamp"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Duration"> @@ -109,6 +141,38 @@ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> </textFieldCell> <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + <prototypeCellViews> + <tableCellView id="AVw-G2-Zyi"> + <rect key="frame" x="80" y="1" width="84" height="17"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="4HO-kO-Qtr"> + <rect key="frame" x="0.0" y="3" width="84" height="14"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <animations/> + <textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Vmq-zJ-NEA"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <binding destination="AVw-G2-Zyi" name="value" keyPath="objectValue.duration" id="n18-Pn-gsL"/> + </connections> + </textField> + </subviews> + <animations/> + <connections> + <outlet property="textField" destination="4HO-kO-Qtr" id="iex-iJ-sA2"/> + </connections> + </tableCellView> + </prototypeCellViews> + <connections> + <binding destination="Gv0-qM-nu4" name="value" keyPath="arrangedObjects.duration" id="YyF-q7-tAL"> + <dictionary key="options"> + <bool key="NSConditionallySetsEditable" value="YES"/> + </dictionary> + </binding> + </connections> </tableColumn> <tableColumn identifier="title" width="715" minWidth="77.217290000000006" maxWidth="1000" id="Z6H-lJ-ipr"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Title"> @@ -122,15 +186,41 @@ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> </textFieldCell> <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + <prototypeCellViews> + <tableCellView id="yZu-QP-dyq"> + <rect key="frame" x="167" y="1" width="715" height="17"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="rpj-MP-nnZ"> + <rect key="frame" x="0.0" y="3" width="715" height="14"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <animations/> + <textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Table View Cell" id="UjY-nz-uSt"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <binding destination="yZu-QP-dyq" name="value" keyPath="objectValue.title" id="vOK-2g-Dwj"/> + <outlet property="delegate" destination="-2" id="eWn-gQ-785"/> + </connections> + </textField> + </subviews> + <animations/> + <connections> + <outlet property="textField" destination="rpj-MP-nnZ" id="fwU-RB-S1Z"/> + </connections> + </tableCellView> + </prototypeCellViews> </tableColumn> </tableColumns> <connections> + <binding destination="Gv0-qM-nu4" name="content" keyPath="arrangedObjects" id="XCD-5A-hAR"/> <binding destination="-2" name="enabled" keyPath="self.job" id="leT-dv-Prc"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> </binding> - <outlet property="dataSource" destination="-2" id="7rd-Et-5BT"/> <outlet property="delegate" destination="-2" id="DOu-XY-6Tq"/> </connections> </tableView> @@ -157,8 +247,13 @@ </scrollView> </subviews> <animations/> - <point key="canvasLocation" x="131" y="343"/> + <point key="canvasLocation" x="268" y="396"/> </view> <userDefaultsController representsSharedInstance="YES" id="coy-s6-QLx"/> + <arrayController objectClassName="HBChapter" editable="NO" id="Gv0-qM-nu4"> + <connections> + <binding destination="-2" name="contentArray" keyPath="self.chapterTitles" id="QE9-1S-jsv"/> + </connections> + </arrayController> </objects> </document> diff --git a/macosx/HBChapter.h b/macosx/HBChapter.h index c72243026..caee0be8b 100644 --- a/macosx/HBChapter.h +++ b/macosx/HBChapter.h @@ -10,10 +10,13 @@ NS_ASSUME_NONNULL_BEGIN @interface HBChapter : NSObject <NSSecureCoding, NSCopying> -- (instancetype)initWithTitle:(NSString *)title duration:(uint64_t)duration NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithTitle:(NSString *)title index:(NSUInteger)idx duration:(uint64_t)duration NS_DESIGNATED_INITIALIZER; @property (nonatomic, readwrite) NSString *title; @property (nonatomic, readonly) NSString *duration; +@property (nonatomic, readwrite) NSUInteger index; + +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; @end diff --git a/macosx/HBChapter.m b/macosx/HBChapter.m index 46d0a7602..c5f0ab081 100644 --- a/macosx/HBChapter.m +++ b/macosx/HBChapter.m @@ -11,11 +11,11 @@ - (instancetype)init { - self = [self initWithTitle:@"No Value" duration:0]; + self = [self initWithTitle:@"No Value" index:0 duration:0]; return self; } -- (instancetype)initWithTitle:(NSString *)title duration:(uint64_t)duration +- (instancetype)initWithTitle:(NSString *)title index:(NSUInteger)idx duration:(uint64_t)duration { NSParameterAssert(title); @@ -28,11 +28,23 @@ _duration = [NSString stringWithFormat:@"%02llu:%02llu:%02llu", hours, minutes, seconds]; _title = [title copy]; + _index = idx; } return self; } +#pragma mark - Properties + +- (void)setTitle:(NSString *)title +{ + if (![title isEqualToString:_title]) + { + [[self.undo prepareWithInvocationTarget:self] setTitle:_title]; + } + _title = title; +} + #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone @@ -62,6 +74,7 @@ encodeObject(_title); encodeObject(_duration); + encodeInteger(_index); } - (instancetype)initWithCoder:(NSCoder *)decoder @@ -72,6 +85,7 @@ { decodeObject(_title, NSString); decodeObject(_duration, NSString); + decodeInteger(_index); return self; } diff --git a/macosx/HBChapterTitlesController.m b/macosx/HBChapterTitlesController.m index 713f431fb..7f04a5139 100644 --- a/macosx/HBChapterTitlesController.m +++ b/macosx/HBChapterTitlesController.m @@ -31,54 +31,18 @@ { _job = job; self.chapterTitles = job.chapterTitles; - [self.table reloadData]; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView -{ - return self.chapterTitles.count; -} - -- (void)tableView:(NSTableView *)aTableView - setObjectValue:(id)anObject - forTableColumn:(NSTableColumn *)aTableColumn - row:(NSInteger)rowIndex -{ - if ([aTableColumn.identifier isEqualToString:@"title"]) - { - [(HBChapter *)self.chapterTitles[rowIndex] setTitle:anObject]; - } -} - -- (id)tableView:(NSTableView *)aTableView - objectValueForTableColumn:(NSTableColumn *)aTableColumn - row:(NSInteger)rowIndex -{ - if ([aTableColumn.identifier isEqualToString:@"index"]) - { - return [NSString stringWithFormat:@"%ld", rowIndex + 1]; - } - else if ([aTableColumn.identifier isEqualToString:@"duration"]) - { - return [(HBChapter *)self.chapterTitles[rowIndex] duration]; - } - else if ([aTableColumn.identifier isEqualToString:@"title"]) - { - return [(HBChapter *)self.chapterTitles[rowIndex] title]; - } - return @"__DATA ERROR__"; } /** * Method to edit the next chapter when the user presses Return. - * We queue the actino on the runloop to avoid interfering + * We queue the action on the runloop to avoid interfering * with the chain of events that handles the edit. */ - (void)controlTextDidEndEditing:(NSNotification *)notification { - NSTableView *chapterTable = [notification object]; - NSInteger column = [chapterTable editedColumn]; - NSInteger row = [chapterTable editedRow]; + NSTableView *chapterTable = self.table; + NSInteger column = 2; + NSInteger row = [self.table rowForView:[notification object]]; NSInteger textMovement; // Edit the cell in the next row, same column @@ -173,7 +137,6 @@ break; } - [self.table reloadData]; } } } diff --git a/macosx/HBController.m b/macosx/HBController.m index 9b8a68883..d4baf40f6 100644 --- a/macosx/HBController.m +++ b/macosx/HBController.m @@ -563,9 +563,15 @@ { [self removeJobObservers]; + // Clear the undo manager + [_job.undo removeAllActions]; + _job.undo = nil; + // Retain the new job _job = job; + job.undo = self.window.undoManager; + // Set the jobs info to the view controllers fPictureViewController.picture = job.picture; fPictureViewController.filters = job.filters; @@ -935,8 +941,9 @@ } else { - self.job = [[HBJob alloc] initWithTitle:title andPreset:self.currentPreset]; - self.job.destURL = [self destURLForJob:self.job]; + HBJob *job = [[HBJob alloc] initWithTitle:title andPreset:self.currentPreset]; + job.destURL = [self destURLForJob:job]; + self.job = job; } // If we are a stream type and a batch scan, grok the output file name from title->name upon title change @@ -998,9 +1005,17 @@ { // Deselect the currently selected Preset if there is one [fPresetsView deselect]; - // Change UI to show "Custom" settings are being used - self.job.presetName = NSLocalizedString(@"Custom", @""); - [self updateFileName]; + + // Update the preset and file name only if we are not + // undoing or redoing, because if so it's already stored + // in the undo manager. + NSUndoManager *undo = self.window.undoManager; + if (!(undo.isUndoing || undo.isRedoing)) + { + // Change UI to show "Custom" settings are being used + self.job.presetName = NSLocalizedString(@"Custom", @""); + [self updateFileName]; + } } #pragma mark - Queue progress diff --git a/macosx/HBFilters.h b/macosx/HBFilters.h index 4c8fec92b..6ad82529c 100644 --- a/macosx/HBFilters.h +++ b/macosx/HBFilters.h @@ -31,6 +31,8 @@ extern NSString * const HBFiltersChangedNotification; @property (nonatomic, readwrite) int deblock; @property (nonatomic, readwrite) BOOL grayscale; +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBFilters.m b/macosx/HBFilters.m index ecdf0f7ad..902f9b8ba 100644 --- a/macosx/HBFilters.m +++ b/macosx/HBFilters.m @@ -52,6 +52,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDetelecine:(NSString *)detelecine { + if (![detelecine isEqualToString:_detelecine]) + { + [[self.undo prepareWithInvocationTarget:self] setDetelecine:_detelecine]; + } if (detelecine) { _detelecine = [detelecine copy]; @@ -66,6 +70,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; // Override setter to avoid nil values. - (void)setDetelecineCustomString:(NSString *)detelecineCustomString { + if (![detelecineCustomString isEqualToString:_detelecineCustomString]) + { + [[self.undo prepareWithInvocationTarget:self] setDetelecineCustomString:_detelecineCustomString]; + } if (detelecineCustomString) { _detelecineCustomString = [detelecineCustomString copy]; @@ -80,6 +88,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDeinterlace:(NSString *)deinterlace { + if (![deinterlace isEqualToString:_deinterlace]) + { + [[self.undo prepareWithInvocationTarget:self] setDeinterlace:_deinterlace]; + } if (deinterlace) { _deinterlace = [deinterlace copy]; @@ -88,12 +100,20 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; { _deinterlace = @"off"; } - [self validateDeinterlacePreset]; + + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + [self validateDeinterlacePreset]; + } [self postChangedNotification]; } - (void)setDeinterlacePreset:(NSString *)deinterlacePreset { + if (![deinterlacePreset isEqualToString:_deinterlacePreset]) + { + [[self.undo prepareWithInvocationTarget:self] setDeinterlacePreset:_deinterlacePreset]; + } if (deinterlacePreset) { _deinterlacePreset = [deinterlacePreset copy]; @@ -103,7 +123,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; _deinterlacePreset = @"fast"; } - [self validateDeinterlacePreset]; + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + [self validateDeinterlacePreset]; + } [self postChangedNotification]; } @@ -124,6 +147,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDeinterlaceCustomString:(NSString *)deinterlaceCustomString { + if (![deinterlaceCustomString isEqualToString:_deinterlaceCustomString]) + { + [[self.undo prepareWithInvocationTarget:self] setDeinterlaceCustomString:_deinterlaceCustomString]; + } if (deinterlaceCustomString) { _deinterlaceCustomString = [deinterlaceCustomString copy]; @@ -138,6 +165,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDenoise:(NSString *)denoise { + if (![denoise isEqualToString:_denoise]) + { + [[self.undo prepareWithInvocationTarget:self] setDenoise:_denoise]; + } if (denoise) { _denoise = [denoise copy]; @@ -152,6 +183,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDenoisePreset:(NSString *)denoisePreset { + if (![denoisePreset isEqualToString:_denoisePreset]) + { + [[self.undo prepareWithInvocationTarget:self] setDenoisePreset:_denoisePreset]; + } if (denoisePreset) { _denoisePreset = [denoisePreset copy]; @@ -166,6 +201,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDenoiseTune:(NSString *)denoiseTune { + if (![denoiseTune isEqualToString:_denoiseTune]) + { + [[self.undo prepareWithInvocationTarget:self] setDenoiseTune:_denoiseTune]; + } if (denoiseTune) { _denoiseTune = [denoiseTune copy]; @@ -180,6 +219,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDenoiseCustomString:(NSString *)denoiseCustomString { + if (![denoiseCustomString isEqualToString:_denoiseCustomString]) + { + [[self.undo prepareWithInvocationTarget:self] setDenoiseCustomString:_denoiseCustomString]; + } if (denoiseCustomString) { _denoiseCustomString = [denoiseCustomString copy]; @@ -194,12 +237,20 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification"; - (void)setDeblock:(int)deblock { + if (deblock != _deblock) + { + [[self.undo prepareWithInvocationTarget:self] setDeblock:_deblock]; + } _deblock = deblock; [self postChangedNotification]; } - (void)setGrayscale:(BOOL)grayscale { + if (grayscale != _grayscale) + { + [[self.undo prepareWithInvocationTarget:self] setGrayscale:_grayscale]; + } _grayscale = grayscale; [self postChangedNotification]; } diff --git a/macosx/HBJob.h b/macosx/HBJob.h index 8c1ab6529..2766bac9a 100644 --- a/macosx/HBJob.h +++ b/macosx/HBJob.h @@ -75,6 +75,8 @@ typedef NS_ENUM(NSUInteger, HBJobState){ @property (nonatomic, readwrite) BOOL chaptersEnabled; @property (nonatomic, readonly) NSArray *chapterTitles; +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBJob.m b/macosx/HBJob.m index b8df8e2cd..f03520bac 100644 --- a/macosx/HBJob.m +++ b/macosx/HBJob.m @@ -92,8 +92,39 @@ NSString *HBChaptersChangedNotification = @"HBChaptersChangedNotification"; withObject:preset]; } +- (void)setUndo:(NSUndoManager *)undo +{ + _undo = undo; + [@[self.video, self.range, self.filters, self.picture, /*self.audio, self.subtitles*/] makeObjectsPerformSelector:@selector(setUndo:) + withObject:_undo]; + [self.chapterTitles makeObjectsPerformSelector:@selector(setUndo:) withObject:_undo]; +} + +- (void)setPresetName:(NSString *)presetName +{ + if (![presetName isEqualToString:_presetName]) + { + [[self.undo prepareWithInvocationTarget:self] setPresetName:_presetName]; + } + _presetName = [presetName copy]; +} + +- (void)setDestURL:(NSURL *)destURL +{ + if (![destURL isEqualTo:_destURL]) + { + [[self.undo prepareWithInvocationTarget:self] setDestURL:_destURL]; + } + _destURL = [destURL copy]; +} + - (void)setContainer:(int)container { + if (container != _container) + { + [[self.undo prepareWithInvocationTarget:self] setContainer:_container]; + } + _container = container; [self.audio containerChanged:container]; @@ -104,14 +135,45 @@ NSString *HBChaptersChangedNotification = @"HBChaptersChangedNotification"; [[NSNotificationCenter defaultCenter] postNotificationName:HBContainerChangedNotification object:self]; } +- (void)setAngle:(int)angle +{ + if (angle != _angle) + { + [[self.undo prepareWithInvocationTarget:self] setAngle:_angle]; + } + _angle = angle; +} + - (void)setTitle:(HBTitle *)title { _title = title; self.range.title = title; } +- (void)setMp4HttpOptimize:(BOOL)mp4HttpOptimize +{ + if (mp4HttpOptimize != _mp4HttpOptimize) + { + [[self.undo prepareWithInvocationTarget:self] setMp4HttpOptimize:_mp4HttpOptimize]; + } + _mp4HttpOptimize = mp4HttpOptimize; +} + +- (void)setMp4iPodCompatible:(BOOL)mp4iPodCompatible +{ + if (mp4iPodCompatible != _mp4iPodCompatible) + { + [[self.undo prepareWithInvocationTarget:self] setMp4iPodCompatible:_mp4iPodCompatible]; + } + _mp4iPodCompatible = mp4iPodCompatible; +} + - (void)setChaptersEnabled:(BOOL)chaptersEnabled { + if (chaptersEnabled != _chaptersEnabled) + { + [[self.undo prepareWithInvocationTarget:self] setChaptersEnabled:_chaptersEnabled]; + } _chaptersEnabled = chaptersEnabled; [[NSNotificationCenter defaultCenter] postNotificationName:HBChaptersChangedNotification object:self]; } diff --git a/macosx/HBPicture.h b/macosx/HBPicture.h index c4773e822..ee0fa99f5 100644 --- a/macosx/HBPicture.h +++ b/macosx/HBPicture.h @@ -50,6 +50,8 @@ extern NSString * const HBPictureChangedNotification; @property (nonatomic, readonly) int sourceHeight; @property (nonatomic, readonly) int sourceDisplayWidth; +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBPicture.m b/macosx/HBPicture.m index e254c30d6..d241ce6d8 100644 --- a/macosx/HBPicture.m +++ b/macosx/HBPicture.m @@ -94,6 +94,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setWidth:(int)width { + if (width != _width) + { + [[self.undo prepareWithInvocationTarget:self] setWidth:_width]; + } _width = width; if (!self.isValidating) @@ -110,6 +114,8 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; if (nil != *ioValue) { int value = [*ioValue intValue]; + int roundedValue = value - (value % self.modulus); + if (value >= self.maxWidth) { *ioValue = @(self.maxWidth); @@ -118,6 +124,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; { *ioValue = @32; } + else if (value != roundedValue) + { + *ioValue = @(roundedValue); + } } return retval; @@ -125,6 +135,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setHeight:(int)height { + if (height != _height) + { + [[self.undo prepareWithInvocationTarget:self] setHeight:_height]; + } _height = height; if (!self.isValidating) { @@ -140,6 +154,8 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; if (nil != *ioValue) { int value = [*ioValue intValue]; + int roundedValue = value - (value % self.modulus); + if (value >= self.maxHeight) { *ioValue = @(self.maxHeight); @@ -148,6 +164,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; { *ioValue = @32; } + else if (value != roundedValue) + { + *ioValue = @(roundedValue); + } } return retval; @@ -155,6 +175,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setDisplayWidth:(int)displayWidth { + if (displayWidth != _displayWidth) + { + [[self.undo prepareWithInvocationTarget:self] setDisplayWidth:_displayWidth]; + } _displayWidth = displayWidth; if (!self.isValidating) { @@ -165,6 +189,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setParWidth:(int)parWidth { + if (parWidth != _parWidth) + { + [[self.undo prepareWithInvocationTarget:self] setParWidth:_parWidth]; + } _parWidth = parWidth; if (!self.isValidating) { @@ -174,6 +202,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setParHeight:(int)parHeight { + if (parHeight != _parHeight) + { + [[self.undo prepareWithInvocationTarget:self] setParHeight:_parHeight]; + } _parHeight = parHeight; if (!self.isValidating) { @@ -183,6 +215,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setCropTop:(int)cropTop { + if (cropTop != _cropTop) + { + [[self.undo prepareWithInvocationTarget:self] setCropTop:_cropTop]; + } _cropTop = cropTop; if (!self.isValidating) { @@ -192,6 +228,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setCropBottom:(int)cropBottom { + if (cropBottom != _cropBottom) + { + [[self.undo prepareWithInvocationTarget:self] setCropBottom:_cropBottom]; + } _cropBottom = cropBottom; if (!self.isValidating) { @@ -201,6 +241,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setCropLeft:(int)cropLeft { + if (cropLeft != _cropLeft) + { + [[self.undo prepareWithInvocationTarget:self] setCropLeft:_cropLeft]; + } _cropLeft = cropLeft; if (!self.isValidating) { @@ -210,6 +254,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setCropRight:(int)cropRight { + if (cropRight != _cropRight) + { + [[self.undo prepareWithInvocationTarget:self] setCropRight:_cropRight]; + } _cropRight = cropRight; if (!self.isValidating) { @@ -275,22 +323,33 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setAutocrop:(BOOL)autocrop { + if (autocrop != _autocrop) + { + [[self.undo prepareWithInvocationTarget:self] setAutocrop:_autocrop]; + } _autocrop = autocrop; if (autocrop && !self.isValidating) { - self.validating = YES; - // Reset the crop values to those determined right after scan - self.cropTop = self.autoCropTop; - self.cropBottom = self.autoCropBottom; - self.cropLeft = self.autoCropLeft; - self.cropRight = self.autoCropRight; - self.validating = NO; + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + self.validating = YES; + // Reset the crop values to those determined right after scan + self.cropTop = self.autoCropTop; + self.cropBottom = self.autoCropBottom; + self.cropLeft = self.autoCropLeft; + self.cropRight = self.autoCropRight; + self.validating = NO; + } [self validateSettings]; } } - (void)setAnamorphicMode:(int)anamorphicMode { + if (anamorphicMode != _anamorphicMode) + { + [[self.undo prepareWithInvocationTarget:self] setAnamorphicMode:_anamorphicMode]; + } _anamorphicMode = anamorphicMode; if (self.anamorphicMode == HB_ANAMORPHIC_STRICT || @@ -307,6 +366,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setKeepDisplayAspect:(BOOL)keepDisplayAspect { + if (keepDisplayAspect != _keepDisplayAspect) + { + [[self.undo prepareWithInvocationTarget:self] setKeepDisplayAspect:_keepDisplayAspect]; + } _keepDisplayAspect = keepDisplayAspect; if (!self.isValidating) { @@ -316,6 +379,10 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)setModulus:(int)modulus { + if (modulus != _modulus) + { + [[self.undo prepareWithInvocationTarget:self] setModulus:_modulus]; + } _modulus = modulus; if (!self.isValidating) { @@ -398,49 +465,50 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; - (void)validateSettings { self.validating = YES; - - self.keep |= self.keepDisplayAspect * HB_KEEP_DISPLAY_ASPECT; - - hb_geometry_t srcGeo, resultGeo; - hb_geometry_settings_t uiGeo; - - srcGeo.width = self.sourceWidth; - srcGeo.height = self.sourceHeight; - srcGeo.par.num = self.sourceParNum; - srcGeo.par.den = self.sourceParDen; - - uiGeo.mode = self.anamorphicMode; - uiGeo.keep = self.keep; - uiGeo.itu_par = 0; - uiGeo.modulus = self.modulus; - - int crop[4] = {self.cropTop, self.cropBottom, self.cropLeft, self.cropRight}; - memcpy(uiGeo.crop, crop, sizeof(int[4])); - uiGeo.geometry.width = self.width; - uiGeo.geometry.height = self.height; - // Modulus added to maxWidth/maxHeight to allow a small amount of - // upscaling to the next mod boundary. - uiGeo.maxWidth = self.sourceWidth - crop[2] - crop[3] + self.modulus - 1; - uiGeo.maxHeight = self.sourceHeight - crop[0] - crop[1] + self.modulus - 1; - - hb_rational_t par = {self.parWidth, self.parHeight}; - uiGeo.geometry.par = par; - if (self.anamorphicMode == HB_ANAMORPHIC_CUSTOM && self.darUpdated) + if (!(self.undo.isUndoing || self.undo.isRedoing)) { - uiGeo.geometry.par.num = self.displayWidth; - uiGeo.geometry.par.den = uiGeo.geometry.width; - } - hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); - - int display_width; - display_width = resultGeo.width * resultGeo.par.num / resultGeo.par.den; + self.keep |= self.keepDisplayAspect * HB_KEEP_DISPLAY_ASPECT; + + hb_geometry_t srcGeo, resultGeo; + hb_geometry_settings_t uiGeo; + + srcGeo.width = self.sourceWidth; + srcGeo.height = self.sourceHeight; + srcGeo.par.num = self.sourceParNum; + srcGeo.par.den = self.sourceParDen; + + uiGeo.mode = self.anamorphicMode; + uiGeo.keep = self.keep; + uiGeo.itu_par = 0; + uiGeo.modulus = self.modulus; + + int crop[4] = {self.cropTop, self.cropBottom, self.cropLeft, self.cropRight}; + memcpy(uiGeo.crop, crop, sizeof(int[4])); + uiGeo.geometry.width = self.width; + uiGeo.geometry.height = self.height; + // Modulus added to maxWidth/maxHeight to allow a small amount of + // upscaling to the next mod boundary. + uiGeo.maxWidth = self.sourceWidth - crop[2] - crop[3] + self.modulus - 1; + uiGeo.maxHeight = self.sourceHeight - crop[0] - crop[1] + self.modulus - 1; + + hb_rational_t par = {self.parWidth, self.parHeight}; + uiGeo.geometry.par = par; + if (self.anamorphicMode == HB_ANAMORPHIC_CUSTOM && self.darUpdated) + { + uiGeo.geometry.par.num = self.displayWidth; + uiGeo.geometry.par.den = uiGeo.geometry.width; + } + hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); - self.width = resultGeo.width; - self.height = resultGeo.height; - self.parWidth = resultGeo.par.num; - self.parHeight = resultGeo.par.den; - self.displayWidth = display_width; + int display_width; + display_width = resultGeo.width * resultGeo.par.num / resultGeo.par.den; + self.width = resultGeo.width; + self.height = resultGeo.height; + self.parWidth = resultGeo.par.num; + self.parHeight = resultGeo.par.den; + self.displayWidth = display_width; + } self.validating = NO; self.keep = 0; self.darUpdated = NO; diff --git a/macosx/HBRange.h b/macosx/HBRange.h index bf18279b8..ad0ff407a 100644 --- a/macosx/HBRange.h +++ b/macosx/HBRange.h @@ -46,6 +46,8 @@ typedef NS_ENUM(NSUInteger, HBRangeType) { @property (nonatomic, readwrite, weak, nullable) HBTitle *title; +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBRange.m b/macosx/HBRange.m index 2f3c8d0dc..2d17e36cd 100644 --- a/macosx/HBRange.m +++ b/macosx/HBRange.m @@ -38,8 +38,22 @@ NSString *HBRangeChangedNotification = @"HBRangeChangedNotification"; [[NSNotificationCenter defaultCenter] postNotificationName:HBRangeChangedNotification object:self]; } +- (void)setType:(HBRangeType)type +{ + if (type != _type) + { + [[self.undo prepareWithInvocationTarget:self] setType:_type]; + } + _type = type; +} + - (void)setChapterStart:(int)chapterStart { + if (chapterStart != _chapterStart) + { + [[self.undo prepareWithInvocationTarget:self] setChapterStart:_chapterStart]; + } + if (chapterStart > self.chapterStop) { self.chapterStop = chapterStart; @@ -52,6 +66,11 @@ NSString *HBRangeChangedNotification = @"HBRangeChangedNotification"; - (void)setChapterStop:(int)chapterStop { + if (chapterStop != _chapterStop) + { + [[self.undo prepareWithInvocationTarget:self] setChapterStop:_chapterStop]; + } + if (chapterStop < self.chapterStart) { self.chapterStart = chapterStop; @@ -62,6 +81,42 @@ NSString *HBRangeChangedNotification = @"HBRangeChangedNotification"; [self postChangedNotification]; } +- (void)setFrameStart:(int)frameStart +{ + if (frameStart != _frameStart) + { + [[self.undo prepareWithInvocationTarget:self] setFrameStart:_frameStart]; + } + _frameStart = frameStart; +} + +- (void)setFrameStop:(int)frameStop +{ + if (frameStop != _frameStop) + { + [[self.undo prepareWithInvocationTarget:self] setFrameStop:_frameStop]; + } + _frameStop = frameStop; +} + +- (void)setSecondsStart:(int)secondsStart +{ + if (secondsStart != _secondsStart) + { + [[self.undo prepareWithInvocationTarget:self] setSecondsStart:_secondsStart]; + } + _secondsStart = secondsStart; +} + +- (void)setSecondsStop:(int)secondsStop +{ + if (secondsStop != _secondsStop) + { + [[self.undo prepareWithInvocationTarget:self] setSecondsStop:_secondsStop]; + } + _secondsStop = secondsStop; +} + - (NSString *)duration { if (self.type == HBRangeTypeChapters) diff --git a/macosx/HBTitle.m b/macosx/HBTitle.m index 59855ca15..0d9f93073 100644 --- a/macosx/HBTitle.m +++ b/macosx/HBTitle.m @@ -258,7 +258,9 @@ extern NSString *keySubTrackSrtCharCode; title = [NSString stringWithFormat:@"Chapter %d", i + 1]; } - [chapters addObject:[[HBChapter alloc] initWithTitle:title duration:chapter->duration]]; + [chapters addObject:[[HBChapter alloc] initWithTitle:title + index:i + 1 + duration:chapter->duration]]; } } diff --git a/macosx/HBVideo.h b/macosx/HBVideo.h index 9b593f869..f8515e5db 100644 --- a/macosx/HBVideo.h +++ b/macosx/HBVideo.h @@ -51,6 +51,8 @@ extern NSString * const HBVideoChangedNotification; @property (nonatomic, readwrite, weak) HBJob *job; @property (nonatomic, readonly) NSString *completeTune; +@property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBVideo.m b/macosx/HBVideo.m index b8a98d406..604e4aecc 100644 --- a/macosx/HBVideo.m +++ b/macosx/HBVideo.m @@ -81,58 +81,97 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification"; { // if so, convert the old setting to the new scale as close as possible // based on percentages - self.quality = floor((maxValue - minValue + 1.) * (previousPercentOfSliderScale)); + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + self.quality = floor((maxValue - minValue + 1.) * (previousPercentOfSliderScale)); + } } } - (void)setEncoder:(int)encoder { + if (encoder != _encoder) + { + [[self.undo prepareWithInvocationTarget:self] setEncoder:_encoder]; + } _encoder = encoder; [self updateQualityBounds]; - [self validatePresetsSettings]; - [self validateAdvancedOptions]; + + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + [self validatePresetsSettings]; + [self validateAdvancedOptions]; + } [self postChangedNotification]; } - (void)setQualityType:(int)qualityType { + if (qualityType != _qualityType) + { + [[self.undo prepareWithInvocationTarget:self] setQualityType:_qualityType]; + } _qualityType = qualityType; [self postChangedNotification]; } - (void)setAvgBitrate:(int)avgBitrate { + if (avgBitrate != _avgBitrate) + { + [[self.undo prepareWithInvocationTarget:self] setAvgBitrate:_avgBitrate]; + } _avgBitrate = avgBitrate; [self postChangedNotification]; } - (void)setQuality:(double)quality { + if (quality != _quality) + { + [[self.undo prepareWithInvocationTarget:self] setQuality:_quality]; + } _quality = quality; [self postChangedNotification]; } - (void)setFrameRate:(int)frameRate { + if (frameRate != _frameRate) + { + [(HBVideo *)[self.undo prepareWithInvocationTarget:self] setFrameRate:_frameRate]; + } _frameRate = frameRate; [self postChangedNotification]; } - (void)setFrameRateMode:(int)frameRateMode { + if (frameRateMode != _frameRateMode) + { + [[self.undo prepareWithInvocationTarget:self] setFrameRateMode:_frameRateMode]; + } _frameRateMode = frameRateMode; [self postChangedNotification]; } - (void)setTwoPass:(BOOL)twoPass { + if (twoPass != _twoPass) + { + [[self.undo prepareWithInvocationTarget:self] setTwoPass:_twoPass]; + } _twoPass = twoPass; [self postChangedNotification]; } - (void)setTurboTwoPass:(BOOL)turboTwoPass { + if (turboTwoPass != _turboTwoPass) + { + [[self.undo prepareWithInvocationTarget:self] setTurboTwoPass:_turboTwoPass]; + } _turboTwoPass = turboTwoPass; [self postChangedNotification]; } @@ -162,12 +201,21 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification"; - (void)setPreset:(NSString *)preset { + if (![preset isEqualToString:_preset]) + { + [[self.undo prepareWithInvocationTarget:self] setPreset:_preset]; + } _preset = [preset copy]; [self postChangedNotification]; } - (void)setTune:(NSString *)tune { + if (![tune isEqualToString:_tune]) + { + [[self.undo prepareWithInvocationTarget:self] setTune:_tune]; + } + if (![tune isEqualToString:@"none"]) { _tune = [tune copy]; @@ -182,18 +230,30 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification"; - (void)setProfile:(NSString *)profile { + if (![profile isEqualToString:_profile]) + { + [[self.undo prepareWithInvocationTarget:self] setProfile:_profile]; + } _profile = [profile copy]; [self postChangedNotification]; } - (void)setLevel:(NSString *)level { + if (![level isEqualToString:_level]) + { + [(HBVideo *)[self.undo prepareWithInvocationTarget:self] setLevel:_level]; + } _level = [level copy]; [self postChangedNotification]; } - (void)setVideoOptionExtra:(NSString *)videoOptionExtra { + if (![videoOptionExtra isEqualToString:_videoOptionExtra]) + { + [[self.undo prepareWithInvocationTarget:self] setVideoOptionExtra:_videoOptionExtra]; + } if (videoOptionExtra != nil) { _videoOptionExtra = [videoOptionExtra copy]; @@ -207,6 +267,10 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification"; - (void)setFastDecode:(BOOL)fastDecode { + if (fastDecode != _fastDecode) + { + [[self.undo prepareWithInvocationTarget:self] setFastDecode:_fastDecode]; + } _fastDecode = fastDecode; [self postChangedNotification]; } |