1 /*******************************************************************************
2 * Copyright (c) 2000, 2012 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.dnd;
16 import org.eclipse.swt.internal.ole.win32.*;
17 import org.eclipse.swt.internal.win32.*;
20 * The class <code>FileTransfer</code> provides a platform specific mechanism
21 * for converting a list of files represented as a java <code>String[]</code> to a
22 * platform specific representation of the data and vice versa.
23 * Each <code>String</code> in the array contains the absolute path for a single
26 * <p>An example of a java <code>String[]</code> containing a list of files is shown
30 * File file1 = new File("C:\\temp\\file1");
31 * File file2 = new File("C:\\temp\\file2");
32 * String[] fileData = new String[2];
33 * fileData[0] = file1.getAbsolutePath();
34 * fileData[1] = file2.getAbsolutePath();
39 public class FileTransfer extends ByteArrayTransfer {
41 private static FileTransfer _instance = new FileTransfer();
42 private static final String CF_HDROP = "CF_HDROP"; //$NON-NLS-1$
43 private static final int CF_HDROPID = COM.CF_HDROP;
44 private static final String CFSTR_SHELLIDLIST = "Shell IDList Array"; //$NON-NLS-1$
45 private static final int CFSTR_SHELLIDLISTID = registerType(CFSTR_SHELLIDLIST);
47 private FileTransfer() {}
50 * Returns the singleton instance of the FileTransfer class.
52 * @return the singleton instance of the FileTransfer class
54 public static FileTransfer getInstance () {
59 * This implementation of <code>javaToNative</code> converts a list of file names
60 * represented by a java <code>String[]</code> to a platform specific representation.
61 * Each <code>String</code> in the array contains the absolute path for a single
64 * @param object a java <code>String[]</code> containing the file names to be converted
65 * @param transferData an empty <code>TransferData</code> object that will
66 * be filled in on return with the platform specific format of the data
68 * @see Transfer#nativeToJava
71 public void javaToNative(Object object, TransferData transferData) {
72 if (!checkFile(object) || !isSupportedType(transferData)) {
73 DND.error(DND.ERROR_INVALID_DATA);
75 String[] fileNames = (String[]) object;
77 if (transferData.type == CF_HDROPID) {
78 StringBuilder allFiles = new StringBuilder();
79 for (int i = 0; i < fileNames.length; i++) {
80 allFiles.append(fileNames[i]);
81 allFiles.append('\0'); // each name is null terminated
83 allFiles.append('\0'); // there is an extra null terminator at the very end
84 char [] buffer = new char [allFiles.length()];
85 allFiles.getChars(0, allFiles.length(), buffer, 0);
86 DROPFILES dropfiles = new DROPFILES();
87 dropfiles.pFiles = DROPFILES.sizeof;
88 dropfiles.pt_x = dropfiles.pt_y = 0;
91 // Allocate the memory because the caller (DropTarget) has not handed it in
92 // The caller of this method must release the data when it is done with it.
93 int byteCount = buffer.length * TCHAR.sizeof;
94 newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, DROPFILES.sizeof + byteCount);
96 OS.MoveMemory(newPtr, dropfiles, DROPFILES.sizeof);
97 OS.MoveMemory(newPtr + DROPFILES.sizeof, buffer, byteCount);
99 } else if (transferData.type == CFSTR_SHELLIDLISTID) {
100 newPtr = generateCidaFromFilepaths(fileNames);
102 transferData.stgmedium = new STGMEDIUM();
103 transferData.stgmedium.tymed = COM.TYMED_HGLOBAL;
104 transferData.stgmedium.unionField = newPtr;
105 transferData.stgmedium.pUnkForRelease = 0;
106 transferData.result = newPtr != 0 ? COM.S_OK : COM.E_FAIL;
110 * This implementation of <code>nativeToJava</code> converts a platform specific
111 * representation of a list of file names to a java <code>String[]</code>.
112 * Each String in the array contains the absolute path for a single file or directory.
114 * @param transferData the platform specific representation of the data to be converted
115 * @return a java <code>String[]</code> containing a list of file names if the conversion
116 * was successful; otherwise null
118 * @see Transfer#javaToNative
121 public Object nativeToJava(TransferData transferData) {
122 if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null;
124 // get file names from IDataObject
125 IDataObject dataObject = new IDataObject(transferData.pIDataObject);
127 FORMATETC formatetc = new FORMATETC();
128 formatetc.cfFormat = COM.CF_HDROP;
130 formatetc.dwAspect = COM.DVASPECT_CONTENT;
131 formatetc.lindex = -1;
132 formatetc.tymed = COM.TYMED_HGLOBAL;
133 STGMEDIUM stgmedium = new STGMEDIUM();
134 stgmedium.tymed = COM.TYMED_HGLOBAL;
135 transferData.result = getData(dataObject, formatetc, stgmedium);
136 dataObject.Release();
137 if (transferData.result != COM.S_OK) return null;
138 // How many files are there?
139 int count = OS.DragQueryFile(stgmedium.unionField, 0xFFFFFFFF, null, 0);
140 String[] fileNames = new String[count];
141 for (int i = 0; i < count; i++) {
142 // How long is the name ?
143 int size = OS.DragQueryFile(stgmedium.unionField, i, null, 0);
144 char [] lpszFile = new char [size + 1];
145 // Get file name and append it to string
146 OS.DragQueryFile(stgmedium.unionField, i, lpszFile, size + 1);
147 fileNames[i] = new String(lpszFile, 0, size);
149 OS.DragFinish(stgmedium.unionField); // frees data associated with HDROP data
154 * Generate {@link CIDA} structure and trailing data to transfer filenames
155 * as {@link #CFSTR_SHELLIDLIST}.
157 * For more information on the {@link CIDA} structure see also {@link #resolveCidaToFilepaths(long)}.
160 * @param fileNames filenames to pack in {@link CIDA}.
161 * @return pointer to global memory chunk filled with generated data or <code>0</code> on failure
163 private long generateCidaFromFilepaths(String[] fileNames) {
164 final int n = fileNames.length;
165 long [] pidls = new long [n];
167 CIDA cida = new CIDA();
169 int cidaSize = CIDA.sizeof + 4 * n;
170 cida.aoffset = cidaSize; // offsets are from cida begin so the first is equal to cida size
172 int[] pidlOffsets = new int[n];
173 int[] pidlSizes = new int[n];
174 int pidlSizeSum = 2; // initialize with 2 for the empty (but double null terminated) parent pidl
175 for (int i = 0; i < n; i++) {
176 TCHAR szfileName = new TCHAR(0, fileNames[i], true);
177 long [] ppv = new long [1];
178 int hr = COM.PathToPIDL(szfileName.chars, ppv);
183 pidlSizes[i] = OS.ILGetSize(pidls[i]);
184 pidlSizeSum += pidlSizes[i];
187 pidlOffsets[0] = cidaSize + 2;
190 pidlOffsets[i] = pidlOffsets[i - 1] + pidlSizes[i - 1];
194 long newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, cidaSize + pidlSizeSum);
196 OS.MoveMemory(newPtr, cida, CIDA.sizeof);
197 OS.MoveMemory(newPtr + CIDA.sizeof, pidlOffsets, 4 * cida.cidl);
198 for (int i = 0; i < n; i++) {
199 OS.MoveMemory(newPtr + pidlOffsets[i], pidls[i], pidlSizes[i]);
204 for (int i = 0; i < n; i++) {
206 OS.CoTaskMemFree(pidls[i]);
213 public boolean isSupportedType(TransferData transferData) {
214 // filter Shell ID List Array transfer only for dropping
215 if (transferData != null && transferData.pIDataObject != 0 && transferData.type == CFSTR_SHELLIDLISTID) {
218 return super.isSupportedType(transferData);
222 protected int[] getTypeIds(){
223 // Note: FileTransfer adds Shell ID List as transfer type but later
224 // limit this type for dragging only.
225 return new int[] {CF_HDROPID, CFSTR_SHELLIDLISTID};
229 protected String[] getTypeNames(){
230 return new String[] {CF_HDROP, CFSTR_SHELLIDLIST};
232 boolean checkFile(Object object) {
233 if (object == null || !(object instanceof String[]) || ((String[])object).length == 0) return false;
234 String[] strings = (String[])object;
235 for (int i = 0; i < strings.length; i++) {
236 if (strings[i] == null || strings[i].length() == 0) return false;
242 protected boolean validate(Object object) {
243 return checkFile(object);