diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee
index 5abf3031..440ff4b3 100644
--- a/lib/tree-view.coffee
+++ b/lib/tree-view.coffee
@@ -738,12 +738,17 @@ class TreeView extends View
onMouseDown: (e) ->
e.stopPropagation()
- # return early if we're opening a contextual menu (right click) during multi-select mode
- if @multiSelectEnabled() and
- e.currentTarget.classList.contains('selected') and
- # mouse right click or ctrl click as right click on darwin platforms
- (e.button is 2 or e.ctrlKey and process.platform is 'darwin')
- return
+ if @multiSelectEnabled() and e.currentTarget.classList.contains('selected')
+ # return early if we're opening a contextual menu (right click) during multi-select mode
+ if (e.button is 2 or e.ctrlKey and process.platform is 'darwin')
+ return
+ # return early unless we're deselecting an entry (metaKey on OSX, ctrl/metaKey on linux/windows)
+ if process.platform is 'darwin'
+ if not e.metaKey
+ return
+ else if process.platform isnt 'darwin'
+ if not (e.ctrlKey or e.metaKey)
+ return
entryToSelect = e.currentTarget
@@ -835,26 +840,33 @@ class TreeView extends View
onDragStart: (e) ->
e.stopPropagation()
- target = $(e.currentTarget).find(".name")
- initialPath = target.data("path")
+ initialPaths = []
+ targets = []
- style = getStyleObject(target[0])
-
- fileNameElement = target.clone()
- .css(style)
+ eventTarget = $(e.currentTarget).find('.name')
+ dragImage = $('
', {class: 'entries list-tree'})
.css(
position: 'absolute'
top: 0
left: 0
)
- fileNameElement.appendTo(document.body)
- e.originalEvent.dataTransfer.effectAllowed = "move"
- e.originalEvent.dataTransfer.setDragImage(fileNameElement[0], 0, 0)
- e.originalEvent.dataTransfer.setData("initialPath", initialPath)
+ for entry in @getSelectedEntries()
+ entryPath = $(entry).find('.name').data('path')
+ unless path.dirname(entryPath) in initialPaths
+ initialPaths.push(entryPath)
+ image = $(entry).clone()
+ image.find('.entry').addBack('.entry').removeClass('selected')
+ dragImage.append(image)
+
+ dragImage.appendTo(document.body)
+
+ e.originalEvent.dataTransfer.effectAllowed = 'move'
+ e.originalEvent.dataTransfer.setDragImage(dragImage[0], 0, 0)
+ e.originalEvent.dataTransfer.setData('initialPaths', initialPaths)
window.requestAnimationFrame ->
- fileNameElement.remove()
+ dragImage.remove()
# Handle entry dragover event; reset default dragover actions
onDragOver: (e) ->
@@ -874,11 +886,13 @@ class TreeView extends View
newDirectoryPath = $(entry).find(".name").data("path")
return false unless newDirectoryPath
- initialPath = e.originalEvent.dataTransfer.getData("initialPath")
+ initialPaths = e.originalEvent.dataTransfer.getData("initialPaths")
- if initialPath
+ if initialPaths
# Drop event from Atom
- @moveEntry(initialPath, newDirectoryPath)
+ # iterate backwards so files in a dir are moved before the dir itself
+ for initialPath in initialPaths.split(',') by -1
+ @moveEntry(initialPath, newDirectoryPath)
else
# Drop event from OS
for file in e.originalEvent.dataTransfer.files
diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee
index 9ebb803e..618700e2 100644
--- a/spec/event-helpers.coffee
+++ b/spec/event-helpers.coffee
@@ -7,6 +7,9 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget) ->
getData: (key) -> @data[key]
setDragImage: (@image) -> return
+ for entry in dragged
+ $(entry).addClass('selected')
+
dragStartEvent = $.Event()
dragStartEvent.target = dragged
dragStartEvent.currentTarget = dragged
@@ -39,4 +42,4 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) ->
for filePath in filePaths
dropEvent.originalEvent.dataTransfer.files.push({path: filePath})
- dropEvent
\ No newline at end of file
+ dropEvent
diff --git a/spec/tree-view-spec.coffee b/spec/tree-view-spec.coffee
index f8c25ef3..69c9e6d0 100644
--- a/spec/tree-view-spec.coffee
+++ b/spec/tree-view-spec.coffee
@@ -3080,8 +3080,10 @@ describe "TreeView", ->
deltaFile = gammaDir[0].entries.children[2]
[dragStartEvent, dragEnterEvent, dropEvent] =
- eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0])
+ eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.find('.header')[0])
+
treeView.onDragStart(dragStartEvent)
+ expect(deltaFile).toHaveClass('selected')
treeView.onDragEnter(dragEnterEvent)
expect(alphaDir).toHaveClass('selected')
@@ -3104,7 +3106,7 @@ describe "TreeView", ->
deltaFile = gammaDir[0].entries.children[2]
[dragStartEvent, dragEnterEvent, dropEvent] =
- eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0], alphaDir[0])
+ eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.find('.header')[0], alphaDir[0])
runs ->
treeView.onDragStart(dragStartEvent)
@@ -3117,6 +3119,65 @@ describe "TreeView", ->
runs ->
expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3
+ describe "when dropping multiple FileViews onto a DirectoryView's header", ->
+ it "should move the files to the hovered directory", ->
+ # Dragging delta.txt onto alphaDir
+ alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
+ alphaDir[0].expand()
+
+ gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
+ gammaDir[0].expand()
+ gammaFiles = [].slice.call(gammaDir[0].entries.children, 1, 3)
+
+ [dragStartEvent, dragEnterEvent, dropEvent] =
+ eventHelpers.buildInternalDragEvents([gammaFiles], alphaDir.find('.header')[0], alphaDir[0])
+
+ runs ->
+ treeView.onDragStart(dragStartEvent)
+ treeView.onDrop(dropEvent)
+ expect(alphaDir[0].children.length).toBe 2
+
+ waitsFor "directory view contents to refresh", ->
+ $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2
+
+ runs ->
+ expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4
+
+ describe "when dropping a DirectoryView and FileViews onto a DirectoryView's header", ->
+ it "should move the files and directory to the hovered directory", ->
+ # Dragging alpha.txt and alphaDir into thetaDir
+ rootDir = $(treeView.roots[0])
+
+ alphaFile = rootDir[0].entries.children[2]
+ alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
+ alphaDir[0].expand()
+
+ gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
+ gammaDir[0].expand()
+ thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first')
+
+ dragged = [alphaFile, alphaDir[0]]
+
+ [dragStartEvent, dragEnterEvent, dropEvent] =
+ eventHelpers.buildInternalDragEvents(dragged, thetaDir.find('.header')[0], thetaDir[0])
+
+ runs ->
+ treeView.onDragStart(dragStartEvent)
+ treeView.onDrop(dropEvent)
+ expect(thetaDir[0].children.length).toBe 2
+
+ waitsFor "directory view contents to refresh", ->
+ $(treeView.roots[0].entries).find('.directory:contains(theta):first .entry').length > 2
+
+ runs ->
+ thetaDir = $(gammaDir[0].entries).find('.directory:contains(theta):first')
+ thetaDir[0].expand()
+ expect(thetaDir.find('.entry').length).toBe 2
+ # alpha dir still has all its entries
+ alphaDir = $(thetaDir[0].entries).find('.directory:contains(alpha):first')
+ alphaDir[0].expand()
+ expect(alphaDir.find('.entry').length).toBe 2
+
describe "when dropping a DirectoryView onto a DirectoryView's header", ->
it "should move the directory to the hovered directory", ->
# Dragging thetaDir onto alphaDir
@@ -3128,7 +3189,7 @@ describe "TreeView", ->
thetaDir = gammaDir[0].entries.children[0]
[dragStartEvent, dragEnterEvent, dropEvent] =
- eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.find('.header')[0], alphaDir[0])
+ eventHelpers.buildInternalDragEvents([thetaDir], alphaDir.find('.header')[0], alphaDir[0])
runs ->
treeView.onDragStart(dragStartEvent)