org.wings.sdnd.SDragAndDropManager Maven / Gradle / Ivy
The newest version!
package org.wings.sdnd;
import org.wings.*;
import org.wings.util.WeakArrayList;
import org.wings.plaf.SDragAndDropManagerCG;
import org.wings.session.SCursor;
import org.wings.event.SMouseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Drag and Drop-Manager to provide a interface between client-side drag-and-drop support (in javascript) and the server-
* side swing-like drag-and-drop
*/
public class SDragAndDropManager extends SComponent implements LowLevelEventListener {
private Collection dragSources;
private Collection dropTargets;
private final transient static Logger LOG = LoggerFactory.getLogger(SDragAndDropManager.class);
private STransferHandler.TransferSupport transferSupport;
private SDragAndDropManagerCG dndCG;
private Map dragCodes;
private Map dropCodes;
private final static SResourceIcon DEFAULT_DND_ICON_STOP = new SResourceIcon("org/wings/icons/dnd/dnd_stop.png");
private final static SResourceIcon DEFAULT_DND_ICON_MOVE = new SResourceIcon("org/wings/icons/dnd/dnd_move.png");
private final static SResourceIcon DEFAULT_DND_ICON_COPY = new SResourceIcon("org/wings/icons/dnd/dnd_copy.png");
private final static SResourceIcon DEFAULT_DND_ICON_LINK = new SResourceIcon("org/wings/icons/dnd/dnd_link.png");
private SIcon DND_ICON_STOP = DEFAULT_DND_ICON_STOP;
private SIcon DND_ICON_MOVE = DEFAULT_DND_ICON_MOVE;
private SIcon DND_ICON_COPY = DEFAULT_DND_ICON_COPY;
private SIcon DND_ICON_LINK = DEFAULT_DND_ICON_LINK;
private String valuesToProcess;
public SDragAndDropManager() {
this.dragSources = new WeakArrayList<>();
this.dropTargets = new WeakArrayList<>();
this.dndCG = (SDragAndDropManagerCG) getCG();
getSession().getDispatcher().register(this);
}
/**
* Sets a Drag-and-Drop Iconset to be used for drag-and-drop in this session
*
* @param iconSet
*/
public void setDNDIcons(DNDIconSet iconSet) {
DND_ICON_STOP = iconSet.getNoActionAllowedIcon();
DND_ICON_MOVE = iconSet.getMoveIcon();
DND_ICON_COPY = iconSet.getCopyIcon();
DND_ICON_LINK = iconSet.getLinkIcon();
}
/**
* Adds component as a Drag Source
*
* @param component
*/
public void addDragSource(SComponent component) {
if (!this.dragSources.contains(component)) {
this.dragSources.add(component);
update(dndCG.getRegistrationUpdate(this, new DragAndDropRegistrationEvent(DDEventType.ADD_DRAGSOURCE, component)));
}
}
/**
* Adds a Drag Source with custom dragcode
*
* @param component
* @param dragCode
*/
public void addDragSource(SComponent component, String dragCode) {
if (this.dragCodes == null) {
this.dragCodes = new WeakHashMap<>();
}
this.dragCodes.put(component, dragCode);
addDragSource(component);
}
/**
* Adds component as a Drop Target
*
* @param component
*/
public void addDropTarget(SComponent component) {
if (!this.dropTargets.contains(component)) {
this.dropTargets.add(component);
update(dndCG.getRegistrationUpdate(this, new DragAndDropRegistrationEvent(DDEventType.ADD_DROPTARGET, component)));
}
}
/**
* Adds a Drop Target with custom dropcode
*
* @param component
* @param dropCode
*/
public void addDropTarget(SComponent component, String dropCode) {
if (this.dropCodes == null) {
this.dropCodes = new WeakHashMap<>();
}
this.dropCodes.put(component, dropCode);
addDropTarget(component);
}
/**
* Removes a drag source from the list
*
* @param component
*/
public void removeDragSource(SComponent component) {
if (this.dragCodes != null) {
this.dragCodes.remove(component);
}
if (this.dragSources.contains(component)) {
this.dragSources.remove(component);
update(dndCG.getRegistrationUpdate(this, new DragAndDropRegistrationEvent(DDEventType.REMOVE_DRAGSOURCE, component)));
}
}
/**
* Removes a drop target from the list
*
* @param component
*/
public void removeDropTarget(SComponent component) {
if (this.dropCodes != null) {
this.dropCodes.remove(component);
}
if (this.dragSources.contains(component)) {
this.dropTargets.remove(component);
update(dndCG.getRegistrationUpdate(this, new DragAndDropRegistrationEvent(DDEventType.REMOVE_DROPTARGET, component)));
}
}
/**
* Returns a Collection of components, that are set as drag sources
*
* @return
*/
public Collection getDragSources() {
return this.dragSources;
}
/**
* Returns a Collection of components, that are set as drop target
*
* @return
*/
public Collection getDropTargets() {
return this.dropTargets;
}
@Override
public boolean isRecursivelyVisible() {
return true;
}
@Override
public boolean isVisible() {
return true;
}
@Override
public SFrame getParentFrame() {
return getSession().getRootFrame();
}
@Override
public void processLowLevelEvent(String name, String... values) {
// System.out.println("DND: name: " + name + " values[0]: " + values[0]);
this.valuesToProcess = values[0];
SForm.addArmedComponent(this);
}
private void resetDndStatus() {
SCursor cursor = getSession().getCursor();
this.transferSupport = null;
cursor.setLabel(null);
cursor.removeIcons("dnd");
cursor.hideCursorIfPossible();
}
private boolean dragStart(SComponent source, STransferHandler sourceTH, SComponent target, int action, SMouseEvent mouseEvent) {
SCursor cursor = getSession().getCursor();
// clean transfersupport (in case a old dragging was aborted for any reason and didn't clean up)
this.transferSupport = null;
// Look for a CustomDragHandler
if (sourceTH instanceof CustomDragHandler) {
if(((CustomDragHandler) sourceTH).dragStart(source, target, action, mouseEvent)) {
return true;
}
}
// This call must and usually will set the TransferSupport
sourceTH.exportAsDrag(source, mouseEvent, action);
if (this.transferSupport == null) {
// abort drag operation
return false;
}
// Start drag operation
// Get the Visual Representation Label and use it
SLabel label = sourceTH.getVisualRepresentationLabel(getTransferSupport().getTransferable());
if (label != null) {
cursor.setLabel(label);
}
// Show it
cursor.setShowCursor(true);
return true;
}
private void dragEnter(SComponent source, SComponent target, STransferHandler targetTH, int action, SMouseEvent mouseEvent) {
SCursor cursor = getSession().getCursor();
if (source == target && !dropTargets.contains(source)) {
// in case of dragstart - remove all icons from category 'dnd', and add the default-STOP-icon
cursor.removeIcons("dnd");
cursor.addIcon("dnd", DND_ICON_STOP, SCursor.HIGH_PRIORITY);
return;
}
// check for a custom dragover handler
if ((targetTH instanceof CustomDragOverHandler)) {
if(((CustomDragOverHandler) targetTH).dragOverEnter(source, target, action, mouseEvent)) {
return;
}
}
// set that this is a (potential) drop - will use droplocation and stuff if possible
transferSupport.setIsDrop(target, source, mouseEvent, action);
if ((transferSupport.getSourceDropActions() & action) == 0) {
// if dropping isn't allowed with requested action
cursor.removeIcons("dnd");
cursor.addIcon("dnd", DND_ICON_STOP, SCursor.HIGH_PRIORITY);
return;
}
// Check if the target-component can import the transferred data
if (targetTH != null && targetTH.canImport(this.transferSupport)) {
cursor.removeIcons("dnd");
switch (action) {
case STransferHandler.MOVE:
cursor.addIcon("dnd", DND_ICON_MOVE, SCursor.HIGH_PRIORITY);
break;
case STransferHandler.COPY:
cursor.addIcon("dnd", DND_ICON_COPY, SCursor.HIGH_PRIORITY);
break;
case STransferHandler.LINK:
cursor.addIcon("dnd", DND_ICON_LINK, SCursor.HIGH_PRIORITY);
break;
default:
LOG.error("Unknown action while dragenter: " + action);
}
} else {
cursor.removeIcons("dnd");
cursor.addIcon("dnd", DND_ICON_STOP, SCursor.HIGH_PRIORITY);
}
}
private void dragLeave(SComponent source, SComponent target, STransferHandler targetTH, int action, SMouseEvent mouseEvent) {
SCursor cursor = getSession().getCursor();
// check for the custom dragover-handler
if (targetTH instanceof CustomDragOverHandler) {
if(((CustomDragOverHandler) targetTH).dragOverLeave(source, target, action, mouseEvent))
return;
}
cursor.removeIcons("dnd");
cursor.addIcon("dnd", DND_ICON_STOP, SCursor.HIGH_PRIORITY);
}
private static void dropStay(SComponent source, SComponent target, STransferHandler targetTH, int action, SMouseEvent mouseEvent) {
// check for the custom dragStay-handler
if (targetTH instanceof CustomDropStayHandler) {
((CustomDropStayHandler) targetTH).dropStay(source, target, action, mouseEvent);
}
}
private void drop(SComponent source, STransferHandler sourceTH, SComponent target, STransferHandler targetTH, int action, SMouseEvent mouseEvent) {
// check for the custom drop-handler
if (targetTH instanceof CustomDropHandler) {
if (((CustomDropHandler) targetTH).drop(source, target, action, mouseEvent)) {
return;
}
}
transferSupport.setIsDrop(target, source, mouseEvent, action);
if ((transferSupport.getSourceDropActions() & action) == 0) {
// if dropping isn't allowed with the requested action
resetDndStatus();
return;
}
// Import the data and call exportDone on the source-TH
if (targetTH != null && targetTH.canImport(transferSupport)) {
targetTH.importData(transferSupport);
sourceTH.exportDone(source, transferSupport.getTransferable(), action);
}
resetDndStatus();
}
private void doFireIntermediateEvents() {
String[] params = valuesToProcess.split(";");
String operation = params[0];
String sourceId = params[1];
String targetId = params[2];
int action = Integer.parseInt(params[3]);
String additionalParams = null;
if (params.length > 4)
additionalParams = params[4];
// Get the source component from the unique id and its transferhandler
SComponent source = getSession().getComponentByName(sourceId);
if (source == null) {
return;
}
STransferHandler sourceTH = source.getTransferHandler();
if (sourceTH == null) {
return;
}
// Get the target component from the unique id and its transferhandler
SComponent target = getSession().getComponentByName(targetId);
STransferHandler targetTH = null;
if (target == null) {
// if there's no target, it's ok if it's a dragstart or abort-operation
if (!("ds".equals(operation) || "ab".equals(operation))) {
LOG.error("target not found: " + targetId + ' ' + valuesToProcess);
return;
}
} else {
targetTH = target.getTransferHandler();
}
// Create a SMouseEvent from the optional parameter string
SMouseEvent mouseEvent = new SMouseEvent(source, 0, new SPoint(additionalParams));
// On Drag-Start Operations
if ("ds".equals(operation)) {
if(dragStart(source, sourceTH, target, action, mouseEvent)) {
target = source;
targetTH = sourceTH;
dragEnter(source, target, targetTH, action, mouseEvent);
}
return;
}
if ("de".equals(operation)) {
// On dragEnter Operation (Mouse entered a element)
dragEnter(source, target, targetTH, action, mouseEvent);
} else if ("dl".equals(operation)) { // dragLeave
dragLeave(source, target, targetTH, action, mouseEvent);
} else if ("st".equals(operation)) { // stayed on element
dropStay(source, target, targetTH, action, mouseEvent);
} else if ("dr".equals(operation)) { // drop
drop(source, sourceTH, target, targetTH, action, mouseEvent);
} else if ("ab".equals(operation)) { // aborted
resetDndStatus();
}
}
@Override
public void fireIntermediateEvents() {
// TODO: move this to 'after' processLowLevelEvents to ensure that all changes to components are made at processIntermediateEvents
try {
doFireIntermediateEvents();
} catch(Exception e) {
LOG.error("Error while processing Drag-and-Drop operation", e);
resetDndStatus();
}
}
/**
* Returns the custom dragcode for component or null if there is none
*
* @param component
* @return
*/
public String getCustomDragCode(SComponent component) {
return (this.dragCodes != null) ? this.dragCodes.get(component) : null;
}
/**
* Returns the custom dropcode for component or null if there is none
*
* @param component
* @return
*/
public String getCustomDropCode(SComponent component) {
return (this.dropCodes != null) ? this.dropCodes.get(component) : null;
}
/**
* Returns the TransferSupport for the current transfer in this session
*
* @return
*/
public STransferHandler.TransferSupport getTransferSupport() {
return this.transferSupport;
}
/**
* Sets the TransferSupport for this session
*
* @param transferSupport
*/
public void setTransferSupport(STransferHandler.TransferSupport transferSupport) {
this.transferSupport = transferSupport;
}
@Override
public boolean isEpochCheckEnabled() {
return false;
}
public enum DDEventType {
ADD_DRAGSOURCE,
REMOVE_DRAGSOURCE,
ADD_DROPTARGET,
REMOVE_DROPTARGET
}
/**
* Represents a Drag and Drop Registration (add/remove) event
*/
public static class DragAndDropRegistrationEvent {
private DDEventType eventType;
private SComponent component;
public DragAndDropRegistrationEvent(DDEventType type, SComponent component) {
this.eventType = type;
this.component = component;
}
public DDEventType getEventType() {
return eventType;
}
public SComponent getComponent() {
return component;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DragAndDropRegistrationEvent event = (DragAndDropRegistrationEvent) o;
if (!component.equals(event.component)) return false;
return eventType == event.eventType;
}
public int hashCode() {
int result = eventType.hashCode();
result = 31 * result + component.hashCode();
return result;
}
}
public static class DNDIconSet {
private SIcon noActionAllowedIcon;
private SIcon moveIcon;
private SIcon copyIcon;
private SIcon linkIcon;
public DNDIconSet(SIcon noActionAllowedIcon, SIcon moveIcon, SIcon copyIcon, SIcon linkIcon) {
this.noActionAllowedIcon = noActionAllowedIcon;
this.moveIcon = moveIcon;
this.copyIcon = copyIcon;
this.linkIcon = linkIcon;
}
public SIcon getNoActionAllowedIcon() {
return noActionAllowedIcon;
}
public void setNoActionAllowedIcon(SIcon noActionAllowedIcon) {
this.noActionAllowedIcon = noActionAllowedIcon;
}
public SIcon getMoveIcon() {
return moveIcon;
}
public void setMoveIcon(SIcon moveIcon) {
this.moveIcon = moveIcon;
}
public SIcon getCopyIcon() {
return copyIcon;
}
public void setCopyIcon(SIcon copyIcon) {
this.copyIcon = copyIcon;
}
public SIcon getLinkIcon() {
return linkIcon;
}
public void setLinkIcon(SIcon linkIcon) {
this.linkIcon = linkIcon;
}
}
}