1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// Licensed under the MIT license.
3
3
4
- import { DataTransfer , DataTransferItem , TreeDragAndDropController } from "vscode" ;
4
+ import * as path from "path" ;
5
+ import { commands , DataTransfer , DataTransferItem , TreeDragAndDropController , Uri , window , workspace , WorkspaceEdit } from "vscode" ;
6
+ import { Commands } from "../commands" ;
5
7
import { Explorer } from "../constants" ;
6
8
import { BaseSymbolNode } from "./baseSymbolNode" ;
9
+ import { ContainerNode , ContainerType } from "./containerNode" ;
7
10
import { DataNode } from "./dataNode" ;
8
11
import { ExplorerNode } from "./explorerNode" ;
9
12
import { FileNode } from "./fileNode" ;
13
+ import { FolderNode } from "./folderNode" ;
14
+ import { explorerNodeCache } from "./nodeCache/explorerNodeCache" ;
15
+ import { PackageNode } from "./packageNode" ;
16
+ import { PackageRootNode } from "./packageRootNode" ;
10
17
import { PrimaryTypeNode } from "./PrimaryTypeNode" ;
18
+ import { ProjectNode } from "./projectNode" ;
19
+ import { WorkspaceNode } from "./workspaceNode" ;
11
20
12
21
export class DragAndDropController implements TreeDragAndDropController < ExplorerNode > {
13
22
@@ -16,17 +25,31 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
16
25
] ;
17
26
dragMimeTypes : string [ ] = [
18
27
Explorer . Mime . TextUriList ,
19
- ] ; ;
28
+ ] ;
20
29
21
30
public handleDrag ( source : ExplorerNode [ ] , treeDataTransfer : DataTransfer ) : void {
22
31
// select many is not supported yet
23
- let dragItem = source [ 0 ] ;
32
+ const dragItem = source [ 0 ] ;
24
33
this . addDragToEditorDataTransfer ( dragItem , treeDataTransfer ) ;
34
+ this . addInternalDragDataTransfer ( dragItem , treeDataTransfer ) ;
35
+ }
36
+
37
+ public async handleDrop ( target : ExplorerNode | undefined , dataTransfer : DataTransfer ) : Promise < void > {
38
+ const data = dataTransfer . get ( Explorer . Mime . JavaProjectExplorer ) ;
39
+ if ( data ) {
40
+ await this . dropFromJavaProjectExplorer ( target , data . value ) ;
41
+ return ;
42
+ }
25
43
}
26
44
45
+ /**
46
+ * Add data transfer that is used when node is dropped to the editor.
47
+ * @param node node being dragged.
48
+ * @param treeDataTransfer A map containing a mapping of the mime type of the corresponding transferred data.
49
+ */
27
50
private addDragToEditorDataTransfer ( node : ExplorerNode , treeDataTransfer : DataTransfer ) {
28
- if ( ( node instanceof PrimaryTypeNode || node instanceof FileNode ) && ( node as DataNode ) . uri ) {
29
- treeDataTransfer . set ( Explorer . Mime . TextUriList , new DataTransferItem ( ( node as DataNode ) . uri ) ) ;
51
+ if ( ( node instanceof PrimaryTypeNode || node instanceof FileNode ) && node . uri ) {
52
+ treeDataTransfer . set ( Explorer . Mime . TextUriList , new DataTransferItem ( node . uri ) ) ;
30
53
} else if ( ( node instanceof BaseSymbolNode ) ) {
31
54
const parent = ( node . getParent ( ) as PrimaryTypeNode ) ;
32
55
if ( parent . uri ) {
@@ -37,4 +60,154 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
37
60
}
38
61
}
39
62
}
63
+
64
+ /**
65
+ * Add data transfer that is used when node is dropped into other Java Project Explorer node.
66
+ * @param node node being dragged.
67
+ * @param treeDataTransfer A map containing a mapping of the mime type of the corresponding transferred data.
68
+ */
69
+ private addInternalDragDataTransfer ( node : ExplorerNode , treeDataTransfer : DataTransfer ) : void {
70
+ // draggable node must have uri
71
+ if ( ! ( node instanceof DataNode ) || ! node . uri ) {
72
+ return ;
73
+ }
74
+
75
+ // whether the node can be dropped will be check in handleDrop(...)
76
+ treeDataTransfer . set ( Explorer . Mime . JavaProjectExplorer , new DataTransferItem ( node . uri ) ) ;
77
+ }
78
+
79
+ /**
80
+ * Handle the DnD event which comes from Java Project explorer itself.
81
+ * @param target the drop node.
82
+ * @param uri uri in the data transfer.
83
+ */
84
+ public async dropFromJavaProjectExplorer ( target : ExplorerNode | undefined , uri : string ) : Promise < void > {
85
+ const source : DataNode | undefined = explorerNodeCache . getDataNode ( Uri . parse ( uri ) ) ;
86
+ if ( ! this . isDraggableNode ( source ) ) {
87
+ return ;
88
+ }
89
+
90
+ if ( ! this . isDroppableNode ( target ) ) {
91
+ return ;
92
+ }
93
+
94
+ // check if the target node is source node itself or its parent.
95
+ if ( target ?. isItselfOrAncestorOf ( source , 1 /*levelToCheck*/ ) ) {
96
+ return ;
97
+ }
98
+
99
+ if ( target instanceof ContainerNode ) {
100
+ if ( target . getContainerType ( ) !== ContainerType . ReferencedLibrary ) {
101
+ return ;
102
+ }
103
+
104
+ if ( ! ( target . getParent ( ) as ProjectNode ) . isUnmanagedFolder ( ) ) {
105
+ return ;
106
+ }
107
+
108
+ // TODO: referenced library
109
+ } else if ( target instanceof PackageRootNode || target instanceof PackageNode
110
+ || target instanceof FolderNode ) {
111
+ await this . move ( source ! , target ) ;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Check whether the dragged node is draggable.
117
+ * @param node the dragged node.
118
+ */
119
+ private isDraggableNode ( node : DataNode | undefined ) : boolean {
120
+ if ( ! node ?. uri ) {
121
+ return false ;
122
+ }
123
+ if ( node instanceof WorkspaceNode || node instanceof ProjectNode
124
+ || node instanceof PackageRootNode || node instanceof ContainerNode
125
+ || node instanceof BaseSymbolNode ) {
126
+ return false ;
127
+ }
128
+
129
+ return this . isUnderSourceRoot ( node ) ;
130
+ }
131
+
132
+ /**
133
+ * Check whether the node is under source root.
134
+ *
135
+ * Note: There is one exception: The primary type directly under an unmanaged folder project,
136
+ * in that case, `true` is returned.
137
+ * @param node DataNode
138
+ */
139
+ private isUnderSourceRoot ( node : DataNode ) : boolean {
140
+ let parent = node . getParent ( ) ;
141
+ while ( parent ) {
142
+ if ( parent instanceof ContainerNode ) {
143
+ return false ;
144
+ }
145
+
146
+ if ( parent instanceof PackageRootNode ) {
147
+ return parent . isSourceRoot ( ) ;
148
+ }
149
+ parent = parent . getParent ( ) ;
150
+ }
151
+ return true ;
152
+ }
153
+
154
+ /**
155
+ * Check whether the node is able to be dropped.
156
+ */
157
+ private isDroppableNode ( node : ExplorerNode | undefined ) : boolean {
158
+ // drop to root is not supported yet
159
+ if ( ! node ) {
160
+ return false ;
161
+ }
162
+
163
+ if ( node instanceof DataNode && ! node . uri ) {
164
+ return false ;
165
+ }
166
+
167
+ if ( node instanceof WorkspaceNode || node instanceof ProjectNode
168
+ || node instanceof BaseSymbolNode ) {
169
+ return false ;
170
+ }
171
+
172
+ let parent : ExplorerNode | undefined = node ;
173
+ while ( parent ) {
174
+ if ( parent instanceof ProjectNode ) {
175
+ return false ;
176
+ } else if ( parent instanceof PackageRootNode ) {
177
+ return parent . isSourceRoot ( ) ;
178
+ } else if ( parent instanceof ContainerNode ) {
179
+ if ( parent . getContainerType ( ) === ContainerType . ReferencedLibrary ) {
180
+ return ( parent . getParent ( ) as ProjectNode ) . isUnmanagedFolder ( ) ;
181
+ }
182
+ return false ;
183
+ }
184
+ parent = parent . getParent ( ) ;
185
+ }
186
+ return false ;
187
+ }
188
+
189
+ /**
190
+ * Trigger a workspace edit that move the source node into the target node.
191
+ */
192
+ private async move ( source : DataNode , target : DataNode ) : Promise < void > {
193
+ const sourceUri = Uri . parse ( source . uri ! ) ;
194
+ const targetUri = Uri . parse ( target . uri ! ) ;
195
+ if ( sourceUri === targetUri ) {
196
+ return ;
197
+ }
198
+
199
+ const newPath = path . join ( targetUri . fsPath , path . basename ( sourceUri . fsPath ) ) ;
200
+ const choice = await window . showInformationMessage (
201
+ `Are you sure you want to move '${ path . basename ( sourceUri . fsPath ) } ' into '${ path . basename ( targetUri . fsPath ) } '?` ,
202
+ { modal : true } ,
203
+ "Move" ,
204
+ ) ;
205
+
206
+ if ( choice === "Move" ) {
207
+ const edit = new WorkspaceEdit ( ) ;
208
+ edit . renameFile ( sourceUri , Uri . file ( newPath ) ) ;
209
+ await workspace . applyEdit ( edit ) ;
210
+ commands . executeCommand ( Commands . VIEW_PACKAGE_REFRESH , /* debounce = */ true ) ;
211
+ }
212
+ }
40
213
}
0 commit comments