--- /dev/null
+package com.vaadin.event;
+
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+import com.vaadin.ui.Component;
+
+/**
+ * An implementation of DropHandler interface.
+ *
+ * AcceptCriterion may be used to configure accept rules. Using them can result
+ * client side verifiable accept rules for quick feedback in UI. Still rules are
+ * also validate on server so implementor don't need to double check validity on
+ * {@link #receive(Transferable)} method.
+ *
+ */
+public abstract class AbstractDropHandler implements DropHandler {
+ /**
+ * Criterion that can be used create policy to accept/discard dragged
+ * content (presented by {@link Transferable}).
+ *
+ * TODO figure out how this can be done partly on client at least in some
+ * cases. isClientSideFilterable() tms.
+ *
+ */
+ public interface AcceptCriterion {
+ public boolean accepts(Transferable transferable);
+ }
+
+ public interface ClientSideVerifiable extends AcceptCriterion {
+
+ /**
+ * May depend on state, like in OR or AND, so to be really
+ * ClientSideVerifiable needs to return true here (instead of just
+ * implementing marker interface).
+ */
+ public boolean isClientSideVerifiable();
+
+ public void paint(PaintTarget target) throws PaintException;
+
+ }
+
+ private static final class AcceptAll implements ClientSideVerifiable {
+ public boolean accepts(Transferable transferable) {
+ return true;
+ }
+
+ public boolean isClientSideVerifiable() {
+ return true;
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("acceptCriterion");
+ target.addAttribute("name", "acceptAll");
+ target.endTag("acceptCriterion");
+ }
+ }
+
+ public static class And implements ClientSideVerifiable {
+ private AcceptCriterion f1;
+ private AcceptCriterion f2;
+
+ public And(AcceptCriterion f1, AcceptCriterion f2) {
+ this.f1 = f1;
+ this.f2 = f2;
+ }
+
+ public boolean accepts(Transferable transferable) {
+ return f1.accepts(transferable) && f2.accepts(transferable);
+ }
+
+ public boolean isClientSideVerifiable() {
+ boolean a1 = f1 instanceof ClientSideVerifiable ? ((ClientSideVerifiable) f1)
+ .isClientSideVerifiable()
+ : false;
+ boolean a2 = f2 instanceof ClientSideVerifiable ? ((ClientSideVerifiable) f2)
+ .isClientSideVerifiable()
+ : false;
+ return a1 && a2;
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("acceptCriterion");
+ target.addAttribute("name", "and");
+ ((ClientSideVerifiable) f1).paint(target);
+ ((ClientSideVerifiable) f2).paint(target);
+ target.endTag("acceptCriterion");
+ }
+ }
+
+ public static class ComponentFilter implements ClientSideVerifiable {
+ private Component component;
+
+ public ComponentFilter(Component component) {
+ this.component = component;
+ }
+
+ public boolean accepts(Transferable transferable) {
+ if (transferable instanceof ComponentTransferrable) {
+ return ((ComponentTransferrable) transferable)
+ .getSourceComponent() == component;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isClientSideVerifiable() {
+ return true;
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("acceptCriterion");
+ target.addAttribute("name", "component");
+ target.addAttribute("component", component);
+ target.endTag("acceptCriterion");
+ }
+ }
+
+ private static final class IsDataBinded implements ClientSideVerifiable {
+ public boolean accepts(Transferable transferable) {
+ if (transferable instanceof DataBindedTransferrable) {
+ return ((DataBindedTransferrable) transferable).getItemId() != null;
+ }
+ return false;
+ }
+
+ public boolean isClientSideVerifiable() {
+ return true;
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("acceptCriterion");
+ target.addAttribute("name", "needsItemId");
+ target.endTag("acceptCriterion");
+ }
+ }
+
+ public class Not implements AcceptCriterion {
+ private AcceptCriterion acceptCriterion;
+
+ public Not(AcceptCriterion acceptCriterion) {
+ this.acceptCriterion = acceptCriterion;
+ }
+
+ public boolean accepts(Transferable transferable) {
+ return !acceptCriterion.accepts(transferable);
+ }
+
+ }
+
+ public class Or implements AcceptCriterion {
+ private AcceptCriterion f1;
+ private AcceptCriterion f2;
+
+ Or(AcceptCriterion f1, AcceptCriterion f2) {
+ this.f1 = f1;
+ this.f2 = f2;
+ }
+
+ public boolean accepts(Transferable transferable) {
+ return f1.accepts(transferable) || f2.accepts(transferable);
+ }
+ }
+
+ public static class OverTreeNode implements ClientSideVerifiable {
+
+ public boolean accepts(Transferable transferable) {
+ try {
+ return transferable.getData("detail").toString().toLowerCase()
+ .equals("center");
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean isClientSideVerifiable() {
+ return true;
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("acceptCriterion");
+ target.addAttribute("name", "overTreeNode");
+ target.endTag("acceptCriterion");
+ }
+
+ }
+
+ public static final AcceptCriterion CRITERION_ACCEPT_ALL = new AcceptAll();
+
+ public static final AcceptCriterion CRITERION_HAS_ITEM_ID = new IsDataBinded();
+
+ private AcceptCriterion acceptCriterion = CRITERION_ACCEPT_ALL;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seecom.vaadin.event.DropHandler#acceptTransferrable(com.vaadin.event.
+ * Transferable)
+ */
+ public boolean acceptTransferrable(Transferable transferable) {
+ return acceptCriterion.accepts(transferable);
+ }
+
+ private boolean clientSideVerifiable() {
+ if (acceptCriterion instanceof ClientSideVerifiable) {
+ return ((ClientSideVerifiable) acceptCriterion)
+ .isClientSideVerifiable();
+ }
+ return false;
+ }
+
+ public void handleDragRequest(DragRequest event) {
+ boolean acceptTransferrable = acceptTransferrable(event
+ .getTransferrable());
+ if (acceptTransferrable) {
+ if (event.getType() == DragEventType.DROP) {
+ receive(event.getTransferrable());
+ } else {
+ event.setResponseParameter("accepted", true);
+ }
+
+ }
+ }
+
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("dh");
+ if (!clientSideVerifiable()) {
+ target.addAttribute("serverValidate", true);
+ } else {
+ ((ClientSideVerifiable) acceptCriterion).paint(target);
+ }
+ target.endTag("dh");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.event.DropHandler#receive(com.vaadin.event.Transferable)
+ */
+ public abstract void receive(Transferable transferable);
+
+ public void setAcceptCriterion(AcceptCriterion acceptCriterion) {
+ this.acceptCriterion = acceptCriterion;
+ }
+
+}
--- /dev/null
+package com.vaadin.event;
+
+import com.vaadin.ui.Component;
+
+public interface ComponentTransferrable extends Transferable {
+
+ /**
+ * @return the component where the drag operation started
+ */
+ public Component getSourceComponent();
+
+}
--- /dev/null
+package com.vaadin.event;
+
+public interface DataBindedTransferrable extends ComponentTransferrable {
+
+ public Object getItemId();
+
+ public Object getPropertyId();
+
+}
--- /dev/null
+package com.vaadin.event;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+
+public class DragRequest {
+
+ private DragEventType dragEventType;
+ private Transferable transferable;
+ private Map<String, Object> responseData;
+
+ public DragRequest(DragEventType dragEventType, Transferable transferable) {
+ this.dragEventType = dragEventType;
+ this.transferable = transferable;
+ }
+
+ public Transferable getTransferrable() {
+ return transferable;
+ }
+
+ public DragEventType getType() {
+ return dragEventType;
+ }
+
+ public Map<String, Object> getResponseData() {
+ return responseData;
+ }
+
+ /**
+ * DropHanler can pass simple parameters back to client side.
+ *
+ * TODO define which types are supported (most likely the same as in UIDL)
+ *
+ * @param key
+ * @param value
+ */
+ public void setResponseParameter(String key, Object value) {
+ if (responseData == null) {
+ responseData = new HashMap<String, Object>();
+ }
+ responseData.put(key, value);
+ }
+
+}
--- /dev/null
+package com.vaadin.event;
+
+public interface DropHandler {
+
+ public void handleDragRequest(DragRequest event);
+
+}
\ No newline at end of file
--- /dev/null
+package com.vaadin.event;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Implementing component most commonly has also setDropHandler method, but
+ * not polluting interface here as component might also have internal
+ * AbstractDropHandler implementation.
+ *
+ */
+public interface HasDropHandler extends Component {
+ public DropHandler getDropHandler();
+}
\ No newline at end of file
--- /dev/null
+package com.vaadin.event;
+
+import java.util.Collection;
+
+public interface Transferable {
+
+ public Object getData(String dataFlawor);
+
+ public void setData(String dataFlawor, Object value);
+
+ public Collection<String> getDataFlawors();
+
+}
--- /dev/null
+package com.vaadin.terminal;
+
+import java.util.Map;
+
+import com.vaadin.event.Transferable;
+
+public interface TransferTranslator {
+
+ /**
+ * Translate translators may convert client side variables to meaningful
+ * values on server side. For example in Selects we convert item identifiers
+ * to generated string keys for the client side. Translators in Selects
+ * should convert them back to item identifiers.
+ * <p>
+ * Translator should remove variables it handled from rawVariables. All non
+ * handled variables are added to Transferable automatically by terminal.
+ *
+ * @param transferable
+ * the Transferable object if one has been created for this drag
+ * and drop operation, null if Transferable is not yet
+ * instantiated
+ * @param rawVariables
+ * @return
+ */
+ public Transferable getTransferrable(Transferable transferable,
+ Map<String, Object> rawVariables, boolean isDropTarget);
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.dom.client.EventTarget;
+import com.google.gwt.event.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Container;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.dd.AbstractDropHandler;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager;
+import com.vaadin.terminal.gwt.client.ui.dd.DragEvent;
+import com.vaadin.terminal.gwt.client.ui.dd.HasDropHandler;
+import com.vaadin.terminal.gwt.client.ui.dd.Html5DragEvent;
+import com.vaadin.terminal.gwt.client.ui.dd.Transferable;
+
+public class VDragDropPane extends VAbsoluteLayout implements Container,
+ HasDropHandler {
+
+ private String paintableId;
+
+ /**
+ * DragEvent is stored here in case of HTML5 drag event.
+ */
+ private DragEvent vaadinDragEvent;
+
+ public VDragDropPane() {
+ super();
+ addDomHandler(new MouseDownHandler() {
+ public void onMouseDown(MouseDownEvent event) {
+ EventTarget eventTarget = event.getNativeEvent()
+ .getEventTarget();
+ Paintable paintable = client.getPaintable((Element) eventTarget
+ .cast());
+ Transferable transferable = new Transferable();
+ transferable.setComponent(paintable);
+ DragEvent drag = DragAndDropManager.get().startDrag(
+ transferable, event.getNativeEvent(), true);
+ Element cloneNode = (Element) ((Widget) paintable).getElement()
+ .cloneNode(true);
+ cloneNode.getStyle().setBackgroundColor("#999");
+ cloneNode.getStyle().setOpacity(0.4);
+ drag.setDragImage(cloneNode);
+ drag.getTransferrable().setData(
+ "mouseDown",
+ new MouseEventDetails(event.getNativeEvent())
+ .serialize());
+ event.preventDefault(); // prevent text selection
+ }
+ }, MouseDownEvent.getType());
+
+ hookHtml5Events(getElement());
+ getStyleElement().getStyle().setBackgroundColor("yellow");
+
+ }
+
+ /**
+ * Prototype code, memory leak risk.
+ *
+ * @param el
+ */
+ private native void hookHtml5Events(Element el)
+ /*-{
+
+ var me = this;
+
+ el.addEventListener("dragenter", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragDropPane::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/Html5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("dragleave", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragDropPane::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/Html5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("dragover", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragDropPane::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/Html5DragEvent;)(ev);
+ }, false);
+
+ el.addEventListener("drop", function(ev) {
+ return me.@com.vaadin.terminal.gwt.client.ui.VDragDropPane::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/Html5DragEvent;)(ev);
+ }, false);
+
+ }-*/;
+
+ public boolean html5DragEnter(Html5DragEvent event) {
+ ApplicationConnection.getConsole().log("HTML 5 Drag Enter");
+ Transferable transferable = new Transferable();
+
+ // TODO refine api somehow so that we will now not use the event preview
+ // method provided by manager
+ vaadinDragEvent = DragAndDropManager.get().startDrag(transferable,
+ event, false);
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ }
+
+ public boolean html5DragLeave(Html5DragEvent event) {
+ ApplicationConnection.getConsole().log("HTML 5 Drag Leave posponed...");
+ DeferredCommand.addCommand(new Command() {
+ public void execute() {
+ // Yes, dragleave happens before drop. Makes no sense to me.
+ // IMO shouldn't fire leave at all if drop happens (I guess this
+ // is what IE does).
+ // In Vaadin we fire it only if drop did not happen.
+ if (vaadinDragEvent != null) {
+ ApplicationConnection.getConsole().log(
+ "...HTML 5 Drag Leave");
+ getDropHandler().dragLeave(vaadinDragEvent);
+ }
+ }
+ });
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ }
+
+ public boolean html5DragOver(Html5DragEvent event) {
+ ApplicationConnection.getConsole().log("HTML 5 Drag Over");
+ getDropHandler().dragOver(vaadinDragEvent);
+ // needed to be set for Safari, otherwise drop will not happen
+ String s = event.getEffectAllowed();
+ if ("all".equals(s) || s.contains("opy")) {
+ event.setDragEffect("copy");
+ } else {
+ event.setDragEffect(s);
+ ApplicationConnection.getConsole().log("Drag effect set to " + s);
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ }
+
+ public boolean html5DragDrop(Html5DragEvent event) {
+ ApplicationConnection.getConsole().log("HTML 5 Drag Drop");
+ Transferable transferable = vaadinDragEvent.getTransferrable();
+
+ JsArrayString types = event.getTypes();
+ for (int i = 0; i < types.length(); i++) {
+ String type = types.get(i);
+ ApplicationConnection.getConsole().log("Type: " + type);
+ if ("text/plain".equals(type)) {
+ String data = event.getDataAsText(type);
+ ApplicationConnection.getConsole().log(type + " : " + data);
+ transferable.setData("text/plain", data);
+ }
+ }
+
+ String fileAsString = event.getFileAsString(0);
+ if (fileAsString != null) {
+ ApplicationConnection.getConsole().log(fileAsString);
+ transferable.setData("fileContents", fileAsString);
+ }
+
+ DragAndDropManager.get().endDrag();
+ vaadinDragEvent = null;
+ event.preventDefault();
+ event.stopPropagation();
+
+ return false;
+ }
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ super.updateFromUIDL(uidl, client);
+ if (!uidl.hasAttribute("cached")) {
+ int childCount = uidl.getChildCount();
+ UIDL childUIDL = uidl.getChildUIDL(childCount - 1);
+ getDropHandler().updateRules(childUIDL);
+ }
+ }
+
+ private AbstractDropHandler dropHandler;
+
+ public AbstractDropHandler getDropHandler() {
+ if (dropHandler == null) {
+ dropHandler = new AbstractDropHandler() {
+
+ @Override
+ public Paintable getPaintable() {
+ return VDragDropPane.this;
+ }
+
+ @Override
+ public void dragLeave(DragEvent drag) {
+ ApplicationConnection.getConsole().log("DragLeave");
+ getStyleElement().getStyle().setBackgroundColor("yellow");
+ }
+
+ @Override
+ public boolean drop(DragEvent drag) {
+ ApplicationConnection.getConsole().log(
+ "Drop" + drag.sinceStart());
+
+ if (getStyleElement().getStyle().getBackgroundColor()
+ .equals("yellow")) {
+ // not accepted
+ ApplicationConnection.getConsole().log(
+ "Drop was not accepted");
+ return false;
+ }
+
+ Transferable transferable = drag.getTransferrable();
+
+ // this is absolute layout based, and we may want to set
+ // component
+ // relatively to where the drag ended.
+ // need to add current location of the drop area
+
+ int absoluteLeft = getAbsoluteLeft();
+ int absoluteTop = getAbsoluteTop();
+
+ transferable.setData("absoluteLeft", absoluteLeft);
+ transferable.setData("absoluteTop", absoluteTop);
+
+ getStyleElement().getStyle().setBackgroundColor("yellow");
+ return super.drop(drag);
+ }
+
+ @Override
+ protected void dragAccepted(DragEvent drag) {
+ getStyleElement().getStyle().setBackgroundColor("green");
+ }
+
+ public ApplicationConnection getApplicationConnection() {
+ return client;
+ }
+ };
+ }
+ return dropHandler;
+ }
+
+ public Paintable getPaintable() {
+ return this;
+ }
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ValueMap;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+
+public abstract class AbstractDropHandler implements DropHandler {
+
+ private boolean serverValidate;
+ private UIDL criterioUIDL;
+
+ public void updateRules(UIDL uidl) {
+ serverValidate = uidl.getBooleanAttribute("serverValidate");
+ int childCount = uidl.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ UIDL childUIDL = uidl.getChildUIDL(i);
+ if (childUIDL.getTag().equals("acceptCriterion")) {
+ criterioUIDL = childUIDL;
+ // TODO consider parsing the criteria tree here instead of
+ // translating uidl during validates()
+
+ }
+ }
+ }
+
+ public boolean validateOnServer() {
+ return serverValidate;
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public void dragOver(DragEvent currentDrag) {
+
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public void dragLeave(DragEvent drag) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * If transferrable is accepted (either via server visit or client side
+ * rules) the default implementation calls {@link #dragAccepted(DragEvent)}
+ * method.
+ */
+ public void dragEnter(final DragEvent drag) {
+ if (serverValidate) {
+ DragAndDropManager.get().visitServer(DragEventType.ENTER,
+ new AcceptCallback() {
+ public void handleResponse(ValueMap responseData) {
+ if (responseData.containsKey("accepted")) {
+ dragAccepted(drag);
+ }
+ }
+ });
+ } else if (validates(drag.getTransferrable())) {
+ dragAccepted(drag);
+ }
+ }
+
+ abstract protected void dragAccepted(DragEvent drag);
+
+ /**
+ * Returns true if client side rules are met.
+ *
+ * @param transferable
+ * @return
+ */
+ protected boolean validates(Transferable transferable) {
+ if (criterioUIDL != null) {
+ String criteriaName = criterioUIDL.getStringAttribute("name");
+ AcceptCriteria acceptCriteria = AcceptCriterion.get(criteriaName);
+ if (acceptCriteria != null) {
+ // ApplicationConnection.getConsole().log(
+ // "Criteria : " + acceptCriteria.getClass().getName());
+ return acceptCriteria.accept(transferable, criterioUIDL);
+ }
+ }
+ return false;
+ }
+
+ public boolean drop(DragEvent drag) {
+ if (serverValidate) {
+ return true;
+ } else {
+ return validates(drag.getTransferrable());
+ }
+ }
+
+ public abstract Paintable getPaintable();
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.vaadin.terminal.gwt.client.ValueMap;
+
+public interface AcceptCallback {
+
+ /**
+ * This method is called by {@link DragAndDropManager} if the
+ * {@link DragEvent} is still active. Developer can update for example drag
+ * icon or target emphasis based on the information returned from server
+ * side. If the drag and drop operation ends or the
+ * {@link AbstractDropHandler} has changed before response arrives, the
+ * method is never called.
+ *
+ * @param responseData
+ */
+ public void handleResponse(ValueMap responseData);
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.vaadin.terminal.gwt.client.UIDL;
+
+public interface AcceptCriteria {
+
+ public boolean accept(Transferable transferable, UIDL configuration);
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+
+public class AcceptCriterion {
+ protected static Map<String, AcceptCriteria> criterion = new HashMap<String, AcceptCriteria>();
+ private static AcceptCriterionImpl impl;
+
+ static {
+ impl = GWT.create(AcceptCriterionImpl.class);
+ impl.populateCriterionMap(criterion);
+ }
+
+ public static AcceptCriteria get(String name) {
+ return criterion.get(name);
+ }
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import java.util.Map;
+
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+
+public class AcceptCriterionImpl {
+
+ private final class OverTreeNode implements AcceptCriteria {
+ public boolean accept(Transferable transferable, UIDL configuration) {
+ Boolean containsKey = (Boolean) transferable
+ .getData("itemIdOverIsNode");
+ if (containsKey != null && containsKey.booleanValue()) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final class ComponentCriteria implements AcceptCriteria {
+ public boolean accept(Transferable transferable, UIDL configuration) {
+ try {
+ // FIXME should have access to client too, change transferrable
+ // to DragEvent??
+ Paintable component = transferable.getComponent();
+ String requiredPid = configuration
+ .getStringAttribute("component");
+ String pid = ((Widget) component).getElement()
+ .getPropertyString("tkPid");
+ return pid.equals(requiredPid);
+ } catch (Exception e) {
+ }
+ return false;
+ }
+ }
+
+ private final class And implements AcceptCriteria {
+ public boolean accept(Transferable transferable, UIDL configuration) {
+ UIDL childUIDL = configuration.getChildUIDL(0);
+ UIDL childUIDL2 = configuration.getChildUIDL(1);
+ AcceptCriteria acceptCriteria = AcceptCriterion.get(childUIDL
+ .getStringAttribute("name"));
+ AcceptCriteria acceptCriteria2 = AcceptCriterion.get(childUIDL2
+ .getStringAttribute("name"));
+ if (acceptCriteria == null || acceptCriteria2 == null) {
+ ApplicationConnection.getConsole().log(
+ "And criteria didn't found a chidl criteria");
+ return false;
+ }
+ boolean accept = acceptCriteria.accept(transferable, childUIDL);
+ boolean accept2 = acceptCriteria2.accept(transferable, childUIDL2);
+ return accept && accept2;
+ }
+ }
+
+ private final class AcceptAll implements AcceptCriteria {
+ public boolean accept(Transferable transferable, UIDL configuration) {
+ return true;
+ }
+ }
+
+ private final class HasItemId implements AcceptCriteria {
+ public boolean accept(Transferable transferable, UIDL configuration) {
+ return transferable.getItemId() != null;
+ }
+ }
+
+ /**
+ * TODO this method could be written by generator
+ *
+ * TODO consider moving implementations to top level classes
+ *
+ * TODO use fully qualified names of server side counterparts as keys
+ */
+ public void populateCriterionMap(Map<String, AcceptCriteria> map) {
+ AcceptCriteria crit;
+
+ crit = new HasItemId();
+ map.put("needsItemId", crit);
+
+ crit = new AcceptAll();
+ map.put("acceptAll", crit);
+
+ crit = new And();
+ map.put("and", crit);
+
+ crit = new OverTreeNode();
+ map.put("overTreeNode", crit);
+
+ crit = new ComponentCriteria();
+ map.put("component", crit);
+
+ }
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.EventListener;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.ValueMap;
+
+/**
+ * Helper class to manage the state of drag and drop event on Vaadin client
+ * side. Can be used to implement most of the drag and drop operation
+ * automatically via cross-browser event preview method or just as a helper when
+ * implementing own low level drag and drop operation (like with HTML5 api).
+ * <p>
+ * Singleton. Only one drag and drop operation can be active anyways. Use
+ * {@link #get()} to get instance.
+ *
+ */
+public class DragAndDropManager {
+
+ public enum DragEventType {
+ ENTER, LEAVE, OVER, DROP
+ }
+
+ private static final String DD_SERVICE = "DD";
+
+ private static DragAndDropManager instance;
+ private HandlerRegistration handlerRegistration;
+ private DragEvent currentDrag;
+
+ /**
+ * If dragging is currently on a drophandler, this field has reference to it
+ */
+ private DropHandler currentDropHandler;
+
+ public DropHandler getCurrentDropHandler() {
+ return currentDropHandler;
+ }
+
+ /**
+ * If drag and drop operation is not handled by {@link DragAndDropManager}s
+ * internal handler, this can be used to update current {@link DropHandler}.
+ *
+ * @param currentDropHandler
+ */
+ public void setCurrentDropHandler(DropHandler currentDropHandler) {
+ this.currentDropHandler = currentDropHandler;
+ }
+
+ private AcceptCallback acceptCallback;
+
+ public static DragAndDropManager get() {
+ if (instance == null) {
+ instance = new DragAndDropManager();
+ }
+ return instance;
+ }
+
+ /* Singleton */
+ private DragAndDropManager() {
+ }
+
+ /**
+ * This method is used to start Vaadin client side drag and drop operation.
+ * Operation may be started by virtually any Widget.
+ * <p>
+ * Cancels possible existing drag. TODO figure out if this is always a bug
+ * if one is active. Maybe a good and cheap lifesaver thought.
+ * <p>
+ * If possible, method automatically detects current {@link DropHandler} and
+ * fires {@link DropHandler#dragEnter(DragEvent)} event on it.
+ * <p>
+ * May also be used to control the drag and drop operation. If this option
+ * is used, {@link DropHandler} is searched on mouse events and appropriate
+ * methods on it called automatically.
+ *
+ * @param transferable
+ * @param nativeEvent
+ * @param handleDragEvents
+ * if true, {@link DragAndDropManager} handles the drag and drop
+ * operation GWT event preview.
+ * @return
+ */
+ public DragEvent startDrag(Transferable transferable,
+ NativeEvent startEvent, boolean handleDragEvents) {
+ interruptDrag();
+
+ currentDrag = new DragEvent(transferable, startEvent);
+ DropHandler dh = null;
+ if (startEvent != null) {
+ dh = findDragTarget((Element) startEvent.getEventTarget().cast());
+ }
+ if (dh != null) {
+ // drag has started on a DropHandler, kind of drag over happens
+ currentDropHandler = dh;
+ updateCurrentEvent(startEvent);
+ dh.dragEnter(currentDrag);
+ }
+
+ if (handleDragEvents) {
+
+ handlerRegistration = Event
+ .addNativePreviewHandler(new NativePreviewHandler() {
+
+ public void onPreviewNativeEvent(
+ NativePreviewEvent event) {
+ updateCurrentEvent(event.getNativeEvent());
+ updateDragImagePosition();
+
+ NativeEvent nativeEvent = event.getNativeEvent();
+ Element targetElement = (Element) nativeEvent
+ .getEventTarget().cast();
+ if (dragElement != null
+ && targetElement.isOrHasChild(dragElement)) {
+ ApplicationConnection.getConsole().log(
+ "Event on dragImage, ignored");
+ event.cancel();
+ nativeEvent.stopPropagation();
+ return;
+ }
+
+ int typeInt = event.getTypeInt();
+ switch (typeInt) {
+ case Event.ONMOUSEOVER:
+ ApplicationConnection.getConsole().log(
+ event.getNativeEvent().getType());
+ DropHandler target = findDragTarget(targetElement);
+ if (target != null && target != currentDrag) {
+ currentDropHandler = target;
+ target.dragEnter(currentDrag);
+ } else if (target == null
+ && currentDropHandler != null) {
+ ApplicationConnection.getConsole().log(
+ "Invalid state!?");
+ currentDropHandler = null;
+ }
+ break;
+ case Event.ONMOUSEOUT:
+ ApplicationConnection.getConsole().log(
+ event.getNativeEvent().getType());
+
+ Element relatedTarget = (Element) nativeEvent
+ .getRelatedEventTarget().cast();
+ DropHandler newDragHanler = findDragTarget(relatedTarget);
+ if (dragElement != null
+ && dragElement
+ .isOrHasChild(relatedTarget)) {
+ ApplicationConnection.getConsole().log(
+ "Mouse out of dragImage, ignored");
+ return;
+ }
+
+ if (currentDropHandler != null
+ && currentDropHandler != newDragHanler) {
+ currentDropHandler.dragLeave(currentDrag);
+ currentDropHandler = null;
+ acceptCallback = null;
+ }
+ break;
+ case Event.ONMOUSEMOVE:
+ if (currentDropHandler != null) {
+ currentDropHandler.dragOver(currentDrag);
+ }
+ nativeEvent.preventDefault();
+
+ break;
+
+ case Event.ONMOUSEUP:
+ endDrag();
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ });
+ }
+ return currentDrag;
+ }
+
+ private void interruptDrag() {
+ if (currentDrag != null) {
+ ApplicationConnection.getConsole()
+ .log("Drag operation interrupted");
+ if (currentDropHandler != null) {
+ currentDrag.currentGwtEvent = null;
+ currentDropHandler.dragLeave(currentDrag);
+ currentDropHandler = null;
+ }
+ currentDrag = null;
+ }
+ }
+
+ private void updateDragImagePosition() {
+ if (currentDrag.currentGwtEvent != null && dragElement != null) {
+ Style style = dragElement.getStyle();
+ int clientY = currentDrag.currentGwtEvent.getClientY() + 6;
+ int clientX = currentDrag.currentGwtEvent.getClientX() + 6;
+ style.setTop(clientY, Unit.PX);
+ style.setLeft(clientX, Unit.PX);
+ }
+ }
+
+ /**
+ * First seeks the widget from this element, then iterates widgets until one
+ * implement HasDropHandler. Returns DropHandler from that.
+ *
+ * @param element
+ * @return
+ */
+ private DropHandler findDragTarget(Element element) {
+
+ EventListener eventListener = Event.getEventListener(element);
+ while (eventListener == null) {
+ element = element.getParentElement();
+ if (element == null) {
+ break;
+ }
+ eventListener = Event.getEventListener(element);
+ }
+ if (eventListener == null) {
+ ApplicationConnection.getConsole().log(
+ "No suitable DropHandler found");
+ return null;
+ } else {
+ Widget w = (Widget) eventListener;
+ while (!(w instanceof HasDropHandler)) {
+ w = w.getParent();
+ if (w == null) {
+ break;
+ }
+ }
+ if (w == null) {
+ ApplicationConnection.getConsole().log(
+ "No suitable DropHandler found2");
+ return null;
+ } else {
+ DropHandler dh = ((HasDropHandler) w).getDropHandler();
+ if (dh == null) {
+ ApplicationConnection.getConsole().log(
+ "No suitable DropHandler found3");
+ }
+ return dh;
+ }
+ }
+ }
+
+ private void updateCurrentEvent(NativeEvent event) {
+ currentDrag.currentGwtEvent = event;
+ }
+
+ public void endDrag() {
+ if (handlerRegistration != null) {
+ handlerRegistration.removeHandler();
+ handlerRegistration = null;
+ }
+ if (currentDropHandler != null) {
+ // we have dropped on a drop target
+ boolean sendTransferrableToServer = currentDropHandler
+ .drop(currentDrag);
+ if (sendTransferrableToServer) {
+ doRequest(DragEventType.DROP);
+ }
+ currentDropHandler = null;
+ acceptCallback = null;
+
+ }
+
+ currentDrag = null;
+
+ if (dragElement != null) {
+ RootPanel.getBodyElement().removeChild(dragElement);
+ dragElement = null;
+ }
+ }
+
+ private int visitId = 0;
+ private Element dragElement;
+
+ /**
+ * Visits server during drag and drop procedure. Transferable and event type
+ * is given to server side counterpart of DropHandler.
+ *
+ * If another server visit is started before the current is received, the
+ * current is just dropped. TODO consider if callback should have
+ * interrupted() method for cleanup.
+ *
+ * @param acceptCallback
+ */
+ public void visitServer(DragEventType type, AcceptCallback acceptCallback) {
+ doRequest(type);
+ this.acceptCallback = acceptCallback;
+ }
+
+ private void doRequest(DragEventType drop) {
+ Paintable paintable = currentDropHandler.getPaintable();
+ ApplicationConnection client = currentDropHandler
+ .getApplicationConnection();
+ /*
+ * For drag events we are using special id that are routed to
+ * "drag service" which then again finds the corresponding DropHandler
+ * on server side.
+ *
+ * TODO add rest of the data in Transferable
+ *
+ * TODO implement partial updates to Transferable (currently the whole
+ * Transferable is sent on each request)
+ */
+ visitId++;
+ client.updateVariable(DD_SERVICE, "visitId", visitId, false);
+ client.updateVariable(DD_SERVICE, "eventId", currentDrag.getEventId(),
+ false);
+ client.updateVariable(DD_SERVICE, "dhowner", paintable, false);
+
+ Transferable transferable = currentDrag.getTransferrable();
+
+ if (transferable.getItemId() != null) {
+ client.updateVariable(DD_SERVICE, "itemId", transferable
+ .getItemId(), false);
+ }
+ if (transferable.getPropertyId() != null) {
+ client.updateVariable(DD_SERVICE, "propertyId", transferable
+ .getPropertyId(), false);
+ }
+
+ client.updateVariable(DD_SERVICE, "component", transferable
+ .getComponent(), false);
+
+ client.updateVariable(DD_SERVICE, "type", drop.ordinal(), false);
+
+ if (currentDrag.currentGwtEvent != null) {
+ try {
+ MouseEventDetails mouseEventDetails = new MouseEventDetails(
+ currentDrag.currentGwtEvent);
+ transferable.setData("mouseEvent", mouseEventDetails
+ .serialize());
+ } catch (Exception e) {
+ // NOP, (at least oophm on Safari) can't serialize html dd event
+ // to
+ // mouseevent
+ }
+ } else {
+ transferable.setData("mouseEvent", null);
+ }
+
+ client.updateVariable(DD_SERVICE, "payload", transferable
+ .getVariableMap(), true);
+
+ }
+
+ public void handleServerResponse(ValueMap valueMap) {
+ if (acceptCallback == null) {
+ return;
+ }
+ int visitId = valueMap.getInt("visitId");
+ if (this.visitId == visitId) {
+ acceptCallback.handleResponse(valueMap);
+ acceptCallback = null;
+ }
+ }
+
+ void setDragElement(Element node) {
+ if (currentDrag != null) {
+ if (dragElement != null && dragElement != node) {
+ RootPanel.getBodyElement().removeChild(dragElement);
+ } else if (node == dragElement) {
+ return;
+ }
+
+ dragElement = node;
+ Style style = node.getStyle();
+ style.setPosition(Position.ABSOLUTE);
+ style.setZIndex(600000);
+ RootPanel.getBodyElement().appendChild(node);
+ }
+ }
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import java.util.Date;
+
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.user.client.Element;
+
+/**
+ * DragEvent used by Vaadin client side engine. Supports components, items,
+ * properties and custom payload (HTML5 style).
+ *
+ *
+ */
+public class DragEvent {
+
+ private static int eventId = 0;
+
+ private Transferable transferable;
+
+ NativeEvent currentGwtEvent;
+
+ private NativeEvent startEvent;
+
+ private int id;
+
+ private Date start;
+
+ DragEvent(Transferable t, NativeEvent startEvent) {
+ transferable = t;
+ this.startEvent = startEvent;
+ id = eventId++;
+ start = new Date();
+ }
+
+ public Transferable getTransferrable() {
+ return transferable;
+ }
+
+ public NativeEvent getCurrentGwtEvent() {
+ return currentGwtEvent;
+ }
+
+ public int getEventId() {
+ return id;
+ }
+
+ public long sinceStart() {
+ return new Date().getTime() - start.getTime();
+ }
+
+ /**
+ * Sets the element that will be used as "drag icon".
+ *
+ * TODO decide if this method should be here or in {@link Transferable} (in
+ * HTML5 it is in DataTransfer) or {@link DragAndDropManager}
+ *
+ * TODO should be possible to override behaviour an set to HTML5
+ * DataTransfer
+ *
+ * @param node
+ */
+ public void setDragImage(Element node) {
+ DragAndDropManager.get().setDragElement(node);
+ }
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+
+/**
+ * Vaadin Widgets (TODO or Paintables, see {@link HasDropHandler}) that want to
+ * receive something via drag and drop implement this interface.
+ */
+public interface DropHandler {
+
+ /**
+ * Called by D'D' manager when drag gets over this drop handler.
+ *
+ * @param drag
+ */
+ public void dragEnter(DragEvent drag);
+
+ /**
+ * Called by D'D' manager when drag gets out this drop handler.
+ *
+ * @param drag
+ */
+ public void dragLeave(DragEvent drag);
+
+ /**
+ * The actual drop happened on this drop handler.
+ *
+ * @param drag
+ * @return true if Tranferrable of this drag event needs to be sent to
+ * server, false if drop was finally canceled or no server visit is
+ * needed
+ */
+ public boolean drop(DragEvent drag);
+
+ /**
+ * When drag is over current drag handler.
+ *
+ * With drag implementation by {@link DragAndDropManager} will be called
+ * when mouse is moved. HTML5 implementations call this continuously even
+ * though mouse is not moved.
+ *
+ * @param currentDrag
+ */
+ public void dragOver(DragEvent currentDrag);
+
+ /**
+ * Returns the Paintable into which this DragHandler is assosiated
+ */
+ public Paintable getPaintable();
+
+ /**
+ * Returns the application connection to which this {@link DropHandler}
+ * belongs to. DragAndDropManager uses this fucction to send Transferable to
+ * server side.
+ */
+ public ApplicationConnection getApplicationConnection();
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.vaadin.terminal.gwt.client.Paintable;
+
+/**
+ * Used to detect Widget from widget tree that has {@link #getDropHandler()}
+ *
+ * Decide whether to get rid of this class. If so, {@link AbstractDropHandler} must
+ * extend {@link Paintable}.
+ *
+ */
+public interface HasDropHandler {
+ public DropHandler getDropHandler();
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.dom.client.NativeEvent;
+
+public class Html5DragEvent extends NativeEvent {
+ protected Html5DragEvent() {
+ }
+
+ public final native JsArrayString getTypes()
+ /*-{
+ return this.dataTransfer.types;
+ }-*/;
+
+ public final native String getDataAsText(String type)
+ /*-{
+ var v = this.dataTransfer.getData(type);
+ return v;
+ }-*/;
+
+ /**
+ * Works on FF 3.6 and possibly with gears.
+ *
+ * @param index
+ * @return
+ */
+ public final native String getFileAsString(int index)
+ /*-{
+ if(this.dataTransfer.files.length > 0 && this.dataTransfer.files[0].getAsText) {
+ return this.dataTransfer.files[index].getAsText("UTF-8");
+ }
+ return null;
+ }-*/;
+
+ public final native void setDragEffect(String effect)
+ /*-{
+ try {
+ this.dataTransfer.dropEffect = effect;
+ } catch (e){}
+ }-*/;
+
+ public final native String getEffectAllowed()
+ /*-{
+ return this.dataTransfer.effectAllowed;
+ }-*/;
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui.dd;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.terminal.TransferTranslator;
+import com.vaadin.terminal.gwt.client.Paintable;
+
+/**
+ * Client side counterpart for Transferable in com.vaadin.event.Transferable
+ *
+ */
+public class Transferable {
+
+ /**
+ * @return the component
+ */
+ public Paintable getComponent() {
+ return component;
+ }
+
+ /**
+ * @param component
+ * the component to set
+ */
+ public void setComponent(Paintable component) {
+ this.component = component;
+ }
+
+ /**
+ * This is commonly actually a key to property id on client side than the
+ * actual propertyId.
+ *
+ * Translated by terminal and {@link TransferTranslator}
+ *
+ * @return the propertyId
+ */
+ public String getPropertyId() {
+ return (String) variables.get("propertyId");
+ }
+
+ /**
+ * This is commonly actually a key to property id on client side than the
+ * actual propertyId.
+ *
+ * Translated by terminal and {@link TransferTranslator}
+ *
+ * @param propertyId
+ * the propertyId to set
+ */
+ public void setPropertyId(String propertyId) {
+ variables.put("propertyId", propertyId);
+ }
+
+ /**
+ * @return the itemId
+ */
+ public String getItemId() {
+ return (String) variables.get("itemId");
+ }
+
+ /**
+ * This is commonly actually a key to item id on client side than the actual
+ * itemId.
+ *
+ * Translated by terminal and {@link TransferTranslator}
+ *
+ * @param itemId
+ * the itemId to set
+ */
+ public void setItemId(String itemId) {
+ variables.put("itemId", itemId);
+ }
+
+ private Paintable component;
+
+ public Object getData(String dataFlawor) {
+ return variables.get(dataFlawor);
+ }
+
+ public void setData(String dataFlawor, Object value) {
+ variables.put(dataFlawor, value);
+ }
+
+ public Collection<String> getDataFlawors() {
+ return variables.keySet();
+ }
+
+ private final Map<String, Object> variables = new HashMap<String, Object>();
+
+ /**
+ * This method should only be called by {@link DragAndDropManager}.
+ *
+ * @return data in this Transferable that needs to be moved to server.
+ */
+ public Map<String, Object> getVariableMap() {
+ return variables;
+ }
+
+}
--- /dev/null
+package com.vaadin.terminal.gwt.server;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.event.ComponentTransferrable;
+import com.vaadin.event.DragRequest;
+import com.vaadin.event.DropHandler;
+import com.vaadin.event.HasDropHandler;
+import com.vaadin.event.Transferable;
+import com.vaadin.terminal.TransferTranslator;
+import com.vaadin.terminal.VariableOwner;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+import com.vaadin.ui.Component;
+
+public class DragAndDropService implements VariableOwner {
+
+ private static final long serialVersionUID = -4745268869323400203L;
+
+ private int lastVisitId;
+
+ private DragRequest currentRequest;
+
+ private int currentEventId;
+
+ private Transferable transferable;
+
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ HasDropHandler dropHandlerOwner = (HasDropHandler) variables
+ .get("dhowner");
+ if (dropHandlerOwner == null) {
+ return;
+ }
+ lastVisitId = (Integer) variables.get("visitId");
+
+ currentRequest = constructDragRequest(variables, dropHandlerOwner);
+
+ DropHandler dropHandler = (dropHandlerOwner).getDropHandler();
+ dropHandler.handleDragRequest(currentRequest);
+ if (currentRequest.getType() == DragEventType.DROP) {
+ // TODO transferable should also be cleaned on each non-dnd
+ // variable change (if visited server, but drop did not happen ->
+ // should do cleanup to release memory)
+ transferable = null;
+ }
+
+ }
+
+ private DragRequest constructDragRequest(Map<String, Object> variables,
+ HasDropHandler dropHandlerOwner) {
+ Transferable transferable = constructTransferrable(variables,
+ dropHandlerOwner);
+
+ int type = (Integer) variables.get("type");
+ DragRequest dragRequest = new DragRequest(DragEventType.values()[type],
+ transferable);
+ return dragRequest;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Transferable constructTransferrable(Map<String, Object> variables,
+ HasDropHandler dropHandlerOwner) {
+ int eventId = (Integer) variables.get("eventId");
+ if (currentEventId != eventId) {
+ transferable = null;
+ }
+ currentEventId = eventId;
+
+ final Component sourceComponent = (Component) variables
+ .get("component");
+ if (sourceComponent != null
+ && sourceComponent instanceof TransferTranslator) {
+ transferable = ((TransferTranslator) sourceComponent)
+ .getTransferrable(transferable, variables, false);
+ } else {
+ if (transferable == null) {
+ if (sourceComponent != null) {
+ transferable = new ComponentTransferrable() {
+
+ private Map<String, Object> td = new HashMap<String, Object>();
+
+ public Component getSourceComponent() {
+ return sourceComponent;
+ }
+
+ public Object getData(String dataFlawor) {
+ return td.get(dataFlawor);
+ }
+
+ public void setData(String dataFlawor, Object value) {
+ td.put(dataFlawor, value);
+ }
+
+ public Collection<String> getDataFlawors() {
+ return td.keySet();
+ }
+
+ };
+ } else {
+ transferable = new Transferable() {
+ private Map<String, Object> td = new HashMap<String, Object>();
+
+ public Object getData(String dataFlawor) {
+ return td.get(dataFlawor);
+ }
+
+ public void setData(String dataFlawor, Object value) {
+ td.put(dataFlawor, value);
+ }
+
+ public Collection<String> getDataFlawors() {
+ return td.keySet();
+ }
+
+ };
+ }
+ }
+ }
+ /*
+ * Also let dropHandler translate variables if it implements
+ * TransferTranslator
+ */
+ if (dropHandlerOwner instanceof TransferTranslator) {
+ transferable = ((TransferTranslator) dropHandlerOwner)
+ .getTransferrable(transferable, variables, true);
+ }
+
+ /*
+ * Add remaining (non-handled) variables to transferable as is
+ */
+ variables = (Map<String, Object>) variables.get("payload");
+ for (String key : variables.keySet()) {
+ transferable.setData(key, variables.get(key));
+ }
+
+ return transferable;
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public boolean isImmediate() {
+ return true;
+ }
+
+ void printJSONResponse(PrintWriter outWriter) {
+ if (isDirty()) {
+ // TODO paint responsedata
+ outWriter.print(", dd : {");
+ outWriter.print("visitId:");
+ outWriter.print(lastVisitId);
+ Map<String, Object> responseData = currentRequest.getResponseData();
+ if (responseData != null) {
+ for (String key : responseData.keySet()) {
+ Object object = responseData.get(key);
+ outWriter.print(",\"");
+ // TODO JSON escaping for key and object
+ outWriter.print(key);
+ outWriter.print("\":");
+ outWriter.print(object);
+ }
+ }
+ outWriter.print("}");
+ currentRequest = null;
+ }
+ }
+
+ private boolean isDirty() {
+ if (currentRequest != null) {
+ return true;
+ }
+ return false;
+ }
+}
--- /dev/null
+package com.vaadin.ui;
+
+import com.vaadin.event.AbstractDropHandler;
+import com.vaadin.event.ComponentTransferrable;
+import com.vaadin.event.DataBindedTransferrable;
+import com.vaadin.event.HasDropHandler;
+import com.vaadin.event.Transferable;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+
+/**
+ * Test class that implements various component related Drag and Drop features.
+ *
+ * TODO consider implementing this kind of general purpose layout class:
+ *
+ * - extend simple component (CssLayout or CustomComponent, instead of absolute
+ * layout)
+ *
+ * - implement both drag source and drop handler with server side api
+ *
+ * - implement html5 drop target etc
+ *
+ * - include a lots of details for drop event (coordinates & sizes of drop
+ * position and widget/Paintable hierarchy up to drop handler)
+ *
+ * This way we could have one rather complex dd component that could be used (by
+ * wrapping layouts) in most common situations with server side api. Core
+ * layouts wouldn't need changes (and have regression risk/ performance
+ * penalty).
+ *
+ */
+@SuppressWarnings("serial")
+@ClientWidget(com.vaadin.terminal.gwt.client.ui.VDragDropPane.class)
+public class DragDropPane extends AbsoluteLayout implements HasDropHandler {
+
+ private AbstractDropHandler abstractDropHandler;
+
+ public DragDropPane(AbstractDropHandler dropHandler) {
+ setWidth("400px");
+ setHeight("300px");
+
+ if (dropHandler == null) {
+ dropHandler = new AbstractDropHandler() {
+ @Override
+ public void receive(Transferable transferable) {
+ if (transferable instanceof ComponentTransferrable) {
+ ComponentTransferrable ctr = (ComponentTransferrable) transferable;
+ Component component = ctr.getSourceComponent();
+
+ if (component.getParent() != DragDropPane.this) {
+ if (transferable instanceof DataBindedTransferrable) {
+ // Item has been dragged, construct a Label from
+ // Item id
+ Label l = new Label();
+ l.setSizeUndefined();
+ l
+ .setValue("ItemId : "
+ + ((DataBindedTransferrable) transferable)
+ .getItemId());
+ DragDropPane.this.addComponent(l);
+ component = l;
+
+ } else {
+ // we have a component that is been dragged, add
+ // it
+ // to
+ // this
+ DragDropPane.this.addComponent(component);
+ }
+ Integer left = (Integer) transferable
+ .getData("absoluteLeft");
+ Integer top = (Integer) transferable
+ .getData("absoluteTop");
+
+ MouseEventDetails eventDetails = MouseEventDetails
+ .deSerialize((String) transferable
+ .getData("mouseEvent"));
+
+ int clientX = eventDetails.getClientX();
+ int clientY = eventDetails.getClientY();
+
+ try {
+ DragDropPane.this.getPosition(component)
+ .setTopValue(clientY - top);
+ DragDropPane.this.getPosition(component)
+ .setLeftValue(clientX - left);
+ } catch (Exception e) {
+ // TODO: handle exception
+ }
+ } else {
+ // drag ended inside the this Pane
+
+ MouseEventDetails start = MouseEventDetails
+ .deSerialize((String) transferable
+ .getData("mouseDown"));
+ MouseEventDetails eventDetails = MouseEventDetails
+ .deSerialize((String) transferable
+ .getData("mouseEvent"));
+
+ int deltaX = eventDetails.getClientX()
+ - start.getClientX();
+ int deltaY = eventDetails.getClientY()
+ - start.getClientY();
+
+ ComponentPosition p = DragDropPane.this
+ .getPosition(component);
+ p.setTopValue(p.getTopValue() + deltaY);
+ p.setLeftValue(p.getLeftValue() + deltaX);
+
+ }
+
+ } else {
+ // drag coming outside of Vaadin
+ String object = (String) transferable
+ .getData("text/plain");
+
+ String content = (String) transferable
+ .getData("fileContents");
+
+ Label l = new Label();
+ l.setCaption("Generated from HTML5 drag:");
+ if (object != null) {
+ l.setValue(object);
+ } else {
+ l.setValue("HTML5 dd");
+ }
+
+ l.setDescription(content);
+ l.setSizeUndefined();
+
+ DragDropPane.this.addComponent(l);
+
+ }
+
+ }
+ };
+ if (dropHandler instanceof AbstractDropHandler) {
+ AbstractDropHandler new_name = dropHandler;
+ new_name
+ .setAcceptCriterion(AbstractDropHandler.CRITERION_ACCEPT_ALL);
+ }
+ }
+ abstractDropHandler = dropHandler;
+ }
+
+ public DragDropPane() {
+ this(null);
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ if (abstractDropHandler instanceof AbstractDropHandler) {
+ AbstractDropHandler new_name = abstractDropHandler;
+ new_name.paint(target);
+ }
+ }
+
+ public AbstractDropHandler getDropHandler() {
+ return abstractDropHandler;
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import com.vaadin.ui.DragDropPane;
+import com.vaadin.ui.Window;
+
+public class AcceptAnythingWindow extends Window {
+
+ public AcceptAnythingWindow() {
+ setCaption("Drop anything here");
+ DragDropPane pane = new DragDropPane();
+ setContent(pane);
+ pane.setSizeFull();
+ setWidth("250px");
+ setHeight("100px");
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import com.vaadin.event.ComponentTransferrable;
+import com.vaadin.event.Transferable;
+import com.vaadin.event.AbstractDropHandler.AcceptCriterion;
+import com.vaadin.ui.DragDropPane;
+import com.vaadin.ui.Tree;
+import com.vaadin.ui.Window;
+
+public class AcceptFromComponent extends Window {
+
+ public AcceptFromComponent(final Tree tree1) {
+ setCaption("Checks the source is tree1 on server");
+
+ DragDropPane pane = new DragDropPane();
+ setContent(pane);
+ pane.getDropHandler().setAcceptCriterion(new AcceptCriterion() {
+ public boolean accepts(Transferable transferable) {
+ if (transferable instanceof ComponentTransferrable) {
+ ComponentTransferrable componentTransferrable = (ComponentTransferrable) transferable;
+ if (componentTransferrable.getSourceComponent() == tree1) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ pane.setSizeFull();
+ setWidth("450px");
+ setHeight("150px");
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import com.vaadin.event.AbstractDropHandler;
+import com.vaadin.event.DragRequest;
+import com.vaadin.event.DropHandler;
+import com.vaadin.event.HasDropHandler;
+import com.vaadin.event.Transferable;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.ClientWidget;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.CustomComponent;
+import com.vaadin.ui.Layout;
+
+/**
+ * Test/Example/Draft code how to build custom DD implementation using the thing
+ * framework provided by Vaadin.
+ *
+ */
+public class CustomDDImplementation extends CustomComponent {
+
+ public CustomDDImplementation() {
+ Layout l = new CssLayout();
+ l.addComponent(new MyDropTarget());
+ l.addComponent(new MyDragSource());
+ }
+
+ /**
+ * Server side component that accepts drags must implement HasDropHandler
+ * that have one method to get reference of DropHandler.
+ *
+ * DropHandler may be implemented directly or probably most commonly using a
+ * half baked implementation {@link AbstractDropHandler}.
+ *
+ * Check the @ClientWidget
+ *
+ */
+ @ClientWidget(VMyDropTarget.class)
+ class MyDropTarget extends AbstractComponent implements HasDropHandler {
+
+ public DropHandler getDropHandler() {
+ return new DropHandler() {
+ public void handleDragRequest(DragRequest event) {
+ Transferable transferable = event.getTransferrable();
+ DragEventType type = event.getType();
+ switch (type) {
+ case DROP:
+ // Do something with data
+
+ break;
+
+ case ENTER:
+ // eg. validate transferrable
+ if (transferable.getDataFlawors().contains("Foo")) {
+ event.getResponseData().put("valueFor",
+ "clientSideCallBack");
+ }
+
+ break;
+ case OVER:
+
+ break;
+ case LEAVE:
+
+ break;
+ default:
+ break;
+ }
+
+ }
+ };
+ }
+
+ }
+
+ /**
+ * Server side implementation of source does not necessary need to contain
+ * anything.
+ *
+ * Check the @ClientWidget
+ *
+ * However component might have different modes to support starting drag
+ * operations that are controlled via server side api.
+ *
+ */
+ @ClientWidget(VMyDragSource.class)
+ public class MyDragSource extends AbstractComponent implements Component {
+
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import java.util.Collection;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.event.AbstractDropHandler;
+import com.vaadin.event.ComponentTransferrable;
+import com.vaadin.event.DataBindedTransferrable;
+import com.vaadin.event.Transferable;
+import com.vaadin.event.AbstractDropHandler.AcceptCriterion;
+import com.vaadin.terminal.ExternalResource;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.DragDropPane;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.Link;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Tree;
+import com.vaadin.ui.Table.DragModes;
+
+/**
+ * DD playground. Better quality example/prototype codes in {@link DDTest2}.
+ */
+public class DDTest1 extends TestBase {
+
+ @Override
+ protected void setup() {
+ GridLayout gl = new GridLayout(3, 2);
+ gl.setSizeFull();
+ gl.setSpacing(true);
+ Layout main = gl;
+
+ DragDropPane pane1 = new DragDropPane();
+ pane1.setSizeFull();
+ pane1.setCaption("Pane1");
+
+ Label label = new Label("Foo");
+ label.setSizeUndefined();
+
+ pane1.addComponent(label);
+
+ Link l = new Link("This is link", new ExternalResource(
+ "http://www.google.com/"));
+ pane1.addComponent(l, "top:100px; left: 20px;");
+
+ label = new Label("Bar");
+ label.setSizeUndefined();
+ pane1.addComponent(label);
+
+ DragDropPane pane2 = new DragDropPane();
+ pane2.setDebugId("pane2");
+ pane2.setSizeFull();
+ pane2
+ .setCaption("Pane2 (accept needs server side visit, only \"Bar\")");
+
+ AcceptCriterion f = new AcceptCriterion() {
+ public boolean accepts(Transferable transferable) {
+ // System.out.println("Simulating 500ms processing...");
+ // try {
+ // Thread.sleep(200);
+ // } catch (InterruptedException e) {
+ // // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+ // System.out.println("Done get to work.");
+ if (transferable instanceof ComponentTransferrable) {
+ ComponentTransferrable ct = (ComponentTransferrable) transferable;
+
+ Component component = ct.getSourceComponent();
+ if (component != null) {
+ if (component.toString() != null
+ && component.toString().contains("Bar")) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ };
+ pane2.getDropHandler().setAcceptCriterion(f);
+
+ DragDropPane pane3 = new DragDropPane();
+ pane3.setSizeFull();
+ pane3.setCaption("Pane3");
+
+ final Tree t = new Tree(
+ "Tree with sorting enabled. Also allows dragging elsewhere.");
+
+ final HierarchicalContainer idx = new HierarchicalContainer();
+ t.setContainerDataSource(idx);
+ t.setDebugId("perseys");
+ t.addItem("Foo");
+ t.addItem("Bar");
+ t.addItem("Bar1");
+ t.addItem("Bar2");
+ t.addItem("Bar3");
+ t.addItem("Bar4");
+ t.addItem("Bar5");
+ t.addItem("Child");
+ t.setParent("Child", "Foo");
+ t.setSizeFull();
+
+ /*
+ * Moves items in tree (and could work in Table too). Also supports
+ * "building" tree.
+ *
+ * TODO fix algorithm, broken in some cases.
+ */
+ AbstractDropHandler itemSorter = new AbstractDropHandler() {
+
+ @Override
+ public void receive(Transferable transferable) {
+ // TODO set properties, so same sorter could be used in Table
+ if (transferable instanceof DataBindedTransferrable) {
+ DataBindedTransferrable transferrable2 = (DataBindedTransferrable) transferable;
+
+ Object itemId = transferrable2.getItemId();
+
+ Object itemIdOver = transferable.getData("itemIdOver");
+
+ String detail = ((String) transferable.getData("detail"))
+ .toLowerCase();
+
+ if ("center".equals(detail)) {
+ t.setParent(itemId, itemIdOver);
+ return;
+ } else if ("top".equals(detail)) {
+ // if on top of the caption area, add before
+ itemIdOver = idx.prevItemId(itemIdOver);
+ }
+
+ if (itemId.equals(itemIdOver)) {
+ // the location is same
+ return;
+ }
+
+ HierarchicalContainer subtree = getSubTree(idx, itemId);
+ boolean removed = idx.removeItem(itemId);
+
+ if (removed) {
+
+ if (detail == null) {
+ System.err
+ .println("No detail of drop place available");
+ }
+
+ Item addItemAfter = idx
+ .addItemAfter(itemIdOver, itemId);
+ populateSubTree(idx, subtree, itemId);
+ // ensure the same parent as with related item
+ Object parent = idx.getParent(itemIdOver);
+ idx.setParent(itemId, parent);
+ }
+ }
+
+ }
+
+ private void populateSubTree(HierarchicalContainer idx,
+ HierarchicalContainer subtree, Object itemId) {
+ Collection children = subtree.getChildren(itemId);
+ if (children != null) {
+
+ for (Object childId : children) {
+ Item addItem = idx.addItem(childId);
+ if (addItem != null) {
+ // did not exist, populate properties
+ Item item = subtree.getItem(itemId);
+ Collection<?> itemPropertyIds = item
+ .getItemPropertyIds();
+ for (Object propId : itemPropertyIds) {
+ addItem.getItemProperty(propId)
+ .setValue(
+ item.getItemProperty(propId)
+ .getValue());
+ }
+ }
+ idx.setParent(childId, itemId);
+ populateSubTree(idx, subtree, childId);
+ }
+ }
+
+ }
+
+ private HierarchicalContainer getSubTree(HierarchicalContainer idx,
+ Object itemId) {
+ HierarchicalContainer hierarchicalContainer = new HierarchicalContainer();
+ Collection containerPropertyIds = idx.getContainerPropertyIds();
+ for (Object object : containerPropertyIds) {
+ hierarchicalContainer.addContainerProperty(object, idx
+ .getType(object), null);
+ }
+ hierarchicalContainer.addItem(itemId);
+ copyChildren(idx, hierarchicalContainer, itemId);
+ return hierarchicalContainer;
+ }
+
+ private void copyChildren(HierarchicalContainer source,
+ HierarchicalContainer target, Object itemId) {
+ Collection children = source.getChildren(itemId);
+ if (children != null) {
+ for (Object childId : children) {
+ Item item = source.getItem(childId);
+ Item addedItem = target.addItem(childId);
+ target.setParent(childId, itemId);
+ Collection<?> itemPropertyIds = item
+ .getItemPropertyIds();
+ for (Object propertyId : itemPropertyIds) {
+ addedItem.getItemProperty(propertyId)
+ .setValue(
+ item.getItemProperty(propertyId)
+ .getValue());
+ }
+ copyChildren(source, target, childId);
+ }
+ }
+
+ }
+
+ };
+
+ /*
+ * Accept only drags that have item identifiers
+ */
+ itemSorter
+ .setAcceptCriterion(AbstractDropHandler.CRITERION_HAS_ITEM_ID);
+
+ t.setDropHandler(itemSorter);
+
+ Table ta = new Table("Test table");
+ ta.setContainerDataSource(idx);
+ ta.addContainerProperty("Foos", String.class, "Foo");
+ ta.addContainerProperty("Bars", String.class, "Bar");
+ ta.setRowHeaderMode(Table.ROW_HEADER_MODE_ID);
+ ta.setSizeFull();
+ ta.setDragMode(DragModes.ROWS);
+
+ main.addComponent(pane1);
+ main.addComponent(pane2);
+ main.addComponent(pane3);
+ main.addComponent(t);
+ main.addComponent(ta);
+ main.addComponent(new Link("Foo", new ExternalResource(
+ "http://www.itmill.com/")));
+
+ getLayout().setSizeFull();
+ addComponent(main);
+
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Random DD tests";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 119;
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import java.util.Collection;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.demo.tutorial.addressbook.data.Person;
+import com.vaadin.demo.tutorial.addressbook.data.PersonContainer;
+import com.vaadin.event.AbstractDropHandler;
+import com.vaadin.event.DataBindedTransferrable;
+import com.vaadin.event.Transferable;
+import com.vaadin.event.AbstractDropHandler.AcceptCriterion;
+import com.vaadin.event.AbstractDropHandler.And;
+import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Tree;
+import com.vaadin.ui.Window;
+
+public class DDTest2 extends TestBase {
+
+ java.util.Random r = new java.util.Random(1);
+
+ HorizontalLayout hl = new HorizontalLayout();
+ Tree tree1 = new Tree("Tree that accepts table rows to folders");
+ Table table = new Table("Drag rows to Tree on left or right");
+ Tree tree2 = new Tree("Accepts items, copies values");
+
+ @Override
+ protected void setup() {
+ Window w = getLayout().getWindow();
+ /* darn reindeer has no icons */
+ w.setTheme("runo");
+
+ hl.addComponent(tree1);
+ hl.addComponent(table);
+ hl.addComponent(tree2);
+ hl.setWidth("100%");
+ hl.setSpacing(true);
+ hl.setExpandRatio(table, 1);
+ popuplateTrees();
+ table.setWidth("100%");
+ table.setPageLength(10);
+ populateTable();
+ addComponent(hl);
+
+ /*
+ * Make table rows draggable
+ */
+ table.setDragMode(Table.DragModes.ROWS);
+
+ AbstractDropHandler dropHandler = new AbstractDropHandler() {
+ @Override
+ public void receive(Transferable transferable) {
+ /*
+ * We know transferrable is from table, so it is of type
+ * DataBindedTransferrable
+ */
+ DataBindedTransferrable tr = (DataBindedTransferrable) transferable;
+ Object itemId = tr.getItemId();
+ Table fromTable = (Table) tr.getSourceComponent();
+ String name = fromTable.getItem(itemId).getItemProperty("Name")
+ .toString();
+ tree1.addItem(name);
+ tree1.setChildrenAllowed(name, false);
+
+ /*
+ * As we also accept only drops on folders, we know data
+ * contains itemIdOver
+ */
+ Object idOver = tr.getData("itemIdOver");
+ tree1.setParent(name, idOver);
+
+ /*
+ * Remove the item from table
+ */
+ table.removeItem(itemId);
+
+ }
+ };
+ AcceptCriterion onNode = new AbstractDropHandler.OverTreeNode();
+ AcceptCriterion fromTree = new AbstractDropHandler.ComponentFilter(
+ table);
+ And and = new AbstractDropHandler.And(fromTree, onNode);
+ dropHandler.setAcceptCriterion(and);
+ tree1.setDropHandler(dropHandler);
+
+ /*
+ * First step done. tree1 now accepts drags only from table and only
+ * over tree nodes aka "folders"
+ */
+
+ /*
+ * Now set the rightmost tree accept any item drag. On drop, copy from
+ * source. Also make drags from tree1 possible.
+ */
+
+ dropHandler = new AbstractDropHandler() {
+ @Override
+ public void receive(Transferable transferable) {
+ if (transferable instanceof DataBindedTransferrable) {
+ DataBindedTransferrable tr = (DataBindedTransferrable) transferable;
+
+ Object itemId = tree2.addItem();
+ tree2.setParent(itemId, tr.getData("itemIdOver"));
+ if (tr.getSourceComponent() == tree1) {
+ // use item id from tree1 as caption
+ tree2.setItemCaption(itemId, (String) tr.getItemId());
+ // if comes from tree1, move subtree too
+ copySubTree(tr.getItemId(), itemId);
+ } else if (tr.getSourceComponent() == table) {
+ // comes from table, override caption with name
+ String name = (String) table.getItem(tr.getItemId())
+ .getItemProperty("Name").getValue();
+ tree2.setItemCaption(itemId, name);
+ } else if (tr.getSourceComponent() == tree2) {
+ tree2.setItemCaption(itemId, tree2.getItemCaption(tr
+ .getItemId()));
+ }
+ }
+ }
+
+ private void copySubTree(Object itemId, Object itemIdTo) {
+ Collection children = tree1.getChildren(itemId);
+ if (children != null) {
+ for (Object childId : children) {
+ Object newItemId = tree2.addItem();
+ tree2.setItemCaption(newItemId, (String) childId);
+ tree2.setParent(newItemId, itemIdTo);
+ copySubTree(childId, newItemId);
+ }
+ }
+ }
+ };
+ dropHandler
+ .setAcceptCriterion(AbstractDropHandler.CRITERION_HAS_ITEM_ID);
+
+ tree2.setDropHandler(dropHandler);
+
+ /*
+ * Finally add two windows with DragDropPane. First accept anything,
+ * second has server side accept rule to allow only drops from Tree1.
+ * Check the code in implementing classes.
+ */
+ Window acceptAnyThing = new AcceptAnythingWindow();
+ Window acceptFromTree1viaServerCheck = new AcceptFromComponent(tree1);
+
+ w.addWindow(acceptAnyThing);
+ acceptAnyThing.setPositionY(450);
+ acceptAnyThing.setPositionX(0);
+ w.addWindow(acceptFromTree1viaServerCheck);
+ acceptFromTree1viaServerCheck.setPositionY(450);
+ acceptFromTree1viaServerCheck.setPositionX(300);
+
+ }
+
+ private void populateTable() {
+ table.addContainerProperty("Name", String.class, "");
+ table.addContainerProperty("Weight", Integer.class, 0);
+
+ PersonContainer testData = PersonContainer.createWithTestData();
+
+ for (int i = 0; i < 10; i++) {
+ Item addItem = table.addItem("Item" + i);
+ Person p = testData.getIdByIndex(i);
+ addItem.getItemProperty("Name").setValue(
+ p.getFirstName() + " " + p.getLastName());
+ addItem.getItemProperty("Weight").setValue(50 + r.nextInt(60));
+ }
+
+ }
+
+ private final static ThemeResource FOLDER = new ThemeResource(
+ "icons/16/folder.png");
+ private final static ThemeResource DOC = new ThemeResource(
+ "icons/16/document.png");
+
+ private void popuplateTrees() {
+ HierarchicalContainer hc = new HierarchicalContainer();
+ hc.addContainerProperty("icon", Resource.class, DOC);
+ Item addItem = hc.addItem("Fats");
+ addItem.getItemProperty("icon").setValue(FOLDER);
+ hc.addItem("Tarja");
+ hc.setParent("Tarja", "Fats");
+ hc.setChildrenAllowed("Tarja", false);
+ addItem = hc.addItem("Thins");
+ addItem.getItemProperty("icon").setValue(FOLDER);
+ addItem = hc.addItem("Anorectic");
+ addItem.getItemProperty("icon").setValue(FOLDER);
+ hc.setParent("Anorectic", "Thins");
+ addItem = hc.addItem("Normal weighted");
+ addItem.getItemProperty("icon").setValue(FOLDER);
+
+ tree1.setContainerDataSource(hc);
+ tree1.setItemIconPropertyId("icon");
+
+ tree2.setContainerDataSource(new HierarchicalContainer());
+
+ tree2.addItem("/");
+
+ }
+
+ @Override
+ protected String getDescription() {
+ return "dd";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 119;
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseMoveEvent;
+import com.google.gwt.event.dom.client.MouseMoveHandler;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager;
+import com.vaadin.terminal.gwt.client.ui.dd.Transferable;
+
+/**
+ * Example code to implement Component that has something to drag.
+ */
+public class VMyDragSource extends Composite implements Paintable,
+ MouseDownHandler, MouseMoveHandler, MouseOutHandler {
+
+ private boolean mouseDown;
+ private MouseDownEvent mDownEvent;
+ private ApplicationConnection client;
+
+ public VMyDragSource() {
+ FlowPanel fp = new FlowPanel();
+ initWidget(fp);
+
+ HTML html = new HTML("DragThis");
+
+ fp.add(html);
+
+ html.addMouseDownHandler(this);
+ html.addMouseMoveHandler(this);
+ html.addMouseOutHandler(this);
+
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (client.updateComponent(this, uidl, true)) {
+ return;
+ }
+ this.client = client;
+ }
+
+ /*
+ * Below a sophisticated drag start implementation. Drag event is started if
+ * mouse is moved 5 pixels with left mouse key down.
+ */
+
+ public void onMouseDown(MouseDownEvent event) {
+ if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
+ mouseDown = true;
+ mDownEvent = event;
+ }
+ }
+
+ public void onMouseMove(MouseMoveEvent event) {
+ if (mouseDown) {
+ int deltaX = Math.abs(mDownEvent.getClientX() - event.getClientX());
+ int deltaY = Math.abs(mDownEvent.getClientY() - event.getClientY());
+ if (deltaX > 5 || deltaY > 5) {
+ // Start the drag and drop operation
+
+ // create Transferable, that contains the payload
+ Transferable transferable = new Transferable();
+ transferable.setData("Text", "myPayload");
+
+ // Tell DragAndDropManager to start a drag and drop operation.
+ // Also let it handle all events (last parameter true). Could
+ // also do all event handling here too.
+ DragAndDropManager.get().startDrag(transferable,
+ mDownEvent.getNativeEvent(), true);
+
+ mouseDown = false;
+ mDownEvent = null;
+ }
+ }
+
+ }
+
+ public void onMouseOut(MouseOutEvent event) {
+ mouseDown = false;
+ mDownEvent = null;
+ }
+
+}
--- /dev/null
+package com.vaadin.tests.dd;
+
+import com.google.gwt.user.client.ui.Composite;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ValueMap;
+import com.vaadin.terminal.gwt.client.ui.dd.AcceptCallback;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager;
+import com.vaadin.terminal.gwt.client.ui.dd.DragEvent;
+import com.vaadin.terminal.gwt.client.ui.dd.DropHandler;
+import com.vaadin.terminal.gwt.client.ui.dd.HasDropHandler;
+import com.vaadin.terminal.gwt.client.ui.dd.DragAndDropManager.DragEventType;
+
+public class VMyDropTarget extends Composite implements HasDropHandler,
+ DropHandler, Paintable {
+
+ private ApplicationConnection client;
+
+ public void dragEnter(DragEvent drag) {
+ DragAndDropManager.get().visitServer(DragEventType.ENTER,
+ new AcceptCallback() {
+ public void handleResponse(ValueMap responseData) {
+ // show hints, error messages etc
+ }
+ });
+ }
+
+ public void dragLeave(DragEvent drag) {
+ // TODO Auto-generated method stub
+ }
+
+ public void dragOver(DragEvent currentDrag) {
+ // TODO Auto-generated method stub
+ }
+
+ public boolean drop(DragEvent drag) {
+ // TODO Auto-generated method stub
+ // return true to tell DDManager do server visit
+ return false;
+ }
+
+ public Paintable getPaintable() {
+ // Drophandler implemented by Paintable itself
+ return this;
+ }
+
+ public DropHandler getDropHandler() {
+ // Drophandler implemented by Paintable itself
+ return this;
+ }
+
+ public ApplicationConnection getApplicationConnection() {
+ return client;
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ this.client = client;
+
+ }
+
+}