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
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.
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 ***** */
41 #include "nsDataObjCollection.h"
42 #include "nsClipboard.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();
62 nsDataObjCollection::~nsDataObjCollection()
71 // IUnknown interface methods - see iunknown.h for documentation
72 STDMETHODIMP
nsDataObjCollection::QueryInterface(REFIID riid
, void** ppv
)
76 if ( (IID_IUnknown
== riid
) || (IID_IDataObject
== riid
) ) {
77 *ppv
= static_cast<IDataObject
*>(this);
82 if ( IID_IDataObjCollection
== riid
) {
83 *ppv
= static_cast<nsIDataObjCollection
*>(this);
91 STDMETHODIMP_(ULONG
) nsDataObjCollection::AddRef()
96 STDMETHODIMP_(ULONG
) nsDataObjCollection::Release()
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
)) {
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
) {
130 return GetText(pFE
, pSTM
);
132 return GetFile(pFE
, pSTM
);
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
)
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
) {
159 for (PRUint32 i
= 0; i
< mDataObjects
.Length(); ++i
) {
160 IDataObject
* dataObj
= mDataObjects
.ElementAt(i
);
161 if (S_OK
== dataObj
->QueryGetData(pFE
)) {
166 return DV_E_FORMATETC
;
169 STDMETHODIMP
nsDataObjCollection::GetCanonicalFormatEtc(LPFORMATETC pFEIn
,
175 STDMETHODIMP
nsDataObjCollection::SetData(LPFORMATETC pFE
,
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)
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
);
201 STDMETHODIMP
nsDataObjCollection::DAdvise(LPFORMATETC pFE
,
203 LPADVISESINK pIAdviseSink
,
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
)
225 HRESULT
nsDataObjCollection::AddGetFormat(FORMATETC
& aFE
)
230 // Registers a DataFlavor/FE pair
231 void nsDataObjCollection::AddDataFlavor(const char * aDataFlavor
,
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
;
239 if (S_OK
!= this->EnumFormatEtc(DATADIR_GET
, &ifEtc
))
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
)) {
248 } // If we didn't find a matching format, add this one
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
;
265 HGLOBAL hGlobalMemory
;
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
);
286 // Now we need to pull out the filename
287 PRUnichar
* buffer
= (PRUnichar
*)GlobalLock(workingmedium
.hGlobal
);
290 buffer
+= sizeof(DROPFILES
)/sizeof(PRUnichar
);
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
)
299 realbuffer
= (PRUnichar
*)((char*)GlobalLock(hGlobalMemory
) + buffersize
);
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
);
313 df
->pFiles
= sizeof(DROPFILES
); //Offset to start of file name string
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
;
326 HRESULT
nsDataObjCollection::GetText(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
)
328 STGMEDIUM workingmedium
;
330 HGLOBAL hGlobalMemory
;
332 PRUint32 buffersize
= 1;
333 PRUint32 alloclen
= 0;
335 hGlobalMemory
= GlobalAlloc(GHND
, buffersize
);
337 if (pFE
->cfFormat
== CF_TEXT
) {
339 for (PRUint32 i
= 0; i
< mDataObjects
.Length(); ++i
) {
340 nsDataObj
* dataObj
= mDataObjects
.ElementAt(i
);
341 hr
= dataObj
->GetData(&fe
, &workingmedium
);
350 // Now we need to pull out the text
351 char* buffer
= (char*)GlobalLock(workingmedium
.hGlobal
);
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
,
361 if (hGlobalMemory
== NULL
)
363 buffer
= ((char*)GlobalLock(hGlobalMemory
) + buffersize
);
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
;
376 if (pFE
->cfFormat
== CF_UNICODETEXT
) {
377 buffersize
= sizeof(PRUnichar
);
379 for (PRUint32 i
= 0; i
< mDataObjects
.Length(); ++i
) {
380 nsDataObj
* dataObj
= mDataObjects
.ElementAt(i
);
381 hr
= dataObj
->GetData(&fe
, &workingmedium
);
390 // Now we need to pull out the text
391 PRUnichar
* buffer
= (PRUnichar
*)GlobalLock(workingmedium
.hGlobal
);
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
,
401 if (hGlobalMemory
== NULL
)
403 buffer
= (PRUnichar
*)((char*)GlobalLock(hGlobalMemory
) + buffersize
);
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
;
420 HRESULT
nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE
,
423 STGMEDIUM workingmedium
;
425 HGLOBAL hGlobalMemory
;
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
);
443 // Now we need to pull out the filedescriptor
444 FILEDESCRIPTOR
* buffer
=
445 (FILEDESCRIPTOR
*)((char*)GlobalLock(workingmedium
.hGlobal
) + sizeof(UINT
));
448 hGlobalMemory
= ::GlobalReAlloc(hGlobalMemory
, buffersize
+ alloclen
, GHND
);
449 if (hGlobalMemory
== NULL
)
451 FILEGROUPDESCRIPTOR
* realbuffer
=
452 (FILEGROUPDESCRIPTOR
*)GlobalLock(hGlobalMemory
);
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
;
469 HRESULT
nsDataObjCollection::GetFileContents(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
)
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
)
482 if (num
== numwanted
)
483 return dataObj
->GetData(pFE
, pSTM
);
489 HRESULT
nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE
,
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
;