summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui/DragAndDropWrapper.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/ui/DragAndDropWrapper.java')
-rw-r--r--server/src/com/vaadin/ui/DragAndDropWrapper.java419
1 files changed, 419 insertions, 0 deletions
diff --git a/server/src/com/vaadin/ui/DragAndDropWrapper.java b/server/src/com/vaadin/ui/DragAndDropWrapper.java
new file mode 100644
index 0000000000..3fd94210d8
--- /dev/null
+++ b/server/src/com/vaadin/ui/DragAndDropWrapper.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui;
+
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.TransferableImpl;
+import com.vaadin.event.dd.DragSource;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.event.dd.DropTarget;
+import com.vaadin.event.dd.TargetDetails;
+import com.vaadin.event.dd.TargetDetailsImpl;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.shared.ui.dd.HorizontalDropLocation;
+import com.vaadin.shared.ui.dd.VerticalDropLocation;
+import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperConstants;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.StreamVariable;
+import com.vaadin.terminal.Vaadin6Component;
+
+@SuppressWarnings("serial")
+public class DragAndDropWrapper extends CustomComponent implements DropTarget,
+ DragSource, Vaadin6Component {
+
+ public class WrapperTransferable extends TransferableImpl {
+
+ private Html5File[] files;
+
+ public WrapperTransferable(Component sourceComponent,
+ Map<String, Object> rawVariables) {
+ super(sourceComponent, rawVariables);
+ Integer fc = (Integer) rawVariables.get("filecount");
+ if (fc != null) {
+ files = new Html5File[fc];
+ for (int i = 0; i < fc; i++) {
+ Html5File file = new Html5File(
+ (String) rawVariables.get("fn" + i), // name
+ (Integer) rawVariables.get("fs" + i), // size
+ (String) rawVariables.get("ft" + i)); // mime
+ String id = (String) rawVariables.get("fi" + i);
+ files[i] = file;
+ receivers.put(id, file);
+ requestRepaint(); // paint Receivers
+ }
+ }
+ }
+
+ /**
+ * The component in wrapper that is being dragged or null if the
+ * transferable is not a component (most likely an html5 drag).
+ *
+ * @return
+ */
+ public Component getDraggedComponent() {
+ Component object = (Component) getData("component");
+ return object;
+ }
+
+ /**
+ * @return the mouse down event that started the drag and drop operation
+ */
+ public MouseEventDetails getMouseDownEvent() {
+ return MouseEventDetails.deSerialize((String) getData("mouseDown"));
+ }
+
+ public Html5File[] getFiles() {
+ return files;
+ }
+
+ public String getText() {
+ String data = (String) getData("Text"); // IE, html5
+ if (data == null) {
+ // check for "text/plain" (webkit)
+ data = (String) getData("text/plain");
+ }
+ return data;
+ }
+
+ public String getHtml() {
+ String data = (String) getData("Html"); // IE, html5
+ if (data == null) {
+ // check for "text/plain" (webkit)
+ data = (String) getData("text/html");
+ }
+ return data;
+ }
+
+ }
+
+ private Map<String, Html5File> receivers = new HashMap<String, Html5File>();
+
+ public class WrapperTargetDetails extends TargetDetailsImpl {
+
+ public WrapperTargetDetails(Map<String, Object> rawDropData) {
+ super(rawDropData, DragAndDropWrapper.this);
+ }
+
+ /**
+ * @return the absolute position of wrapper on the page
+ */
+ public Integer getAbsoluteLeft() {
+ return (Integer) getData("absoluteLeft");
+ }
+
+ /**
+ *
+ * @return the absolute position of wrapper on the page
+ */
+ public Integer getAbsoluteTop() {
+ return (Integer) getData("absoluteTop");
+ }
+
+ /**
+ * @return details about the actual event that caused the event details.
+ * Practically mouse move or mouse up.
+ */
+ public MouseEventDetails getMouseEvent() {
+ return MouseEventDetails
+ .deSerialize((String) getData("mouseEvent"));
+ }
+
+ /**
+ * @return a detail about the drags vertical position over the wrapper.
+ */
+ public VerticalDropLocation getVerticalDropLocation() {
+ return VerticalDropLocation
+ .valueOf((String) getData("verticalLocation"));
+ }
+
+ /**
+ * @return a detail about the drags horizontal position over the
+ * wrapper.
+ */
+ public HorizontalDropLocation getHorizontalDropLocation() {
+ return HorizontalDropLocation
+ .valueOf((String) getData("horizontalLocation"));
+ }
+
+ /**
+ * @deprecated use {@link #getVerticalDropLocation()} instead
+ */
+ @Deprecated
+ public VerticalDropLocation verticalDropLocation() {
+ return getVerticalDropLocation();
+ }
+
+ /**
+ * @deprecated use {@link #getHorizontalDropLocation()} instead
+ */
+ @Deprecated
+ public HorizontalDropLocation horizontalDropLocation() {
+ return getHorizontalDropLocation();
+ }
+
+ }
+
+ public enum DragStartMode {
+ /**
+ * {@link DragAndDropWrapper} does not start drag events at all
+ */
+ NONE,
+ /**
+ * The component on which the drag started will be shown as drag image.
+ */
+ COMPONENT,
+ /**
+ * The whole wrapper is used as a drag image when dragging.
+ */
+ WRAPPER,
+ /**
+ * The whole wrapper is used to start an HTML5 drag.
+ *
+ * NOTE: In Internet Explorer 6 to 8, this prevents user interactions
+ * with the wrapper's contents. For example, clicking a button inside
+ * the wrapper will no longer work.
+ */
+ HTML5,
+ }
+
+ private final Map<String, Object> html5DataFlavors = new LinkedHashMap<String, Object>();
+ private DragStartMode dragStartMode = DragStartMode.NONE;
+
+ /**
+ * Wraps given component in a {@link DragAndDropWrapper}.
+ *
+ * @param root
+ * the component to be wrapped
+ */
+ public DragAndDropWrapper(Component root) {
+ super(root);
+ }
+
+ /**
+ * Sets data flavors available in the DragAndDropWrapper is used to start an
+ * HTML5 style drags. Most commonly the "Text" flavor should be set.
+ * Multiple data types can be set.
+ *
+ * @param type
+ * the string identifier of the drag "payload". E.g. "Text" or
+ * "text/html"
+ * @param value
+ * the value
+ */
+ public void setHTML5DataFlavor(String type, Object value) {
+ html5DataFlavors.put(type, value);
+ requestRepaint();
+ }
+
+ @Override
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ // TODO Remove once Vaadin6Component is no longer implemented
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ target.addAttribute(DragAndDropWrapperConstants.DRAG_START_MODE,
+ dragStartMode.ordinal());
+ if (getDropHandler() != null) {
+ getDropHandler().getAcceptCriterion().paint(target);
+ }
+ if (receivers != null && receivers.size() > 0) {
+ for (Iterator<Entry<String, Html5File>> it = receivers.entrySet()
+ .iterator(); it.hasNext();) {
+ Entry<String, com.vaadin.ui.Html5File> entry = it.next();
+ String id = entry.getKey();
+ Html5File html5File = entry.getValue();
+ if (html5File.getStreamVariable() != null) {
+ target.addVariable(this, "rec-" + id, new ProxyReceiver(
+ html5File));
+ // these are cleaned from receivers once the upload has
+ // started
+ } else {
+ // instructs the client side not to send the file
+ target.addVariable(this, "rec-" + id, (String) null);
+ // forget the file from subsequent paints
+ it.remove();
+ }
+ }
+ }
+ target.addAttribute(DragAndDropWrapperConstants.HTML5_DATA_FLAVORS,
+ html5DataFlavors);
+ }
+
+ private DropHandler dropHandler;
+
+ @Override
+ public DropHandler getDropHandler() {
+ return dropHandler;
+ }
+
+ public void setDropHandler(DropHandler dropHandler) {
+ this.dropHandler = dropHandler;
+ requestRepaint();
+ }
+
+ @Override
+ public TargetDetails translateDropTargetDetails(
+ Map<String, Object> clientVariables) {
+ return new WrapperTargetDetails(clientVariables);
+ }
+
+ @Override
+ public Transferable getTransferable(final Map<String, Object> rawVariables) {
+ return new WrapperTransferable(this, rawVariables);
+ }
+
+ public void setDragStartMode(DragStartMode dragStartMode) {
+ this.dragStartMode = dragStartMode;
+ requestRepaint();
+ }
+
+ public DragStartMode getDragStartMode() {
+ return dragStartMode;
+ }
+
+ final class ProxyReceiver implements StreamVariable {
+
+ private Html5File file;
+
+ public ProxyReceiver(Html5File file) {
+ this.file = file;
+ }
+
+ private boolean listenProgressOfUploadedFile;
+
+ @Override
+ public OutputStream getOutputStream() {
+ if (file.getStreamVariable() == null) {
+ return null;
+ }
+ return file.getStreamVariable().getOutputStream();
+ }
+
+ @Override
+ public boolean listenProgress() {
+ return file.getStreamVariable().listenProgress();
+ }
+
+ @Override
+ public void onProgress(StreamingProgressEvent event) {
+ file.getStreamVariable().onProgress(
+ new ReceivingEventWrapper(event));
+ }
+
+ @Override
+ public void streamingStarted(StreamingStartEvent event) {
+ listenProgressOfUploadedFile = file.getStreamVariable() != null;
+ if (listenProgressOfUploadedFile) {
+ file.getStreamVariable().streamingStarted(
+ new ReceivingEventWrapper(event));
+ }
+ // no need tell to the client about this receiver on next paint
+ receivers.remove(file);
+ // let the terminal GC the streamvariable and not to accept other
+ // file uploads to this variable
+ event.disposeStreamVariable();
+ }
+
+ @Override
+ public void streamingFinished(StreamingEndEvent event) {
+ if (listenProgressOfUploadedFile) {
+ file.getStreamVariable().streamingFinished(
+ new ReceivingEventWrapper(event));
+ }
+ }
+
+ @Override
+ public void streamingFailed(final StreamingErrorEvent event) {
+ if (listenProgressOfUploadedFile) {
+ file.getStreamVariable().streamingFailed(
+ new ReceivingEventWrapper(event));
+ }
+ }
+
+ @Override
+ public boolean isInterrupted() {
+ return file.getStreamVariable().isInterrupted();
+ }
+
+ /*
+ * With XHR2 file posts we can't provide as much information from the
+ * terminal as with multipart request. This helper class wraps the
+ * terminal event and provides the lacking information from the
+ * Html5File.
+ */
+ class ReceivingEventWrapper implements StreamingErrorEvent,
+ StreamingEndEvent, StreamingStartEvent, StreamingProgressEvent {
+
+ private StreamingEvent wrappedEvent;
+
+ ReceivingEventWrapper(StreamingEvent e) {
+ wrappedEvent = e;
+ }
+
+ @Override
+ public String getMimeType() {
+ return file.getType();
+ }
+
+ @Override
+ public String getFileName() {
+ return file.getFileName();
+ }
+
+ @Override
+ public long getContentLength() {
+ return file.getFileSize();
+ }
+
+ public StreamVariable getReceiver() {
+ return ProxyReceiver.this;
+ }
+
+ @Override
+ public Exception getException() {
+ if (wrappedEvent instanceof StreamingErrorEvent) {
+ return ((StreamingErrorEvent) wrappedEvent).getException();
+ }
+ return null;
+ }
+
+ @Override
+ public long getBytesReceived() {
+ return wrappedEvent.getBytesReceived();
+ }
+
+ /**
+ * Calling this method has no effect. DD files are receive only once
+ * anyway.
+ */
+ @Override
+ public void disposeStreamVariable() {
+
+ }
+ }
+
+ }
+
+}