com.archimatetool.editor.views.tree.TreeModelViewerDragDropHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.archimatetool.editor.views.tree.TreeModelViewerDragDropHandler.java

Source

/**
 * This program and the accompanying materials
 * are made available under the terms of the License
 * which accompanies this distribution in the file LICENSE.txt
 */
package com.archimatetool.editor.views.tree;

import java.io.File;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.TreeItem;

import com.archimatetool.editor.model.IEditorModelManager;
import com.archimatetool.editor.model.commands.NonNotifyingCompoundCommand;
import com.archimatetool.editor.views.tree.commands.MoveFolderCommand;
import com.archimatetool.editor.views.tree.commands.MoveObjectCommand;
import com.archimatetool.model.FolderType;
import com.archimatetool.model.IArchimateModel;
import com.archimatetool.model.IArchimateModelElement;
import com.archimatetool.model.IFolder;
import com.archimatetool.model.INameable;

/**
 * Model Tree Drag Drop Handler
 * 
 * @author Phillip Beauvoir
 */
public class TreeModelViewerDragDropHandler {

    private StructuredViewer fViewer;

    /**
     * Drag operations we support
     */
    private int fDragOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;

    /**
     * Drop operations we support on the tree
     */
    private int fDropOperations = DND.DROP_MOVE;

    /**
     * Whether we have a valid tree selection
     */
    private boolean fIsValidTreeSelection;

    // Can only drag local type
    Transfer[] sourceTransferTypes = new Transfer[] { LocalSelectionTransfer.getTransfer() };

    // Can drop local and file types
    Transfer[] targetTransferTypes = new Transfer[] { LocalSelectionTransfer.getTransfer(),
            FileTransfer.getInstance() };

    public TreeModelViewerDragDropHandler(StructuredViewer viewer) {
        fViewer = viewer;
        registerDragSupport();
        registerDropSupport();
    }

    /**
     * Register drag support that starts from the Tree
     */
    private void registerDragSupport() {
        fViewer.addDragSupport(fDragOperations, sourceTransferTypes, new DragSourceListener() {

            public void dragFinished(DragSourceEvent event) {
                LocalSelectionTransfer.getTransfer().setSelection(null);
                fIsValidTreeSelection = false; // Reset to default
            }

            public void dragSetData(DragSourceEvent event) {
                // For consistency set the data to the selection even though
                // the selection is provided by the LocalSelectionTransfer
                // to the drop target adapter.
                event.data = LocalSelectionTransfer.getTransfer().getSelection();
            }

            public void dragStart(DragSourceEvent event) {
                // Drag started from the Tree
                IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection();
                fIsValidTreeSelection = isValidTreeSelection(selection);

                LocalSelectionTransfer.getTransfer().setSelection(selection);
                event.doit = true;
            }
        });
    }

    private void registerDropSupport() {
        fViewer.addDropSupport(fDropOperations, targetTransferTypes, new DropTargetListener() {
            public void dragEnter(DropTargetEvent event) {
            }

            public void dragLeave(DropTargetEvent event) {
            }

            public void dragOperationChanged(DropTargetEvent event) {
                event.detail = getEventDetail(event);
            }

            public void dragOver(DropTargetEvent event) {
                event.detail = getEventDetail(event);

                if (event.detail == DND.DROP_NONE) {
                    event.feedback = DND.FEEDBACK_NONE;
                }

                event.feedback |= DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND;
            }

            public void drop(DropTargetEvent event) {
                doDropOperation(event);
            }

            public void dropAccept(DropTargetEvent event) {
            }

            private int getEventDetail(DropTargetEvent event) {
                return isValidSelection(event) && isValidDropTarget(event) ? DND.DROP_MOVE : DND.DROP_NONE;
            }

        });
    }

    /**
     * Determine whether we have a valid selection of objects dragged from the Tree
     * Do it at the start of the drag operation for optimal speed.
     */
    boolean isValidTreeSelection(IStructuredSelection selection) {
        if (selection == null || selection.isEmpty()) {
            return false;
        }

        IArchimateModel model = null;

        for (Object object : selection.toArray()) {
            // Can't drag Models
            if (object instanceof IArchimateModel) {
                return false;
            }
            // Can only drag user folders
            if (object instanceof IFolder && ((IFolder) object).getType() != FolderType.USER) {
                return false;
            }
            // Don't allow mixed parent models
            if (object instanceof IArchimateModelElement) {
                IArchimateModel m = ((IArchimateModelElement) object).getArchimateModel();
                if (model != null && m != model) {
                    return false;
                }
                model = m;
            }
        }

        return true;
    }

    boolean isValidSelection(DropTargetEvent event) {
        return fIsValidTreeSelection || isValidFileSelection(event);
    }

    boolean isValidFileSelection(DropTargetEvent event) {
        return isFileDragOperation(event.currentDataType);
    }

    void doDropOperation(DropTargetEvent event) {
        //boolean move = event.detail == DND.DROP_MOVE;

        // Local
        if (isLocalTreeDragOperation(event.currentDataType)) {
            Object parent = getTargetParent(event);
            if (parent instanceof IFolder) {
                IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer()
                        .getSelection();
                moveTreeObjects((IFolder) parent, selection.toArray());
            }
        }
        // File
        else if (isFileDragOperation(event.currentDataType)) {
            addFileObjects((String[]) event.data);
        }
    }

    /**
     * Add external file objects dragged from the desktop by opening each file as a model
     */
    private void addFileObjects(final String[] paths) {
        BusyIndicator.showWhile(null, new Runnable() {
            @Override
            public void run() {
                for (String path : paths) {
                    File file = new File(path);
                    // Archimate model
                    if (file.getName().toLowerCase().endsWith(IEditorModelManager.ARCHIMATE_FILE_EXTENSION)) {
                        IEditorModelManager.INSTANCE.openModel(file);
                    }
                }
            }
        });
    }

    /**
     * Move Tree Objects
     */
    void moveTreeObjects(IFolder newParent, Object[] objects) {
        final CompoundCommand compoundCommand = new NonNotifyingCompoundCommand() {
            @Override
            public String getLabel() {
                return getCommands().size() > 1 ? Messages.TreeModelViewerDragDropHandler_0 : super.getLabel();
            }
        };

        for (Object object : objects) {
            if (object instanceof IFolder) { // This first - folders go in folders
                if (!newParent.getFolders().contains(object)) {
                    compoundCommand.add(new MoveFolderCommand(newParent, (IFolder) object));
                }
            } else if (object instanceof INameable) {
                if (!newParent.getElements().contains(object)) {
                    compoundCommand.add(new MoveObjectCommand(newParent, (INameable) object));
                }
            }
        }

        CommandStack stack = (CommandStack) newParent.getAdapter(CommandStack.class);
        stack.execute(compoundCommand);
    }

    /**
     * Determine the target parent from the drop event
     * 
     * @param event
     * @return
     */
    Object getTargetParent(DropTargetEvent event) {
        // Dropped on blank area, null
        if (event.item == null) {
            return null;
        }

        Object objectDroppedOn = event.item.getData();

        // Folder
        if (objectDroppedOn instanceof IFolder) {
            return objectDroppedOn;
        }

        // Otherwise null
        return null;
    }

    /**
     * @return True if target is valid
     */
    boolean isValidDropTarget(DropTargetEvent event) {
        // File from desktop onto blank area
        if (isFileDragOperation(event.currentDataType)) {
            return event.item == null;
        }

        // Local Tree Selection...

        // Dragging onto a Folder
        Object parent = getTargetParent(event);
        if (parent instanceof IFolder) {
            IFolder targetfolder = (IFolder) parent;
            IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer()
                    .getSelection();
            for (Object object : selection.toList()) {
                // must have the same top folder type - a restriction which one day we should not enforce!
                if (!hasCommonAncestorFolder(targetfolder, (EObject) object)) {
                    return false;
                }
                if (!canDropObject(object, (TreeItem) event.item)) {
                    return false;
                }
            }
            return true;
        }

        return false;
    }

    /**
     * Check if eObject1 and eObject2 have a common ancestor folder
     * i.e dragged objects can only share the same hole
     */
    boolean hasCommonAncestorFolder(EObject eObject1, EObject eObject2) {
        while (eObject1.eContainer() instanceof IFolder) {
            eObject1 = eObject1.eContainer();
        }

        while (eObject2.eContainer() instanceof IFolder) {
            eObject2 = eObject2.eContainer();
        }

        return (eObject1 == eObject2);
    }

    /**
     * Return true if object can be dropped on a target tree item
     */
    boolean canDropObject(Object object, TreeItem targetTreeItem) {
        if (targetTreeItem == null) { // Root tree
            return false;
        }

        if (object == targetTreeItem.getData()) { // Cannot drop onto itself
            return false;
        }

        // If moving a folder check that target folder is not a descendant of the source folder
        if (object instanceof IFolder) {
            while ((targetTreeItem = targetTreeItem.getParentItem()) != null) {
                if (targetTreeItem.getData() == object) {
                    return false;
                }
            }
        }

        return true;
    }

    boolean isLocalTreeDragOperation(TransferData dataType) {
        return LocalSelectionTransfer.getTransfer().isSupportedType(dataType);
    }

    boolean isFileDragOperation(TransferData dataType) {
        return FileTransfer.getInstance().isSupportedType(dataType);
    }
}