forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			405 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include <shlobj.h>
 | 
						|
 | 
						|
#include "nsDataObjCollection.h"
 | 
						|
#include "nsClipboard.h"
 | 
						|
#include "IEnumFE.h"
 | 
						|
 | 
						|
#include <ole2.h>
 | 
						|
 | 
						|
// {25589C3E-1FAC-47b9-BF43-CAEA89B79533}
 | 
						|
const IID IID_IDataObjCollection =
 | 
						|
  {0x25589c3e, 0x1fac, 0x47b9, {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}};
 | 
						|
 | 
						|
/*
 | 
						|
 * Class nsDataObjCollection
 | 
						|
 */
 | 
						|
 | 
						|
nsDataObjCollection::nsDataObjCollection()
 | 
						|
  : m_cRef(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
nsDataObjCollection::~nsDataObjCollection()
 | 
						|
{
 | 
						|
  mDataObjects.Clear();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// IUnknown interface methods - see iunknown.h for documentation
 | 
						|
STDMETHODIMP nsDataObjCollection::QueryInterface(REFIID riid, void** ppv)
 | 
						|
{
 | 
						|
  *ppv=nullptr;
 | 
						|
 | 
						|
  if ( (IID_IUnknown == riid) || (IID_IDataObject  == riid) ) {
 | 
						|
    *ppv = static_cast<IDataObject*>(this); 
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( IID_IDataObjCollection  == riid ) {
 | 
						|
    *ppv = static_cast<nsIDataObjCollection*>(this); 
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
  //offer to operate asynchronously (required by nsDragService)
 | 
						|
  if (IID_IAsyncOperation == riid) {
 | 
						|
    *ppv = static_cast<IAsyncOperation*>(this);
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return E_NOINTERFACE;
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP_(ULONG) nsDataObjCollection::AddRef()
 | 
						|
{
 | 
						|
  return ++m_cRef;
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP_(ULONG) nsDataObjCollection::Release()
 | 
						|
{
 | 
						|
  if (0 != --m_cRef)
 | 
						|
    return m_cRef;
 | 
						|
 | 
						|
  delete this;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
// IDataObject methods
 | 
						|
STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  static CLIPFORMAT fileDescriptorFlavorA =
 | 
						|
                               ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
 | 
						|
  static CLIPFORMAT fileDescriptorFlavorW =
 | 
						|
                               ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
 | 
						|
  static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
 | 
						|
 | 
						|
  switch (pFE->cfFormat) {
 | 
						|
  case CF_TEXT:
 | 
						|
  case CF_UNICODETEXT:
 | 
						|
    return GetText(pFE, pSTM);
 | 
						|
  case CF_HDROP:
 | 
						|
    return GetFile(pFE, pSTM);
 | 
						|
  default:
 | 
						|
    if (pFE->cfFormat == fileDescriptorFlavorA ||
 | 
						|
        pFE->cfFormat == fileDescriptorFlavorW) {
 | 
						|
      return GetFileDescriptors(pFE, pSTM);
 | 
						|
    }
 | 
						|
    if (pFE->cfFormat == fileFlavor) {
 | 
						|
      return GetFileContents(pFE, pSTM);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return GetFirstSupporting(pFE, pSTM);
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP nsDataObjCollection::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  return E_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
// Other objects querying to see if we support a particular format
 | 
						|
STDMETHODIMP nsDataObjCollection::QueryGetData(LPFORMATETC pFE)
 | 
						|
{
 | 
						|
  UINT format = nsClipboard::GetFormat(MULTI_MIME);
 | 
						|
 | 
						|
  if (format == pFE->cfFormat) {
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    IDataObject * dataObj = mDataObjects.ElementAt(i);
 | 
						|
    if (S_OK == dataObj->QueryGetData(pFE)) {
 | 
						|
      return S_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return DV_E_FORMATETC;
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP nsDataObjCollection::SetData(LPFORMATETC pFE,
 | 
						|
                                          LPSTGMEDIUM pSTM,
 | 
						|
                                          BOOL fRelease)
 | 
						|
{
 | 
						|
  // Set arbitrary data formats on the first object in the collection and let
 | 
						|
  // it handle the heavy lifting
 | 
						|
  if (mDataObjects.Length() == 0)
 | 
						|
    return E_FAIL;
 | 
						|
  return mDataObjects.ElementAt(0)->SetData(pFE, pSTM, fRelease);
 | 
						|
}
 | 
						|
 | 
						|
// Registers a DataFlavor/FE pair
 | 
						|
void nsDataObjCollection::AddDataFlavor(const char * aDataFlavor,
 | 
						|
                                        LPFORMATETC aFE)
 | 
						|
{
 | 
						|
  // Add the FormatEtc to our list if it's not already there.  We don't care
 | 
						|
  // about the internal aDataFlavor because nsDataObj handles that.
 | 
						|
  IEnumFORMATETC * ifEtc;
 | 
						|
  FORMATETC fEtc;
 | 
						|
  ULONG num;
 | 
						|
  if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc))
 | 
						|
    return;
 | 
						|
  while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
 | 
						|
    NS_ASSERTION(1 == num,
 | 
						|
         "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
 | 
						|
    if (FormatsMatch(fEtc, *aFE)) {
 | 
						|
      ifEtc->Release();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  } // If we didn't find a matching format, add this one
 | 
						|
  ifEtc->Release();
 | 
						|
  m_enumFE->AddFormatEtc(aFE);
 | 
						|
}
 | 
						|
 | 
						|
// We accept ownership of the nsDataObj which we free on destruction
 | 
						|
void nsDataObjCollection::AddDataObject(IDataObject * aDataObj)
 | 
						|
{
 | 
						|
  nsDataObj* dataObj = reinterpret_cast<nsDataObj*>(aDataObj);
 | 
						|
  mDataObjects.AppendElement(dataObj);
 | 
						|
}
 | 
						|
 | 
						|
// Methods for getting data
 | 
						|
HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  // Make enough space for the header and the trailing null
 | 
						|
  uint32_t buffersize = sizeof(DROPFILES) + sizeof(char16_t);
 | 
						|
  uint32_t alloclen = 0;
 | 
						|
  char16_t* realbuffer;
 | 
						|
  nsAutoString filename;
 | 
						|
  
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
    if (hr != S_OK) {
 | 
						|
      switch (hr) {
 | 
						|
      case DV_E_FORMATETC:
 | 
						|
        continue;
 | 
						|
      default:
 | 
						|
        return hr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Now we need to pull out the filename
 | 
						|
    char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
 | 
						|
    if (buffer == nullptr)
 | 
						|
      return E_FAIL;
 | 
						|
    buffer += sizeof(DROPFILES)/sizeof(char16_t);
 | 
						|
    filename = buffer;
 | 
						|
    GlobalUnlock(workingmedium.hGlobal);
 | 
						|
    ReleaseStgMedium(&workingmedium);
 | 
						|
    // Now put the filename into our buffer
 | 
						|
    alloclen = (filename.Length() + 1) * sizeof(char16_t);
 | 
						|
    hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
    if (hGlobalMemory == nullptr)
 | 
						|
      return E_FAIL;
 | 
						|
    realbuffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
    if (!realbuffer)
 | 
						|
      return E_FAIL;
 | 
						|
    realbuffer--; // Overwrite the preceding null
 | 
						|
    memcpy(realbuffer, filename.get(), alloclen);
 | 
						|
    GlobalUnlock(hGlobalMemory);
 | 
						|
    buffersize += alloclen;
 | 
						|
  }
 | 
						|
  // We get the last null (on the double null terminator) for free since we used
 | 
						|
  // the zero memory flag when we allocated.  All we need to do is fill the
 | 
						|
  // DROPFILES structure
 | 
						|
  DROPFILES* df = (DROPFILES*)GlobalLock(hGlobalMemory);
 | 
						|
  if (!df)
 | 
						|
    return E_FAIL;
 | 
						|
  df->pFiles = sizeof(DROPFILES); //Offset to start of file name string
 | 
						|
  df->fNC    = 0;
 | 
						|
  df->pt.x   = 0;
 | 
						|
  df->pt.y   = 0;
 | 
						|
  df->fWide  = TRUE; // utf-16 chars
 | 
						|
  GlobalUnlock(hGlobalMemory);
 | 
						|
  // Finally fill out the STGMEDIUM struct
 | 
						|
  pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
  pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
 | 
						|
  pSTM->hGlobal = hGlobalMemory;
 | 
						|
  return S_OK;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  uint32_t buffersize = 1;
 | 
						|
  uint32_t alloclen = 0;
 | 
						|
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  if (pFE->cfFormat == CF_TEXT) {
 | 
						|
    nsAutoCString text;
 | 
						|
    for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
      nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
      hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
      if (hr != S_OK) {
 | 
						|
        switch (hr) {
 | 
						|
        case DV_E_FORMATETC:
 | 
						|
          continue;
 | 
						|
        default:
 | 
						|
          return hr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Now we need to pull out the text
 | 
						|
      char* buffer = (char*)GlobalLock(workingmedium.hGlobal);
 | 
						|
      if (buffer == nullptr)
 | 
						|
        return E_FAIL;
 | 
						|
      text = buffer;
 | 
						|
      GlobalUnlock(workingmedium.hGlobal);
 | 
						|
      ReleaseStgMedium(&workingmedium);
 | 
						|
      // Now put the text into our buffer
 | 
						|
      alloclen = text.Length();
 | 
						|
      hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
 | 
						|
                                      GHND);
 | 
						|
      if (hGlobalMemory == nullptr)
 | 
						|
        return E_FAIL;
 | 
						|
      buffer = ((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
      if (!buffer)
 | 
						|
        return E_FAIL;
 | 
						|
      buffer--; // Overwrite the preceding null
 | 
						|
      memcpy(buffer, text.get(), alloclen);
 | 
						|
      GlobalUnlock(hGlobalMemory);
 | 
						|
      buffersize += alloclen;
 | 
						|
    }
 | 
						|
    pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
    pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
 | 
						|
    pSTM->hGlobal = hGlobalMemory;
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
  if (pFE->cfFormat == CF_UNICODETEXT) {
 | 
						|
    buffersize = sizeof(char16_t);
 | 
						|
    nsAutoString text;
 | 
						|
    for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
      nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
      hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
      if (hr != S_OK) {
 | 
						|
        switch (hr) {
 | 
						|
        case DV_E_FORMATETC:
 | 
						|
          continue;
 | 
						|
        default:
 | 
						|
          return hr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Now we need to pull out the text
 | 
						|
      char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
 | 
						|
      if (buffer == nullptr)
 | 
						|
        return E_FAIL;
 | 
						|
      text = buffer;
 | 
						|
      GlobalUnlock(workingmedium.hGlobal);
 | 
						|
      ReleaseStgMedium(&workingmedium);
 | 
						|
      // Now put the text into our buffer
 | 
						|
      alloclen = text.Length() * sizeof(char16_t);
 | 
						|
      hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
 | 
						|
                                      GHND);
 | 
						|
      if (hGlobalMemory == nullptr)
 | 
						|
        return E_FAIL;
 | 
						|
      buffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
      if (!buffer)
 | 
						|
        return E_FAIL;
 | 
						|
      buffer--; // Overwrite the preceding null
 | 
						|
      memcpy(buffer, text.get(), alloclen);
 | 
						|
      GlobalUnlock(hGlobalMemory);
 | 
						|
      buffersize += alloclen;
 | 
						|
    }
 | 
						|
    pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
    pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
 | 
						|
    pSTM->hGlobal = hGlobalMemory;
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  return E_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
 | 
						|
                                                LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  uint32_t buffersize = sizeof(UINT);
 | 
						|
  uint32_t alloclen = sizeof(FILEDESCRIPTOR);
 | 
						|
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
    if (hr != S_OK) {
 | 
						|
      switch (hr) {
 | 
						|
      case DV_E_FORMATETC:
 | 
						|
        continue;
 | 
						|
      default:
 | 
						|
        return hr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Now we need to pull out the filedescriptor
 | 
						|
    FILEDESCRIPTOR* buffer =
 | 
						|
     (FILEDESCRIPTOR*)((char*)GlobalLock(workingmedium.hGlobal) + sizeof(UINT));
 | 
						|
    if (buffer == nullptr)
 | 
						|
      return E_FAIL;
 | 
						|
    hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
    if (hGlobalMemory == nullptr)
 | 
						|
      return E_FAIL;
 | 
						|
    FILEGROUPDESCRIPTOR* realbuffer =
 | 
						|
                                (FILEGROUPDESCRIPTOR*)GlobalLock(hGlobalMemory);
 | 
						|
    if (!realbuffer)
 | 
						|
      return E_FAIL;
 | 
						|
    FILEDESCRIPTOR* copyloc = (FILEDESCRIPTOR*)((char*)realbuffer + buffersize);
 | 
						|
    memcpy(copyloc, buffer, alloclen);
 | 
						|
    realbuffer->cItems++;
 | 
						|
    GlobalUnlock(hGlobalMemory);
 | 
						|
    GlobalUnlock(workingmedium.hGlobal);
 | 
						|
    ReleaseStgMedium(&workingmedium);
 | 
						|
    buffersize += alloclen;
 | 
						|
  }
 | 
						|
  pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
  pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
 | 
						|
  pSTM->hGlobal = hGlobalMemory;
 | 
						|
  return S_OK;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFileContents(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  ULONG num = 0;
 | 
						|
  ULONG numwanted = (pFE->lindex == -1) ? 0 : pFE->lindex;
 | 
						|
  FORMATETC fEtc = *pFE;
 | 
						|
  fEtc.lindex = -1;  // We're lying to the data object so it thinks it's alone
 | 
						|
 | 
						|
  // The key for this data type is to figure out which data object the index
 | 
						|
  // corresponds to and then just pass it along
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    if (dataObj->QueryGetData(&fEtc) != S_OK)
 | 
						|
      continue;
 | 
						|
    if (num == numwanted)
 | 
						|
      return dataObj->GetData(pFE, pSTM);
 | 
						|
    num++;
 | 
						|
  }
 | 
						|
  return DV_E_LINDEX;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE,
 | 
						|
                                                LPSTGMEDIUM pSTM)
 | 
						|
{
 | 
						|
  // There is no way to pass more than one of this, so just find the first data
 | 
						|
  // object that supports it and pass it along
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    if (mDataObjects.ElementAt(i)->QueryGetData(pFE) == S_OK)
 | 
						|
      return mDataObjects.ElementAt(i)->GetData(pFE, pSTM);
 | 
						|
  }
 | 
						|
  return DV_E_FORMATETC;
 | 
						|
}
 |