CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / windows / nsDataObjCollection.cpp
blob3b11cd1319a88ce272c8fb8a840952b0e3ad58ec
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Kyle Huey <me@kylehuey.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include <shlobj.h>
41 #include "nsDataObjCollection.h"
42 #include "nsClipboard.h"
43 #include "IEnumFE.h"
45 #include <ole2.h>
47 // {25589C3E-1FAC-47b9-BF43-CAEA89B79533}
48 const IID IID_IDataObjCollection =
49 {0x25589c3e, 0x1fac, 0x47b9, {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}};
52 * Class nsDataObjCollection
55 nsDataObjCollection::nsDataObjCollection()
56 : m_cRef(0), mIsAsyncMode(FALSE), mIsInOperation(FALSE)
58 m_enumFE = new CEnumFormatEtc();
59 m_enumFE->AddRef();
62 nsDataObjCollection::~nsDataObjCollection()
64 mDataFlavors.Clear();
65 mDataObjects.Clear();
67 m_enumFE->Release();
71 // IUnknown interface methods - see iunknown.h for documentation
72 STDMETHODIMP nsDataObjCollection::QueryInterface(REFIID riid, void** ppv)
74 *ppv=NULL;
76 if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) {
77 *ppv = static_cast<IDataObject*>(this);
78 AddRef();
79 return NOERROR;
82 if ( IID_IDataObjCollection == riid ) {
83 *ppv = static_cast<nsIDataObjCollection*>(this);
84 AddRef();
85 return NOERROR;
88 return E_NOINTERFACE;
91 STDMETHODIMP_(ULONG) nsDataObjCollection::AddRef()
93 return ++m_cRef;
96 STDMETHODIMP_(ULONG) nsDataObjCollection::Release()
98 if (0 != --m_cRef)
99 return m_cRef;
101 delete this;
103 return 0;
106 BOOL nsDataObjCollection::FormatsMatch(const FORMATETC& source,
107 const FORMATETC& target) const
109 if ((source.cfFormat == target.cfFormat) &&
110 (source.dwAspect & target.dwAspect) &&
111 (source.tymed & target.tymed)) {
112 return TRUE;
113 } else {
114 return FALSE;
118 // IDataObject methods
119 STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
121 static CLIPFORMAT fileDescriptorFlavorA =
122 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
123 static CLIPFORMAT fileDescriptorFlavorW =
124 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
125 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
127 switch (pFE->cfFormat) {
128 case CF_TEXT:
129 case CF_UNICODETEXT:
130 return GetText(pFE, pSTM);
131 case CF_HDROP:
132 return GetFile(pFE, pSTM);
133 default:
134 if (pFE->cfFormat == fileDescriptorFlavorA ||
135 pFE->cfFormat == fileDescriptorFlavorW) {
136 return GetFileDescriptors(pFE, pSTM);
138 if (pFE->cfFormat == fileFlavor) {
139 return GetFileContents(pFE, pSTM);
142 return GetFirstSupporting(pFE, pSTM);
145 STDMETHODIMP nsDataObjCollection::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
147 return E_FAIL;
150 // Other objects querying to see if we support a particular format
151 STDMETHODIMP nsDataObjCollection::QueryGetData(LPFORMATETC pFE)
153 UINT format = nsClipboard::GetFormat(MULTI_MIME);
155 if (format == pFE->cfFormat) {
156 return S_OK;
159 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
160 IDataObject * dataObj = mDataObjects.ElementAt(i);
161 if (S_OK == dataObj->QueryGetData(pFE)) {
162 return S_OK;
166 return DV_E_FORMATETC;
169 STDMETHODIMP nsDataObjCollection::GetCanonicalFormatEtc(LPFORMATETC pFEIn,
170 LPFORMATETC pFEOut)
172 return E_NOTIMPL;
175 STDMETHODIMP nsDataObjCollection::SetData(LPFORMATETC pFE,
176 LPSTGMEDIUM pSTM,
177 BOOL fRelease)
179 // Set arbitrary data formats on the first object in the collection and let
180 // it handle the heavy lifting
181 if (mDataObjects.Length() == 0)
182 return E_FAIL;
183 return mDataObjects.ElementAt(0)->SetData(pFE, pSTM, fRelease);
186 STDMETHODIMP nsDataObjCollection::EnumFormatEtc(DWORD dwDir,
187 LPENUMFORMATETC *ppEnum)
189 if (dwDir == DATADIR_GET) {
190 // Clone addref's the new enumerator.
191 m_enumFE->Clone(ppEnum);
192 if (!(*ppEnum))
193 return E_FAIL;
194 (*ppEnum)->Reset();
195 return S_OK;
198 return E_NOTIMPL;
201 STDMETHODIMP nsDataObjCollection::DAdvise(LPFORMATETC pFE,
202 DWORD dwFlags,
203 LPADVISESINK pIAdviseSink,
204 DWORD* pdwConn)
206 return OLE_E_ADVISENOTSUPPORTED;
209 STDMETHODIMP nsDataObjCollection::DUnadvise(DWORD dwConn)
211 return OLE_E_ADVISENOTSUPPORTED;
214 STDMETHODIMP nsDataObjCollection::EnumDAdvise(LPENUMSTATDATA *ppEnum)
216 return OLE_E_ADVISENOTSUPPORTED;
219 // GetData and SetData helper functions
220 HRESULT nsDataObjCollection::AddSetFormat(FORMATETC& aFE)
222 return S_OK;
225 HRESULT nsDataObjCollection::AddGetFormat(FORMATETC& aFE)
227 return S_OK;
230 // Registers a DataFlavor/FE pair
231 void nsDataObjCollection::AddDataFlavor(const char * aDataFlavor,
232 LPFORMATETC aFE)
234 // Add the FormatEtc to our list if it's not already there. We don't care
235 // about the internal aDataFlavor because nsDataObj handles that.
236 IEnumFORMATETC * ifEtc;
237 FORMATETC fEtc;
238 ULONG num;
239 if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc))
240 return;
241 while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
242 NS_ASSERTION(1 == num,
243 "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
244 if (FormatsMatch(fEtc, *aFE)) {
245 ifEtc->Release();
246 return;
248 } // If we didn't find a matching format, add this one
249 ifEtc->Release();
250 m_enumFE->AddFormatEtc(aFE);
253 // We accept ownership of the nsDataObj which we free on destruction
254 void nsDataObjCollection::AddDataObject(IDataObject * aDataObj)
256 nsDataObj* dataObj = reinterpret_cast<nsDataObj*>(aDataObj);
257 mDataObjects.AppendElement(dataObj);
260 // Methods for getting data
261 HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
263 STGMEDIUM workingmedium;
264 FORMATETC fe = *pFE;
265 HGLOBAL hGlobalMemory;
266 HRESULT hr;
267 // Make enough space for the header and the trailing null
268 PRUint32 buffersize = sizeof(DROPFILES) + sizeof(PRUnichar);
269 PRUint32 alloclen = 0;
270 PRUnichar* realbuffer;
271 nsAutoString filename;
273 hGlobalMemory = GlobalAlloc(GHND, buffersize);
275 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
276 nsDataObj* dataObj = mDataObjects.ElementAt(i);
277 hr = dataObj->GetData(&fe, &workingmedium);
278 if (hr != S_OK) {
279 switch (hr) {
280 case DV_E_FORMATETC:
281 continue;
282 default:
283 return hr;
286 // Now we need to pull out the filename
287 PRUnichar* buffer = (PRUnichar*)GlobalLock(workingmedium.hGlobal);
288 if (buffer == NULL)
289 return E_FAIL;
290 buffer += sizeof(DROPFILES)/sizeof(PRUnichar);
291 filename = buffer;
292 GlobalUnlock(workingmedium.hGlobal);
293 ReleaseStgMedium(&workingmedium);
294 // Now put the filename into our buffer
295 alloclen = (filename.Length() + 1) * sizeof(PRUnichar);
296 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
297 if (hGlobalMemory == NULL)
298 return E_FAIL;
299 realbuffer = (PRUnichar*)((char*)GlobalLock(hGlobalMemory) + buffersize);
300 if (!realbuffer)
301 return E_FAIL;
302 realbuffer--; // Overwrite the preceding null
303 memcpy(realbuffer, filename.get(), alloclen);
304 GlobalUnlock(hGlobalMemory);
305 buffersize += alloclen;
307 // We get the last null (on the double null terminator) for free since we used
308 // the zero memory flag when we allocated. All we need to do is fill the
309 // DROPFILES structure
310 DROPFILES* df = (DROPFILES*)GlobalLock(hGlobalMemory);
311 if (!df)
312 return E_FAIL;
313 df->pFiles = sizeof(DROPFILES); //Offset to start of file name string
314 df->fNC = 0;
315 df->pt.x = 0;
316 df->pt.y = 0;
317 df->fWide = TRUE; // utf-16 chars
318 GlobalUnlock(hGlobalMemory);
319 // Finally fill out the STGMEDIUM struct
320 pSTM->tymed = TYMED_HGLOBAL;
321 pSTM->pUnkForRelease = NULL; // Caller gets to free the data
322 pSTM->hGlobal = hGlobalMemory;
323 return S_OK;
326 HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
328 STGMEDIUM workingmedium;
329 FORMATETC fe = *pFE;
330 HGLOBAL hGlobalMemory;
331 HRESULT hr;
332 PRUint32 buffersize = 1;
333 PRUint32 alloclen = 0;
335 hGlobalMemory = GlobalAlloc(GHND, buffersize);
337 if (pFE->cfFormat == CF_TEXT) {
338 nsCAutoString text;
339 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
340 nsDataObj* dataObj = mDataObjects.ElementAt(i);
341 hr = dataObj->GetData(&fe, &workingmedium);
342 if (hr != S_OK) {
343 switch (hr) {
344 case DV_E_FORMATETC:
345 continue;
346 default:
347 return hr;
350 // Now we need to pull out the text
351 char* buffer = (char*)GlobalLock(workingmedium.hGlobal);
352 if (buffer == NULL)
353 return E_FAIL;
354 text = buffer;
355 GlobalUnlock(workingmedium.hGlobal);
356 ReleaseStgMedium(&workingmedium);
357 // Now put the text into our buffer
358 alloclen = text.Length();
359 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
360 GHND);
361 if (hGlobalMemory == NULL)
362 return E_FAIL;
363 buffer = ((char*)GlobalLock(hGlobalMemory) + buffersize);
364 if (!buffer)
365 return E_FAIL;
366 buffer--; // Overwrite the preceding null
367 memcpy(buffer, text.get(), alloclen);
368 GlobalUnlock(hGlobalMemory);
369 buffersize += alloclen;
371 pSTM->tymed = TYMED_HGLOBAL;
372 pSTM->pUnkForRelease = NULL; // Caller gets to free the data
373 pSTM->hGlobal = hGlobalMemory;
374 return S_OK;
376 if (pFE->cfFormat == CF_UNICODETEXT) {
377 buffersize = sizeof(PRUnichar);
378 nsAutoString text;
379 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
380 nsDataObj* dataObj = mDataObjects.ElementAt(i);
381 hr = dataObj->GetData(&fe, &workingmedium);
382 if (hr != S_OK) {
383 switch (hr) {
384 case DV_E_FORMATETC:
385 continue;
386 default:
387 return hr;
390 // Now we need to pull out the text
391 PRUnichar* buffer = (PRUnichar*)GlobalLock(workingmedium.hGlobal);
392 if (buffer == NULL)
393 return E_FAIL;
394 text = buffer;
395 GlobalUnlock(workingmedium.hGlobal);
396 ReleaseStgMedium(&workingmedium);
397 // Now put the text into our buffer
398 alloclen = text.Length() * sizeof(PRUnichar);
399 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
400 GHND);
401 if (hGlobalMemory == NULL)
402 return E_FAIL;
403 buffer = (PRUnichar*)((char*)GlobalLock(hGlobalMemory) + buffersize);
404 if (!buffer)
405 return E_FAIL;
406 buffer--; // Overwrite the preceding null
407 memcpy(buffer, text.get(), alloclen);
408 GlobalUnlock(hGlobalMemory);
409 buffersize += alloclen;
411 pSTM->tymed = TYMED_HGLOBAL;
412 pSTM->pUnkForRelease = NULL; // Caller gets to free the data
413 pSTM->hGlobal = hGlobalMemory;
414 return S_OK;
417 return E_FAIL;
420 HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
421 LPSTGMEDIUM pSTM)
423 STGMEDIUM workingmedium;
424 FORMATETC fe = *pFE;
425 HGLOBAL hGlobalMemory;
426 HRESULT hr;
427 PRUint32 buffersize = sizeof(FILEGROUPDESCRIPTOR);
428 PRUint32 alloclen = sizeof(FILEDESCRIPTOR);
430 hGlobalMemory = GlobalAlloc(GHND, buffersize);
432 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
433 nsDataObj* dataObj = mDataObjects.ElementAt(i);
434 hr = dataObj->GetData(&fe, &workingmedium);
435 if (hr != S_OK) {
436 switch (hr) {
437 case DV_E_FORMATETC:
438 continue;
439 default:
440 return hr;
443 // Now we need to pull out the filedescriptor
444 FILEDESCRIPTOR* buffer =
445 (FILEDESCRIPTOR*)((char*)GlobalLock(workingmedium.hGlobal) + sizeof(UINT));
446 if (buffer == NULL)
447 return E_FAIL;
448 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
449 if (hGlobalMemory == NULL)
450 return E_FAIL;
451 FILEGROUPDESCRIPTOR* realbuffer =
452 (FILEGROUPDESCRIPTOR*)GlobalLock(hGlobalMemory);
453 if (!realbuffer)
454 return E_FAIL;
455 FILEDESCRIPTOR* copyloc = (FILEDESCRIPTOR*)((char*)realbuffer + buffersize);
456 memcpy(copyloc, buffer, sizeof(FILEDESCRIPTOR));
457 realbuffer->cItems++;
458 GlobalUnlock(hGlobalMemory);
459 GlobalUnlock(workingmedium.hGlobal);
460 ReleaseStgMedium(&workingmedium);
461 buffersize += alloclen;
463 pSTM->tymed = TYMED_HGLOBAL;
464 pSTM->pUnkForRelease = NULL; // Caller gets to free the data
465 pSTM->hGlobal = hGlobalMemory;
466 return S_OK;
469 HRESULT nsDataObjCollection::GetFileContents(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
471 ULONG num = 0;
472 ULONG numwanted = (pFE->lindex == -1) ? 0 : pFE->lindex;
473 FORMATETC fEtc = *pFE;
474 fEtc.lindex = -1; // We're lying to the data object so it thinks it's alone
476 // The key for this data type is to figure out which data object the index
477 // corresponds to and then just pass it along
478 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
479 nsDataObj* dataObj = mDataObjects.ElementAt(i);
480 if (dataObj->QueryGetData(&fEtc) != S_OK)
481 continue;
482 if (num == numwanted)
483 return dataObj->GetData(pFE, pSTM);
484 numwanted++;
486 return DV_E_LINDEX;
489 HRESULT nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE,
490 LPSTGMEDIUM pSTM)
492 // There is no way to pass more than one of this, so just find the first data
493 // object that supports it and pass it along
494 for (PRUint32 i = 0; i < mDataObjects.Length(); ++i) {
495 if (mDataObjects.ElementAt(i)->QueryGetData(pFE) == S_OK)
496 return mDataObjects.ElementAt(i)->GetData(pFE, pSTM);
498 return DV_E_FORMATETC;