/*
* Copyright (c) 2014, grossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.spi.impl.swt.common.dnd;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.jowidgets.common.dnd.DropAction;
import org.jowidgets.common.dnd.DropMode;
import org.jowidgets.common.types.Position;
import org.jowidgets.spi.clipboard.TransferContainer;
import org.jowidgets.spi.clipboard.TransferObject;
import org.jowidgets.spi.clipboard.TransferTypeSpi;
import org.jowidgets.spi.dnd.IDropEventSpi;
import org.jowidgets.spi.dnd.IDropResponseSpi;
import org.jowidgets.spi.dnd.IDropTargetSpi;
import org.jowidgets.spi.impl.dnd.AbstractDropTargetObservableSpi;
import org.jowidgets.spi.impl.dnd.DropEventSpiImpl;
import org.jowidgets.spi.impl.dnd.DropResponseSpiImpl;
import org.jowidgets.spi.impl.swt.common.clipboard.ObjectTransfer;
import org.jowidgets.spi.impl.swt.common.util.PositionConvert;
import org.jowidgets.util.Assert;
import org.jowidgets.util.EmptyCheck;
public final class SwtDropTarget extends AbstractDropTargetObservableSpi implements IDropTargetSpi {
private static final TextTransfer TEXT_TRANSFER = TextTransfer.getInstance();
private static final ObjectTransfer OBJECT_TRANSFER = ObjectTransfer.getInstance();
private final Control control;
private final IDropSelectionProvider dropSelectionProvider;
private Set<TransferTypeSpi> supportedTypes;
private Set<DropAction> actions;
private DropTarget dropTarget;
private DropMode defaultDropMode;
public SwtDropTarget(final Control control, final IDropSelectionProvider dropSelectionProvider) {
Assert.paramNotNull(control, "control");
Assert.paramNotNull(dropSelectionProvider, "dropSelectionProvider");
this.control = control;
this.dropSelectionProvider = dropSelectionProvider;
}
@Override
public void setTransferTypes(final Collection<TransferTypeSpi> supportedTypes) {
this.supportedTypes = new LinkedHashSet<TransferTypeSpi>(supportedTypes);
if (dropTarget != null) {
dropTarget.setTransfer(DragDropUtil.createTransfers(supportedTypes));
}
createDropTargetIfNecessary();
}
@Override
public void setActions(final Set<DropAction> actions) {
if (dropTarget != null) {
dropTarget.dispose();
dropTarget = null;
}
this.actions = new HashSet<DropAction>(actions);
createDropTargetIfNecessary();
}
@Override
public void setDefaultDropMode(final DropMode dropMode) {
Assert.paramNotNull(dropMode, "dropMode");
this.defaultDropMode = dropMode;
}
@Override
protected void setActive(final boolean active) {
if (active) {
createDropTargetIfNecessary();
}
else {
if (dropTarget != null) {
dropTarget.dispose();
dropTarget = null;
}
}
}
private void createDropTargetIfNecessary() {
if (dropTarget == null && isActive() && !EmptyCheck.isEmpty(actions) && !EmptyCheck.isEmpty(supportedTypes)) {
this.dropTarget = new DropTarget(control, DragDropUtil.createOperations(actions));
dropTarget.setTransfer(DragDropUtil.createTransfers(supportedTypes));
dropTarget.addDropListener(new DropTargetListenerImpl());
}
}
private final class DropTargetListenerImpl implements DropTargetListener {
@Override
public void dragEnter(final DropTargetEvent event) {
handleDropTargetEvent(event, new IEventPublisher() {
@Override
public void fireEvent(final IDropEventSpi dropEvent, final IDropResponseSpi dropResponse) {
fireDragEnter(dropEvent, dropResponse);
}
});
}
@Override
public void dragLeave(final DropTargetEvent event) {
fireDragExit();
}
@Override
public void dragOperationChanged(final DropTargetEvent event) {
handleDropTargetEvent(event, new IEventPublisher() {
@Override
public void fireEvent(final IDropEventSpi dropEvent, final IDropResponseSpi dropResponse) {
fireDragOperationChanged(dropEvent, dropResponse);
}
});
}
@Override
public void dragOver(final DropTargetEvent event) {
handleDropTargetEvent(event, new IEventPublisher() {
@Override
public void fireEvent(final IDropEventSpi dropEvent, final IDropResponseSpi dropResponse) {
fireDragOver(dropEvent, dropResponse);
}
});
}
@Override
public void drop(final DropTargetEvent event) {
handleDropTargetEvent(event, new IEventPublisher() {
@Override
public void fireEvent(final IDropEventSpi dropEvent, final IDropResponseSpi dropResponse) {
fireDrop(dropEvent);
}
});
}
@Override
public void dropAccept(final DropTargetEvent event) {
handleDropTargetEvent(event, new IEventPublisher() {
@Override
public void fireEvent(final IDropEventSpi dropEvent, final IDropResponseSpi dropResponse) {
fireDropAccept(dropEvent, dropResponse);
}
});
}
}
private void handleDropTargetEvent(final DropTargetEvent event, final IEventPublisher eventPublisher) {
final IDropEventSpi dropEvent = createDropEvent(event);
final DropResponseSpiImpl dropResponse = new DropResponseSpiImpl();
eventPublisher.fireEvent(dropEvent, dropResponse);
handleDropAction(dropResponse.getAccepted(), dropResponse.isRejected(), event);
setFeedback(dropResponse.getDropMode(), event);
}
private void handleDropAction(final DropAction dropAction, final boolean rejected, final DropTargetEvent event) {
if (rejected && dropAction == null) {
event.detail = DND.DROP_NONE;
}
else if (dropAction != null) {
event.detail = DragDropUtil.createOperations(Collections.singleton(dropAction));
}
}
private void setFeedback(final DropMode dropMode, final DropTargetEvent event) {
if (dropMode != null) {
setFeedbackImpl(dropMode, event);
}
else if (defaultDropMode != null) {
setFeedbackImpl(defaultDropMode, event);
}
}
private void setFeedbackImpl(final DropMode dropMode, final DropTargetEvent event) {
final Integer feedback = dropSelectionProvider.getFeedback(event.item, getPosition(event), dropMode);
if (feedback != null) {
event.feedback = feedback.intValue();
}
}
private IDropEventSpi createDropEvent(final DropTargetEvent event) {
final TransferData transferData = event.currentDataType;
if (TEXT_TRANSFER.isSupportedType(transferData)) {
final TransferTypeSpi transferType = DragDropUtil.getStringTransferType(supportedTypes);
//Only create a event, if this drop target supports the transfer type
if (transferType != null) {
Object data = event.data;
if (data == null) {
data = TEXT_TRANSFER.nativeToJava(transferData);
}
return createDropEvent(event, data, transferType);
}
}
else if (OBJECT_TRANSFER.isSupportedType(transferData)) {
Object data = event.data;
if (data == null) {
data = OBJECT_TRANSFER.nativeToJava(transferData);
}
if (data instanceof TransferContainer) {
for (final TransferObject transferObject : ((TransferContainer) data).getTransferObjetcs()) {
final TransferTypeSpi transferType = transferObject.getTransferType();
//Only create a event, if this drop target supports the transfer type
if (supportedTypes.contains(transferType)) {
return createDropEvent(event, transferObject.getData(), transferType);
}
}
}
}
return createDropEvent(event, null, TransferTypeSpi.UNKNOWN_TYPE);
}
private IDropEventSpi createDropEvent(final DropTargetEvent event, final Object data, final TransferTypeSpi transferType) {
final Position position = getPosition(event);
return new DropEventSpiImpl(
position,
getDropSelection(event, position),
getSupportedActions(event),
getDropAction(event),
data,
transferType);
}
private Position getPosition(final DropTargetEvent event) {
return PositionConvert.convert(control.toControl(new Point(event.x, event.y)));
}
private Object getDropSelection(final DropTargetEvent event, final Position position) {
return dropSelectionProvider.getDropSelection(event.item, position, event.feedback);
}
private Set<DropAction> getSupportedActions(final DropTargetEvent event) {
return DragDropUtil.createActions(event.operations);
}
private DropAction getDropAction(final DropTargetEvent event) {
return DragDropUtil.getDropAction(event.detail);
}
private interface IEventPublisher {
void fireEvent(IDropEventSpi dropEvent, IDropResponseSpi dropResponse);
}
}