X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2415cf6725d5cfb11f52d29e5d28dfdaa197b366..07aaf32633ecf18ec3edfbb41793a112914792d0:/src/msw/ole/dataobj.cpp diff --git a/src/msw/ole/dataobj.cpp b/src/msw/ole/dataobj.cpp index b1d1e0e8aa..2c2c44603f 100644 --- a/src/msw/ole/dataobj.cpp +++ b/src/msw/ole/dataobj.cpp @@ -35,6 +35,8 @@ #if wxUSE_OLE && defined(__WIN32__) && !defined(__GNUWIN32_OLD__) +#include "wx/scopedarray.h" +#include "wx/vector.h" #include "wx/msw/private.h" // includes #ifdef __WXWINCE__ @@ -67,6 +69,96 @@ #define GetTymedName(tymed) wxEmptyString #endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL +namespace +{ + +wxDataFormat HtmlFormatFixup(wxDataFormat format) +{ + // Since the HTML format is dynamically registered, the wxDF_HTML + // format does not match the native constant in the way other formats do, + // so for the format checks below to work, we must change the native + // id to the wxDF_HTML constant. + wxChar s_szBuf[256]; + if (::GetClipboardFormatName(format, s_szBuf, WXSIZEOF(s_szBuf))) + { + if (s_szBuf == wxString("HTML Format")) + format = wxDF_HTML; + } + return format; +} + +// helper function for wxCopyStgMedium() +HGLOBAL wxGlobalClone(HGLOBAL hglobIn) +{ + HGLOBAL hglobOut = NULL; + + LPVOID pvIn = GlobalLock(hglobIn); + if (pvIn) + { + SIZE_T cb = GlobalSize(hglobIn); + hglobOut = GlobalAlloc(GMEM_FIXED, cb); + if (hglobOut) + { + CopyMemory(hglobOut, pvIn, cb); + } + GlobalUnlock(hglobIn); + } + + return hglobOut; +} + +// Copies the given STGMEDIUM structure. +// +// This is an local implementation of the function with the same name in +// urlmon.lib but to use that function would require linking with urlmon.lib +// and we don't want to require it, so simple reimplement it here. +HRESULT wxCopyStgMedium(const STGMEDIUM *pmediumIn, STGMEDIUM *pmediumOut) +{ + HRESULT hres = S_OK; + STGMEDIUM stgmOut = *pmediumIn; + + if (pmediumIn->pUnkForRelease == NULL && + !(pmediumIn->tymed & (TYMED_ISTREAM | TYMED_ISTORAGE))) + { + // Object needs to be cloned. + if (pmediumIn->tymed == TYMED_HGLOBAL) + { + stgmOut.hGlobal = wxGlobalClone(pmediumIn->hGlobal); + if (!stgmOut.hGlobal) + { + hres = E_OUTOFMEMORY; + } + } + else + { + hres = DV_E_TYMED; // Don't know how to clone GDI objects. + } + } + + if ( SUCCEEDED(hres) ) + { + switch ( stgmOut.tymed ) + { + case TYMED_ISTREAM: + stgmOut.pstm->AddRef(); + break; + + case TYMED_ISTORAGE: + stgmOut.pstg->AddRef(); + break; + } + + if ( stgmOut.pUnkForRelease ) + stgmOut.pUnkForRelease->AddRef(); + + *pmediumOut = stgmOut; + } + + return hres; +} + +} // anonymous namespace + // ---------------------------------------------------------------------------- // wxIEnumFORMATETC interface implementation // ---------------------------------------------------------------------------- @@ -127,8 +219,122 @@ private: bool m_mustDelete; wxDECLARE_NO_COPY_CLASS(wxIDataObject); + + // The following code is need to be able to store system data the operating + // system is using for it own purposes, e.g. drag images. + + class SystemDataEntry + { + public: + // Ctor takes ownership of the pointers. + SystemDataEntry(FORMATETC *pformatetc, STGMEDIUM *pmedium) + : pformatetc(pformatetc), pmedium(pmedium) + { + } + + ~SystemDataEntry() + { + delete pformatetc; + delete pmedium; + } + + FORMATETC *pformatetc; + STGMEDIUM *pmedium; + }; + typedef wxVector SystemData; + + // get system data specified by the given format + bool GetSystemData(wxDataFormat format, STGMEDIUM*) const; + + // determines if the data object contains system data specified by the given format. + bool HasSystemData(wxDataFormat format) const; + + // save system data + HRESULT SaveSystemData(FORMATETC*, STGMEDIUM*, BOOL fRelease); + + // container for system data + SystemData m_systemData; }; +bool +wxIDataObject::GetSystemData(wxDataFormat format, STGMEDIUM *pmedium) const +{ + for ( SystemData::const_iterator it = m_systemData.begin(); + it != m_systemData.end(); + ++it ) + { + FORMATETC* formatEtc = (*it)->pformatetc; + if ( formatEtc->cfFormat == format ) + { + wxCopyStgMedium((*it)->pmedium, pmedium); + return true; + } + } + + return false; +} + +bool +wxIDataObject::HasSystemData(wxDataFormat format) const +{ + for ( SystemData::const_iterator it = m_systemData.begin(); + it != m_systemData.end(); + ++it ) + { + FORMATETC* formatEtc = (*it)->pformatetc; + if ( formatEtc->cfFormat == format ) + return true; + } + + return false; +} + +// save system data +HRESULT +wxIDataObject::SaveSystemData(FORMATETC *pformatetc, + STGMEDIUM *pmedium, + BOOL fRelease) +{ + if ( pformatetc == NULL || pmedium == NULL ) + return E_INVALIDARG; + + // remove entry if already available + for ( SystemData::iterator it = m_systemData.begin(); + it != m_systemData.end(); + ++it ) + { + if ( pformatetc->tymed & (*it)->pformatetc->tymed && + pformatetc->dwAspect == (*it)->pformatetc->dwAspect && + pformatetc->cfFormat == (*it)->pformatetc->cfFormat ) + { + delete (*it); + m_systemData.erase(it); + break; + } + } + + // create new format/medium + FORMATETC* pnewformatEtc = new FORMATETC; + STGMEDIUM* pnewmedium = new STGMEDIUM; + + wxZeroMemory(*pnewformatEtc); + wxZeroMemory(*pnewmedium); + + // copy format + *pnewformatEtc = *pformatetc; + + // copy or take ownerschip of medium + if ( fRelease ) + *pnewmedium = *pmedium; + else + wxCopyStgMedium(pmedium, pnewmedium); + + // save entry + m_systemData.push_back(new SystemDataEntry(pnewformatEtc, pnewmedium)); + + return S_OK; +} + // ============================================================================ // implementation // ============================================================================ @@ -139,7 +345,7 @@ private: void wxDataFormat::SetId(const wxString& format) { - m_format = (wxDataFormat::NativeFormat)::RegisterClipboardFormat(format.wx_str()); + m_format = (wxDataFormat::NativeFormat)::RegisterClipboardFormat(format.t_str()); if ( !m_format ) { wxLogError(_("Couldn't register clipboard format '%s'."), format); @@ -183,7 +389,10 @@ wxIEnumFORMATETC::wxIEnumFORMATETC(const wxDataFormat *formats, ULONG nCount) m_nCount = nCount; m_formats = new CLIPFORMAT[nCount]; for ( ULONG n = 0; n < nCount; n++ ) { - m_formats[n] = formats[n].GetFormatId(); + if (formats[n].GetFormatId() != wxDF_HTML) + m_formats[n] = formats[n].GetFormatId(); + else + m_formats[n] = ::RegisterClipboardFormat(wxT("HTML Format")); } } @@ -271,6 +480,14 @@ wxIDataObject::wxIDataObject(wxDataObject *pDataObject) wxIDataObject::~wxIDataObject() { + // delete system data + for ( SystemData::iterator it = m_systemData.begin(); + it != m_systemData.end(); + ++it ) + { + delete (*it); + } + if ( m_mustDelete ) { delete m_pDataObject; @@ -290,6 +507,14 @@ STDMETHODIMP wxIDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) // for the bitmaps and metafiles we use the handles instead of global memory // to pass the data wxDataFormat format = (wxDataFormat::NativeFormat)pformatetcIn->cfFormat; + format = HtmlFormatFixup(format); + + // is this system data? + if ( GetSystemData(format, pmedium) ) + { + // pmedium is already filled with corresponding data, so we're ready. + return S_OK; + } switch ( format ) { @@ -426,21 +651,30 @@ STDMETHODIMP wxIDataObject::SetData(FORMATETC *pformatetc, m_pDataObject->SetData(wxDF_ENHMETAFILE, 0, &pmedium->hEnhMetaFile); break; + case TYMED_ISTREAM: + // check if this format is supported + if ( !m_pDataObject->IsSupported(pformatetc->cfFormat, + wxDataObject::Set) ) + { + // As this is not a supported format (content data), assume it + // is system data and save it. + return SaveSystemData(pformatetc, pmedium, fRelease); + } + break; + case TYMED_MFPICT: // fall through - we pass METAFILEPICT through HGLOBAL case TYMED_HGLOBAL: { wxDataFormat format = pformatetc->cfFormat; - // this is quite weird, but for file drag and drop, explorer - // calls our SetData() with the formats we do *not* support! - // - // as we can't fix this bug in explorer (it's a bug because it - // should only use formats returned by EnumFormatEtc), do the - // check here + format = HtmlFormatFixup(format); + + // check if this format is supported if ( !m_pDataObject->IsSupported(format, wxDataObject::Set) ) { - // go away! - return DV_E_FORMATETC; + // As above, assume that unsupported format must be system + // data and just save it. + return SaveSystemData(pformatetc, pmedium, fRelease); } // copy data @@ -459,6 +693,7 @@ STDMETHODIMP wxIDataObject::SetData(FORMATETC *pformatetc, size_t size; switch ( format ) { + case wxDF_HTML: case CF_TEXT: case CF_OEMTEXT: size = strlen((const char *)pBuf); @@ -567,10 +802,19 @@ STDMETHODIMP wxIDataObject::QueryGetData(FORMATETC *pformatetc) // and now check the type of data requested wxDataFormat format = pformatetc->cfFormat; + format = HtmlFormatFixup(format); + if ( m_pDataObject->IsSupportedFormat(format) ) { wxLogTrace(wxTRACE_OleCalls, wxT("wxIDataObject::QueryGetData: %s ok"), wxGetFormatName(format)); } + else if ( HasSystemData(format) ) + { + wxLogTrace(wxTRACE_OleCalls, wxT("wxIDataObject::QueryGetData: %s ok (system data)"), + wxGetFormatName(format)); + // this is system data, so no further checks needed. + return S_OK; + } else { wxLogTrace(wxTRACE_OleCalls, wxT("wxIDataObject::QueryGetData: %s unsupported"), @@ -616,20 +860,31 @@ STDMETHODIMP wxIDataObject::EnumFormatEtc(DWORD dwDir, wxDataObject::Direction dir = dwDir == DATADIR_GET ? wxDataObject::Get : wxDataObject::Set; - ULONG nFormatCount = wx_truncate_cast(ULONG, m_pDataObject->GetFormatCount(dir)); - wxDataFormat format; - wxDataFormat *formats; - formats = nFormatCount == 1 ? &format : new wxDataFormat[nFormatCount]; - m_pDataObject->GetAllFormats(formats, dir); + // format count is total of user specified and system formats. + const size_t ourFormatCount = m_pDataObject->GetFormatCount(dir); + const size_t sysFormatCount = m_systemData.size(); - wxIEnumFORMATETC *pEnum = new wxIEnumFORMATETC(formats, nFormatCount); - pEnum->AddRef(); - *ppenumFormatEtc = pEnum; + const ULONG + nFormatCount = wx_truncate_cast(ULONG, ourFormatCount + sysFormatCount); + + // fill format array with formats ... + wxScopedArray formats(new wxDataFormat[nFormatCount]); - if ( formats != &format ) { - delete [] formats; + // ... from content data (supported formats) + m_pDataObject->GetAllFormats(formats.get(), dir); + + // ... from system data + for ( size_t j = 0; j < sysFormatCount; j++ ) + { + SystemDataEntry* entry = m_systemData[j]; + wxDataFormat& format = formats[ourFormatCount + j]; + format = entry->pformatetc->cfFormat; } + wxIEnumFORMATETC *pEnum = new wxIEnumFORMATETC(formats.get(), nFormatCount); + pEnum->AddRef(); + *ppenumFormatEtc = pEnum; + return S_OK; } @@ -675,7 +930,7 @@ void wxDataObject::SetAutoDelete() ((wxIDataObject *)m_pIDataObject)->SetDeleteFlag(); m_pIDataObject->Release(); - // so that the dtor doesnt' crash + // so that the dtor doesn't crash m_pIDataObject = NULL; } @@ -1051,7 +1306,7 @@ size_t wxFileDataObject::GetDataSize() const if ( wxGetOsVersion() == wxOS_WINDOWS_9X ) { // Win9x always uses ANSI file names and MSLU doesn't help with this - sizeOfChar = sizeof(char); + sizeOfChar = 1; } else { @@ -1061,7 +1316,7 @@ size_t wxFileDataObject::GetDataSize() const static const size_t sizeOfChar = sizeof(wxChar); #endif // wxUSE_UNICODE_MSLU/!wxUSE_UNICODE_MSLU - // inital size of DROPFILES struct + null byte + // initial size of DROPFILES struct + null byte size_t sz = sizeof(DROPFILES) + sizeOfChar; const size_t count = m_filenames.size(); @@ -1070,7 +1325,7 @@ size_t wxFileDataObject::GetDataSize() const // add filename length plus null byte size_t len; #if wxUSE_UNICODE_MSLU - if ( sizeOfChar == sizeof(char) ) + if ( sizeOfChar == 1 ) len = strlen(m_filenames[i].mb_str(*wxConvFileName)); else #endif // wxUSE_UNICODE_MSLU @@ -1107,7 +1362,7 @@ bool wxFileDataObject::GetDataHere(void *WXUNUSED_IN_WINCE(pData)) const pDrop->fWide = wxUSE_UNICODE; #endif - const size_t sizeOfChar = pDrop->fWide ? sizeof(wchar_t) : sizeof(char); + const size_t sizeOfChar = pDrop->fWide ? sizeof(wchar_t) : 1; // set start of filenames list (null separated) BYTE *pbuf = (BYTE *)(pDrop + 1); @@ -1118,7 +1373,7 @@ bool wxFileDataObject::GetDataHere(void *WXUNUSED_IN_WINCE(pData)) const // copy filename to pbuf and add null terminator size_t len; #if wxUSE_UNICODE_MSLU - if ( sizeOfChar == sizeof(char) ) + if ( sizeOfChar == 1 ) { wxCharBuffer buf(m_filenames[i].mb_str(*wxConvFileName)); len = strlen(buf); @@ -1128,7 +1383,7 @@ bool wxFileDataObject::GetDataHere(void *WXUNUSED_IN_WINCE(pData)) const #endif // wxUSE_UNICODE_MSLU { len = m_filenames[i].length(); - memcpy(pbuf, m_filenames[i].wx_str(), len*sizeOfChar); + memcpy(pbuf, m_filenames[i].t_str(), len*sizeOfChar); } pbuf += len*sizeOfChar;