svn changeset:11835/svn branch:6.3tags/6.7.0.beta1
@@ -13,6 +13,7 @@ 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.google.gwt.xhr.client.XMLHttpRequest; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
@@ -193,44 +194,16 @@ public class VDragAndDropWrapper extends VCustomComponent implements | |||
transferable.setData("filecount", fileCount); | |||
for (int i = 0; i < fileCount; i++) { | |||
final int fileId = filecounter++; | |||
final VHtml5File file = event.getFile(fileCount); | |||
transferable.setData("fn" + fileId, file.getName()); | |||
transferable.setData("ft" + fileId, file.getType()); | |||
transferable.setData("fs" + fileId, file.getSize()); | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
/* | |||
* File contents is sent deferred to allow quick | |||
* reaction on GUI although file upload may last long. | |||
* TODO make this use apache file upload instead of our | |||
* variable post like in upload. Currently stalls the | |||
* GUI during upload. Also need to use dataurl to | |||
* support all possible bytes in file content | |||
*/ | |||
file.readAsDataUrl(new Callback() { | |||
public void handleFile(JavaScriptObject object) { | |||
client.updateVariable(client | |||
.getPid(VDragAndDropWrapper.this), | |||
"file" + fileId, object.toString(), | |||
true); | |||
} | |||
}); | |||
} | |||
}); | |||
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()); | |||
postFile(fileId, file); | |||
} | |||
} | |||
// TODO remove this when above cleaner and more standard compliance | |||
// system works | |||
String fileAsString = event.getFileAsString(0); | |||
if (fileAsString != null) { | |||
ApplicationConnection.getConsole().log(fileAsString); | |||
transferable.setData("fileContents", fileAsString); | |||
} | |||
VDragAndDropManager.get().endDrag(); | |||
vaadinDragEvent = null; | |||
event.preventDefault(); | |||
@@ -239,6 +212,97 @@ public class VDragAndDropWrapper extends VCustomComponent implements | |||
return false; | |||
} | |||
static class ExtendedXHR extends XMLHttpRequest { | |||
protected ExtendedXHR() { | |||
} | |||
public final native void sendBinary(JavaScriptObject data) | |||
/*-{ | |||
//this.overrideMimeType('text/plain; charset=x-user-defined-binary'); | |||
this.sendAsBinary(data); | |||
}-*/; | |||
} | |||
/** | |||
* | |||
* Currently supports only FF36 as no other browser supprots natively File | |||
* api. | |||
* | |||
* @param fileId | |||
* @param data | |||
*/ | |||
private void postFile(final int fileId, final VHtml5File file) { | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
/* | |||
* File contents is sent deferred to allow quick reaction on GUI | |||
* although file upload may last long. TODO make this use apache | |||
* file upload instead of our variable post like in upload. | |||
* Currently stalls the GUI during upload. Also need to use | |||
* dataurl to support all possible bytes in file content | |||
*/ | |||
file.readAsBinary(new Callback() { | |||
public void handleFile(final JavaScriptObject object) { | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
ExtendedXHR extendedXHR = (ExtendedXHR) ExtendedXHR | |||
.create(); | |||
extendedXHR.open("POST", client.getAppUri()); | |||
extendedXHR | |||
.setRequestHeader( | |||
"PaintableId", | |||
client | |||
.getPid(VDragAndDropWrapper.this)); | |||
extendedXHR.setRequestHeader("FileId", "" | |||
+ fileId); | |||
// extendedXHR.setRequestHeader("Connection", | |||
// "close"); | |||
multipartSend( | |||
extendedXHR, | |||
object, | |||
"XHRFILE" | |||
+ client | |||
.getPid(VDragAndDropWrapper.this) | |||
+ "." + fileId); | |||
} | |||
}); | |||
} | |||
}); | |||
} | |||
}); | |||
} | |||
private native void multipartSend(JavaScriptObject xhr, | |||
JavaScriptObject data, String name) | |||
/*-{ | |||
var boundaryString = "------------------------------------------VAADINXHRFILEUPLOAD"; | |||
var boundary = "--" + boundaryString; | |||
var CRLF = "\r\n"; | |||
xhr.setRequestHeader("Content-type", "multipart/form-data; boundary=\"" + boundaryString + "\""); | |||
var requestBody = boundary | |||
+ CRLF | |||
+ "Content-Disposition: form-data; name=\""+name+"\"; filename=\"file\"" | |||
+ CRLF | |||
+ "Content-Type: application/octet-stream" // hard coded, type sent separately | |||
+ CRLF + CRLF + data.target.result + CRLF + boundary + "--" + CRLF; | |||
xhr.setRequestHeader("Content-Length", requestBody.length); | |||
xhr.sendAsBinary(requestBody); | |||
}-*/; | |||
public VDropHandler getDropHandler() { | |||
return dropHandler; | |||
} |
@@ -59,7 +59,7 @@ public class VHtml5DragEvent extends NativeEvent { | |||
public final native VHtml5File getFile(int fileIndex) | |||
/*-{ | |||
return this.dataTransfer.files[i]; | |||
return this.dataTransfer.files[fileIndex]; | |||
}-*/; | |||
} |
@@ -14,17 +14,17 @@ public class VHtml5File extends JavaScriptObject { | |||
public native final String getName() | |||
/*-{ | |||
return name; | |||
return this.name; | |||
}-*/; | |||
public native final String getType() | |||
/*-{ | |||
return type; | |||
return this.type; | |||
}-*/; | |||
public native final int getSize() | |||
/*-{ | |||
return size; | |||
return this.size; | |||
}-*/; | |||
public native final void readAsBinary(final Callback callback) | |||
@@ -33,7 +33,9 @@ public class VHtml5File extends JavaScriptObject { | |||
r.onloadend = function(content) { | |||
callback.@com.vaadin.terminal.gwt.client.ui.dd.VHtml5File.Callback::handleFile(Lcom/google/gwt/core/client/JavaScriptObject;)(content); | |||
}; | |||
r.readAsBinary(this); | |||
r.readAsBinaryString(this); | |||
var j = 0; | |||
}-*/; | |||
public native final void readAsDataUrl(final Callback callback) |
@@ -1252,6 +1252,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements | |||
return RequestType.STATIC_FILE; | |||
} else if (isApplicationRequest(request)) { | |||
return RequestType.APPLICATION_RESOURCE; | |||
} else if (request.getHeader("FileId") != null) { | |||
return RequestType.FILE_UPLOAD; | |||
} | |||
return RequestType.OTHER; | |||
@@ -62,6 +62,7 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout; | |||
import com.vaadin.ui.AbstractField; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.DragAndDropWrapper; | |||
import com.vaadin.ui.Upload; | |||
import com.vaadin.ui.Window; | |||
import com.vaadin.ui.Upload.UploadException; | |||
@@ -359,6 +360,7 @@ public abstract class AbstractCommunicationManager implements | |||
*/ | |||
protected void doHandleFileUpload(Request request, Response response) | |||
throws IOException, FileUploadException { | |||
// Create a new file upload handler | |||
final FileUpload upload = createFileUpload(); | |||
@@ -386,22 +388,6 @@ public abstract class AbstractCommunicationManager implements | |||
if (item.isFormField()) { | |||
// ignored, upload requests contains only files | |||
} else { | |||
int separatorPos = name.lastIndexOf("_"); | |||
final String pid = name.substring(0, separatorPos); | |||
final Upload uploadComponent = (Upload) idPaintableMap | |||
.get(pid); | |||
if (uploadComponent == null) { | |||
throw new FileUploadException( | |||
"Upload component not found"); | |||
} | |||
if (uploadComponent.isReadOnly()) { | |||
throw new FileUploadException( | |||
"Warning: ignored file upload because upload component is set as read-only"); | |||
} | |||
synchronized (application) { | |||
// put upload component into receiving state | |||
uploadComponent.startUpload(); | |||
} | |||
final UploadStream upstream = new UploadStream() { | |||
public String getContentName() { | |||
@@ -422,22 +408,55 @@ public abstract class AbstractCommunicationManager implements | |||
}; | |||
// tell UploadProgressListener which component is receiving | |||
// file | |||
pl.setUpload(uploadComponent); | |||
if (name.startsWith("XHRFILE")) { | |||
String[] split = item.getFieldName().substring(7) | |||
.split("\\."); | |||
DragAndDropWrapper ddw = (DragAndDropWrapper) idPaintableMap | |||
.get(split[0]); | |||
ddw.receiveFile(upstream, split[1]); | |||
try { | |||
uploadComponent.receiveUpload(upstream); | |||
} catch (UploadException e) { | |||
// error happened while receiving file. Handle the | |||
// error in the same manner as it would have happened in | |||
// variable change. | |||
String debugId = ddw.getDebugId(); | |||
} else { | |||
int separatorPos = name.lastIndexOf("_"); | |||
final String pid = name.substring(0, separatorPos); | |||
final Upload uploadComponent = (Upload) idPaintableMap | |||
.get(pid); | |||
if (uploadComponent == null) { | |||
throw new FileUploadException( | |||
"Upload component not found"); | |||
} | |||
if (uploadComponent.isReadOnly()) { | |||
throw new FileUploadException( | |||
"Warning: ignored file upload because upload component is set as read-only"); | |||
} | |||
synchronized (application) { | |||
handleChangeVariablesError(application, | |||
uploadComponent, e, | |||
new HashMap<String, Object>()); | |||
// put upload component into receiving state | |||
uploadComponent.startUpload(); | |||
} | |||
// tell UploadProgressListener which component is | |||
// receiving | |||
// file | |||
pl.setUpload(uploadComponent); | |||
try { | |||
uploadComponent.receiveUpload(upstream); | |||
} catch (UploadException e) { | |||
// error happened while receiving file. Handle the | |||
// error in the same manner as it would have | |||
// happened in | |||
// variable change. | |||
synchronized (application) { | |||
handleChangeVariablesError(application, | |||
uploadComponent, e, | |||
new HashMap<String, Object>()); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} catch (final FileUploadException e) { |
@@ -3,8 +3,11 @@ | |||
*/ | |||
package com.vaadin.ui; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.vaadin.event.Transferable; | |||
import com.vaadin.event.TransferableImpl; | |||
@@ -15,10 +18,13 @@ import com.vaadin.event.dd.DropTargetDetails; | |||
import com.vaadin.event.dd.DropTargetDetailsImpl; | |||
import com.vaadin.terminal.PaintException; | |||
import com.vaadin.terminal.PaintTarget; | |||
import com.vaadin.terminal.UploadStream; | |||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||
import com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper; | |||
import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation; | |||
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; | |||
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet; | |||
import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable.Html5File; | |||
import com.vaadin.ui.Upload.Receiver; | |||
@ClientWidget(VDragAndDropWrapper.class) | |||
@@ -27,9 +33,24 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, | |||
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 id = (String) rawVariables.get("fi" + i); | |||
file.name = (String) rawVariables.get("fn" + i); | |||
file.size = (Integer) rawVariables.get("fs" + i); | |||
file.type = (String) rawVariables.get("ft" + i); | |||
files[i] = file; | |||
receivers.put(id, file); | |||
} | |||
} | |||
} | |||
/** | |||
@@ -51,21 +72,28 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, | |||
} | |||
public Html5File[] getFiles() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
return files; | |||
} | |||
public class Html5File { | |||
public String name; | |||
private String id; | |||
private int size; | |||
private Receiver receiver; | |||
private String type; | |||
public String getFileName() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
return name; | |||
} | |||
public int getFileSize() { | |||
return size; | |||
} | |||
// public int getFileSize() { | |||
// // TODO Auto-generated method stub | |||
// return 0; | |||
// } | |||
public String getType() { | |||
return type; | |||
} | |||
/** | |||
* HTML5 drags are read from client disk with a callback. This and | |||
@@ -77,14 +105,15 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, | |||
* implementation writes the file contents as it arrives. | |||
*/ | |||
public void receive(Receiver receiver) { | |||
// TODO Auto-generated method stub | |||
this.receiver = receiver; | |||
} | |||
} | |||
} | |||
private Map<String, Html5File> receivers = new HashMap<String, Html5File>(); | |||
public class WrapperDropDetails extends DropTargetDetailsImpl { | |||
/** | |||
@@ -195,13 +224,34 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, | |||
return dragStartMode; | |||
} | |||
@Override | |||
public void changeVariables(Object source, Map<String, Object> variables) { | |||
super.changeVariables(source, variables); | |||
/** | |||
* This method should only be used by Vaadin terminal implementation. This | |||
* is not end user api. | |||
* | |||
* TODO should fire progress events + end/succes events like upload | |||
* | |||
* @param upstream | |||
* @param fileId | |||
*/ | |||
public void receiveFile(UploadStream upstream, String fileId) { | |||
Html5File file = receivers.get(fileId); | |||
if (file != null && file.receiver != null) { | |||
OutputStream receiveUpload = file.receiver.receiveUpload(file | |||
.getFileName(), "TODO"); | |||
InputStream stream = upstream.getStream(); | |||
byte[] buf = new byte[AbstractApplicationServlet.MAX_BUFFER_SIZE]; | |||
int bytesRead; | |||
try { | |||
while ((bytesRead = stream.read(buf)) != -1) { | |||
receiveUpload.write(buf, 0, bytesRead); | |||
} | |||
} catch (IOException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
Set<String> keySet = variables.keySet(); | |||
for (String string : keySet) { | |||
// TODO get files | |||
} | |||
} | |||
} |
@@ -1,5 +1,9 @@ | |||
package com.vaadin.tests.dd; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
@@ -22,7 +26,9 @@ import com.vaadin.event.dd.acceptCriteria.AcceptCriterion; | |||
import com.vaadin.event.dd.acceptCriteria.IsSameSourceAndTarget; | |||
import com.vaadin.event.dd.acceptCriteria.Not; | |||
import com.vaadin.terminal.Resource; | |||
import com.vaadin.terminal.StreamResource; | |||
import com.vaadin.terminal.ThemeResource; | |||
import com.vaadin.terminal.StreamResource.StreamSource; | |||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.tests.util.TestUtils; | |||
@@ -34,9 +40,12 @@ import com.vaadin.ui.Embedded; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.SplitPanel; | |||
import com.vaadin.ui.Tree; | |||
import com.vaadin.ui.Window; | |||
import com.vaadin.ui.AbsoluteLayout.ComponentPosition; | |||
import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable.Html5File; | |||
import com.vaadin.ui.Tree.TreeDragMode; | |||
import com.vaadin.ui.Tree.TreeDropTargetDetails; | |||
import com.vaadin.ui.Upload.Receiver; | |||
public class DDTest6 extends TestBase { | |||
@@ -52,6 +61,8 @@ public class DDTest6 extends TestBase { | |||
private SplitPanel sp; | |||
private BeanItemContainer<File> fs1; | |||
private static int count; | |||
private static DDTest6 instance; | |||
@@ -70,7 +81,7 @@ public class DDTest6 extends TestBase { | |||
tree1 = new Tree("Volume 1"); | |||
tree1.setImmediate(true); | |||
BeanItemContainer<File> fs1 = new BeanItemContainer<File>(File.class); | |||
fs1 = new BeanItemContainer<File>(File.class); | |||
tree1.setContainerDataSource(fs1); | |||
for (int i = 0; i < files.length; i++) { | |||
fs1.addBean(files[i]); | |||
@@ -169,11 +180,18 @@ public class DDTest6 extends TestBase { | |||
public static class File { | |||
private Resource icon = DOC; | |||
private String name; | |||
private ByteArrayOutputStream bas; | |||
private String type; | |||
public File(String fileName) { | |||
name = fileName; | |||
} | |||
public File(String fileName, ByteArrayOutputStream bas) { | |||
this(fileName); | |||
this.bas = bas; | |||
} | |||
public void setIcon(Resource icon) { | |||
this.icon = icon; | |||
} | |||
@@ -189,6 +207,28 @@ public class DDTest6 extends TestBase { | |||
public String getName() { | |||
return name; | |||
} | |||
public void setType(String type) { | |||
this.type = type; | |||
} | |||
public String getType() { | |||
return type; | |||
} | |||
public Resource getResource() { | |||
StreamSource streamSource = new StreamSource() { | |||
public InputStream getStream() { | |||
if (bas != null) { | |||
byte[] byteArray = bas.toByteArray(); | |||
return new ByteArrayInputStream(byteArray); | |||
} | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
}; | |||
return new StreamResource(streamSource, getName(), DDTest6.get()); | |||
} | |||
} | |||
public static class Folder extends File { | |||
@@ -202,7 +242,7 @@ public class DDTest6 extends TestBase { | |||
@Override | |||
protected String getDescription() { | |||
return "dd: tree and web desktop tests. TODO add traditional icon area on right side with DragAndDropWrapper and absolutelayouts + more files, auto-opening folders"; | |||
return "dd: tree and web desktop tests. FF36 supports draggin files from client side. (try dragging png image + double click) TODO more files, auto-opening folders"; | |||
} | |||
@Override | |||
@@ -210,6 +250,16 @@ public class DDTest6 extends TestBase { | |||
return 119; | |||
} | |||
private void openFile(File file) { | |||
// ATM supports only images. | |||
Embedded embedded = new Embedded(file.getName(), file.getResource()); | |||
Window w = new Window(file.getName()); | |||
w.addComponent(embedded); | |||
getMainWindow().addWindow(w); | |||
} | |||
static class FolderView extends DragAndDropWrapper implements DropHandler { | |||
static final HashMap<Folder, FolderView> views = new HashMap<Folder, FolderView>(); | |||
@@ -313,6 +363,35 @@ public class DDTest6 extends TestBase { | |||
File draggedFile = (File) ((DataBoundTransferable) dropEvent | |||
.getTransferable()).getItemId(); | |||
DDTest6.get().setParent(draggedFile, folder); | |||
} else { | |||
// expecting this to be an html5 drag | |||
WrapperTransferable tr = (WrapperTransferable) dropEvent | |||
.getTransferable(); | |||
Html5File[] files2 = tr.getFiles(); | |||
if (files2 != null) { | |||
for (Html5File html5File : files2) { | |||
String fileName = html5File.getFileName(); | |||
// int bytes = html5File.getFileSize(); | |||
final ByteArrayOutputStream bas = new ByteArrayOutputStream(); | |||
Receiver receiver = new Receiver() { | |||
public OutputStream receiveUpload(String filename, | |||
String MIMEType) { | |||
return bas; | |||
} | |||
}; | |||
html5File.receive(receiver); | |||
File file = new File(fileName, bas); | |||
file.setType(html5File.getType()); | |||
DDTest6.get().fs1.addBean(file); | |||
DDTest6.get().tree1.setChildrenAllowed(file, false); | |||
DDTest6.get().setParent(file, folder); | |||
} | |||
} | |||
} | |||
} | |||
@@ -342,11 +421,15 @@ public class DDTest6 extends TestBase { | |||
l.addListener(new LayoutClickListener() { | |||
public void layoutClick(LayoutClickEvent event) { | |||
if (file instanceof Folder) { | |||
if (event.isDoubleClick()) { | |||
if (event.isDoubleClick()) { | |||
if (file instanceof Folder) { | |||
get().tree1.setValue(file); | |||
} else { | |||
String type = file.getType(); | |||
if (type != null && type.equals("image/png")) { | |||
DDTest6.get().openFile(file); | |||
} | |||
} | |||
} | |||
} | |||
@@ -372,7 +455,6 @@ public class DDTest6 extends TestBase { | |||
f = (File) ((DataBoundTransferable) dropEvent | |||
.getTransferable()).getItemId(); | |||
} | |||
// TODO accept drags from Tree too | |||
if (f != null) { | |||
get().setParent(f, (Folder) FileIcon.this.file); |