From 81e56ba84b9a24159f343022e28350147068ff62 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 11 Feb 2018 22:10:28 -0800 Subject: [PATCH] Implement and validate the Copy command for the sidebar. Fix #115. --- Evergreen.xcodeproj/project.pbxproj | 8 ++ .../Sidebar/FeedPasteboardWriter.swift | 8 ++ .../Sidebar/FolderPasteboardWriter.swift | 82 +++++++++++++++++++ .../Sidebar/SidebarViewController.swift | 16 +++- .../Sidebar/SmartFeedPasteboardWriter.swift | 43 ++++++++++ Evergreen/SmartFeeds/PseudoFeed.swift | 2 +- Evergreen/SmartFeeds/SmartFeed.swift | 4 + Evergreen/SmartFeeds/UnreadFeed.swift | 6 +- 8 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 Evergreen/MainWindow/Sidebar/FolderPasteboardWriter.swift create mode 100644 Evergreen/MainWindow/Sidebar/SmartFeedPasteboardWriter.swift diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 5f05e01e3..1c56ee375 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -103,6 +103,8 @@ 84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A37CB4201ECD610087C5AF /* RenameWindowController.swift */; }; 84A37CBB201ECE590087C5AF /* RenameSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84A37CB9201ECE590087C5AF /* RenameSheet.xib */; }; 84AAF2BF202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AAF2BE202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift */; }; + 84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */; }; + 84AD1EBA2031649C00BC20B7 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; }; 84B06FAE1ED37DBD00F0B54B /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FA91ED37DAD00F0B54B /* RSCore.framework */; }; 84B06FAF1ED37DBD00F0B54B /* RSCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FA91ED37DAD00F0B54B /* RSCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84B06FB21ED37DBD00F0B54B /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06F9D1ED37DA000F0B54B /* RSDatabase.framework */; }; @@ -622,6 +624,8 @@ 84A6B6931FB8D43C006754AC /* DinosaursWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DinosaursWindow.xib; sourceTree = ""; }; 84A6B6951FB8DBD2006754AC /* DinosaursWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DinosaursWindowController.swift; sourceTree = ""; }; 84AAF2BE202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineContextualMenuDelegate.swift; sourceTree = ""; }; + 84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPasteboardWriter.swift; sourceTree = ""; }; + 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeedPasteboardWriter.swift; sourceTree = ""; }; 84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = Frameworks/RSDatabase/RSDatabase.xcodeproj; sourceTree = ""; }; 84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = Frameworks/RSCore/RSCore.xcodeproj; sourceTree = ""; }; 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSWeb.xcodeproj; path = Frameworks/RSWeb/RSWeb.xcodeproj; sourceTree = ""; }; @@ -953,6 +957,8 @@ 849A97611ED9EB96007D329B /* SidebarTreeControllerDelegate.swift */, 849A97631ED9EB96007D329B /* UnreadCountView.swift */, 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */, + 84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */, + 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */, 849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */, 84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */, 847FA120202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift */, @@ -1895,6 +1901,7 @@ 842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */, 84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */, 845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */, + 84AD1EBA2031649C00BC20B7 /* SmartFeedPasteboardWriter.swift in Sources */, 84CC88181FE59CBF00644329 /* SmartFeedsController.swift in Sources */, 849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */, 849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */, @@ -1925,6 +1932,7 @@ 849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */, 843A3B5620311E7700BF76EC /* FeedListOutlineView.swift in Sources */, 8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */, + 84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */, 84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */, 845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */, 845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */, diff --git a/Evergreen/MainWindow/Sidebar/FeedPasteboardWriter.swift b/Evergreen/MainWindow/Sidebar/FeedPasteboardWriter.swift index 6554d0281..80369f621 100644 --- a/Evergreen/MainWindow/Sidebar/FeedPasteboardWriter.swift +++ b/Evergreen/MainWindow/Sidebar/FeedPasteboardWriter.swift @@ -8,6 +8,14 @@ import AppKit import Data +import RSCore + +extension Feed: PasteboardWriterOwner { + + public var pasteboardWriter: NSPasteboardWriting { + return FeedPasteboardWriter(feed: self) + } +} @objc final class FeedPasteboardWriter: NSObject, NSPasteboardWriting { diff --git a/Evergreen/MainWindow/Sidebar/FolderPasteboardWriter.swift b/Evergreen/MainWindow/Sidebar/FolderPasteboardWriter.swift new file mode 100644 index 000000000..0b12bb676 --- /dev/null +++ b/Evergreen/MainWindow/Sidebar/FolderPasteboardWriter.swift @@ -0,0 +1,82 @@ +// +// FolderPasteboardWriter.swift +// Evergreen +// +// Created by Brent Simmons on 2/11/18. +// Copyright © 2018 Ranchero Software. All rights reserved. +// + +import AppKit +import Account +import RSCore + +extension Folder: PasteboardWriterOwner { + + public var pasteboardWriter: NSPasteboardWriting { + return FolderPasteboardWriter(folder: self) + } +} + +@objc final class FolderPasteboardWriter: NSObject, NSPasteboardWriting { + + private let folder: Folder + static let folderUTIInternal = "com.ranchero.evergreen.internal.folder" + static let folderUTIInternalType = NSPasteboard.PasteboardType(rawValue: folderUTIInternal) + + init(folder: Folder) { + + self.folder = folder + } + + // MARK: - NSPasteboardWriting + + func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + + return [.string, FolderPasteboardWriter.folderUTIInternalType] + } + + func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + + let plist: Any? + + switch type { + case .string: + plist = folder.nameForDisplay + case FolderPasteboardWriter.folderUTIInternalType: + plist = internalDictionary() + default: + plist = nil + } + + return plist + } +} + +private extension FolderPasteboardWriter { + + private struct Key { + + static let name = "name" + + // Internal + static let accountID = "accountID" + static let folderID = "folderID" + } + + func internalDictionary() -> [String: Any] { + + var d = [String: Any]() + + d[Key.folderID] = folder.folderID + if let name = folder.name { + d[Key.name] = name + } + if let accountID = folder.account?.accountID { + d[Key.accountID] = accountID + } + + return d + + } +} + diff --git a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift index 06b47480a..5b99b589d 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift @@ -167,8 +167,9 @@ import RSCore @IBAction func copy(_ sender: Any?) { + NSPasteboard.general.copyObjects(selectedObjects) } - + // MARK: Navigation func canGoToNextUnread() -> Bool { @@ -307,6 +308,19 @@ import RSCore } } +// MARK: NSUserInterfaceValidations + +extension SidebarViewController: NSUserInterfaceValidations { + + func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { + + if item.action == #selector(copy(_:)) { + return NSPasteboard.general.canCopyAtLeastOneObject(selectedObjects) + } + return true + } +} + //MARK: - Private private extension SidebarViewController { diff --git a/Evergreen/MainWindow/Sidebar/SmartFeedPasteboardWriter.swift b/Evergreen/MainWindow/Sidebar/SmartFeedPasteboardWriter.swift new file mode 100644 index 000000000..0d716c9f5 --- /dev/null +++ b/Evergreen/MainWindow/Sidebar/SmartFeedPasteboardWriter.swift @@ -0,0 +1,43 @@ +// +// SmartFeedPasteboardWriter.swift +// Evergreen +// +// Created by Brent Simmons on 2/11/18. +// Copyright © 2018 Ranchero Software. All rights reserved. +// + +import AppKit +import Account +import RSCore + +@objc final class SmartFeedPasteboardWriter: NSObject, NSPasteboardWriting { + + private let smartFeed: PseudoFeed + + init(smartFeed: PseudoFeed) { + + self.smartFeed = smartFeed + } + + // MARK: - NSPasteboardWriting + + func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + + return [.string] + } + + func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + + let plist: Any? + + switch type { + case .string: + plist = smartFeed.nameForDisplay + default: + plist = nil + } + + return plist + } +} + diff --git a/Evergreen/SmartFeeds/PseudoFeed.swift b/Evergreen/SmartFeeds/PseudoFeed.swift index aca8b0c4a..c84cf8d6e 100644 --- a/Evergreen/SmartFeeds/PseudoFeed.swift +++ b/Evergreen/SmartFeeds/PseudoFeed.swift @@ -10,7 +10,7 @@ import Foundation import Data import RSCore -protocol PseudoFeed: class, DisplayNameProvider, UnreadCountProvider, SmallIconProvider { +protocol PseudoFeed: class, DisplayNameProvider, UnreadCountProvider, SmallIconProvider, PasteboardWriterOwner { } diff --git a/Evergreen/SmartFeeds/SmartFeed.swift b/Evergreen/SmartFeeds/SmartFeed.swift index 99de45209..5086f5c2b 100644 --- a/Evergreen/SmartFeeds/SmartFeed.swift +++ b/Evergreen/SmartFeeds/SmartFeed.swift @@ -32,6 +32,10 @@ final class SmartFeed: PseudoFeed { } } + var pasteboardWriter: NSPasteboardWriting { + return SmartFeedPasteboardWriter(smartFeed: self) + } + private let delegate: SmartFeedDelegate private var timer: Timer? private var unreadCounts = [Account: Int]() diff --git a/Evergreen/SmartFeeds/UnreadFeed.swift b/Evergreen/SmartFeeds/UnreadFeed.swift index eae26a601..98c69bb68 100644 --- a/Evergreen/SmartFeeds/UnreadFeed.swift +++ b/Evergreen/SmartFeeds/UnreadFeed.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Ranchero Software. All rights reserved. // -import Foundation +import AppKit import Account import Data @@ -24,6 +24,10 @@ final class UnreadFeed: PseudoFeed { } } + var pasteboardWriter: NSPasteboardWriting { + return SmartFeedPasteboardWriter(smartFeed: self) + } + init() { self.unreadCount = appDelegate.unreadCount