From ba64fed16a7927596382e37ebe2035dddcca948b Mon Sep 17 00:00:00 2001 From: Daniel Barber Date: Mon, 17 Jun 2019 20:38:59 -0400 Subject: [PATCH] Drag album songs onto queue --- Persephone.xcodeproj/project.pbxproj | 4 ++ .../Controllers/QueueViewController.swift | 2 +- .../DataSources/AlbumTracksDataSource.swift | 10 ++++ Persephone/DataSources/QueueDataSource.swift | 52 ++++++++++++------- Persephone/Extensions/NSPasteboard.swift | 13 +++++ .../Extensions/MPDClient+Command.swift | 6 +++ .../Extensions/MPDClient+Queue.swift | 8 +++ Persephone/MPDClient/Models/MPDCommand.swift | 1 + Persephone/State/Actions/MPDActions.swift | 5 ++ Persephone/State/Reducers/MPDReducer.swift | 3 ++ 10 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 Persephone/Extensions/NSPasteboard.swift diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 8ac1d32..829a9f2 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD62220720300F747E6 /* AlbumItemView.swift */; }; E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; }; E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; }; + E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39822B85D0400CA8CBD /* NSPasteboard.swift */; }; E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; }; E4A3A6A122A457B600EA2C40 /* AlbumDetailSongListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */; }; E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; }; @@ -291,6 +292,7 @@ E47E2FD62220720300F747E6 /* AlbumItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemView.swift; sourceTree = ""; }; E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = ""; }; E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = ""; }; + E489E39822B85D0400CA8CBD /* NSPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboard.swift; sourceTree = ""; }; E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = ""; }; E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = ""; }; E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = ""; }; @@ -446,6 +448,7 @@ E435E3E1221CD4E200184CFC /* NSFont.swift */, E435E3E3221CD75D00184CFC /* NSImage.swift */, E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */, + E489E39822B85D0400CA8CBD /* NSPasteboard.swift */, ); path = Extensions; sourceTree = ""; @@ -957,6 +960,7 @@ E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */, E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */, E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */, + E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */, E465049A21E94DF500A70F4C /* WindowController.swift in Sources */, E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */, E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */, diff --git a/Persephone/Controllers/QueueViewController.swift b/Persephone/Controllers/QueueViewController.swift index db9d275..502832d 100644 --- a/Persephone/Controllers/QueueViewController.swift +++ b/Persephone/Controllers/QueueViewController.swift @@ -23,7 +23,7 @@ class QueueViewController: NSViewController { } queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle - queueView.registerForDraggedTypes([REORDER_PASTEBOARD_TYPE]) + queueView.registerForDraggedTypes([.songPasteboardType]) queueView.draggingDestinationFeedbackStyle = .regular } diff --git a/Persephone/DataSources/AlbumTracksDataSource.swift b/Persephone/DataSources/AlbumTracksDataSource.swift index 39834c5..3dd9dc7 100644 --- a/Persephone/DataSources/AlbumTracksDataSource.swift +++ b/Persephone/DataSources/AlbumTracksDataSource.swift @@ -39,6 +39,16 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource { } } + func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { + let item = albumSongs[row] + + let pasteboardItem = NSPasteboardItem() + + pasteboardItem.setPropertyList(["songUri": item.song?.mpdSong.uriString], forType: .songPasteboardType) + + return pasteboardItem + } + func numberOfRows(in tableView: NSTableView) -> Int { return albumSongs.count } diff --git a/Persephone/DataSources/QueueDataSource.swift b/Persephone/DataSources/QueueDataSource.swift index 4649ae7..506df4a 100644 --- a/Persephone/DataSources/QueueDataSource.swift +++ b/Persephone/DataSources/QueueDataSource.swift @@ -8,8 +8,6 @@ import AppKit -let REORDER_PASTEBOARD_TYPE = NSPasteboard.PasteboardType("me.danbarber.persephone") - class QueueDataSource: NSObject, NSOutlineViewDataSource { var queue: [QueueItem] = [] var queueIcon: NSImage? = nil @@ -47,7 +45,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { let pasteboardItem = NSPasteboardItem() - pasteboardItem.setPropertyList(["queuePos": queueItem.queuePos], forType: REORDER_PASTEBOARD_TYPE) + pasteboardItem.setPropertyList(["queuePos": queueItem.queuePos], forType: .songPasteboardType) return pasteboardItem } @@ -56,34 +54,50 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { var newQueuePos = index - 1 guard let draggingTypes = info.draggingPasteboard.types, - draggingTypes.contains(REORDER_PASTEBOARD_TYPE), - let payload = info.draggingPasteboard.propertyList(forType: REORDER_PASTEBOARD_TYPE) as? [String: Int], + draggingTypes.contains(.songPasteboardType) + else { return [] } + + if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: Int], let queuePos = payload["queuePos"], - newQueuePos >= 0 - else { return [] } + newQueuePos >= 0 { - if newQueuePos > queuePos { newQueuePos -= 1 } + if newQueuePos > queuePos { newQueuePos -= 1 } - guard queuePos != newQueuePos - else { return [] } + guard queuePos != newQueuePos + else { return [] } - return .move + return .move + } else if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: String], + let _ = payload["songUri"], + newQueuePos >= 0 { + return .copy + } + + return [] } func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { var newQueuePos = index - 1 - guard let payload = info.draggingPasteboard.propertyList(forType: REORDER_PASTEBOARD_TYPE) as? [String: Int], - let queuePos = payload["queuePos"] - else { return false } + if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: Int], + let queuePos = payload["queuePos"] { - if newQueuePos > queuePos { newQueuePos -= 1 } + if newQueuePos > queuePos { newQueuePos -= 1 } - guard queuePos != newQueuePos - else { return false } + guard queuePos != newQueuePos + else { return false } - App.store.dispatch(MPDMoveSongInQueue(oldQueuePos: queuePos, newQueuePos: newQueuePos)) + App.store.dispatch(MPDMoveSongInQueue(oldQueuePos: queuePos, newQueuePos: newQueuePos)) - return true + return true + } else if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: String], + let songUri = payload["songUri"] { + + App.store.dispatch(MPDAddSongToQueue(songUri: songUri, queuePos: newQueuePos)) + + return true + } + + return false } } diff --git a/Persephone/Extensions/NSPasteboard.swift b/Persephone/Extensions/NSPasteboard.swift new file mode 100644 index 0000000..52e9401 --- /dev/null +++ b/Persephone/Extensions/NSPasteboard.swift @@ -0,0 +1,13 @@ +// +// NSPasteboard.swift +// Persephone +// +// Created by Daniel Barber on 2019/6/17. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import AppKit + +extension NSPasteboard.PasteboardType { + static let songPasteboardType = NSPasteboard.PasteboardType("me.danbarber.persephone") +} diff --git a/Persephone/MPDClient/Extensions/MPDClient+Command.swift b/Persephone/MPDClient/Extensions/MPDClient+Command.swift index 41b776a..cff97af 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Command.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Command.swift @@ -75,6 +75,12 @@ extension MPDClient { else { return } sendMoveSongInQueue(at: oldQueuePos, to: newQueuePos) + case .addSongToQueue: + guard let songUri = userData["uri"] as? String, + let queuePos = userData["queuePos"] as? Int + else { return } + sendAddSongToQueue(uri: songUri, at: queuePos) + // Album commands case .fetchAllAlbums: allAlbums() diff --git a/Persephone/MPDClient/Extensions/MPDClient+Queue.swift b/Persephone/MPDClient/Extensions/MPDClient+Queue.swift index ee0ab5a..4828f02 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Queue.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Queue.swift @@ -34,6 +34,10 @@ extension MPDClient { enqueueCommand(command: .moveSongInQueue, userData: ["oldQueuePos": queuePos, "newQueuePos": newQueuePos]) } + func addSongToQueue(songUri: String, at queuePos: Int) { + enqueueCommand(command: .addSongToQueue, userData: ["uri": songUri, "queuePos": queuePos]) + } + func sendPlayTrack(at queuePos: Int) { mpd_run_play_pos(self.connection, UInt32(queuePos)) } @@ -72,4 +76,8 @@ extension MPDClient { func sendMoveSongInQueue(at oldQueuePos: Int, to newQueuePos: Int) { mpd_run_move(self.connection, UInt32(oldQueuePos), UInt32(newQueuePos)) } + + func sendAddSongToQueue(uri: String, at queuePos: Int) { + mpd_run_add_id_to(self.connection, uri, UInt32(queuePos)) + } } diff --git a/Persephone/MPDClient/Models/MPDCommand.swift b/Persephone/MPDClient/Models/MPDCommand.swift index bec3845..def0fd5 100644 --- a/Persephone/MPDClient/Models/MPDCommand.swift +++ b/Persephone/MPDClient/Models/MPDCommand.swift @@ -34,6 +34,7 @@ extension MPDClient { case appendSong case removeSong case moveSongInQueue + case addSongToQueue // Album commands case fetchAllAlbums diff --git a/Persephone/State/Actions/MPDActions.swift b/Persephone/State/Actions/MPDActions.swift index bc95a68..c6d9a78 100644 --- a/Persephone/State/Actions/MPDActions.swift +++ b/Persephone/State/Actions/MPDActions.swift @@ -23,6 +23,11 @@ struct MPDMoveSongInQueue: Action { let newQueuePos: Int } +struct MPDAddSongToQueue: Action { + let songUri: String + let queuePos: Int +} + struct MPDAppendTrack: Action { let song: MPDClient.MPDSong } diff --git a/Persephone/State/Reducers/MPDReducer.swift b/Persephone/State/Reducers/MPDReducer.swift index c25386d..56d2dbe 100644 --- a/Persephone/State/Reducers/MPDReducer.swift +++ b/Persephone/State/Reducers/MPDReducer.swift @@ -36,6 +36,9 @@ func mpdReducer(action: Action, state: MPDState?) -> MPDState { case let action as MPDMoveSongInQueue: App.mpdClient.moveSongInQueue(at: action.oldQueuePos, to: action.newQueuePos) + case let action as MPDAddSongToQueue: + App.mpdClient.addSongToQueue(songUri: action.songUri, at: action.queuePos) + case let action as MPDAppendTrack: App.mpdClient.appendSong(action.song)