summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamiano Galassi <[email protected]>2015-10-20 18:55:08 +0200
committerDamiano Galassi <[email protected]>2015-10-20 18:55:08 +0200
commit7f50c27989a63e87d2f17c6495d29e136ccfd837 (patch)
tree280036131fa6d7dd1a11ef84a55c4dfad3e96aca
parentc32d5236135c6be0b4987fb74de511e3332d7396 (diff)
MacGui: added undo/redo support to the video, picture, filters, chapters and range parts of HBJob.
-rw-r--r--macosx/English.lproj/ChaptersTitles.xib103
-rw-r--r--macosx/HBChapter.h5
-rw-r--r--macosx/HBChapter.m18
-rw-r--r--macosx/HBChapterTitlesController.m45
-rw-r--r--macosx/HBController.m25
-rw-r--r--macosx/HBFilters.h2
-rw-r--r--macosx/HBFilters.m55
-rw-r--r--macosx/HBJob.h2
-rw-r--r--macosx/HBJob.m62
-rw-r--r--macosx/HBPicture.h2
-rw-r--r--macosx/HBPicture.m162
-rw-r--r--macosx/HBRange.h2
-rw-r--r--macosx/HBRange.m55
-rw-r--r--macosx/HBTitle.m4
-rw-r--r--macosx/HBVideo.h2
-rw-r--r--macosx/HBVideo.m70
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];
}