You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DragAndDropWrapper.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /*
  2. * Copyright 2011 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.OutputStream;
  18. import java.util.HashMap;
  19. import java.util.Iterator;
  20. import java.util.LinkedHashMap;
  21. import java.util.Map;
  22. import java.util.Map.Entry;
  23. import com.vaadin.event.Transferable;
  24. import com.vaadin.event.TransferableImpl;
  25. import com.vaadin.event.dd.DragSource;
  26. import com.vaadin.event.dd.DropHandler;
  27. import com.vaadin.event.dd.DropTarget;
  28. import com.vaadin.event.dd.TargetDetails;
  29. import com.vaadin.event.dd.TargetDetailsImpl;
  30. import com.vaadin.shared.MouseEventDetails;
  31. import com.vaadin.shared.ui.dd.HorizontalDropLocation;
  32. import com.vaadin.shared.ui.dd.VerticalDropLocation;
  33. import com.vaadin.shared.ui.draganddropwrapper.DragAndDropWrapperConstants;
  34. import com.vaadin.terminal.PaintException;
  35. import com.vaadin.terminal.PaintTarget;
  36. import com.vaadin.terminal.StreamVariable;
  37. import com.vaadin.terminal.Vaadin6Component;
  38. @SuppressWarnings("serial")
  39. public class DragAndDropWrapper extends CustomComponent implements DropTarget,
  40. DragSource, Vaadin6Component {
  41. public class WrapperTransferable extends TransferableImpl {
  42. private Html5File[] files;
  43. public WrapperTransferable(Component sourceComponent,
  44. Map<String, Object> rawVariables) {
  45. super(sourceComponent, rawVariables);
  46. Integer fc = (Integer) rawVariables.get("filecount");
  47. if (fc != null) {
  48. files = new Html5File[fc];
  49. for (int i = 0; i < fc; i++) {
  50. Html5File file = new Html5File(
  51. (String) rawVariables.get("fn" + i), // name
  52. (Integer) rawVariables.get("fs" + i), // size
  53. (String) rawVariables.get("ft" + i)); // mime
  54. String id = (String) rawVariables.get("fi" + i);
  55. files[i] = file;
  56. receivers.put(id, file);
  57. requestRepaint(); // paint Receivers
  58. }
  59. }
  60. }
  61. /**
  62. * The component in wrapper that is being dragged or null if the
  63. * transferable is not a component (most likely an html5 drag).
  64. *
  65. * @return
  66. */
  67. public Component getDraggedComponent() {
  68. Component object = (Component) getData("component");
  69. return object;
  70. }
  71. /**
  72. * @return the mouse down event that started the drag and drop operation
  73. */
  74. public MouseEventDetails getMouseDownEvent() {
  75. return MouseEventDetails.deSerialize((String) getData("mouseDown"));
  76. }
  77. public Html5File[] getFiles() {
  78. return files;
  79. }
  80. public String getText() {
  81. String data = (String) getData("Text"); // IE, html5
  82. if (data == null) {
  83. // check for "text/plain" (webkit)
  84. data = (String) getData("text/plain");
  85. }
  86. return data;
  87. }
  88. public String getHtml() {
  89. String data = (String) getData("Html"); // IE, html5
  90. if (data == null) {
  91. // check for "text/plain" (webkit)
  92. data = (String) getData("text/html");
  93. }
  94. return data;
  95. }
  96. }
  97. private Map<String, Html5File> receivers = new HashMap<String, Html5File>();
  98. public class WrapperTargetDetails extends TargetDetailsImpl {
  99. public WrapperTargetDetails(Map<String, Object> rawDropData) {
  100. super(rawDropData, DragAndDropWrapper.this);
  101. }
  102. /**
  103. * @return the absolute position of wrapper on the page
  104. */
  105. public Integer getAbsoluteLeft() {
  106. return (Integer) getData("absoluteLeft");
  107. }
  108. /**
  109. *
  110. * @return the absolute position of wrapper on the page
  111. */
  112. public Integer getAbsoluteTop() {
  113. return (Integer) getData("absoluteTop");
  114. }
  115. /**
  116. * @return details about the actual event that caused the event details.
  117. * Practically mouse move or mouse up.
  118. */
  119. public MouseEventDetails getMouseEvent() {
  120. return MouseEventDetails
  121. .deSerialize((String) getData("mouseEvent"));
  122. }
  123. /**
  124. * @return a detail about the drags vertical position over the wrapper.
  125. */
  126. public VerticalDropLocation getVerticalDropLocation() {
  127. return VerticalDropLocation
  128. .valueOf((String) getData("verticalLocation"));
  129. }
  130. /**
  131. * @return a detail about the drags horizontal position over the
  132. * wrapper.
  133. */
  134. public HorizontalDropLocation getHorizontalDropLocation() {
  135. return HorizontalDropLocation
  136. .valueOf((String) getData("horizontalLocation"));
  137. }
  138. }
  139. public enum DragStartMode {
  140. /**
  141. * {@link DragAndDropWrapper} does not start drag events at all
  142. */
  143. NONE,
  144. /**
  145. * The component on which the drag started will be shown as drag image.
  146. */
  147. COMPONENT,
  148. /**
  149. * The whole wrapper is used as a drag image when dragging.
  150. */
  151. WRAPPER,
  152. /**
  153. * The whole wrapper is used to start an HTML5 drag.
  154. *
  155. * NOTE: In Internet Explorer 6 to 8, this prevents user interactions
  156. * with the wrapper's contents. For example, clicking a button inside
  157. * the wrapper will no longer work.
  158. */
  159. HTML5,
  160. }
  161. private final Map<String, Object> html5DataFlavors = new LinkedHashMap<String, Object>();
  162. private DragStartMode dragStartMode = DragStartMode.NONE;
  163. /**
  164. * Wraps given component in a {@link DragAndDropWrapper}.
  165. *
  166. * @param root
  167. * the component to be wrapped
  168. */
  169. public DragAndDropWrapper(Component root) {
  170. super(root);
  171. }
  172. /**
  173. * Sets data flavors available in the DragAndDropWrapper is used to start an
  174. * HTML5 style drags. Most commonly the "Text" flavor should be set.
  175. * Multiple data types can be set.
  176. *
  177. * @param type
  178. * the string identifier of the drag "payload". E.g. "Text" or
  179. * "text/html"
  180. * @param value
  181. * the value
  182. */
  183. public void setHTML5DataFlavor(String type, Object value) {
  184. html5DataFlavors.put(type, value);
  185. requestRepaint();
  186. }
  187. @Override
  188. public void changeVariables(Object source, Map<String, Object> variables) {
  189. // TODO Remove once Vaadin6Component is no longer implemented
  190. }
  191. @Override
  192. public void paintContent(PaintTarget target) throws PaintException {
  193. target.addAttribute(DragAndDropWrapperConstants.DRAG_START_MODE,
  194. dragStartMode.ordinal());
  195. if (getDropHandler() != null) {
  196. getDropHandler().getAcceptCriterion().paint(target);
  197. }
  198. if (receivers != null && receivers.size() > 0) {
  199. for (Iterator<Entry<String, Html5File>> it = receivers.entrySet()
  200. .iterator(); it.hasNext();) {
  201. Entry<String, com.vaadin.ui.Html5File> entry = it.next();
  202. String id = entry.getKey();
  203. Html5File html5File = entry.getValue();
  204. if (html5File.getStreamVariable() != null) {
  205. target.addVariable(this, "rec-" + id, new ProxyReceiver(
  206. html5File));
  207. // these are cleaned from receivers once the upload has
  208. // started
  209. } else {
  210. // instructs the client side not to send the file
  211. target.addVariable(this, "rec-" + id, (String) null);
  212. // forget the file from subsequent paints
  213. it.remove();
  214. }
  215. }
  216. }
  217. target.addAttribute(DragAndDropWrapperConstants.HTML5_DATA_FLAVORS,
  218. html5DataFlavors);
  219. }
  220. private DropHandler dropHandler;
  221. @Override
  222. public DropHandler getDropHandler() {
  223. return dropHandler;
  224. }
  225. public void setDropHandler(DropHandler dropHandler) {
  226. this.dropHandler = dropHandler;
  227. requestRepaint();
  228. }
  229. @Override
  230. public TargetDetails translateDropTargetDetails(
  231. Map<String, Object> clientVariables) {
  232. return new WrapperTargetDetails(clientVariables);
  233. }
  234. @Override
  235. public Transferable getTransferable(final Map<String, Object> rawVariables) {
  236. return new WrapperTransferable(this, rawVariables);
  237. }
  238. public void setDragStartMode(DragStartMode dragStartMode) {
  239. this.dragStartMode = dragStartMode;
  240. requestRepaint();
  241. }
  242. public DragStartMode getDragStartMode() {
  243. return dragStartMode;
  244. }
  245. final class ProxyReceiver implements StreamVariable {
  246. private Html5File file;
  247. public ProxyReceiver(Html5File file) {
  248. this.file = file;
  249. }
  250. private boolean listenProgressOfUploadedFile;
  251. @Override
  252. public OutputStream getOutputStream() {
  253. if (file.getStreamVariable() == null) {
  254. return null;
  255. }
  256. return file.getStreamVariable().getOutputStream();
  257. }
  258. @Override
  259. public boolean listenProgress() {
  260. return file.getStreamVariable().listenProgress();
  261. }
  262. @Override
  263. public void onProgress(StreamingProgressEvent event) {
  264. file.getStreamVariable().onProgress(
  265. new ReceivingEventWrapper(event));
  266. }
  267. @Override
  268. public void streamingStarted(StreamingStartEvent event) {
  269. listenProgressOfUploadedFile = file.getStreamVariable() != null;
  270. if (listenProgressOfUploadedFile) {
  271. file.getStreamVariable().streamingStarted(
  272. new ReceivingEventWrapper(event));
  273. }
  274. // no need tell to the client about this receiver on next paint
  275. receivers.remove(file);
  276. // let the terminal GC the streamvariable and not to accept other
  277. // file uploads to this variable
  278. event.disposeStreamVariable();
  279. }
  280. @Override
  281. public void streamingFinished(StreamingEndEvent event) {
  282. if (listenProgressOfUploadedFile) {
  283. file.getStreamVariable().streamingFinished(
  284. new ReceivingEventWrapper(event));
  285. }
  286. }
  287. @Override
  288. public void streamingFailed(final StreamingErrorEvent event) {
  289. if (listenProgressOfUploadedFile) {
  290. file.getStreamVariable().streamingFailed(
  291. new ReceivingEventWrapper(event));
  292. }
  293. }
  294. @Override
  295. public boolean isInterrupted() {
  296. return file.getStreamVariable().isInterrupted();
  297. }
  298. /*
  299. * With XHR2 file posts we can't provide as much information from the
  300. * terminal as with multipart request. This helper class wraps the
  301. * terminal event and provides the lacking information from the
  302. * Html5File.
  303. */
  304. class ReceivingEventWrapper implements StreamingErrorEvent,
  305. StreamingEndEvent, StreamingStartEvent, StreamingProgressEvent {
  306. private StreamingEvent wrappedEvent;
  307. ReceivingEventWrapper(StreamingEvent e) {
  308. wrappedEvent = e;
  309. }
  310. @Override
  311. public String getMimeType() {
  312. return file.getType();
  313. }
  314. @Override
  315. public String getFileName() {
  316. return file.getFileName();
  317. }
  318. @Override
  319. public long getContentLength() {
  320. return file.getFileSize();
  321. }
  322. public StreamVariable getReceiver() {
  323. return ProxyReceiver.this;
  324. }
  325. @Override
  326. public Exception getException() {
  327. if (wrappedEvent instanceof StreamingErrorEvent) {
  328. return ((StreamingErrorEvent) wrappedEvent).getException();
  329. }
  330. return null;
  331. }
  332. @Override
  333. public long getBytesReceived() {
  334. return wrappedEvent.getBytesReceived();
  335. }
  336. /**
  337. * Calling this method has no effect. DD files are receive only once
  338. * anyway.
  339. */
  340. @Override
  341. public void disposeStreamVariable() {
  342. }
  343. }
  344. }
  345. }