001: /*******************************************************************************
002: * Copyright (c) 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: ******************************************************************************/package org.eclipse.ui.navigator.resources;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014:
015: import org.eclipse.core.resources.IContainer;
016: import org.eclipse.core.resources.IProject;
017: import org.eclipse.core.resources.IResource;
018: import org.eclipse.core.runtime.CoreException;
019: import org.eclipse.core.runtime.IAdaptable;
020: import org.eclipse.core.runtime.IStatus;
021: import org.eclipse.core.runtime.MultiStatus;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.jface.dialogs.ErrorDialog;
024: import org.eclipse.jface.util.LocalSelectionTransfer;
025: import org.eclipse.jface.viewers.ISelection;
026: import org.eclipse.jface.viewers.IStructuredSelection;
027: import org.eclipse.swt.dnd.DND;
028: import org.eclipse.swt.dnd.DropTargetEvent;
029: import org.eclipse.swt.dnd.FileTransfer;
030: import org.eclipse.swt.dnd.TransferData;
031: import org.eclipse.swt.widgets.Display;
032: import org.eclipse.swt.widgets.Shell;
033: import org.eclipse.ui.PlatformUI;
034: import org.eclipse.ui.actions.CopyFilesAndFoldersOperation;
035: import org.eclipse.ui.actions.MoveFilesAndFoldersOperation;
036: import org.eclipse.ui.actions.ReadOnlyStateChecker;
037: import org.eclipse.ui.internal.navigator.resources.plugin.WorkbenchNavigatorMessages;
038: import org.eclipse.ui.internal.navigator.resources.plugin.WorkbenchNavigatorPlugin;
039: import org.eclipse.ui.navigator.CommonDropAdapter;
040: import org.eclipse.ui.navigator.CommonDropAdapterAssistant;
041: import org.eclipse.ui.part.ResourceTransfer;
042:
043: /**
044: *
045: * Clients may reference this class in the <b>dropAssistant</b> element of a
046: * <b>org.eclipse.ui.navigator.navigatorContent</b> extension point.
047: *
048: * <p>
049: * Clients may not extend or instantiate this class for any purpose.
050: * Clients may have no direct dependencies on the contract of this class.
051: * </p>
052: *
053: * @since 3.2
054: *
055: */
056: public class ResourceDropAdapterAssistant extends
057: CommonDropAdapterAssistant {
058:
059: private static final boolean DEBUG = false;
060: private static final IResource[] NO_RESOURCES = new IResource[0];
061:
062: /*
063: * (non-Javadoc)
064: *
065: * @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#isSupportedType(org.eclipse.swt.dnd.TransferData)
066: */
067: public boolean isSupportedType(TransferData aTransferType) {
068: return super .isSupportedType(aTransferType)
069: || FileTransfer.getInstance().isSupportedType(
070: aTransferType);
071: }
072:
073: /*
074: * (non-Javadoc)
075: *
076: * @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#validateDrop(java.lang.Object,
077: * int, org.eclipse.swt.dnd.TransferData)
078: */
079: public IStatus validateDrop(Object target, int aDropOperation,
080: TransferData transferType) {
081:
082: if (!(target instanceof IResource)) {
083: return WorkbenchNavigatorPlugin
084: .createStatus(
085: IStatus.INFO,
086: 0,
087: WorkbenchNavigatorMessages.DropAdapter_targetMustBeResource,
088: null);
089: }
090: IResource resource = (IResource) target;
091: if (!resource.isAccessible()) {
092: return WorkbenchNavigatorPlugin
093: .createErrorStatus(
094: 0,
095: WorkbenchNavigatorMessages.DropAdapter_canNotDropIntoClosedProject,
096: null);
097: }
098: IContainer destination = getActualTarget(resource);
099: if (destination.getType() == IResource.ROOT) {
100: return WorkbenchNavigatorPlugin
101: .createErrorStatus(
102: 0,
103: WorkbenchNavigatorMessages.DropAdapter_resourcesCanNotBeSiblings,
104: null);
105: }
106: String message = null;
107: // drag within Eclipse?
108: if (LocalSelectionTransfer.getTransfer().isSupportedType(
109: transferType)) {
110: IResource[] selectedResources = getSelectedResources();
111:
112: boolean bProjectDrop = false;
113: for (int iRes = 0; iRes < selectedResources.length; iRes++) {
114: IResource res = selectedResources[iRes];
115: if (res instanceof IProject) {
116: bProjectDrop = true;
117: }
118: }
119: if (bProjectDrop) {
120: // drop of projects not supported on other IResources
121: // "Path for project must have only one segment."
122: message = WorkbenchNavigatorMessages.DropAdapter_canNotDropProjectIntoProject;
123: } else {
124: if (selectedResources.length == 0) {
125: message = WorkbenchNavigatorMessages.DropAdapter_dropOperationErrorOther;
126: } else {
127: CopyFilesAndFoldersOperation operation;
128: if (aDropOperation == DND.DROP_COPY) {
129: if (DEBUG) {
130: System.out
131: .println("ResourceDropAdapterAssistant.validateDrop validating COPY."); //$NON-NLS-1$
132: }
133:
134: operation = new CopyFilesAndFoldersOperation(
135: getShell());
136: } else {
137: if (DEBUG) {
138: System.out
139: .println("ResourceDropAdapterAssistant.validateDrop validating MOVE."); //$NON-NLS-1$
140: }
141: operation = new MoveFilesAndFoldersOperation(
142: getShell());
143: }
144: message = operation.validateDestination(
145: destination, selectedResources);
146: }
147: }
148: } // file import?
149: else if (FileTransfer.getInstance().isSupportedType(
150: transferType)) {
151: String[] sourceNames = (String[]) FileTransfer
152: .getInstance().nativeToJava(transferType);
153: if (sourceNames == null) {
154: // source names will be null on Linux. Use empty names to do
155: // destination validation.
156: // Fixes bug 29778
157: sourceNames = new String[0];
158: }
159: CopyFilesAndFoldersOperation copyOperation = new CopyFilesAndFoldersOperation(
160: getShell());
161: message = copyOperation.validateImportDestination(
162: destination, sourceNames);
163: }
164: if (message != null) {
165: return WorkbenchNavigatorPlugin.createErrorStatus(0,
166: message, null);
167: }
168: return Status.OK_STATUS;
169: }
170:
171: /*
172: * (non-Javadoc)
173: *
174: * @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#handleDrop(CommonDropAdapter,
175: * DropTargetEvent, Object)
176: */
177: public IStatus handleDrop(CommonDropAdapter aDropAdapter,
178: DropTargetEvent aDropTargetEvent, Object aTarget) {
179:
180: if (DEBUG) {
181: System.out
182: .println("ResourceDropAdapterAssistant.handleDrop (begin)"); //$NON-NLS-1$
183: }
184:
185: // alwaysOverwrite = false;
186: if (aDropAdapter.getCurrentTarget() == null
187: || aDropTargetEvent.data == null) {
188: return Status.CANCEL_STATUS;
189: }
190: IStatus status = null;
191: IResource[] resources = null;
192: TransferData currentTransfer = aDropAdapter
193: .getCurrentTransfer();
194: if (LocalSelectionTransfer.getTransfer().isSupportedType(
195: currentTransfer)) {
196: resources = getSelectedResources();
197: } else if (ResourceTransfer.getInstance().isSupportedType(
198: currentTransfer)) {
199: resources = (IResource[]) aDropTargetEvent.data;
200: }
201:
202: if (FileTransfer.getInstance().isSupportedType(currentTransfer)) {
203: status = performFileDrop(aDropAdapter,
204: aDropTargetEvent.data);
205: } else if (resources != null && resources.length > 0) {
206: if (aDropAdapter.getCurrentOperation() == DND.DROP_COPY) {
207: if (DEBUG) {
208: System.out
209: .println("ResourceDropAdapterAssistant.handleDrop executing COPY."); //$NON-NLS-1$
210: }
211: status = performResourceCopy(aDropAdapter, getShell(),
212: resources);
213: } else {
214: if (DEBUG) {
215: System.out
216: .println("ResourceDropAdapterAssistant.handleDrop executing MOVE."); //$NON-NLS-1$
217: }
218:
219: status = performResourceMove(aDropAdapter, resources);
220: }
221: }
222: openError(status);
223: IContainer target = getActualTarget((IResource) aDropAdapter
224: .getCurrentTarget());
225: if (target != null && target.isAccessible()) {
226: try {
227: target.refreshLocal(IResource.DEPTH_ONE, null);
228: } catch (CoreException e) {
229: }
230: }
231: return status;
232: }
233:
234: /*
235: * (non-Javadoc)
236: *
237: * @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#validatePluginTransferDrop(org.eclipse.jface.viewers.IStructuredSelection,
238: * java.lang.Object)
239: */
240: public IStatus validatePluginTransferDrop(
241: IStructuredSelection aDragSelection, Object aDropTarget) {
242: if (!(aDropTarget instanceof IResource)) {
243: return WorkbenchNavigatorPlugin
244: .createStatus(
245: IStatus.INFO,
246: 0,
247: WorkbenchNavigatorMessages.DropAdapter_targetMustBeResource,
248: null);
249: }
250: IResource resource = (IResource) aDropTarget;
251: if (!resource.isAccessible()) {
252: return WorkbenchNavigatorPlugin
253: .createErrorStatus(
254: 0,
255: WorkbenchNavigatorMessages.DropAdapter_canNotDropIntoClosedProject,
256: null);
257: }
258: IContainer destination = getActualTarget(resource);
259: if (destination.getType() == IResource.ROOT) {
260: return WorkbenchNavigatorPlugin
261: .createErrorStatus(
262: 0,
263: WorkbenchNavigatorMessages.DropAdapter_resourcesCanNotBeSiblings,
264: null);
265: }
266:
267: IResource[] selectedResources = getSelectedResources(aDragSelection);
268:
269: String message = null;
270: if (selectedResources.length == 0) {
271: message = WorkbenchNavigatorMessages.DropAdapter_dropOperationErrorOther;
272: } else {
273: MoveFilesAndFoldersOperation operation;
274:
275: operation = new MoveFilesAndFoldersOperation(getShell());
276: message = operation.validateDestination(destination,
277: selectedResources);
278: }
279: if (message != null) {
280: return WorkbenchNavigatorPlugin.createErrorStatus(0,
281: message, null);
282: }
283: return Status.OK_STATUS;
284: }
285:
286: /* (non-Javadoc)
287: * @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#handlePluginTransferDrop(org.eclipse.jface.viewers.IStructuredSelection, java.lang.Object)
288: */
289: public IStatus handlePluginTransferDrop(
290: IStructuredSelection aDragSelection, Object aDropTarget) {
291:
292: IContainer target = getActualTarget((IResource) aDropTarget);
293: IResource[] resources = getSelectedResources(aDragSelection);
294:
295: MoveFilesAndFoldersOperation operation = new MoveFilesAndFoldersOperation(
296: getShell());
297: operation.copyResources(resources, target);
298:
299: if (target != null && target.isAccessible()) {
300: try {
301: target.refreshLocal(IResource.DEPTH_ONE, null);
302: } catch (CoreException e) {
303: }
304: }
305: return Status.OK_STATUS;
306: }
307:
308: /**
309: * Returns the actual target of the drop, given the resource under the
310: * mouse. If the mouse target is a file, then the drop actually occurs in
311: * its parent. If the drop location is before or after the mouse target and
312: * feedback is enabled, the target is also the parent.
313: */
314: private IContainer getActualTarget(IResource mouseTarget) {
315:
316: /* if cursor is on a file, return the parent */
317: if (mouseTarget.getType() == IResource.FILE) {
318: return mouseTarget.getParent();
319: }
320: /* otherwise the mouseTarget is the real target */
321: return (IContainer) mouseTarget;
322: }
323:
324: /**
325: * Returns the resource selection from the LocalSelectionTransfer.
326: *
327: * @return the resource selection from the LocalSelectionTransfer
328: */
329: private IResource[] getSelectedResources() {
330:
331: ISelection selection = LocalSelectionTransfer.getTransfer()
332: .getSelection();
333: if (selection instanceof IStructuredSelection) {
334: return getSelectedResources((IStructuredSelection) selection);
335: }
336: return NO_RESOURCES;
337: }
338:
339: /**
340: * Returns the resource selection from the LocalSelectionTransfer.
341: *
342: * @return the resource selection from the LocalSelectionTransfer
343: */
344: private IResource[] getSelectedResources(
345: IStructuredSelection selection) {
346: ArrayList selectedResources = new ArrayList();
347:
348: for (Iterator i = selection.iterator(); i.hasNext();) {
349: Object o = i.next();
350: if (o instanceof IResource) {
351: selectedResources.add(o);
352: } else if (o instanceof IAdaptable) {
353: IAdaptable a = (IAdaptable) o;
354: IResource r = (IResource) a.getAdapter(IResource.class);
355: if (r != null) {
356: selectedResources.add(r);
357: }
358: }
359: }
360: return (IResource[]) selectedResources
361: .toArray(new IResource[selectedResources.size()]);
362: }
363:
364: /**
365: * Performs a resource copy
366: */
367: private IStatus performResourceCopy(CommonDropAdapter dropAdapter,
368: Shell shell, IResource[] sources) {
369: MultiStatus problems = new MultiStatus(PlatformUI.PLUGIN_ID, 1,
370: WorkbenchNavigatorMessages.DropAdapter_problemsMoving,
371: null);
372: mergeStatus(problems, validateTarget(dropAdapter
373: .getCurrentTarget(), dropAdapter.getCurrentTransfer(),
374: dropAdapter.getCurrentOperation()));
375:
376: IContainer target = getActualTarget((IResource) dropAdapter
377: .getCurrentTarget());
378: CopyFilesAndFoldersOperation operation = new CopyFilesAndFoldersOperation(
379: shell);
380: operation.copyResources(sources, target);
381:
382: return problems;
383: }
384:
385: /**
386: * Performs a resource move
387: */
388: private IStatus performResourceMove(CommonDropAdapter dropAdapter,
389: IResource[] sources) {
390: MultiStatus problems = new MultiStatus(PlatformUI.PLUGIN_ID, 1,
391: WorkbenchNavigatorMessages.DropAdapter_problemsMoving,
392: null);
393: mergeStatus(problems, validateTarget(dropAdapter
394: .getCurrentTarget(), dropAdapter.getCurrentTransfer(),
395: dropAdapter.getCurrentOperation()));
396:
397: IContainer target = getActualTarget((IResource) dropAdapter
398: .getCurrentTarget());
399: ReadOnlyStateChecker checker = new ReadOnlyStateChecker(
400: getShell(),
401: WorkbenchNavigatorMessages.MoveResourceAction_title,
402: WorkbenchNavigatorMessages.MoveResourceAction_checkMoveMessage);
403: sources = checker.checkReadOnlyResources(sources);
404: MoveFilesAndFoldersOperation operation = new MoveFilesAndFoldersOperation(
405: getShell());
406: operation.copyResources(sources, target);
407:
408: return problems;
409: }
410:
411: /**
412: * Performs a drop using the FileTransfer transfer type.
413: */
414: private IStatus performFileDrop(CommonDropAdapter anAdapter,
415: Object data) {
416: MultiStatus problems = new MultiStatus(
417: PlatformUI.PLUGIN_ID,
418: 0,
419: WorkbenchNavigatorMessages.DropAdapter_problemImporting,
420: null);
421: mergeStatus(problems, validateTarget(anAdapter
422: .getCurrentTarget(), anAdapter.getCurrentTransfer(),
423: anAdapter.getCurrentOperation()));
424:
425: final IContainer target = getActualTarget((IResource) anAdapter
426: .getCurrentTarget());
427: final String[] names = (String[]) data;
428: // Run the import operation asynchronously.
429: // Otherwise the drag source (e.g., Windows Explorer) will be blocked
430: // while the operation executes. Fixes bug 16478.
431: Display.getCurrent().asyncExec(new Runnable() {
432: public void run() {
433: getShell().forceActive();
434: CopyFilesAndFoldersOperation operation = new CopyFilesAndFoldersOperation(
435: getShell());
436: operation.copyFiles(names, target);
437: }
438: });
439: return problems;
440: }
441:
442: /**
443: * Ensures that the drop target meets certain criteria
444: */
445: private IStatus validateTarget(Object target,
446: TransferData transferType, int dropOperation) {
447: if (!(target instanceof IResource)) {
448: return WorkbenchNavigatorPlugin
449: .createInfoStatus(WorkbenchNavigatorMessages.DropAdapter_targetMustBeResource);
450: }
451: IResource resource = (IResource) target;
452: if (!resource.isAccessible()) {
453: return WorkbenchNavigatorPlugin
454: .createErrorStatus(WorkbenchNavigatorMessages.DropAdapter_canNotDropIntoClosedProject);
455: }
456: IContainer destination = getActualTarget(resource);
457: if (destination.getType() == IResource.ROOT) {
458: return WorkbenchNavigatorPlugin
459: .createErrorStatus(WorkbenchNavigatorMessages.DropAdapter_resourcesCanNotBeSiblings);
460: }
461: String message = null;
462: // drag within Eclipse?
463: if (LocalSelectionTransfer.getTransfer().isSupportedType(
464: transferType)) {
465: IResource[] selectedResources = getSelectedResources();
466:
467: if (selectedResources.length == 0) {
468: message = WorkbenchNavigatorMessages.DropAdapter_dropOperationErrorOther;
469: } else {
470: CopyFilesAndFoldersOperation operation;
471: if (dropOperation == DND.DROP_COPY) {
472: operation = new CopyFilesAndFoldersOperation(
473: getShell());
474: } else {
475: operation = new MoveFilesAndFoldersOperation(
476: getShell());
477: }
478: message = operation.validateDestination(destination,
479: selectedResources);
480: }
481: } // file import?
482: else if (FileTransfer.getInstance().isSupportedType(
483: transferType)) {
484: String[] sourceNames = (String[]) FileTransfer
485: .getInstance().nativeToJava(transferType);
486: if (sourceNames == null) {
487: // source names will be null on Linux. Use empty names to do
488: // destination validation.
489: // Fixes bug 29778
490: sourceNames = new String[0];
491: }
492: CopyFilesAndFoldersOperation copyOperation = new CopyFilesAndFoldersOperation(
493: getShell());
494: message = copyOperation.validateImportDestination(
495: destination, sourceNames);
496: }
497: if (message != null) {
498: return WorkbenchNavigatorPlugin.createErrorStatus(message);
499: }
500: return Status.OK_STATUS;
501: }
502:
503: /**
504: * Adds the given status to the list of problems. Discards OK statuses. If
505: * the status is a multi-status, only its children are added.
506: */
507: private void mergeStatus(MultiStatus status, IStatus toMerge) {
508: if (!toMerge.isOK()) {
509: status.merge(toMerge);
510: }
511: }
512:
513: /**
514: * Opens an error dialog if necessary. Takes care of complex rules necessary
515: * for making the error dialog look nice.
516: */
517: private void openError(IStatus status) {
518: if (status == null) {
519: return;
520: }
521:
522: String genericTitle = WorkbenchNavigatorMessages.DropAdapter_title;
523: int codes = IStatus.ERROR | IStatus.WARNING;
524:
525: // simple case: one error, not a multistatus
526: if (!status.isMultiStatus()) {
527: ErrorDialog.openError(getShell(), genericTitle, null,
528: status, codes);
529: return;
530: }
531:
532: // one error, single child of multistatus
533: IStatus[] children = status.getChildren();
534: if (children.length == 1) {
535: ErrorDialog.openError(getShell(), status.getMessage(),
536: null, children[0], codes);
537: return;
538: }
539: // several problems
540: ErrorDialog.openError(getShell(), genericTitle, null, status,
541: codes);
542: }
543:
544: }
|