mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			657 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
 | |
| // reserved. Use of this source code is governed by a BSD-style license that
 | |
| // can be found in the LICENSE file.
 | |
| 
 | |
| #include "tests/cefclient/browser/osr_dragdrop_win.h"
 | |
| 
 | |
| #if defined(CEF_USE_ATL)
 | |
| 
 | |
| #include <shellapi.h>
 | |
| #include <shlobj.h>
 | |
| #include <windowsx.h>
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <string>
 | |
| 
 | |
| #include "include/wrapper/cef_helpers.h"
 | |
| #include "tests/cefclient/browser/bytes_write_handler.h"
 | |
| #include "tests/cefclient/browser/resource.h"
 | |
| #include "tests/shared/browser/util_win.h"
 | |
| 
 | |
| namespace client {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| DWORD DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops) {
 | |
|   DWORD effect = DROPEFFECT_NONE;
 | |
|   if (allowed_ops & DRAG_OPERATION_COPY)
 | |
|     effect |= DROPEFFECT_COPY;
 | |
|   if (allowed_ops & DRAG_OPERATION_LINK)
 | |
|     effect |= DROPEFFECT_LINK;
 | |
|   if (allowed_ops & DRAG_OPERATION_MOVE)
 | |
|     effect |= DROPEFFECT_MOVE;
 | |
|   return effect;
 | |
| }
 | |
| 
 | |
| CefRenderHandler::DragOperationsMask DropEffectToDragOperation(DWORD effect) {
 | |
|   DWORD operation = DRAG_OPERATION_NONE;
 | |
|   if (effect & DROPEFFECT_COPY)
 | |
|     operation |= DRAG_OPERATION_COPY;
 | |
|   if (effect & DROPEFFECT_LINK)
 | |
|     operation |= DRAG_OPERATION_LINK;
 | |
|   if (effect & DROPEFFECT_MOVE)
 | |
|     operation |= DRAG_OPERATION_MOVE;
 | |
|   return static_cast<CefRenderHandler::DragOperationsMask>(operation);
 | |
| }
 | |
| 
 | |
| CefMouseEvent ToMouseEvent(POINTL p, DWORD key_state, HWND hWnd) {
 | |
|   CefMouseEvent ev;
 | |
|   POINT screen_point = {p.x, p.y};
 | |
|   ScreenToClient(hWnd, &screen_point);
 | |
|   ev.x = screen_point.x;
 | |
|   ev.y = screen_point.y;
 | |
|   ev.modifiers = GetCefMouseModifiers(key_state);
 | |
|   return ev;
 | |
| }
 | |
| 
 | |
| void GetStorageForBytes(STGMEDIUM* storage, const void* data, size_t bytes) {
 | |
|   HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
 | |
|   if (handle) {
 | |
|     memcpy(handle, data, bytes);
 | |
|   }
 | |
| 
 | |
|   storage->hGlobal = handle;
 | |
|   storage->tymed = TYMED_HGLOBAL;
 | |
|   storage->pUnkForRelease = NULL;
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void GetStorageForString(STGMEDIUM* stgmed, const std::basic_string<T>& data) {
 | |
|   GetStorageForBytes(
 | |
|       stgmed, data.c_str(),
 | |
|       (data.size() + 1) * sizeof(typename std::basic_string<T>::value_type));
 | |
| }
 | |
| 
 | |
| void GetStorageForFileDescriptor(STGMEDIUM* storage,
 | |
|                                  const std::wstring& file_name) {
 | |
|   DCHECK(!file_name.empty());
 | |
|   HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
 | |
| 
 | |
|   FILEGROUPDESCRIPTOR* descriptor =
 | |
|       reinterpret_cast<FILEGROUPDESCRIPTOR*>(hdata);
 | |
|   descriptor->cItems = 1;
 | |
|   descriptor->fgd[0].dwFlags = FD_LINKUI;
 | |
|   wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(),
 | |
|             std::min(file_name.size(), static_cast<size_t>(MAX_PATH - 1u)));
 | |
| 
 | |
|   storage->tymed = TYMED_HGLOBAL;
 | |
|   storage->hGlobal = hdata;
 | |
|   storage->pUnkForRelease = NULL;
 | |
| }
 | |
| 
 | |
| // Helper method for converting from text/html to MS CF_HTML.
 | |
| // Documentation for the CF_HTML format is available at
 | |
| // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
 | |
| std::string HtmlToCFHtml(const std::string& html, const std::string& base_url) {
 | |
|   if (html.empty())
 | |
|     return std::string();
 | |
| 
 | |
| #define MAX_DIGITS 10
 | |
| #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
 | |
| #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
 | |
| #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
 | |
| 
 | |
|   static const char* header =
 | |
|       "Version:0.9\r\n"
 | |
|       "StartHTML:" NUMBER_FORMAT
 | |
|       "\r\n"
 | |
|       "EndHTML:" NUMBER_FORMAT
 | |
|       "\r\n"
 | |
|       "StartFragment:" NUMBER_FORMAT
 | |
|       "\r\n"
 | |
|       "EndFragment:" NUMBER_FORMAT "\r\n";
 | |
|   static const char* source_url_prefix = "SourceURL:";
 | |
| 
 | |
|   static const char* start_markup = "<html>\r\n<body>\r\n<!--StartFragment-->";
 | |
|   static const char* end_markup = "<!--EndFragment-->\r\n</body>\r\n</html>";
 | |
| 
 | |
|   // Calculate offsets
 | |
|   size_t start_html_offset =
 | |
|       strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
 | |
|   if (!base_url.empty()) {
 | |
|     start_html_offset +=
 | |
|         strlen(source_url_prefix) + base_url.length() + 2;  // Add 2 for \r\n.
 | |
|   }
 | |
|   size_t start_fragment_offset = start_html_offset + strlen(start_markup);
 | |
|   size_t end_fragment_offset = start_fragment_offset + html.length();
 | |
|   size_t end_html_offset = end_fragment_offset + strlen(end_markup);
 | |
|   char raw_result[1024];
 | |
|   _snprintf(raw_result, sizeof(1024), header, start_html_offset,
 | |
|             end_html_offset, start_fragment_offset, end_fragment_offset);
 | |
|   std::string result = raw_result;
 | |
|   if (!base_url.empty()) {
 | |
|     result.append(source_url_prefix);
 | |
|     result.append(base_url);
 | |
|     result.append("\r\n");
 | |
|   }
 | |
|   result.append(start_markup);
 | |
|   result.append(html);
 | |
|   result.append(end_markup);
 | |
| 
 | |
| #undef MAX_DIGITS
 | |
| #undef MAKE_NUMBER_FORMAT_1
 | |
| #undef MAKE_NUMBER_FORMAT_2
 | |
| #undef NUMBER_FORMAT
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void CFHtmlExtractMetadata(const std::string& cf_html,
 | |
|                            std::string* base_url,
 | |
|                            size_t* html_start,
 | |
|                            size_t* fragment_start,
 | |
|                            size_t* fragment_end) {
 | |
|   // Obtain base_url if present.
 | |
|   if (base_url) {
 | |
|     static std::string src_url_str("SourceURL:");
 | |
|     size_t line_start = cf_html.find(src_url_str);
 | |
|     if (line_start != std::string::npos) {
 | |
|       size_t src_end = cf_html.find("\n", line_start);
 | |
|       size_t src_start = line_start + src_url_str.length();
 | |
|       if (src_end != std::string::npos && src_start != std::string::npos) {
 | |
|         *base_url = cf_html.substr(src_start, src_end - src_start);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Find the markup between "<!--StartFragment-->" and "<!--EndFragment-->".
 | |
|   // If the comments cannot be found, like copying from OpenOffice Writer,
 | |
|   // we simply fall back to using StartFragment/EndFragment bytecount values
 | |
|   // to determine the fragment indexes.
 | |
|   std::string cf_html_lower = cf_html;
 | |
|   size_t markup_start = cf_html_lower.find("<html", 0);
 | |
|   if (html_start) {
 | |
|     *html_start = markup_start;
 | |
|   }
 | |
|   size_t tag_start = cf_html.find("<!--StartFragment", markup_start);
 | |
|   if (tag_start == std::string::npos) {
 | |
|     static std::string start_fragment_str("StartFragment:");
 | |
|     size_t start_fragment_start = cf_html.find(start_fragment_str);
 | |
|     if (start_fragment_start != std::string::npos) {
 | |
|       *fragment_start =
 | |
|           static_cast<size_t>(atoi(cf_html.c_str() + start_fragment_start +
 | |
|                                    start_fragment_str.length()));
 | |
|     }
 | |
| 
 | |
|     static std::string end_fragment_str("EndFragment:");
 | |
|     size_t end_fragment_start = cf_html.find(end_fragment_str);
 | |
|     if (end_fragment_start != std::string::npos) {
 | |
|       *fragment_end = static_cast<size_t>(atoi(
 | |
|           cf_html.c_str() + end_fragment_start + end_fragment_str.length()));
 | |
|     }
 | |
|   } else {
 | |
|     *fragment_start = cf_html.find('>', tag_start) + 1;
 | |
|     size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
 | |
|     *fragment_end = cf_html.rfind('<', tag_end);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CFHtmlToHtml(const std::string& cf_html,
 | |
|                   std::string* html,
 | |
|                   std::string* base_url) {
 | |
|   size_t frag_start = std::string::npos;
 | |
|   size_t frag_end = std::string::npos;
 | |
| 
 | |
|   CFHtmlExtractMetadata(cf_html, base_url, NULL, &frag_start, &frag_end);
 | |
| 
 | |
|   if (html && frag_start != std::string::npos &&
 | |
|       frag_end != std::string::npos) {
 | |
|     *html = cf_html.substr(frag_start, frag_end - frag_start);
 | |
|   }
 | |
| }
 | |
| 
 | |
| const DWORD moz_url_format = ::RegisterClipboardFormat(L"text/x-moz-url");
 | |
| const DWORD html_format = ::RegisterClipboardFormat(L"HTML Format");
 | |
| const DWORD file_desc_format = ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
 | |
| const DWORD file_contents_format =
 | |
|     ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
 | |
| 
 | |
| bool DragDataToDataObject(CefRefPtr<CefDragData> drag_data,
 | |
|                           IDataObject** data_object) {
 | |
|   const int kMaxDataObjects = 10;
 | |
|   FORMATETC fmtetcs[kMaxDataObjects];
 | |
|   STGMEDIUM stgmeds[kMaxDataObjects];
 | |
|   FORMATETC fmtetc = {0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
 | |
|   int curr_index = 0;
 | |
|   CefString text = drag_data->GetFragmentText();
 | |
|   if (!text.empty()) {
 | |
|     fmtetc.cfFormat = CF_UNICODETEXT;
 | |
|     fmtetcs[curr_index] = fmtetc;
 | |
|     GetStorageForString(&stgmeds[curr_index], text.ToWString());
 | |
|     curr_index++;
 | |
|   }
 | |
|   if (drag_data->IsLink() && !drag_data->GetLinkURL().empty()) {
 | |
|     std::wstring x_moz_url_str = drag_data->GetLinkURL().ToWString();
 | |
|     x_moz_url_str += '\n';
 | |
|     x_moz_url_str += drag_data->GetLinkTitle().ToWString();
 | |
|     fmtetc.cfFormat = moz_url_format;
 | |
|     fmtetcs[curr_index] = fmtetc;
 | |
|     GetStorageForString(&stgmeds[curr_index], x_moz_url_str);
 | |
|     curr_index++;
 | |
|   }
 | |
|   CefString html = drag_data->GetFragmentHtml();
 | |
|   if (!html.empty()) {
 | |
|     CefString base_url = drag_data->GetFragmentBaseURL();
 | |
|     std::string cfhtml = HtmlToCFHtml(html.ToString(), base_url.ToString());
 | |
|     fmtetc.cfFormat = html_format;
 | |
|     fmtetcs[curr_index] = fmtetc;
 | |
|     GetStorageForString(&stgmeds[curr_index], cfhtml);
 | |
|     curr_index++;
 | |
|   }
 | |
| 
 | |
|   size_t bufferSize = drag_data->GetFileContents(NULL);
 | |
|   if (bufferSize) {
 | |
|     CefRefPtr<BytesWriteHandler> handler = new BytesWriteHandler(bufferSize);
 | |
|     CefRefPtr<CefStreamWriter> writer =
 | |
|         CefStreamWriter::CreateForHandler(handler.get());
 | |
|     drag_data->GetFileContents(writer);
 | |
|     DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(bufferSize));
 | |
|     CefString fileName = drag_data->GetFileName();
 | |
|     GetStorageForFileDescriptor(&stgmeds[curr_index], fileName.ToWString());
 | |
|     fmtetc.cfFormat = file_desc_format;
 | |
|     fmtetcs[curr_index] = fmtetc;
 | |
|     curr_index++;
 | |
|     GetStorageForBytes(&stgmeds[curr_index], handler->GetData(),
 | |
|                        handler->GetDataSize());
 | |
|     fmtetc.cfFormat = file_contents_format;
 | |
|     fmtetcs[curr_index] = fmtetc;
 | |
|     curr_index++;
 | |
|   }
 | |
|   DCHECK_LT(curr_index, kMaxDataObjects);
 | |
| 
 | |
|   CComPtr<DataObjectWin> obj =
 | |
|       DataObjectWin::Create(fmtetcs, stgmeds, curr_index);
 | |
|   (*data_object) = obj.Detach();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefDragData> DataObjectToDragData(IDataObject* data_object) {
 | |
|   CefRefPtr<CefDragData> drag_data = CefDragData::Create();
 | |
|   IEnumFORMATETC* enumFormats = nullptr;
 | |
|   HRESULT res = data_object->EnumFormatEtc(DATADIR_GET, &enumFormats);
 | |
|   if (res != S_OK)
 | |
|     return drag_data;
 | |
|   enumFormats->Reset();
 | |
|   const int kCelt = 10;
 | |
| 
 | |
|   ULONG celtFetched;
 | |
|   do {
 | |
|     celtFetched = kCelt;
 | |
|     FORMATETC rgelt[kCelt];
 | |
|     res = enumFormats->Next(kCelt, rgelt, &celtFetched);
 | |
|     for (unsigned i = 0; i < celtFetched; i++) {
 | |
|       CLIPFORMAT format = rgelt[i].cfFormat;
 | |
|       if (!(format == CF_UNICODETEXT || format == CF_TEXT ||
 | |
|             format == moz_url_format || format == html_format ||
 | |
|             format == CF_HDROP) ||
 | |
|           rgelt[i].tymed != TYMED_HGLOBAL)
 | |
|         continue;
 | |
|       STGMEDIUM medium;
 | |
|       if (data_object->GetData(&rgelt[i], &medium) == S_OK) {
 | |
|         if (!medium.hGlobal) {
 | |
|           ReleaseStgMedium(&medium);
 | |
|           continue;
 | |
|         }
 | |
|         void* hGlobal = GlobalLock(medium.hGlobal);
 | |
|         if (!hGlobal) {
 | |
|           ReleaseStgMedium(&medium);
 | |
|           continue;
 | |
|         }
 | |
|         if (format == CF_UNICODETEXT) {
 | |
|           CefString text;
 | |
|           text.FromWString((std::wstring::value_type*)hGlobal);
 | |
|           drag_data->SetFragmentText(text);
 | |
|         } else if (format == CF_TEXT) {
 | |
|           CefString text;
 | |
|           text.FromString((std::string::value_type*)hGlobal);
 | |
|           drag_data->SetFragmentText(text);
 | |
|         } else if (format == moz_url_format) {
 | |
|           std::wstring html((std::wstring::value_type*)hGlobal);
 | |
|           size_t pos = html.rfind('\n');
 | |
|           CefString url(html.substr(0, pos));
 | |
|           CefString title(html.substr(pos + 1));
 | |
|           drag_data->SetLinkURL(url);
 | |
|           drag_data->SetLinkTitle(title);
 | |
|         } else if (format == html_format) {
 | |
|           std::string cf_html((std::string::value_type*)hGlobal);
 | |
|           std::string base_url;
 | |
|           std::string html;
 | |
|           CFHtmlToHtml(cf_html, &html, &base_url);
 | |
|           drag_data->SetFragmentHtml(html);
 | |
|           drag_data->SetFragmentBaseURL(base_url);
 | |
|         }
 | |
|         if (format == CF_HDROP) {
 | |
|           HDROP hdrop = (HDROP)hGlobal;
 | |
|           const int kMaxFilenameLen = 4096;
 | |
|           const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
 | |
|           for (unsigned int x = 0; x < num_files; ++x) {
 | |
|             wchar_t filename[kMaxFilenameLen];
 | |
|             if (!DragQueryFileW(hdrop, x, filename, kMaxFilenameLen))
 | |
|               continue;
 | |
|             WCHAR* name = wcsrchr(filename, '\\');
 | |
|             drag_data->AddFile(filename, (name ? name + 1 : filename));
 | |
|           }
 | |
|         }
 | |
|         if (medium.hGlobal)
 | |
|           GlobalUnlock(medium.hGlobal);
 | |
|         if (format == CF_HDROP)
 | |
|           DragFinish((HDROP)hGlobal);
 | |
|         else
 | |
|           ReleaseStgMedium(&medium);
 | |
|       }
 | |
|     }
 | |
|   } while (res == S_OK);
 | |
|   enumFormats->Release();
 | |
|   return drag_data;
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| CComPtr<DropTargetWin> DropTargetWin::Create(OsrDragEvents* callback,
 | |
|                                              HWND hWnd) {
 | |
|   return CComPtr<DropTargetWin>(new DropTargetWin(callback, hWnd));
 | |
| }
 | |
| 
 | |
| HRESULT DropTargetWin::DragEnter(IDataObject* data_object,
 | |
|                                  DWORD key_state,
 | |
|                                  POINTL cursor_position,
 | |
|                                  DWORD* effect) {
 | |
|   if (!callback_)
 | |
|     return E_UNEXPECTED;
 | |
| 
 | |
|   CefRefPtr<CefDragData> drag_data = current_drag_data_;
 | |
|   if (!drag_data) {
 | |
|     drag_data = DataObjectToDragData(data_object);
 | |
|   }
 | |
|   CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
 | |
|   CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
 | |
|   mask = callback_->OnDragEnter(drag_data, ev, mask);
 | |
|   *effect = DragOperationToDropEffect(mask);
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| CefBrowserHost::DragOperationsMask DropTargetWin::StartDragging(
 | |
|     CefRefPtr<CefBrowser> browser,
 | |
|     CefRefPtr<CefDragData> drag_data,
 | |
|     CefRenderHandler::DragOperationsMask allowed_ops,
 | |
|     int x,
 | |
|     int y) {
 | |
|   CComPtr<IDataObject> dataObject;
 | |
|   DWORD resEffect = DROPEFFECT_NONE;
 | |
|   if (DragDataToDataObject(drag_data, &dataObject)) {
 | |
|     CComPtr<DropSourceWin> dropSource = DropSourceWin::Create();
 | |
|     DWORD effect = DragOperationToDropEffect(allowed_ops);
 | |
|     current_drag_data_ = drag_data->Clone();
 | |
|     current_drag_data_->ResetFileContents();
 | |
|     HRESULT res = DoDragDrop(dataObject, dropSource, effect, &resEffect);
 | |
|     if (res != DRAGDROP_S_DROP)
 | |
|       resEffect = DROPEFFECT_NONE;
 | |
|     current_drag_data_ = nullptr;
 | |
|   }
 | |
|   return DropEffectToDragOperation(resEffect);
 | |
| }
 | |
| 
 | |
| HRESULT DropTargetWin::DragOver(DWORD key_state,
 | |
|                                 POINTL cursor_position,
 | |
|                                 DWORD* effect) {
 | |
|   if (!callback_)
 | |
|     return E_UNEXPECTED;
 | |
|   CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
 | |
|   CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
 | |
|   mask = callback_->OnDragOver(ev, mask);
 | |
|   *effect = DragOperationToDropEffect(mask);
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT DropTargetWin::DragLeave() {
 | |
|   if (!callback_)
 | |
|     return E_UNEXPECTED;
 | |
|   callback_->OnDragLeave();
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT DropTargetWin::Drop(IDataObject* data_object,
 | |
|                             DWORD key_state,
 | |
|                             POINTL cursor_position,
 | |
|                             DWORD* effect) {
 | |
|   if (!callback_)
 | |
|     return E_UNEXPECTED;
 | |
|   CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
 | |
|   CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
 | |
|   mask = callback_->OnDrop(ev, mask);
 | |
|   *effect = DragOperationToDropEffect(mask);
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| CComPtr<DropSourceWin> DropSourceWin::Create() {
 | |
|   return CComPtr<DropSourceWin>(new DropSourceWin());
 | |
| }
 | |
| 
 | |
| HRESULT DropSourceWin::GiveFeedback(DWORD dwEffect) {
 | |
|   return DRAGDROP_S_USEDEFAULTCURSORS;
 | |
| }
 | |
| 
 | |
| HRESULT DropSourceWin::QueryContinueDrag(BOOL fEscapePressed,
 | |
|                                          DWORD grfKeyState) {
 | |
|   if (fEscapePressed) {
 | |
|     return DRAGDROP_S_CANCEL;
 | |
|   }
 | |
| 
 | |
|   if (!(grfKeyState & MK_LBUTTON)) {
 | |
|     return DRAGDROP_S_DROP;
 | |
|   }
 | |
| 
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT DragEnumFormatEtc::CreateEnumFormatEtc(
 | |
|     UINT cfmt,
 | |
|     FORMATETC* afmt,
 | |
|     IEnumFORMATETC** ppEnumFormatEtc) {
 | |
|   if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
 | |
|     return E_INVALIDARG;
 | |
| 
 | |
|   *ppEnumFormatEtc = new DragEnumFormatEtc(afmt, cfmt);
 | |
| 
 | |
|   return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY;
 | |
| }
 | |
| 
 | |
| HRESULT DragEnumFormatEtc::Next(ULONG celt,
 | |
|                                 FORMATETC* pFormatEtc,
 | |
|                                 ULONG* pceltFetched) {
 | |
|   ULONG copied = 0;
 | |
| 
 | |
|   // copy the FORMATETC structures into the caller's buffer
 | |
|   while (m_nIndex < m_nNumFormats && copied < celt) {
 | |
|     DeepCopyFormatEtc(&pFormatEtc[copied], &m_pFormatEtc[m_nIndex]);
 | |
|     copied++;
 | |
|     m_nIndex++;
 | |
|   }
 | |
| 
 | |
|   // store result
 | |
|   if (pceltFetched != 0)
 | |
|     *pceltFetched = copied;
 | |
| 
 | |
|   // did we copy all that was requested?
 | |
|   return (copied == celt) ? S_OK : S_FALSE;
 | |
| }
 | |
| HRESULT DragEnumFormatEtc::Skip(ULONG celt) {
 | |
|   m_nIndex += celt;
 | |
|   return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
 | |
| }
 | |
| HRESULT DragEnumFormatEtc::Reset(void) {
 | |
|   m_nIndex = 0;
 | |
|   return S_OK;
 | |
| }
 | |
| HRESULT DragEnumFormatEtc::Clone(IEnumFORMATETC** ppEnumFormatEtc) {
 | |
|   HRESULT hResult;
 | |
| 
 | |
|   // make a duplicate enumerator
 | |
|   hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
 | |
| 
 | |
|   if (hResult == S_OK) {
 | |
|     // manually set the index state
 | |
|     reinterpret_cast<DragEnumFormatEtc*>(*ppEnumFormatEtc)->m_nIndex = m_nIndex;
 | |
|   }
 | |
| 
 | |
|   return hResult;
 | |
| }
 | |
| 
 | |
| DragEnumFormatEtc::DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats) {
 | |
|   AddRef();
 | |
| 
 | |
|   m_nIndex = 0;
 | |
|   m_nNumFormats = nNumFormats;
 | |
|   m_pFormatEtc = new FORMATETC[nNumFormats];
 | |
| 
 | |
|   // make a new copy of each FORMATETC structure
 | |
|   for (int i = 0; i < nNumFormats; i++) {
 | |
|     DeepCopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
 | |
|   }
 | |
| }
 | |
| DragEnumFormatEtc::~DragEnumFormatEtc() {
 | |
|   // first free any DVTARGETDEVICE structures
 | |
|   for (ULONG i = 0; i < m_nNumFormats; i++) {
 | |
|     if (m_pFormatEtc[i].ptd)
 | |
|       CoTaskMemFree(m_pFormatEtc[i].ptd);
 | |
|   }
 | |
| 
 | |
|   // now free the main array
 | |
|   delete[] m_pFormatEtc;
 | |
| }
 | |
| 
 | |
| void DragEnumFormatEtc::DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) {
 | |
|   // copy the source FORMATETC into dest
 | |
|   *dest = *source;
 | |
|   if (source->ptd) {
 | |
|     // allocate memory for the DVTARGETDEVICE if necessary
 | |
|     dest->ptd = reinterpret_cast<DVTARGETDEVICE*>(
 | |
|         CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
 | |
| 
 | |
|     // copy the contents of the source DVTARGETDEVICE into dest->ptd
 | |
|     *(dest->ptd) = *(source->ptd);
 | |
|   }
 | |
| }
 | |
| 
 | |
| CComPtr<DataObjectWin> DataObjectWin::Create(FORMATETC* fmtetc,
 | |
|                                              STGMEDIUM* stgmed,
 | |
|                                              int count) {
 | |
|   return CComPtr<DataObjectWin>(new DataObjectWin(fmtetc, stgmed, count));
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pmedium) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::QueryGetData(FORMATETC* pFormatEtc) {
 | |
|   return (LookupFormatEtc(pFormatEtc) == -1) ? DV_E_FORMATETC : S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::GetCanonicalFormatEtc(FORMATETC* pFormatEct,
 | |
|                                              FORMATETC* pFormatEtcOut) {
 | |
|   pFormatEtcOut->ptd = NULL;
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::SetData(FORMATETC* pFormatEtc,
 | |
|                                STGMEDIUM* pMedium,
 | |
|                                BOOL fRelease) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::DAdvise(FORMATETC* pFormatEtc,
 | |
|                                DWORD advf,
 | |
|                                IAdviseSink*,
 | |
|                                DWORD*) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::DUnadvise(DWORD dwConnection) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::EnumFormatEtc(DWORD dwDirection,
 | |
|                                      IEnumFORMATETC** ppEnumFormatEtc) {
 | |
|   return DragEnumFormatEtc::CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc,
 | |
|                                                 ppEnumFormatEtc);
 | |
| }
 | |
| 
 | |
| HRESULT DataObjectWin::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) {
 | |
|   int idx;
 | |
| 
 | |
|   // try to match the specified FORMATETC with one of our supported formats
 | |
|   if ((idx = LookupFormatEtc(pFormatEtc)) == -1)
 | |
|     return DV_E_FORMATETC;
 | |
| 
 | |
|   // found a match - transfer data into supplied storage medium
 | |
|   pMedium->tymed = m_pFormatEtc[idx].tymed;
 | |
|   pMedium->pUnkForRelease = 0;
 | |
| 
 | |
|   // copy the data into the caller's storage medium
 | |
|   switch (m_pFormatEtc[idx].tymed) {
 | |
|     case TYMED_HGLOBAL:
 | |
|       pMedium->hGlobal = DupGlobalMem(m_pStgMedium[idx].hGlobal);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       return DV_E_FORMATETC;
 | |
|   }
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HGLOBAL DataObjectWin::DupGlobalMem(HGLOBAL hMem) {
 | |
|   DWORD len = GlobalSize(hMem);
 | |
|   PVOID source = GlobalLock(hMem);
 | |
|   PVOID dest = GlobalAlloc(GMEM_FIXED, len);
 | |
| 
 | |
|   memcpy(dest, source, len);
 | |
|   GlobalUnlock(hMem);
 | |
|   return dest;
 | |
| }
 | |
| 
 | |
| int DataObjectWin::LookupFormatEtc(FORMATETC* pFormatEtc) {
 | |
|   // check each of our formats in turn to see if one matches
 | |
|   for (int i = 0; i < m_nNumFormats; i++) {
 | |
|     if ((m_pFormatEtc[i].tymed & pFormatEtc->tymed) &&
 | |
|         m_pFormatEtc[i].cfFormat == pFormatEtc->cfFormat &&
 | |
|         m_pFormatEtc[i].dwAspect == pFormatEtc->dwAspect) {
 | |
|       // return index of stored format
 | |
|       return i;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // error, format not found
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| DataObjectWin::DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count)
 | |
|     : ref_count_(0) {
 | |
|   m_nNumFormats = count;
 | |
| 
 | |
|   m_pFormatEtc = new FORMATETC[count];
 | |
|   m_pStgMedium = new STGMEDIUM[count];
 | |
| 
 | |
|   for (int i = 0; i < count; i++) {
 | |
|     m_pFormatEtc[i] = fmtetc[i];
 | |
|     m_pStgMedium[i] = stgmed[i];
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace client
 | |
| 
 | |
| #endif  // defined(CEF_USE_ATL)
 |