summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authorDamiano Galassi <[email protected]>2020-01-13 17:19:43 +0100
committerDamiano Galassi <[email protected]>2020-01-13 17:19:43 +0100
commit80127fdb821c1e795b486d957e36bdab3f5de1f6 (patch)
treecab65feadbe05d7d89abf7869245e4a9186d03a9 /macosx
parentfce3bbbf92011ee2eb9a3805f6b426ff1ffb6eba (diff)
MacGui: improve presets view to allow multiple selection, drag & drop from and to Finder.
Diffstat (limited to 'macosx')
-rw-r--r--macosx/Base.lproj/Presets.xib21
-rw-r--r--macosx/HBFilePromiseProvider.h19
-rw-r--r--macosx/HBFilePromiseProvider.m40
-rw-r--r--macosx/HBPreset.h7
-rw-r--r--macosx/HBPreset.m28
-rw-r--r--macosx/HBPresetsManager.m2
-rw-r--r--macosx/HBPresetsViewController.m239
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj6
-rw-r--r--macosx/NSArray+HBAdditions.h1
-rw-r--r--macosx/NSArray+HBAdditions.m12
10 files changed, 248 insertions, 127 deletions
diff --git a/macosx/Base.lproj/Presets.xib b/macosx/Base.lproj/Presets.xib
index 7487802b0..23f4cedf5 100644
--- a/macosx/Base.lproj/Presets.xib
+++ b/macosx/Base.lproj/Presets.xib
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
- <development version="8000" identifier="xcode"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -31,21 +30,21 @@ Overrides all encode settings. Settings may be further adjusted after selecting
<rect key="frame" x="1" y="1" width="222" height="264"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="14" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" outlineTableColumn="jhC-ge-H1w" id="00W-tb-wgY">
+ <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="14" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" outlineTableColumn="jhC-ge-H1w" id="00W-tb-wgY">
<rect key="frame" x="0.0" y="0.0" width="222" height="264"/>
- <autoresizingMask key="autoresizingMask"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="name" width="219" minWidth="16" maxWidth="1000" id="jhC-ge-H1w">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
- <font key="font" metaFont="smallSystem"/>
+ <font key="font" metaFont="label" size="11"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="4tC-UE-40G">
- <font key="font" metaFont="smallSystem"/>
+ <font key="font" metaFont="label" size="11"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -58,7 +57,7 @@ Overrides all encode settings. Settings may be further adjusted after selecting
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VdJ-Vk-fzJ">
<rect key="frame" x="0.0" y="0.0" width="219" height="14"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="oBC-Nh-TwB">
- <font key="font" metaFont="smallSystem"/>
+ <font key="font" metaFont="label" size="11"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<connections>
@@ -125,14 +124,14 @@ Overrides all encode settings. Settings may be further adjusted after selecting
<binding destination="-2" name="enabled" keyPath="self.enabled" id="lmV-Y3-JoF"/>
</connections>
</button>
- <popUpButton toolTip="Show additional options." verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ybq-Zt-sta">
- <rect key="frame" x="58" y="3" width="35" height="23"/>
+ <popUpButton toolTip="Show additional options." translatesAutoresizingMaskIntoConstraints="NO" id="Ybq-Zt-sta">
+ <rect key="frame" x="58" y="4" width="35" height="22"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="ROP-Ic-SbK"/>
</constraints>
<popUpButtonCell key="cell" type="smallSquare" bezelStyle="smallSquare" imagePosition="only" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" pullsDown="YES" id="2JY-O9-FR6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="menu"/>
+ <font key="font" metaFont="system"/>
<menu key="menu" title="Presets Action Menu" id="LQk-kD-5sj">
<items>
<menuItem state="on" image="NSActionTemplate" hidden="YES" id="KPx-Ep-mb4"/>
diff --git a/macosx/HBFilePromiseProvider.h b/macosx/HBFilePromiseProvider.h
new file mode 100644
index 000000000..0666a7afc
--- /dev/null
+++ b/macosx/HBFilePromiseProvider.h
@@ -0,0 +1,19 @@
+//
+// HBFilePromiseProvider.h
+// HandBrake
+//
+// Created by Damiano Galassi on 09/01/2020.
+// Copyright © 2020 HandBrake. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+#define kHandBrakeInternalPBoardType @"fr.handbrake.internalPBoardType"
+
+@interface HBFilePromiseProvider : NSFilePromiseProvider
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/macosx/HBFilePromiseProvider.m b/macosx/HBFilePromiseProvider.m
new file mode 100644
index 000000000..b4c9e356e
--- /dev/null
+++ b/macosx/HBFilePromiseProvider.m
@@ -0,0 +1,40 @@
+//
+// HBFilePromiseProvider.m
+// HandBrake
+//
+// Created by Damiano Galassi on 09/01/2020.
+// Copyright © 2020 HandBrake. All rights reserved.
+//
+
+#import "HBFilePromiseProvider.h"
+
+@implementation HBFilePromiseProvider
+
+- (NSArray<NSPasteboardType> *)writableTypesForPasteboard:(NSPasteboard *)pasteboard
+{
+ NSMutableArray<NSPasteboardType> *types = [[super writableTypesForPasteboard:pasteboard] mutableCopy];
+ [types addObject:kHandBrakeInternalPBoardType];
+ return types;
+}
+
+- (NSPasteboardWritingOptions)writingOptionsForType:(NSPasteboardType)type pasteboard:(NSPasteboard *)pasteboard
+{
+ if ([type isEqualToString:kHandBrakeInternalPBoardType])
+ {
+ return 0;
+ }
+
+ return [super writingOptionsForType:type pasteboard:pasteboard];
+}
+
+- (id)pasteboardPropertyListForType:(NSPasteboardType)type
+{
+ if ([type isEqualToString:kHandBrakeInternalPBoardType])
+ {
+ return kHandBrakeInternalPBoardType;
+ }
+
+ return [super pasteboardPropertyListForType:type];
+}
+
+@end
diff --git a/macosx/HBPreset.h b/macosx/HBPreset.h
index 3e95d3bab..75a8a6a42 100644
--- a/macosx/HBPreset.h
+++ b/macosx/HBPreset.h
@@ -9,10 +9,6 @@
NS_ASSUME_NONNULL_BEGIN
-typedef NS_ENUM(NSUInteger, HBPresetFormat) {
- HBPresetFormatPlist,
- HBPresetFormatJson,
-};
/**
* HBPreset
* Stores a preset dictionary.
@@ -42,12 +38,11 @@ typedef NS_ENUM(NSUInteger, HBPresetFormat) {
*
* @param url The URL to which to write the preset.
* @param atomically A flag that specifies whether the output should be written atomically.
- * @param format the format of the file to write.
* @param removeRoot whether the root preset should be written or not.
*
* @return YES if the location is written successfully, otherwise NO.
*/
-- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically format:(HBPresetFormat)format removeRoot:(BOOL)removeRoot;
+- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically removeRoot:(BOOL)removeRoot error:(NSError * __autoreleasing *)outError;
/**
* The name of the preset.
diff --git a/macosx/HBPreset.m b/macosx/HBPreset.m
index 013e11b9c..f0fec1310 100644
--- a/macosx/HBPreset.m
+++ b/macosx/HBPreset.m
@@ -186,6 +186,7 @@
[self.children addObject:preset];
}
}
+ [self resetBuiltInAndDefaultState];
return self;
}
else if (outError)
@@ -246,7 +247,7 @@
return output;
}
-- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically format:(HBPresetFormat)format removeRoot:(BOOL)removeRoot
+- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically removeRoot:(BOOL)removeRoot error:(NSError * __autoreleasing *)outError
{
BOOL success = NO;
NSArray *presetList;
@@ -268,16 +269,10 @@
@"VersionMinor": @(minor),
@"VersionMicro": @(micro) };
- if (format == HBPresetFormatPlist)
- {
- success = [dict writeToURL:url atomically:atomically];
- }
- else
- {
- NSUInteger sortKeys = (1UL << 1); // NSJSONWritingSortedKeys in 10.13 sdk;
- NSData *jsonPreset = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted | sortKeys error:NULL];
- success = [jsonPreset writeToURL:url atomically:atomically];
- }
+
+ NSUInteger sortKeys = (1UL << 1); // NSJSONWritingSortedKeys in 10.13 sdk;
+ NSData *jsonPreset = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted | sortKeys error:NULL];
+ success = [jsonPreset writeToURL:url options:NSDataWritingAtomic error:outError];
return success;
}
@@ -300,6 +295,17 @@
}
}
+- (void)resetBuiltInAndDefaultState
+{
+ _isBuiltIn = NO;
+ _isDefault = NO;
+
+ for (HBPreset *child in self.children)
+ {
+ [child resetBuiltInAndDefaultState];
+ }
+}
+
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
diff --git a/macosx/HBPresetsManager.m b/macosx/HBPresetsManager.m
index 4692a37a6..f8aede8a1 100644
--- a/macosx/HBPresetsManager.m
+++ b/macosx/HBPresetsManager.m
@@ -248,7 +248,7 @@ typedef NS_ENUM(NSUInteger, HBPresetLoadingResult) {
- (BOOL)savePresetsToURL:(NSURL *)url
{
- return [self.root writeToURL:url atomically:YES format:HBPresetFormatJson removeRoot:YES];
+ return [self.root writeToURL:url atomically:YES removeRoot:YES error:NULL];
}
- (BOOL)savePresets
diff --git a/macosx/HBPresetsViewController.m b/macosx/HBPresetsViewController.m
index bf49c91cf..f71c1bac4 100644
--- a/macosx/HBPresetsViewController.m
+++ b/macosx/HBPresetsViewController.m
@@ -6,12 +6,11 @@
#import "HBPresetsViewController.h"
#import "HBAddCategoryController.h"
+#import "HBFilePromiseProvider.h"
+#import "NSArray+HBAdditions.h"
@import HandBrakeKit;
-// drag and drop pasteboard type
-#define kHandBrakePresetPBoardType @"handBrakePresetPBoardType"
-
// KVO Context
static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
@@ -44,7 +43,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
@end
-@interface HBPresetsViewController () <NSOutlineViewDelegate>
+@interface HBPresetsViewController () <NSOutlineViewDelegate, NSOutlineViewDataSource, NSFilePromiseProviderDelegate>
@property (nonatomic, strong) HBPresetsManager *presets;
@property (nonatomic, readwrite) HBPreset *selectedPresetInternal;
@@ -56,7 +55,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
/**
* Helper var for drag & drop
*/
-@property (nonatomic, strong) NSArray *dragNodesArray;
+@property (nonatomic, strong, nullable) NSArray *dragNodesArray;
/**
* The status (expanded or not) of the categories.
@@ -77,7 +76,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
{
_presets = presetManager;
_selectedPresetInternal = presetManager.defaultPreset;
- _expandedNodes = [[NSArray arrayWithArray:[[NSUserDefaults standardUserDefaults]
+ _expandedNodes = [[NSArray arrayWithArray:[NSUserDefaults.standardUserDefaults
objectForKey:@"HBPreviewViewExpandedStatus"]] mutableCopy];
_showHeader = YES;
}
@@ -94,7 +93,9 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
}
// drag and drop support
- [self.outlineView registerForDraggedTypes:@[kHandBrakePresetPBoardType]];
+ [self.outlineView registerForDraggedTypes:@[kHandBrakeInternalPBoardType, (NSString *)kUTTypeFileURL]];
+ [self.outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
+ [self.outlineView setDraggingSourceOperationMask:NSDragOperationMove forLocal:YES];
// Re-expand the items
[self expandNodes:[self.treeController.arrangedObjects childNodes]];
@@ -146,8 +147,13 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
return YES;
}
-#pragma mark -
-#pragma mark Import Export Preset(s)
+#pragma mark - Import Export Preset(s)
+
+- (nonnull NSString *)fileNameForPreset:(HBPreset *)preset
+{
+ NSString *name = preset.name == nil || preset.name.length == 0 ? @"Unnamed preset" : preset.name;
+ return [name stringByAppendingPathExtension:@"json"];
+}
- (IBAction)exportPreset:(id)sender
{
@@ -161,20 +167,63 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
// We get the current file name and path from the destination field here
NSURL *defaultExportDirectory = [[NSURL fileURLWithPath:NSHomeDirectory()] URLByAppendingPathComponent:@"Desktop" isDirectory:YES];
panel.directoryURL = defaultExportDirectory;
- panel.nameFieldStringValue = [NSString stringWithFormat:@"%@.json", selectedPreset.name];
+ panel.nameFieldStringValue = [self fileNameForPreset:selectedPreset];
[panel beginWithCompletionHandler:^(NSInteger result)
{
if (result == NSModalResponseOK)
{
NSURL *presetExportDirectory = [panel.URL URLByDeletingLastPathComponent];
- [[NSUserDefaults standardUserDefaults] setURL:presetExportDirectory forKey:@"LastPresetExportDirectoryURL"];
+ [NSUserDefaults.standardUserDefaults setURL:presetExportDirectory forKey:@"LastPresetExportDirectoryURL"];
- [selectedPreset writeToURL:panel.URL atomically:YES format:HBPresetFormatJson removeRoot:NO];
+ NSError *error = NULL;
+ BOOL success = [selectedPreset writeToURL:panel.URL atomically:YES removeRoot:NO error:&error];
+ if (success == NO)
+ {
+ [self presentError:error];
+ }
}
}];
}
+- (void)doImportPreset:(NSArray<NSURL *> *)URLs atIndexPath:(nullable NSIndexPath *)indexPath
+{
+ if (indexPath == nil)
+ {
+ for (HBPreset *preset in self.presets.root.children)
+ {
+ if (preset.isBuiltIn == NO && preset.isLeaf == NO)
+ {
+ indexPath = [[self.presets indexPathOfPreset:preset] indexPathByAddingIndex:0];
+ }
+ }
+
+ if (indexPath == nil)
+ {
+ indexPath = [NSIndexPath indexPathWithIndex:self.presets.root.countOfChildren];
+ }
+ }
+
+ for (NSURL *url in URLs)
+ {
+ NSError *error;
+ HBPreset *preset = [[HBPreset alloc] initWithContentsOfURL:url error:&error];
+
+ if (preset)
+ {
+ for (HBPreset *child in preset.children)
+ {
+ [self.treeController insertObject:child atArrangedObjectIndexPath:indexPath];
+ }
+ [self.presets savePresets];
+ }
+ else
+ {
+ [self presentError:error];
+ }
+ }
+}
+
- (IBAction)importPreset:(id)sender
{
NSOpenPanel *panel = [NSOpenPanel openPanel];
@@ -184,9 +233,9 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
panel.canChooseDirectories = NO;
panel.allowedFileTypes = @[@"plist", @"xml", @"json"];
- if ([[NSUserDefaults standardUserDefaults] URLForKey:@"LastPresetImportDirectoryURL"])
+ if ([NSUserDefaults.standardUserDefaults URLForKey:@"LastPresetImportDirectoryURL"])
{
- panel.directoryURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"LastPresetImportDirectoryURL"];
+ panel.directoryURL = [NSUserDefaults.standardUserDefaults URLForKey:@"LastPresetImportDirectoryURL"];
}
else
{
@@ -195,25 +244,11 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
[panel beginWithCompletionHandler:^(NSInteger result)
{
- [[NSUserDefaults standardUserDefaults] setURL:panel.directoryURL forKey:@"LastPresetImportDirectoryURL"];
+ [NSUserDefaults.standardUserDefaults setURL:panel.directoryURL forKey:@"LastPresetImportDirectoryURL"];
if (result == NSModalResponseOK)
{
- for (NSURL *url in panel.URLs)
- {
- NSError *error;
- HBPreset *import = [[HBPreset alloc] initWithContentsOfURL:url error:&error];
-
- if (import == nil)
- {
- [self presentError:error];
- }
-
- for (HBPreset *child in import.children)
- {
- [self.presets addPreset:child];
- }
- }
+ [self doImportPreset:panel.URLs atIndexPath:nil];
}
}];
}
@@ -241,7 +276,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
if (self.delegate && [[self.treeController.selectedObjects firstObject] isLeaf])
{
[self.delegate selectionDidChange];
- [[NSNotificationCenter defaultCenter] postNotificationName:HBPresetsChangedNotification object:nil];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBPresetsChangedNotification object:nil];
}
}
@@ -269,7 +304,10 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
if (status == NSAlertFirstButtonReturn)
{
- [self.presets deletePresetAtIndexPath:[self.treeController selectionIndexPath]];
+ for (NSIndexPath *indexPath in self.treeController.selectionIndexPaths.reverseObjectEnumerator)
+ {
+ [self.presets deletePresetAtIndexPath:indexPath];
+ }
[self setSelection:self.presets.defaultPreset];
}
}
@@ -293,7 +331,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
if (selectedNode.isLeaf)
{
self.presets.defaultPreset = selectedNode;
- [[NSNotificationCenter defaultCenter] postNotificationName:HBPresetsChangedNotification object:nil];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBPresetsChangedNotification object:nil];
}
}
@@ -340,55 +378,59 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
- HBPreset *node = [[[notification userInfo] valueForKey:@"NSObject"] representedObject];
+ HBPreset *node = [notification.userInfo[@"NSObject"] representedObject];
if (![self.expandedNodes containsObject:@(node.hash)])
{
[self.expandedNodes addObject:@(node.hash)];
- [[NSUserDefaults standardUserDefaults] setObject:self.expandedNodes forKey:@"HBPreviewViewExpandedStatus"];
+ [NSUserDefaults.standardUserDefaults setObject:self.expandedNodes forKey:@"HBPreviewViewExpandedStatus"];
}
}
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
- HBPreset *node = [[[notification userInfo] valueForKey:@"NSObject"] representedObject];
+ HBPreset *node = [notification.userInfo[@"NSObject"] representedObject];
[self.expandedNodes removeObject:@(node.hash)];
- [[NSUserDefaults standardUserDefaults] setObject:self.expandedNodes forKey:@"HBPreviewViewExpandedStatus"];
+ [NSUserDefaults.standardUserDefaults setObject:self.expandedNodes forKey:@"HBPreviewViewExpandedStatus"];
}
#pragma mark - Drag & Drops
-/**
- * draggingSourceOperationMaskForLocal <NSDraggingSource override>
- */
-- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+- (void)outlineView:(NSOutlineView *)outlineView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forItems:(NSArray *)draggedItems
{
- return NSDragOperationMove;
+ self.dragNodesArray = draggedItems;
}
-/**
- * outlineView:writeItems:toPasteboard
- */
-- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
+- (void)outlineView:(NSOutlineView *)outlineView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
- // Return no if we are trying to drag a built-in preset
- for (id item in items) {
- if ([[item representedObject] isBuiltIn])
- return NO;
- }
+ self.dragNodesArray = nil;
+}
- [pboard declareTypes:@[kHandBrakePresetPBoardType] owner:self];
+- (id<NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView
+ pasteboardWriterForItem:(id)item {
+ if (@available(macOS 10.12, *))
+ {
+ HBFilePromiseProvider *filePromise = [[HBFilePromiseProvider alloc] initWithFileType:@"public.text" delegate:self];
+ filePromise.userInfo = [item representedObject];
+ return filePromise;
+ }
+ else
+ {
+ return [[NSPasteboardItem alloc] initWithPasteboardPropertyList:@1 ofType:kHandBrakeInternalPBoardType];
+ }
+}
- // keep track of this nodes for drag feedback in "validateDrop"
- self.dragNodesArray = items;
+- (nonnull NSString *)filePromiseProvider:(nonnull NSFilePromiseProvider *)filePromiseProvider fileNameForType:(nonnull NSString *)fileType
+{
+ return [self fileNameForPreset:filePromiseProvider.userInfo];
+}
- return YES;
+- (void)filePromiseProvider:(nonnull NSFilePromiseProvider *)filePromiseProvider writePromiseToURL:(nonnull NSURL *)url completionHandler:(nonnull void (^)(NSError * _Nullable))completionHandler
+{
+ NSError *error = NULL;
+ [filePromiseProvider.userInfo writeToURL:url atomically:YES removeRoot:NO error:&error];
+ completionHandler(error);
}
-/**
- * outlineView:validateDrop:proposedItem:proposedChildrenIndex:
- *
- * This method is used by NSOutlineView to determine a valid drop target.
- */
- (NSDragOperation)outlineView:(NSOutlineView *)ov
validateDrop:(id <NSDraggingInfo>)info
proposedItem:(id)item
@@ -396,22 +438,23 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
{
NSDragOperation result = NSDragOperationNone;
- if (!item)
- {
- if (index == 0)
+ if (info.draggingSource == nil)
+ {
+ if ([[item representedObject] isBuiltIn] ||
+ ([[item representedObject] isLeaf] && index == -1))
{
- // don't allow to drop on top
result = NSDragOperationNone;
}
else
{
- // no item to drop on
- result = NSDragOperationGeneric;
+ result = NSDragOperationCopy;
}
- }
- else
- {
- if (index == -1 || [[item representedObject] isBuiltIn] || [self.dragNodesArray containsObject:item])
+ }
+ else if ([self.dragNodesArray allSatisfy:^BOOL(id _Nonnull object) { return [[object representedObject] isBuiltIn] == NO; }])
+ {
+ if ([[item representedObject] isBuiltIn] ||
+ ([[item representedObject] isLeaf] && index == -1) ||
+ [self.dragNodesArray containsObject:item])
{
// don't allow dropping on a child
result = NSDragOperationNone;
@@ -421,16 +464,11 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
// drop location is a container
result = NSDragOperationMove;
}
- }
+ }
return result;
}
-/**
- * handleInternalDrops:pboard:withIndexPath:
- *
- * The user is doing an intra-app drag within the outline view.
- */
- (void)handleInternalDrops:(NSPasteboard *)pboard withIndexPath:(NSIndexPath *)indexPath
{
// user is doing an intra app drag within the outline view:
@@ -438,7 +476,7 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
// move the items to their new place (we do this backwards, otherwise they will end up in reverse order)
NSInteger idx;
- for (idx = ([newNodes count] - 1); idx >= 0; idx--)
+ for (idx = newNodes.count - 1; idx >= 0; idx--)
{
[self.treeController moveNode:newNodes[idx] toIndexPath:indexPath];
@@ -452,53 +490,58 @@ static void *HBPresetsViewControllerContext = &HBPresetsViewControllerContext;
// keep the moved nodes selected
NSMutableArray *indexPathList = [NSMutableArray array];
- for (NSUInteger i = 0; i < [newNodes count]; i++)
- {
- [indexPathList addObject:[newNodes[i] indexPath]];
- }
- [self.treeController setSelectionIndexPaths: indexPathList];
+ for (id node in newNodes)
+ {
+ [indexPathList addObject:[node indexPath]];
+ }
+ [self.treeController setSelectionIndexPaths:indexPathList];
+}
+
+- (void)handleExternalDrops:(NSPasteboard *)pboard withIndexPath:(NSIndexPath *)indexPath
+{
+ NSArray<NSURL *> *URLs = [pboard readObjectsForClasses:@[[NSURL class]] options:nil];
+ [self doImportPreset:URLs atIndexPath:indexPath];
}
-/**
- * outlineView:acceptDrop:item:childIndex
- *
- * This method is called when the mouse is released over an outline view that previously decided to allow a drop
- * via the validateDrop method. The data source should incorporate the data from the dragging pasteboard at this time.
- * 'index' is the location to insert the data as a child of 'item', and are the values previously set in the validateDrop: method.
- *
- */
- (BOOL)outlineView:(NSOutlineView *)ov acceptDrop:(id <NSDraggingInfo>)info item:(id)targetItem childIndex:(NSInteger)index
{
- // note that "targetItem" is a NSTreeNode proxy
- //
BOOL result = NO;
+ // note that "targetItem" is a NSTreeNode proxy
// find the index path to insert our dropped object(s)
NSIndexPath *indexPath;
if (targetItem)
{
- // drop down inside the tree node:
- // feth the index path to insert our dropped node
- indexPath = [[targetItem indexPath] indexPathByAddingIndex:index];
+ // drop down inside the tree node: fetch the index path to insert our dropped node
+ indexPath = index == -1 ? [[targetItem indexPath] indexPathByAddingIndex:0] : [[targetItem indexPath] indexPathByAddingIndex:index];
}
else
{
// drop at the top root level
if (index == -1) // drop area might be ambiguous (not at a particular location)
+ {
indexPath = [NSIndexPath indexPathWithIndex:self.presets.root.children.count]; // drop at the end of the top level
+ }
else
+ {
indexPath = [NSIndexPath indexPathWithIndex:index]; // drop at a particular place at the top level
+ }
}
- NSPasteboard *pboard = [info draggingPasteboard]; // get the pasteboard
+ NSPasteboard *pboard = info.draggingPasteboard;
- // check the dragging type -
- if ([pboard availableTypeFromArray:@[kHandBrakePresetPBoardType]])
+ // check the dragging type
+ if ([pboard availableTypeFromArray:@[kHandBrakeInternalPBoardType]])
{
// user is doing an intra-app drag within the outline view
[self handleInternalDrops:pboard withIndexPath:indexPath];
result = YES;
}
+ else if ([pboard availableTypeFromArray:@[(NSString *)kUTTypeFileURL]])
+ {
+ [self handleExternalDrops:pboard withIndexPath:indexPath];
+ result = YES;
+ }
return result;
}
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index 637460b40..e12062dd1 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -277,6 +277,7 @@
A9B6B9F1217B408E00B957AE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A9B6B9EF217B408E00B957AE /* InfoPlist.strings */; };
A9B6B9F4217B408E00B957AE /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A9B6B9F2217B408E00B957AE /* Localizable.strings */; };
A9BC24C91A69293E007DC41A /* HBAttributedStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9BC24C81A69293E007DC41A /* HBAttributedStringAdditions.m */; };
+ A9C3A50123C7A66100269CA3 /* HBFilePromiseProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A9C3A50023C7A66100269CA3 /* HBFilePromiseProvider.m */; };
A9C61F9E22E31CDB00C28E7C /* HBFlippedClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = A9C61F9D22E31CDB00C28E7C /* HBFlippedClipView.m */; };
A9CE0A921F57EC3400724577 /* HBImageUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = A9CE0A911F57EC3400724577 /* HBImageUtilities.m */; };
A9CF25F71990D6820023F727 /* HBPresetsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9CF25F61990D6820023F727 /* HBPresetsViewController.m */; };
@@ -732,6 +733,8 @@
A9C183941A716B8F00C897C2 /* HBTitleSelectionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBTitleSelectionController.m; sourceTree = "<group>"; };
A9C313B222F98F0D0001C438 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/HBQueueInfoViewController.strings; sourceTree = "<group>"; };
A9C313B322F98F0D0001C438 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/HBQueueTableViewController.strings; sourceTree = "<group>"; };
+ A9C3A4FF23C7A66100269CA3 /* HBFilePromiseProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBFilePromiseProvider.h; sourceTree = "<group>"; };
+ A9C3A50023C7A66100269CA3 /* HBFilePromiseProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBFilePromiseProvider.m; sourceTree = "<group>"; };
A9C61F9C22E31CDB00C28E7C /* HBFlippedClipView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBFlippedClipView.h; sourceTree = "<group>"; };
A9C61F9D22E31CDB00C28E7C /* HBFlippedClipView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBFlippedClipView.m; sourceTree = "<group>"; };
A9CAC26E1CCB6B0F00A39E72 /* HBPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBPlayer.h; sourceTree = "<group>"; };
@@ -1280,6 +1283,8 @@
A9706CB31AC1436F00BAEAA8 /* HBApplication.m */,
A964D3A622FDE91A00DFCAEA /* HBRemoteCore.h */,
A964D3A722FDE91B00DFCAEA /* HBRemoteCore.m */,
+ A9C3A4FF23C7A66100269CA3 /* HBFilePromiseProvider.h */,
+ A9C3A50023C7A66100269CA3 /* HBFilePromiseProvider.m */,
A95BA15F220CA5DB00A2F9F9 /* HBDistributedArray.h */,
A95BA160220CA5DB00A2F9F9 /* HBDistributedArray.m */,
A9706CB51AC1437800BAEAA8 /* HBExceptionAlertController.h */,
@@ -1949,6 +1954,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ A9C3A50123C7A66100269CA3 /* HBFilePromiseProvider.m in Sources */,
A954010322FF397000F83A5F /* HBRedirect.m in Sources */,
A95BA161220CA5DB00A2F9F9 /* HBDistributedArray.m in Sources */,
A916C99B1C844A0800C7B560 /* HBTableView.m in Sources */,
diff --git a/macosx/NSArray+HBAdditions.h b/macosx/NSArray+HBAdditions.h
index ee7a6bdc2..414dcf1dd 100644
--- a/macosx/NSArray+HBAdditions.h
+++ b/macosx/NSArray+HBAdditions.h
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface NSArray (HBAdditions)
+- (BOOL)allSatisfy:(BOOL (^)(id object))block;
- (NSArray *)filteredArrayUsingBlock:(BOOL (^)(id object))block;
- (NSIndexSet *)indexesOfObjectsUsingBlock:(BOOL (^)(id object))block;
diff --git a/macosx/NSArray+HBAdditions.m b/macosx/NSArray+HBAdditions.m
index cad90ba70..540c571b9 100644
--- a/macosx/NSArray+HBAdditions.m
+++ b/macosx/NSArray+HBAdditions.m
@@ -8,6 +8,18 @@
@implementation NSArray (HBAdditions)
+- (BOOL)allSatisfy:(BOOL (^)(id object))block
+{
+ for (id object in self)
+ {
+ if (block(object) == NO)
+ {
+ return NO;
+ }
+ }
+ return YES;
+}
+
- (NSArray *)filteredArrayUsingBlock:(BOOL (^)(id object))block
{
NSMutableArray *filteredArray = [NSMutableArray array];