Fixed issue #2591: Enable accent coloring for search term matches in log messages
[TortoiseGit.git] / src / Git / GitDataObject.cpp
blobcf3a568bff3ccb481eb800c415a628f72bde5927
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016-2017 - TortoiseGit
4 // Copyright (C) 2007-2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "GitDataObject.h"
21 #include "Git.h"
22 #include "UnicodeUtils.h"
23 #include "PathUtils.h"
24 #include "TempFile.h"
25 #include "StringUtils.h"
26 #include <strsafe.h>
28 CLIPFORMAT CF_FILECONTENTS = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILECONTENTS);
29 CLIPFORMAT CF_FILEDESCRIPTOR = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
30 CLIPFORMAT CF_PREFERREDDROPEFFECT = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
31 CLIPFORMAT CF_INETURL = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_INETURL);
32 CLIPFORMAT CF_SHELLURL = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLURL);
33 CLIPFORMAT CF_FILE_ATTRIBUTES_ARRAY = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILE_ATTRIBUTES_ARRAY);
35 GitDataObject::GitDataObject(const CTGitPathList& gitpaths, const CGitHash& rev, int stripLength)
36 : m_gitPaths(gitpaths)
37 , m_revision(rev)
38 , m_bInOperation(FALSE)
39 , m_bIsAsync(TRUE)
40 , m_cRefCount(0)
41 , m_iStripLength(stripLength)
42 , m_containsExistingFiles(false)
44 ASSERT((m_revision.IsEmpty() && m_iStripLength == -1) || (!m_revision.IsEmpty() && m_iStripLength >= -1)); // m_iStripLength only possible if rev is set
45 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
47 if ((m_gitPaths[i].m_Action == 0 || (m_gitPaths[i].m_Action & ~(CTGitPath::LOGACTIONS_MISSING | CTGitPath::LOGACTIONS_DELETED))) && !m_gitPaths[i].IsDirectory())
49 m_containsExistingFiles = true;
50 break;
55 GitDataObject::~GitDataObject()
57 for (size_t i = 0; i < m_vecStgMedium.size(); ++i)
59 ReleaseStgMedium(m_vecStgMedium[i]);
60 delete m_vecStgMedium[i];
63 for (size_t j = 0; j < m_vecFormatEtc.size(); ++j)
64 delete m_vecFormatEtc[j];
67 //////////////////////////////////////////////////////////////////////////
68 // IUnknown
69 //////////////////////////////////////////////////////////////////////////
71 STDMETHODIMP GitDataObject::QueryInterface(REFIID riid, void** ppvObject)
73 if (!ppvObject)
74 return E_POINTER;
75 *ppvObject = NULL;
76 if (IsEqualIID(IID_IUnknown, riid) || IsEqualIID(IID_IDataObject, riid))
77 *ppvObject = static_cast<IDataObject*>(this);
78 else if (IsEqualIID(riid, IID_IDataObjectAsyncCapability))
79 *ppvObject = static_cast<IDataObjectAsyncCapability*>(this);
80 else
81 return E_NOINTERFACE;
83 AddRef();
84 return S_OK;
87 STDMETHODIMP_(ULONG) GitDataObject::AddRef(void)
89 return ++m_cRefCount;
92 STDMETHODIMP_(ULONG) GitDataObject::Release(void)
94 --m_cRefCount;
95 if (m_cRefCount == 0)
97 delete this;
98 return 0;
100 return m_cRefCount;
103 //////////////////////////////////////////////////////////////////////////
104 // IDataObject
105 //////////////////////////////////////////////////////////////////////////
106 STDMETHODIMP GitDataObject::GetData(FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
108 if (!pformatetcIn)
109 return E_INVALIDARG;
110 if (!pmedium)
111 return E_POINTER;
112 pmedium->hGlobal = nullptr;
114 if ((pformatetcIn->tymed & TYMED_ISTREAM) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && (pformatetcIn->cfFormat == CF_FILECONTENTS))
116 // supports the IStream format.
117 // The lindex param is the index of the file to return
118 CString filepath;
119 IStream* pIStream = nullptr;
121 // Note: we don't get called for directories since those are simply created and don't
122 // need to be fetched.
124 // Note2: It would be really nice if we could get a stream from the subversion library
125 // from where we could read the file contents. But the Subversion lib is not implemented
126 // to *read* from a remote stream but so that the library *writes* to a stream we pass.
127 // Since we can't get such a read stream, we have to fetch the file in whole first to
128 // a temp location and then pass the shell an IStream for that temp file.
130 if (m_revision.IsEmpty())
132 if ((pformatetcIn->lindex >= 0) && (pformatetcIn->lindex < (LONG)m_allPaths.size()))
133 filepath = g_Git.CombinePath(m_allPaths[pformatetcIn->lindex]);
135 else
137 filepath = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
138 if ((pformatetcIn->lindex >= 0) && (pformatetcIn->lindex < (LONG)m_allPaths.size()))
140 if (g_Git.GetOneFile(m_revision.ToString(), m_allPaths[pformatetcIn->lindex], filepath))
142 DeleteFile(filepath);
143 return STG_E_ACCESSDENIED;
148 HRESULT res = SHCreateStreamOnFileEx(filepath, STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pIStream);
149 if (res == S_OK)
151 // http://blogs.msdn.com/b/oldnewthing/archive/2014/09/18/10558763.aspx
152 LARGE_INTEGER liZero = { 0, 0 };
153 pIStream->Seek(liZero, STREAM_SEEK_END, nullptr);
155 pmedium->pstm = pIStream;
156 pmedium->tymed = TYMED_ISTREAM;
157 return S_OK;
159 return res;
161 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && (pformatetcIn->cfFormat == CF_FILEDESCRIPTOR))
163 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
165 if (m_gitPaths[i].m_Action & (CTGitPath::LOGACTIONS_MISSING | CTGitPath::LOGACTIONS_DELETED) || m_gitPaths[i].IsDirectory())
166 continue;
167 m_allPaths.push_back(m_gitPaths[i]);
170 size_t dataSize = sizeof(FILEGROUPDESCRIPTOR) + ((max(1, m_allPaths.size()) - 1) * sizeof(FILEDESCRIPTOR));
171 HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, dataSize);
173 FILEGROUPDESCRIPTOR* files = (FILEGROUPDESCRIPTOR*)GlobalLock(data);
174 files->cItems = static_cast<UINT>(m_allPaths.size());
175 int index = 0;
176 for (auto it = m_allPaths.cbegin(); it != m_allPaths.cend(); ++it)
178 CString temp(m_iStripLength > 0 ? it->GetWinPathString().Mid(m_iStripLength + 1) : (m_iStripLength == 0 ? it->GetWinPathString() : it->GetUIFileOrDirectoryName()));
179 if (temp.GetLength() < MAX_PATH)
180 wcscpy_s(files->fgd[index].cFileName, (LPCTSTR)temp);
181 else
183 files->cItems--;
184 continue;
186 files->fgd[index].dwFlags = FD_ATTRIBUTES | FD_PROGRESSUI | FD_FILESIZE | FD_LINKUI;
187 files->fgd[index].dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
189 // Always set the file size to 0 even if we 'know' the file size (infodata.size64).
190 // Because for text files, the file size is too low due to the EOLs getting converted
191 // to CRLF (from LF as stored in the repository). And a too low file size set here
192 // prevents the shell from reading the full file later - it only reads the stream up
193 // to the number of bytes specified here. Which means we would end up with a truncated
194 // text file (binary files are still ok though).
195 files->fgd[index].nFileSizeLow = 0;
196 files->fgd[index].nFileSizeHigh = 0;
198 ++index;
201 GlobalUnlock(data);
203 pmedium->hGlobal = data;
204 pmedium->tymed = TYMED_HGLOBAL;
205 return S_OK;
207 // handling CF_PREFERREDDROPEFFECT is necessary to tell the shell that it should *not* ask for the
208 // CF_FILEDESCRIPTOR until the drop actually occurs. If we don't handle CF_PREFERREDDROPEFFECT, the shell
209 // will ask for the file descriptor for every object (file/folder) the mouse pointer hovers over and is
210 // a potential drop target.
211 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->cfFormat == CF_PREFERREDDROPEFFECT))
213 HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, sizeof(DWORD));
214 if (!data)
215 return E_OUTOFMEMORY;
216 DWORD* effect = (DWORD*)GlobalLock(data);
217 if (!effect)
218 return E_OUTOFMEMORY;
219 (*effect) = DROPEFFECT_COPY;
220 GlobalUnlock(data);
221 pmedium->hGlobal = data;
222 pmedium->tymed = TYMED_HGLOBAL;
223 return S_OK;
225 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && (pformatetcIn->cfFormat == CF_TEXT))
227 // caller wants text
228 // create the string from the path list
229 CString text;
230 if (!m_gitPaths.IsEmpty())
232 // create a single string where the URLs are separated by newlines
233 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
235 text += m_gitPaths[i].GetWinPathString();
236 text += L"\r\n";
239 CStringA texta = CUnicodeUtils::GetUTF8(text);
240 pmedium->tymed = TYMED_HGLOBAL;
241 pmedium->hGlobal = GlobalAlloc(GHND, (texta.GetLength() + 1) * sizeof(char));
242 if (pmedium->hGlobal)
244 char* pMem = (char*)GlobalLock(pmedium->hGlobal);
245 strcpy_s(pMem, texta.GetLength() + 1, (LPCSTR)texta);
246 GlobalUnlock(pmedium->hGlobal);
248 pmedium->pUnkForRelease = nullptr;
249 return S_OK;
251 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && ((pformatetcIn->cfFormat == CF_UNICODETEXT) || (pformatetcIn->cfFormat == CF_INETURL) || (pformatetcIn->cfFormat == CF_SHELLURL)))
253 // caller wants Unicode text
254 // create the string from the path list
255 CString text;
256 if (!m_gitPaths.IsEmpty())
258 // create a single string where the URLs are separated by newlines
259 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
261 if (pformatetcIn->cfFormat == CF_UNICODETEXT)
262 text += m_gitPaths[i].GetWinPathString();
263 else
264 text += g_Git.CombinePath(m_gitPaths[i]);
265 text += L"\r\n";
268 pmedium->tymed = TYMED_HGLOBAL;
269 pmedium->hGlobal = GlobalAlloc(GHND, (text.GetLength() + 1) * sizeof(TCHAR));
270 if (pmedium->hGlobal)
272 TCHAR* pMem = (TCHAR*)GlobalLock(pmedium->hGlobal);
273 wcscpy_s(pMem, text.GetLength() + 1, (LPCTSTR)text);
274 GlobalUnlock(pmedium->hGlobal);
276 pmedium->pUnkForRelease = nullptr;
277 return S_OK;
279 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && (pformatetcIn->cfFormat == CF_HDROP))
281 int nLength = 0;
283 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
285 if (m_gitPaths[i].m_Action & (CTGitPath::LOGACTIONS_MISSING | CTGitPath::LOGACTIONS_DELETED) || m_gitPaths[i].IsDirectory())
286 continue;
288 nLength += g_Git.CombinePath(m_gitPaths[i]).GetLength();
289 nLength += 1; // '\0' separator
292 int nBufferSize = sizeof(DROPFILES) + (nLength + 1) * sizeof(TCHAR);
293 auto pBuffer = std::make_unique<char[]>(nBufferSize);
294 SecureZeroMemory(pBuffer.get(), nBufferSize);
296 DROPFILES* df = (DROPFILES*)pBuffer.get();
297 df->pFiles = sizeof(DROPFILES);
298 df->fWide = 1;
300 TCHAR* pFilenames = (TCHAR*)(pBuffer.get() + sizeof(DROPFILES));
301 TCHAR* pCurrentFilename = pFilenames;
303 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
305 if (m_gitPaths[i].m_Action & (CTGitPath::LOGACTIONS_MISSING | CTGitPath::LOGACTIONS_DELETED) || m_gitPaths[i].IsDirectory())
306 continue;
307 CString str = g_Git.CombinePath(m_gitPaths[i]);
308 wcscpy_s(pCurrentFilename, str.GetLength() + 1, (LPCWSTR)str);
309 pCurrentFilename += str.GetLength();
310 *pCurrentFilename = '\0'; // separator between file names
311 pCurrentFilename++;
313 *pCurrentFilename = '\0'; // terminate array
315 pmedium->tymed = TYMED_HGLOBAL;
316 pmedium->hGlobal = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, nBufferSize);
317 if (pmedium->hGlobal)
319 LPVOID pMem = ::GlobalLock(pmedium->hGlobal);
320 if (pMem)
321 memcpy(pMem, pBuffer.get(), nBufferSize);
322 GlobalUnlock(pmedium->hGlobal);
324 pmedium->pUnkForRelease = nullptr;
325 return S_OK;
327 else if ((pformatetcIn->tymed & TYMED_HGLOBAL) && (pformatetcIn->dwAspect == DVASPECT_CONTENT) && (pformatetcIn->cfFormat == CF_FILE_ATTRIBUTES_ARRAY))
329 int nBufferSize = sizeof(FILE_ATTRIBUTES_ARRAY) + m_gitPaths.GetCount() * sizeof(DWORD);
330 auto pBuffer = std::make_unique<char[]>(nBufferSize);
331 SecureZeroMemory(pBuffer.get(), nBufferSize);
333 FILE_ATTRIBUTES_ARRAY* cf = (FILE_ATTRIBUTES_ARRAY*)pBuffer.get();
334 cf->cItems = m_gitPaths.GetCount();
335 cf->dwProductFileAttributes = DWORD_MAX;
336 cf->dwSumFileAttributes = 0;
337 for (int i = 0; i < m_gitPaths.GetCount(); ++i)
339 DWORD fileattribs = FILE_ATTRIBUTE_NORMAL;
340 cf->rgdwFileAttributes[i] = fileattribs;
341 cf->dwProductFileAttributes &= fileattribs;
342 cf->dwSumFileAttributes |= fileattribs;
345 pmedium->tymed = TYMED_HGLOBAL;
346 pmedium->hGlobal = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, nBufferSize);
347 if (pmedium->hGlobal)
349 LPVOID pMem = ::GlobalLock(pmedium->hGlobal);
350 if (pMem)
351 memcpy(pMem, pBuffer.get(), nBufferSize);
352 GlobalUnlock(pmedium->hGlobal);
354 pmedium->pUnkForRelease = nullptr;
355 return S_OK;
358 for (size_t i = 0; i < m_vecFormatEtc.size(); ++i)
360 if ((pformatetcIn->tymed == m_vecFormatEtc[i]->tymed) &&
361 (pformatetcIn->dwAspect == m_vecFormatEtc[i]->dwAspect) &&
362 (pformatetcIn->cfFormat == m_vecFormatEtc[i]->cfFormat))
364 CopyMedium(pmedium, m_vecStgMedium[i], m_vecFormatEtc[i]);
365 return S_OK;
369 return DV_E_FORMATETC;
372 STDMETHODIMP GitDataObject::GetDataHere(FORMATETC* /*pformatetc*/, STGMEDIUM* /*pmedium*/)
374 return E_NOTIMPL;
377 STDMETHODIMP GitDataObject::QueryGetData(FORMATETC* pformatetc)
379 if (!pformatetc)
380 return E_INVALIDARG;
382 if (!(DVASPECT_CONTENT & pformatetc->dwAspect))
383 return DV_E_DVASPECT;
385 if ((pformatetc->tymed & TYMED_ISTREAM) &&
386 (pformatetc->dwAspect == DVASPECT_CONTENT) &&
387 (pformatetc->cfFormat == CF_FILECONTENTS) &&
388 m_containsExistingFiles)
390 return S_OK;
392 if ((pformatetc->tymed & TYMED_HGLOBAL) &&
393 (pformatetc->dwAspect == DVASPECT_CONTENT) &&
394 ((pformatetc->cfFormat == CF_TEXT) || (pformatetc->cfFormat == CF_UNICODETEXT) || (pformatetc->cfFormat == CF_PREFERREDDROPEFFECT)))
396 return S_OK;
398 if ((pformatetc->tymed & TYMED_HGLOBAL) &&
399 (pformatetc->dwAspect == DVASPECT_CONTENT) &&
400 (pformatetc->cfFormat == CF_FILEDESCRIPTOR) &&
401 !m_revision.IsEmpty() &&
402 m_containsExistingFiles)
404 return S_OK;
406 if ((pformatetc->tymed & TYMED_HGLOBAL) &&
407 (pformatetc->dwAspect == DVASPECT_CONTENT) &&
408 ((pformatetc->cfFormat == CF_HDROP) || (pformatetc->cfFormat == CF_INETURL) || (pformatetc->cfFormat == CF_SHELLURL)) &&
409 m_revision.IsEmpty() &&
410 m_containsExistingFiles)
412 return S_OK;
414 if ((pformatetc->tymed & TYMED_HGLOBAL) &&
415 (pformatetc->dwAspect == DVASPECT_CONTENT) &&
416 (pformatetc->cfFormat == CF_FILE_ATTRIBUTES_ARRAY) &&
417 m_containsExistingFiles)
419 return S_OK;
422 for (size_t i = 0; i < m_vecFormatEtc.size(); ++i)
424 if ((pformatetc->tymed == m_vecFormatEtc[i]->tymed) &&
425 (pformatetc->dwAspect == m_vecFormatEtc[i]->dwAspect) &&
426 (pformatetc->cfFormat == m_vecFormatEtc[i]->cfFormat))
427 return S_OK;
430 return DV_E_TYMED;
433 STDMETHODIMP GitDataObject::GetCanonicalFormatEtc(FORMATETC* /*pformatectIn*/, FORMATETC* pformatetcOut)
435 if (!pformatetcOut)
436 return E_INVALIDARG;
437 return DATA_S_SAMEFORMATETC;
440 STDMETHODIMP GitDataObject::SetData(FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
442 if (!pformatetc || !pmedium)
443 return E_INVALIDARG;
445 FORMATETC* fetc = new (std::nothrow) FORMATETC;
446 STGMEDIUM* pStgMed = new (std::nothrow) STGMEDIUM;
448 if (!fetc || !pStgMed)
450 delete fetc;
451 delete pStgMed;
452 return E_OUTOFMEMORY;
454 SecureZeroMemory(fetc, sizeof(FORMATETC));
455 SecureZeroMemory(pStgMed, sizeof(STGMEDIUM));
457 // do we already store this format?
458 for (size_t i = 0; i < m_vecFormatEtc.size(); ++i)
460 if ((pformatetc->tymed == m_vecFormatEtc[i]->tymed) &&
461 (pformatetc->dwAspect == m_vecFormatEtc[i]->dwAspect) &&
462 (pformatetc->cfFormat == m_vecFormatEtc[i]->cfFormat))
464 // yes, this format is already in our object:
465 // we have to replace the existing format. To do that, we
466 // remove the format we already have
467 delete m_vecFormatEtc[i];
468 m_vecFormatEtc.erase(m_vecFormatEtc.begin() + i);
469 ReleaseStgMedium(m_vecStgMedium[i]);
470 delete m_vecStgMedium[i];
471 m_vecStgMedium.erase(m_vecStgMedium.begin() + i);
472 break;
476 *fetc = *pformatetc;
477 m_vecFormatEtc.push_back(fetc);
479 if (fRelease)
480 *pStgMed = *pmedium;
481 else
482 CopyMedium(pStgMed, pmedium, pformatetc);
483 m_vecStgMedium.push_back(pStgMed);
485 return S_OK;
488 STDMETHODIMP GitDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
490 if (!ppenumFormatEtc)
491 return E_POINTER;
493 *ppenumFormatEtc = nullptr;
494 switch (dwDirection)
496 case DATADIR_GET:
497 *ppenumFormatEtc = new (std::nothrow) CGitEnumFormatEtc(m_vecFormatEtc, m_revision.IsEmpty(), m_containsExistingFiles);
498 if (!*ppenumFormatEtc)
499 return E_OUTOFMEMORY;
500 (*ppenumFormatEtc)->AddRef();
501 break;
502 default:
503 return E_NOTIMPL;
505 return S_OK;
508 STDMETHODIMP GitDataObject::DAdvise(FORMATETC* /*pformatetc*/, DWORD /*advf*/, IAdviseSink* /*pAdvSink*/, DWORD* /*pdwConnection*/)
510 return OLE_E_ADVISENOTSUPPORTED;
513 STDMETHODIMP GitDataObject::DUnadvise(DWORD /*dwConnection*/)
515 return E_NOTIMPL;
518 HRESULT STDMETHODCALLTYPE GitDataObject::EnumDAdvise(IEnumSTATDATA** /*ppenumAdvise*/)
520 return OLE_E_ADVISENOTSUPPORTED;
523 void GitDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
525 switch (pMedSrc->tymed)
527 case TYMED_HGLOBAL:
528 pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, 0);
529 break;
530 case TYMED_GDI:
531 pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, 0);
532 break;
533 case TYMED_MFPICT:
534 pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, 0);
535 break;
536 case TYMED_ENHMF:
537 pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, 0);
538 break;
539 case TYMED_FILE:
540 pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, 0);
541 break;
542 case TYMED_ISTREAM:
543 pMedDest->pstm = pMedSrc->pstm;
544 pMedSrc->pstm->AddRef();
545 break;
546 case TYMED_ISTORAGE:
547 pMedDest->pstg = pMedSrc->pstg;
548 pMedSrc->pstg->AddRef();
549 break;
550 case TYMED_NULL:
551 default:
552 break;
554 pMedDest->tymed = pMedSrc->tymed;
555 pMedDest->pUnkForRelease = nullptr;
556 if (pMedSrc->pUnkForRelease)
558 pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
559 pMedSrc->pUnkForRelease->AddRef();
564 //////////////////////////////////////////////////////////////////////////
565 // IAsyncOperation
566 //////////////////////////////////////////////////////////////////////////
567 HRESULT STDMETHODCALLTYPE GitDataObject::SetAsyncMode(BOOL fDoOpAsync)
569 m_bIsAsync = fDoOpAsync;
570 return S_OK;
573 HRESULT STDMETHODCALLTYPE GitDataObject::GetAsyncMode(BOOL* pfIsOpAsync)
575 if (!pfIsOpAsync)
576 return E_FAIL;
578 *pfIsOpAsync = m_bIsAsync;
580 return S_OK;
583 HRESULT STDMETHODCALLTYPE GitDataObject::StartOperation(IBindCtx* /*pbcReserved*/)
585 m_bInOperation = TRUE;
586 return S_OK;
589 HRESULT STDMETHODCALLTYPE GitDataObject::InOperation(BOOL* pfInAsyncOp)
591 if (!pfInAsyncOp)
592 return E_FAIL;
594 *pfInAsyncOp = m_bInOperation;
596 return S_OK;
599 HRESULT STDMETHODCALLTYPE GitDataObject::EndOperation(HRESULT /*hResult*/, IBindCtx* /*pbcReserved*/, DWORD /*dwEffects*/)
601 m_bInOperation = FALSE;
602 return S_OK;
605 HRESULT GitDataObject::SetDropDescription(DROPIMAGETYPE image, LPCTSTR format, LPCTSTR insert)
607 if (!format || !insert)
608 return E_INVALIDARG;
610 FORMATETC fetc = { 0 };
611 fetc.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DROPDESCRIPTION);
612 fetc.dwAspect = DVASPECT_CONTENT;
613 fetc.lindex = -1;
614 fetc.tymed = TYMED_HGLOBAL;
616 STGMEDIUM medium = { 0 };
617 medium.hGlobal = GlobalAlloc(GHND, sizeof(DROPDESCRIPTION));
618 if (medium.hGlobal == 0)
619 return E_OUTOFMEMORY;
621 DROPDESCRIPTION* pDropDescription = (DROPDESCRIPTION*)GlobalLock(medium.hGlobal);
622 if (!pDropDescription)
623 return E_FAIL;
624 StringCchCopy(pDropDescription->szInsert, _countof(pDropDescription->szInsert), insert);
625 StringCchCopy(pDropDescription->szMessage, _countof(pDropDescription->szMessage), format);
626 pDropDescription->type = image;
627 GlobalUnlock(medium.hGlobal);
628 return SetData(&fetc, &medium, TRUE);
631 void CGitEnumFormatEtc::Init(bool localonly, bool containsExistingFiles)
633 int index = 0;
634 m_formats[index].cfFormat = CF_UNICODETEXT;
635 m_formats[index].dwAspect = DVASPECT_CONTENT;
636 m_formats[index].lindex = -1;
637 m_formats[index].ptd = nullptr;
638 m_formats[index].tymed = TYMED_HGLOBAL;
639 index++;
641 m_formats[index].cfFormat = CF_TEXT;
642 m_formats[index].dwAspect = DVASPECT_CONTENT;
643 m_formats[index].lindex = -1;
644 m_formats[index].ptd = nullptr;
645 m_formats[index].tymed = TYMED_HGLOBAL;
646 index++;
648 m_formats[index].cfFormat = CF_PREFERREDDROPEFFECT;
649 m_formats[index].dwAspect = DVASPECT_CONTENT;
650 m_formats[index].lindex = -1;
651 m_formats[index].ptd = nullptr;
652 m_formats[index].tymed = TYMED_HGLOBAL;
653 index++;
655 if (containsExistingFiles && localonly)
657 m_formats[index].cfFormat = CF_INETURL;
658 m_formats[index].dwAspect = DVASPECT_CONTENT;
659 m_formats[index].lindex = -1;
660 m_formats[index].ptd = nullptr;
661 m_formats[index].tymed = TYMED_HGLOBAL;
662 index++;
664 m_formats[index].cfFormat = CF_SHELLURL;
665 m_formats[index].dwAspect = DVASPECT_CONTENT;
666 m_formats[index].lindex = -1;
667 m_formats[index].ptd = nullptr;
668 m_formats[index].tymed = TYMED_HGLOBAL;
669 index++;
672 m_formats[index].cfFormat = CF_FILE_ATTRIBUTES_ARRAY;
673 m_formats[index].dwAspect = DVASPECT_CONTENT;
674 m_formats[index].lindex = -1;
675 m_formats[index].ptd = nullptr;
676 m_formats[index].tymed = TYMED_HGLOBAL;
677 index++;
679 if (containsExistingFiles && localonly)
681 m_formats[index].cfFormat = CF_HDROP;
682 m_formats[index].dwAspect = DVASPECT_CONTENT;
683 m_formats[index].lindex = -1;
684 m_formats[index].ptd = nullptr;
685 m_formats[index].tymed = TYMED_HGLOBAL;
686 index++;
688 else if (containsExistingFiles)
690 m_formats[index].cfFormat = CF_FILECONTENTS;
691 m_formats[index].dwAspect = DVASPECT_CONTENT;
692 m_formats[index].lindex = -1;
693 m_formats[index].ptd = nullptr;
694 m_formats[index].tymed = TYMED_ISTREAM;
695 index++;
697 m_formats[index].cfFormat = CF_FILEDESCRIPTOR;
698 m_formats[index].dwAspect = DVASPECT_CONTENT;
699 m_formats[index].lindex = -1;
700 m_formats[index].ptd = nullptr;
701 m_formats[index].tymed = TYMED_HGLOBAL;
702 index++;
704 // clear possible leftovers
705 while (index < GITDATAOBJECT_NUMFORMATS)
707 m_formats[index].cfFormat = 0;
708 m_formats[index].dwAspect = 0;
709 m_formats[index].lindex = -1;
710 m_formats[index].ptd = nullptr;
711 m_formats[index].tymed = 0;
712 index++;
716 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector<FORMATETC>& vec, bool localonly, bool containsExistingFiles)
717 : m_cRefCount(0)
718 , m_iCur(0)
719 , m_localonly(localonly)
720 , m_containsExistingFiles(containsExistingFiles)
722 for (size_t i = 0; i < vec.size(); ++i)
723 m_vecFormatEtc.push_back(vec[i]);
724 Init(localonly, containsExistingFiles);
727 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector<FORMATETC*>& vec, bool localonly, bool containsExistingFiles)
728 : m_cRefCount(0)
729 , m_iCur(0)
730 , m_localonly(localonly)
731 , m_containsExistingFiles(containsExistingFiles)
733 for (size_t i = 0; i < vec.size(); ++i)
734 m_vecFormatEtc.push_back(*vec[i]);
735 Init(localonly, containsExistingFiles);
738 STDMETHODIMP CGitEnumFormatEtc::QueryInterface(REFIID refiid, void** ppv)
740 if (!ppv)
741 return E_POINTER;
742 *ppv = nullptr;
743 if (IID_IUnknown == refiid || IID_IEnumFORMATETC == refiid)
744 *ppv = static_cast<IEnumFORMATETC*>(this);
745 else
746 return E_NOINTERFACE;
748 AddRef();
749 return S_OK;
752 STDMETHODIMP_(ULONG) CGitEnumFormatEtc::AddRef(void)
754 return ++m_cRefCount;
757 STDMETHODIMP_(ULONG) CGitEnumFormatEtc::Release(void)
759 --m_cRefCount;
760 if (m_cRefCount == 0)
762 delete this;
763 return 0;
765 return m_cRefCount;
768 STDMETHODIMP CGitEnumFormatEtc::Next(ULONG celt, LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
770 if (celt <= 0)
771 return E_INVALIDARG;
772 if (!pceltFetched && celt != 1) // pceltFetched can be NULL only for 1 item request
773 return E_POINTER;
774 if (!lpFormatEtc)
775 return E_POINTER;
777 if (pceltFetched)
778 *pceltFetched = 0;
780 if (m_iCur >= GITDATAOBJECT_NUMFORMATS)
781 return S_FALSE;
783 ULONG cReturn = celt;
785 while (m_iCur < (GITDATAOBJECT_NUMFORMATS + m_vecFormatEtc.size()) && cReturn > 0)
787 if (m_iCur < GITDATAOBJECT_NUMFORMATS)
788 *lpFormatEtc++ = m_formats[m_iCur++];
789 else
790 *lpFormatEtc++ = m_vecFormatEtc[m_iCur++ - GITDATAOBJECT_NUMFORMATS];
791 --cReturn;
794 if (pceltFetched)
795 *pceltFetched = celt - cReturn;
797 return (cReturn == 0) ? S_OK : S_FALSE;
800 STDMETHODIMP CGitEnumFormatEtc::Skip(ULONG celt)
802 if ((m_iCur + int(celt)) >= (GITDATAOBJECT_NUMFORMATS + m_vecFormatEtc.size()))
803 return S_FALSE;
804 m_iCur += celt;
805 return S_OK;
808 STDMETHODIMP CGitEnumFormatEtc::Reset(void)
810 m_iCur = 0;
811 return S_OK;
814 STDMETHODIMP CGitEnumFormatEtc::Clone(IEnumFORMATETC** ppCloneEnumFormatEtc)
816 if (!ppCloneEnumFormatEtc)
817 return E_POINTER;
819 CGitEnumFormatEtc *newEnum = new (std::nothrow) CGitEnumFormatEtc(m_vecFormatEtc, m_localonly, m_containsExistingFiles);
820 if (!newEnum)
821 return E_OUTOFMEMORY;
823 newEnum->AddRef();
824 newEnum->m_iCur = m_iCur;
825 *ppCloneEnumFormatEtc = newEnum;
826 return S_OK;