123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- /*
- @VaadinApache2LicenseForJavaFiles@
- */
- package com.vaadin.terminal.gwt.client.ui;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
-
- import com.google.gwt.core.client.GWT;
- import com.google.gwt.core.client.JsArrayString;
- import com.google.gwt.core.client.Scheduler;
- 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.TouchStartEvent;
- import com.google.gwt.event.dom.client.TouchStartHandler;
- import com.google.gwt.user.client.Command;
- import com.google.gwt.user.client.Element;
- import com.google.gwt.user.client.Event;
- import com.google.gwt.user.client.Timer;
- import com.google.gwt.user.client.ui.Widget;
- import com.google.gwt.xhr.client.ReadyStateChangeHandler;
- import com.google.gwt.xhr.client.XMLHttpRequest;
- import com.vaadin.terminal.gwt.client.ApplicationConnection;
- import com.vaadin.terminal.gwt.client.ComponentConnector;
- import com.vaadin.terminal.gwt.client.Connector;
- import com.vaadin.terminal.gwt.client.ConnectorMap;
- import com.vaadin.terminal.gwt.client.MouseEventDetails;
- import com.vaadin.terminal.gwt.client.RenderInformation;
- import com.vaadin.terminal.gwt.client.RenderInformation.Size;
- import com.vaadin.terminal.gwt.client.Util;
- import com.vaadin.terminal.gwt.client.VConsole;
- import com.vaadin.terminal.gwt.client.VTooltip;
- import com.vaadin.terminal.gwt.client.ValueMap;
- import com.vaadin.terminal.gwt.client.ui.dd.DDUtil;
- import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation;
- import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler;
- import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCallback;
- import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
- import com.vaadin.terminal.gwt.client.ui.dd.VDragEvent;
- import com.vaadin.terminal.gwt.client.ui.dd.VDropHandler;
- import com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler;
- import com.vaadin.terminal.gwt.client.ui.dd.VHtml5DragEvent;
- import com.vaadin.terminal.gwt.client.ui.dd.VHtml5File;
- import com.vaadin.terminal.gwt.client.ui.dd.VTransferable;
- import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
-
- /**
- *
- * Must have features pending:
- *
- * drop details: locations + sizes in document hierarchy up to wrapper
- *
- */
- public class VDragAndDropWrapper extends VCustomComponent implements
- VHasDropHandler {
- public static final String DRAG_START_MODE = "dragStartMode";
- public static final String HTML5_DATA_FLAVORS = "html5-data-flavors";
-
- private static final String CLASSNAME = "v-ddwrapper";
- protected static final String DRAGGABLE = "draggable";
-
- public VDragAndDropWrapper() {
- super();
- sinkEvents(VTooltip.TOOLTIP_EVENTS);
-
- hookHtml5Events(getElement());
- setStyleName(CLASSNAME);
- addDomHandler(new MouseDownHandler() {
- public void onMouseDown(MouseDownEvent event) {
- if (startDrag(event.getNativeEvent())) {
- event.preventDefault(); // prevent text selection
- }
- }
- }, MouseDownEvent.getType());
-
- addDomHandler(new TouchStartHandler() {
- public void onTouchStart(TouchStartEvent event) {
- if (startDrag(event.getNativeEvent())) {
- /*
- * Dont let eg. panel start scrolling.
- */
- event.stopPropagation();
- }
- }
- }, TouchStartEvent.getType());
-
- sinkEvents(Event.TOUCHEVENTS);
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
-
- if (client != null) {
- client.handleTooltipEvent(event, this);
- }
- }
-
- /**
- * Starts a drag and drop operation from mousedown or touchstart event if
- * required conditions are met.
- *
- * @param event
- * @return true if the event was handled as a drag start event
- */
- private boolean startDrag(NativeEvent event) {
- if (dragStartMode == WRAPPER || dragStartMode == COMPONENT) {
- VTransferable transferable = new VTransferable();
- transferable.setDragSource(ConnectorMap.get(client).getConnector(
- VDragAndDropWrapper.this));
-
- ComponentConnector paintable = Util.findPaintable(client,
- (Element) event.getEventTarget().cast());
- Widget widget = paintable.getWidget();
- transferable.setData("component", paintable);
- VDragEvent dragEvent = VDragAndDropManager.get().startDrag(
- transferable, event, true);
-
- transferable.setData("mouseDown",
- new MouseEventDetails(event).serialize());
-
- if (dragStartMode == WRAPPER) {
- dragEvent.createDragImage(getElement(), true);
- } else {
- dragEvent.createDragImage(widget.getElement(), true);
- }
- return true;
- }
- return false;
- }
-
- protected final static int NONE = 0;
- protected final static int COMPONENT = 1;
- protected final static int WRAPPER = 2;
- protected final static int HTML5 = 3;
-
- protected int dragStartMode;
-
- ApplicationConnection client;
- VAbstractDropHandler dropHandler;
- private VDragEvent vaadinDragEvent;
-
- int filecounter = 0;
- Map<String, String> fileIdToReceiver;
- ValueMap html5DataFlavors;
- private Element dragStartElement;
-
- protected void initDragStartMode() {
- Element div = getElement();
- if (dragStartMode == HTML5) {
- if (dragStartElement == null) {
- dragStartElement = getDragStartElement();
- dragStartElement.setPropertyBoolean(DRAGGABLE, true);
- VConsole.log("draggable = "
- + dragStartElement.getPropertyBoolean(DRAGGABLE));
- hookHtml5DragStart(dragStartElement);
- VConsole.log("drag start listeners hooked.");
- }
- } else {
- dragStartElement = null;
- if (div.hasAttribute(DRAGGABLE)) {
- div.removeAttribute(DRAGGABLE);
- }
- }
- }
-
- protected Element getDragStartElement() {
- return getElement();
- }
-
- private boolean uploading;
-
- private ReadyStateChangeHandler readyStateChangeHandler = new ReadyStateChangeHandler() {
- public void onReadyStateChange(XMLHttpRequest xhr) {
- if (xhr.getReadyState() == XMLHttpRequest.DONE) {
- // visit server for possible
- // variable changes
- client.sendPendingVariableChanges();
- uploading = false;
- startNextUpload();
- xhr.clearOnReadyStateChange();
- }
- }
- };
- private Timer dragleavetimer;
-
- void startNextUpload() {
- Scheduler.get().scheduleDeferred(new Command() {
-
- public void execute() {
- if (!uploading) {
- if (fileIds.size() > 0) {
-
- uploading = true;
- final Integer fileId = fileIds.remove(0);
- VHtml5File file = files.remove(0);
- final String receiverUrl = client
- .translateVaadinUri(fileIdToReceiver
- .remove(fileId.toString()));
- ExtendedXHR extendedXHR = (ExtendedXHR) ExtendedXHR
- .create();
- extendedXHR
- .setOnReadyStateChange(readyStateChangeHandler);
- extendedXHR.open("POST", receiverUrl);
- extendedXHR.postFile(file);
- }
- }
-
- }
- });
-
- }
-
- public boolean html5DragStart(VHtml5DragEvent event) {
- if (dragStartMode == HTML5) {
- /*
- * Populate html5 payload with dataflavors from the serverside
- */
- JsArrayString flavors = html5DataFlavors.getKeyArray();
- for (int i = 0; i < flavors.length(); i++) {
- String flavor = flavors.get(i);
- event.setHtml5DataFlavor(flavor,
- html5DataFlavors.getString(flavor));
- }
- event.setEffectAllowed("copy");
- return true;
- }
- return false;
- }
-
- public boolean html5DragEnter(VHtml5DragEvent event) {
- if (dropHandler == null) {
- return true;
- }
- try {
- if (dragleavetimer != null) {
- // returned quickly back to wrapper
- dragleavetimer.cancel();
- dragleavetimer = null;
- }
- if (VDragAndDropManager.get().getCurrentDropHandler() != getDropHandler()) {
- VTransferable transferable = new VTransferable();
- transferable.setDragSource(ConnectorMap.get(client)
- .getConnector(this));
-
- vaadinDragEvent = VDragAndDropManager.get().startDrag(
- transferable, event, false);
- VDragAndDropManager.get().setCurrentDropHandler(
- getDropHandler());
- }
- try {
- event.preventDefault();
- event.stopPropagation();
- } catch (Exception e) {
- // VConsole.log("IE9 fails");
- }
- return false;
- } catch (Exception e) {
- GWT.getUncaughtExceptionHandler().onUncaughtException(e);
- return true;
- }
- }
-
- public boolean html5DragLeave(VHtml5DragEvent event) {
- if (dropHandler == null) {
- return true;
- }
-
- try {
- dragleavetimer = new Timer() {
- @Override
- public void run() {
- // 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
- && VDragAndDropManager.get()
- .getCurrentDropHandler() == getDropHandler()) {
- VDragAndDropManager.get().interruptDrag();
- }
- }
- };
- dragleavetimer.schedule(350);
- try {
- event.preventDefault();
- event.stopPropagation();
- } catch (Exception e) {
- // VConsole.log("IE9 fails");
- }
- return false;
- } catch (Exception e) {
- GWT.getUncaughtExceptionHandler().onUncaughtException(e);
- return true;
- }
- }
-
- public boolean html5DragOver(VHtml5DragEvent event) {
- if (dropHandler == null) {
- return true;
- }
-
- if (dragleavetimer != null) {
- // returned quickly back to wrapper
- dragleavetimer.cancel();
- dragleavetimer = null;
- }
-
- vaadinDragEvent.setCurrentGwtEvent(event);
- getDropHandler().dragOver(vaadinDragEvent);
-
- String s = event.getEffectAllowed();
- if ("all".equals(s) || s.contains("opy")) {
- event.setDropEffect("copy");
- } else {
- event.setDropEffect(s);
- }
-
- try {
- event.preventDefault();
- event.stopPropagation();
- } catch (Exception e) {
- // VConsole.log("IE9 fails");
- }
- return false;
- }
-
- public boolean html5DragDrop(VHtml5DragEvent event) {
- if (dropHandler == null || !currentlyValid) {
- return true;
- }
- try {
-
- VTransferable transferable = vaadinDragEvent.getTransferable();
-
- JsArrayString types = event.getTypes();
- for (int i = 0; i < types.length(); i++) {
- String type = types.get(i);
- if (isAcceptedType(type)) {
- String data = event.getDataAsText(type);
- if (data != null) {
- transferable.setData(type, data);
- }
- }
- }
-
- int fileCount = event.getFileCount();
- if (fileCount > 0) {
- transferable.setData("filecount", fileCount);
- for (int i = 0; i < fileCount; i++) {
- final int fileId = filecounter++;
- final VHtml5File file = event.getFile(i);
- transferable.setData("fi" + i, "" + fileId);
- transferable.setData("fn" + i, file.getName());
- transferable.setData("ft" + i, file.getType());
- transferable.setData("fs" + i, file.getSize());
- queueFilePost(fileId, file);
- }
-
- }
-
- VDragAndDropManager.get().endDrag();
- vaadinDragEvent = null;
- try {
- event.preventDefault();
- event.stopPropagation();
- } catch (Exception e) {
- // VConsole.log("IE9 fails");
- }
- return false;
- } catch (Exception e) {
- GWT.getUncaughtExceptionHandler().onUncaughtException(e);
- return true;
- }
-
- }
-
- protected String[] acceptedTypes = new String[] { "Text", "Url",
- "text/html", "text/plain", "text/rtf" };
-
- private boolean isAcceptedType(String type) {
- for (String t : acceptedTypes) {
- if (t.equals(type)) {
- return true;
- }
- }
- return false;
- }
-
- static class ExtendedXHR extends XMLHttpRequest {
-
- protected ExtendedXHR() {
- }
-
- public final native void postFile(VHtml5File file)
- /*-{
-
- this.setRequestHeader('Content-Type', 'multipart/form-data');
- this.send(file);
- }-*/;
-
- }
-
- /**
- * Currently supports only FF36 as no other browser supports natively File
- * api.
- *
- * @param fileId
- * @param data
- */
- List<Integer> fileIds = new ArrayList<Integer>();
- List<VHtml5File> files = new ArrayList<VHtml5File>();
-
- private void queueFilePost(final int fileId, final VHtml5File file) {
- fileIds.add(fileId);
- files.add(file);
- }
-
- private String getPid() {
- return ConnectorMap.get(client).getConnectorId((Connector) this);
- }
-
- public VDropHandler getDropHandler() {
- return dropHandler;
- }
-
- protected VerticalDropLocation verticalDropLocation;
- protected HorizontalDropLocation horizontalDropLocation;
- private VerticalDropLocation emphasizedVDrop;
- private HorizontalDropLocation emphasizedHDrop;
-
- /**
- * Flag used by html5 dd
- */
- private boolean currentlyValid;
-
- private static final String OVER_STYLE = "v-ddwrapper-over";
-
- public class CustomDropHandler extends VAbstractDropHandler {
-
- @Override
- public void dragEnter(VDragEvent drag) {
- updateDropDetails(drag);
- currentlyValid = false;
- super.dragEnter(drag);
- }
-
- @Override
- public void dragLeave(VDragEvent drag) {
- deEmphasis(true);
- dragleavetimer = null;
- }
-
- @Override
- public void dragOver(final VDragEvent drag) {
- boolean detailsChanged = updateDropDetails(drag);
- if (detailsChanged) {
- currentlyValid = false;
- validate(new VAcceptCallback() {
- public void accepted(VDragEvent event) {
- dragAccepted(drag);
- }
- }, drag);
- }
- }
-
- @Override
- public boolean drop(VDragEvent drag) {
- deEmphasis(true);
-
- Map<String, Object> dd = drag.getDropDetails();
-
- // 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();
-
- dd.put("absoluteLeft", absoluteLeft);
- dd.put("absoluteTop", absoluteTop);
-
- if (verticalDropLocation != null) {
- dd.put("verticalLocation", verticalDropLocation.toString());
- dd.put("horizontalLocation", horizontalDropLocation.toString());
- }
-
- return super.drop(drag);
- }
-
- @Override
- protected void dragAccepted(VDragEvent drag) {
- currentlyValid = true;
- emphasis(drag);
- }
-
- @Override
- public ComponentConnector getPaintable() {
- return ConnectorMap.get(client).getConnector(
- VDragAndDropWrapper.this);
- }
-
- public ApplicationConnection getApplicationConnection() {
- return client;
- }
-
- }
-
- protected native void hookHtml5DragStart(Element el)
- /*-{
- var me = this;
- el.addEventListener("dragstart", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
- }-*/;
-
- /**
- * Prototype code, memory leak risk.
- *
- * @param el
- */
- protected native void hookHtml5Events(Element el)
- /*-{
- var me = this;
-
- el.addEventListener("dragenter", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("dragleave", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("dragover", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
-
- el.addEventListener("drop", function(ev) {
- return me.@com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
- }-*/;
-
- public boolean updateDropDetails(VDragEvent drag) {
- VerticalDropLocation oldVL = verticalDropLocation;
- verticalDropLocation = DDUtil.getVerticalDropLocation(getElement(),
- drag.getCurrentGwtEvent(), 0.2);
- drag.getDropDetails().put("verticalLocation",
- verticalDropLocation.toString());
- HorizontalDropLocation oldHL = horizontalDropLocation;
- horizontalDropLocation = DDUtil.getHorizontalDropLocation(getElement(),
- drag.getCurrentGwtEvent(), 0.2);
- drag.getDropDetails().put("horizontalLocation",
- horizontalDropLocation.toString());
- if (oldHL != horizontalDropLocation || oldVL != verticalDropLocation) {
- return true;
- } else {
- return false;
- }
- }
-
- protected void deEmphasis(boolean doLayout) {
- if (emphasizedVDrop != null) {
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE, false);
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
- + emphasizedVDrop.toString().toLowerCase(), false);
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
- + emphasizedHDrop.toString().toLowerCase(), false);
- }
- if (doLayout) {
- client.doLayout(false);
- }
- }
-
- protected void emphasis(VDragEvent drag) {
- Size size = new RenderInformation.Size(getOffsetWidth(),
- getOffsetHeight());
- deEmphasis(false);
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE, true);
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
- + verticalDropLocation.toString().toLowerCase(), true);
- VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE + "-"
- + horizontalDropLocation.toString().toLowerCase(), true);
- emphasizedVDrop = verticalDropLocation;
- emphasizedHDrop = horizontalDropLocation;
-
- // TODO build (to be an example) an emphasis mode where drag image
- // is fitted before or after the content
- client.doLayout(false);
- }
-
- }
|