All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy