1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016-2019, 2021-2022 - 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.
20 #include "GitDataObject.h"
22 #include "UnicodeUtils.h"
23 #include "PathUtils.h"
25 #include "StringUtils.h"
28 CLIPFORMAT CF_FILECONTENTS
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_FILECONTENTS
));
29 CLIPFORMAT CF_FILEDESCRIPTOR
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR
));
30 CLIPFORMAT CF_PREFERREDDROPEFFECT
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
));
31 CLIPFORMAT CF_INETURL
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_INETURL
));
32 CLIPFORMAT CF_SHELLURL
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_SHELLURL
));
33 CLIPFORMAT CF_FILE_ATTRIBUTES_ARRAY
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_FILE_ATTRIBUTES_ARRAY
));
35 GitDataObject::GitDataObject(const CTGitPathList
& gitpaths
, const CGitHash
& rev
, int stripLength
)
36 : m_gitPaths(gitpaths
)
38 , m_bInOperation(FALSE
)
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;
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 //////////////////////////////////////////////////////////////////////////
69 //////////////////////////////////////////////////////////////////////////
71 STDMETHODIMP
GitDataObject::QueryInterface(REFIID riid
, void** ppvObject
)
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);
87 STDMETHODIMP_(ULONG
) GitDataObject::AddRef()
92 STDMETHODIMP_(ULONG
) GitDataObject::Release()
103 //////////////////////////////////////////////////////////////////////////
105 //////////////////////////////////////////////////////////////////////////
106 STDMETHODIMP
GitDataObject::GetData(FORMATETC
* pformatetcIn
, STGMEDIUM
* pmedium
)
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
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
< static_cast<LONG
>(m_allPaths
.size()))
133 filepath
= g_Git
.CombinePath(m_allPaths
[pformatetcIn
->lindex
]);
137 filepath
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
138 if (pformatetcIn
->lindex
>= 0 && pformatetcIn
->lindex
< static_cast<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
);
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
;
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())
167 m_allPaths
.push_back(m_gitPaths
[i
]);
170 size_t dataSize
= sizeof(FILEGROUPDESCRIPTOR
) + ((max(size_t(1), m_allPaths
.size()) - 1) * sizeof(FILEDESCRIPTOR
));
171 HGLOBAL data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
| GMEM_ZEROINIT
, dataSize
);
173 auto files
= static_cast<FILEGROUPDESCRIPTOR
*>(GlobalLock(data
));
174 files
->cItems
= static_cast<UINT
>(m_allPaths
.size());
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
, static_cast<LPCWSTR
>(temp
));
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;
203 pmedium
->hGlobal
= data
;
204 pmedium
->tymed
= TYMED_HGLOBAL
;
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
));
215 return E_OUTOFMEMORY
;
216 auto effect
= static_cast<DWORD
*>(GlobalLock(data
));
220 return E_OUTOFMEMORY
;
222 (*effect
) = DROPEFFECT_COPY
;
224 pmedium
->hGlobal
= data
;
225 pmedium
->tymed
= TYMED_HGLOBAL
;
228 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_TEXT
))
231 // create the string from the path list
233 if (!m_gitPaths
.IsEmpty())
235 // create a single string where the URLs are separated by newlines
236 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
238 text
+= m_gitPaths
[i
].GetWinPathString();
242 CStringA texta
= CUnicodeUtils::GetUTF8(text
);
243 pmedium
->tymed
= TYMED_HGLOBAL
;
244 pmedium
->hGlobal
= GlobalAlloc(GHND
, (texta
.GetLength() + 1) * sizeof(char));
245 if (pmedium
->hGlobal
)
247 auto pMem
= static_cast<char*>(GlobalLock(pmedium
->hGlobal
));
248 strcpy_s(pMem
, texta
.GetLength() + 1, static_cast<LPCSTR
>(texta
));
249 GlobalUnlock(pmedium
->hGlobal
);
251 pmedium
->pUnkForRelease
= nullptr;
254 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && ((pformatetcIn
->cfFormat
== CF_UNICODETEXT
) || (pformatetcIn
->cfFormat
== CF_INETURL
) || (pformatetcIn
->cfFormat
== CF_SHELLURL
)))
256 // caller wants Unicode text
257 // create the string from the path list
259 if (!m_gitPaths
.IsEmpty())
261 // create a single string where the URLs are separated by newlines
262 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
264 if (pformatetcIn
->cfFormat
== CF_UNICODETEXT
)
265 text
+= m_gitPaths
[i
].GetWinPathString();
267 text
+= g_Git
.CombinePath(m_gitPaths
[i
]);
271 pmedium
->tymed
= TYMED_HGLOBAL
;
272 pmedium
->hGlobal
= GlobalAlloc(GHND
, (text
.GetLength() + 1) * sizeof(wchar_t));
273 if (pmedium
->hGlobal
)
275 auto pMem
= static_cast<wchar_t*>(GlobalLock(pmedium
->hGlobal
));
276 wcscpy_s(pMem
, text
.GetLength() + 1, static_cast<LPCWSTR
>(text
));
277 GlobalUnlock(pmedium
->hGlobal
);
279 pmedium
->pUnkForRelease
= nullptr;
282 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_HDROP
))
286 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
288 if (m_gitPaths
[i
].m_Action
& (CTGitPath::LOGACTIONS_MISSING
| CTGitPath::LOGACTIONS_DELETED
) || m_gitPaths
[i
].IsDirectory())
291 nLength
+= g_Git
.CombinePath(m_gitPaths
[i
]).GetLength();
292 nLength
+= 1; // '\0' separator
295 int nBufferSize
= sizeof(DROPFILES
) + (nLength
+ 1) * sizeof(wchar_t);
296 auto pBuffer
= std::make_unique
<char[]>(nBufferSize
);
298 auto df
= reinterpret_cast<DROPFILES
*>(pBuffer
.get());
299 df
->pFiles
= sizeof(DROPFILES
);
302 auto pFilenames
= reinterpret_cast<wchar_t*>(pBuffer
.get() + sizeof(DROPFILES
));
303 wchar_t* pCurrentFilename
= pFilenames
;
305 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
307 if (m_gitPaths
[i
].m_Action
& (CTGitPath::LOGACTIONS_MISSING
| CTGitPath::LOGACTIONS_DELETED
) || m_gitPaths
[i
].IsDirectory())
309 CString str
= g_Git
.CombinePath(m_gitPaths
[i
]);
310 wcscpy_s(pCurrentFilename
, str
.GetLength() + 1, static_cast<LPCWSTR
>(str
));
311 pCurrentFilename
+= str
.GetLength();
312 *pCurrentFilename
= '\0'; // separator between file names
315 *pCurrentFilename
= '\0'; // terminate array
317 pmedium
->tymed
= TYMED_HGLOBAL
;
318 pmedium
->hGlobal
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_MOVEABLE
| GMEM_DDESHARE
, nBufferSize
);
319 if (pmedium
->hGlobal
)
321 LPVOID pMem
= ::GlobalLock(pmedium
->hGlobal
);
323 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
324 GlobalUnlock(pmedium
->hGlobal
);
326 pmedium
->pUnkForRelease
= nullptr;
329 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_FILE_ATTRIBUTES_ARRAY
))
331 int nBufferSize
= sizeof(FILE_ATTRIBUTES_ARRAY
) + m_gitPaths
.GetCount() * sizeof(DWORD
);
332 auto pBuffer
= std::make_unique
<char[]>(nBufferSize
);
334 auto cf
= reinterpret_cast<FILE_ATTRIBUTES_ARRAY
*>(pBuffer
.get());
335 cf
->cItems
= m_gitPaths
.GetCount();
336 cf
->dwProductFileAttributes
= DWORD_MAX
;
337 cf
->dwSumFileAttributes
= 0;
338 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
340 DWORD fileattribs
= FILE_ATTRIBUTE_NORMAL
;
341 cf
->rgdwFileAttributes
[i
] = fileattribs
;
342 cf
->dwProductFileAttributes
&= fileattribs
;
343 cf
->dwSumFileAttributes
|= fileattribs
;
346 pmedium
->tymed
= TYMED_HGLOBAL
;
347 pmedium
->hGlobal
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_MOVEABLE
| GMEM_DDESHARE
, nBufferSize
);
348 if (pmedium
->hGlobal
)
350 LPVOID pMem
= ::GlobalLock(pmedium
->hGlobal
);
352 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
353 GlobalUnlock(pmedium
->hGlobal
);
355 pmedium
->pUnkForRelease
= nullptr;
359 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
361 if ((pformatetcIn
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
362 (pformatetcIn
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
363 (pformatetcIn
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
365 CopyMedium(pmedium
, m_vecStgMedium
[i
], m_vecFormatEtc
[i
]);
370 return DV_E_FORMATETC
;
373 STDMETHODIMP
GitDataObject::GetDataHere(FORMATETC
* /*pformatetc*/, STGMEDIUM
* /*pmedium*/)
378 STDMETHODIMP
GitDataObject::QueryGetData(FORMATETC
* pformatetc
)
383 if (!(DVASPECT_CONTENT
& pformatetc
->dwAspect
))
384 return DV_E_DVASPECT
;
386 if ((pformatetc
->tymed
& TYMED_ISTREAM
) &&
387 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
388 (pformatetc
->cfFormat
== CF_FILECONTENTS
) &&
389 m_containsExistingFiles
)
393 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
394 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
395 ((pformatetc
->cfFormat
== CF_TEXT
) || (pformatetc
->cfFormat
== CF_UNICODETEXT
) || (pformatetc
->cfFormat
== CF_PREFERREDDROPEFFECT
)))
399 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
400 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
401 (pformatetc
->cfFormat
== CF_FILEDESCRIPTOR
) &&
402 !m_revision
.IsEmpty() &&
403 m_containsExistingFiles
)
407 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
408 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
409 ((pformatetc
->cfFormat
== CF_HDROP
) || (pformatetc
->cfFormat
== CF_INETURL
) || (pformatetc
->cfFormat
== CF_SHELLURL
)) &&
410 m_revision
.IsEmpty() &&
411 m_containsExistingFiles
)
415 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
416 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
417 (pformatetc
->cfFormat
== CF_FILE_ATTRIBUTES_ARRAY
) &&
418 m_containsExistingFiles
)
423 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
425 if ((pformatetc
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
426 (pformatetc
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
427 (pformatetc
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
434 STDMETHODIMP
GitDataObject::GetCanonicalFormatEtc(FORMATETC
* /*pformatectIn*/, FORMATETC
* pformatetcOut
)
438 return DATA_S_SAMEFORMATETC
;
441 STDMETHODIMP
GitDataObject::SetData(FORMATETC
* pformatetc
, STGMEDIUM
* pmedium
, BOOL fRelease
)
443 if (!pformatetc
|| !pmedium
)
446 FORMATETC
* fetc
= new (std::nothrow
) FORMATETC
;
447 STGMEDIUM
* pStgMed
= new (std::nothrow
) STGMEDIUM
;
449 if (!fetc
|| !pStgMed
)
453 return E_OUTOFMEMORY
;
455 SecureZeroMemory(fetc
, sizeof(FORMATETC
));
456 SecureZeroMemory(pStgMed
, sizeof(STGMEDIUM
));
458 // do we already store this format?
459 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
461 if ((pformatetc
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
462 (pformatetc
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
463 (pformatetc
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
465 // yes, this format is already in our object:
466 // we have to replace the existing format. To do that, we
467 // remove the format we already have
468 delete m_vecFormatEtc
[i
];
469 m_vecFormatEtc
.erase(m_vecFormatEtc
.begin() + i
);
470 ReleaseStgMedium(m_vecStgMedium
[i
]);
471 delete m_vecStgMedium
[i
];
472 m_vecStgMedium
.erase(m_vecStgMedium
.begin() + i
);
478 m_vecFormatEtc
.push_back(fetc
);
483 CopyMedium(pStgMed
, pmedium
, pformatetc
);
484 m_vecStgMedium
.push_back(pStgMed
);
489 STDMETHODIMP
GitDataObject::EnumFormatEtc(DWORD dwDirection
, IEnumFORMATETC
** ppenumFormatEtc
)
491 if (!ppenumFormatEtc
)
494 *ppenumFormatEtc
= nullptr;
498 *ppenumFormatEtc
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_revision
.IsEmpty(), m_containsExistingFiles
);
499 if (!*ppenumFormatEtc
)
500 return E_OUTOFMEMORY
;
501 (*ppenumFormatEtc
)->AddRef();
509 STDMETHODIMP
GitDataObject::DAdvise(FORMATETC
* /*pformatetc*/, DWORD
/*advf*/, IAdviseSink
* /*pAdvSink*/, DWORD
* /*pdwConnection*/)
511 return OLE_E_ADVISENOTSUPPORTED
;
514 STDMETHODIMP
GitDataObject::DUnadvise(DWORD
/*dwConnection*/)
519 HRESULT STDMETHODCALLTYPE
GitDataObject::EnumDAdvise(IEnumSTATDATA
** /*ppenumAdvise*/)
521 return OLE_E_ADVISENOTSUPPORTED
;
524 void GitDataObject::CopyMedium(STGMEDIUM
* pMedDest
, STGMEDIUM
* pMedSrc
, FORMATETC
* pFmtSrc
)
526 switch (pMedSrc
->tymed
)
529 pMedDest
->hGlobal
= static_cast<HGLOBAL
>(OleDuplicateData(pMedSrc
->hGlobal
, pFmtSrc
->cfFormat
, 0));
532 pMedDest
->hBitmap
= static_cast<HBITMAP
>(OleDuplicateData(pMedSrc
->hBitmap
, pFmtSrc
->cfFormat
, 0));
535 pMedDest
->hMetaFilePict
= static_cast<HMETAFILEPICT
>(OleDuplicateData(pMedSrc
->hMetaFilePict
, pFmtSrc
->cfFormat
, 0));
538 pMedDest
->hEnhMetaFile
= static_cast<HENHMETAFILE
>(OleDuplicateData(pMedSrc
->hEnhMetaFile
, pFmtSrc
->cfFormat
, 0));
541 pMedSrc
->lpszFileName
= static_cast<LPOLESTR
>(OleDuplicateData(pMedSrc
->lpszFileName
, pFmtSrc
->cfFormat
, 0));
544 pMedDest
->pstm
= pMedSrc
->pstm
;
545 pMedSrc
->pstm
->AddRef();
548 pMedDest
->pstg
= pMedSrc
->pstg
;
549 pMedSrc
->pstg
->AddRef();
555 pMedDest
->tymed
= pMedSrc
->tymed
;
556 pMedDest
->pUnkForRelease
= nullptr;
557 if (pMedSrc
->pUnkForRelease
)
559 pMedDest
->pUnkForRelease
= pMedSrc
->pUnkForRelease
;
560 pMedSrc
->pUnkForRelease
->AddRef();
565 //////////////////////////////////////////////////////////////////////////
567 //////////////////////////////////////////////////////////////////////////
568 HRESULT STDMETHODCALLTYPE
GitDataObject::SetAsyncMode(BOOL fDoOpAsync
)
570 m_bIsAsync
= fDoOpAsync
;
574 HRESULT STDMETHODCALLTYPE
GitDataObject::GetAsyncMode(BOOL
* pfIsOpAsync
)
579 *pfIsOpAsync
= m_bIsAsync
;
584 HRESULT STDMETHODCALLTYPE
GitDataObject::StartOperation(IBindCtx
* /*pbcReserved*/)
586 m_bInOperation
= TRUE
;
590 HRESULT STDMETHODCALLTYPE
GitDataObject::InOperation(BOOL
* pfInAsyncOp
)
595 *pfInAsyncOp
= m_bInOperation
;
600 HRESULT STDMETHODCALLTYPE
GitDataObject::EndOperation(HRESULT
/*hResult*/, IBindCtx
* /*pbcReserved*/, DWORD
/*dwEffects*/)
602 m_bInOperation
= FALSE
;
606 HRESULT
GitDataObject::SetDropDescription(DROPIMAGETYPE image
, LPCWSTR format
, LPCWSTR insert
)
608 if (!format
|| !insert
)
611 FORMATETC fetc
= { 0 };
612 fetc
.cfFormat
= static_cast<CLIPFORMAT
>(RegisterClipboardFormat(CFSTR_DROPDESCRIPTION
));
613 fetc
.dwAspect
= DVASPECT_CONTENT
;
615 fetc
.tymed
= TYMED_HGLOBAL
;
617 STGMEDIUM medium
= { 0 };
618 medium
.hGlobal
= GlobalAlloc(GHND
, sizeof(DROPDESCRIPTION
));
619 if (medium
.hGlobal
== 0)
620 return E_OUTOFMEMORY
;
622 auto pDropDescription
= static_cast<DROPDESCRIPTION
*>(GlobalLock(medium
.hGlobal
));
623 if (!pDropDescription
)
625 StringCchCopy(pDropDescription
->szInsert
, _countof(pDropDescription
->szInsert
), insert
);
626 StringCchCopy(pDropDescription
->szMessage
, _countof(pDropDescription
->szMessage
), format
);
627 pDropDescription
->type
= image
;
628 GlobalUnlock(medium
.hGlobal
);
629 return SetData(&fetc
, &medium
, TRUE
);
632 void CGitEnumFormatEtc::Init(bool localonly
, bool containsExistingFiles
)
635 m_formats
[index
].cfFormat
= CF_UNICODETEXT
;
636 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
637 m_formats
[index
].lindex
= -1;
638 m_formats
[index
].ptd
= nullptr;
639 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
642 m_formats
[index
].cfFormat
= CF_TEXT
;
643 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
644 m_formats
[index
].lindex
= -1;
645 m_formats
[index
].ptd
= nullptr;
646 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
649 m_formats
[index
].cfFormat
= CF_PREFERREDDROPEFFECT
;
650 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
651 m_formats
[index
].lindex
= -1;
652 m_formats
[index
].ptd
= nullptr;
653 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
656 if (containsExistingFiles
&& localonly
)
658 m_formats
[index
].cfFormat
= CF_INETURL
;
659 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
660 m_formats
[index
].lindex
= -1;
661 m_formats
[index
].ptd
= nullptr;
662 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
665 m_formats
[index
].cfFormat
= CF_SHELLURL
;
666 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
667 m_formats
[index
].lindex
= -1;
668 m_formats
[index
].ptd
= nullptr;
669 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
673 m_formats
[index
].cfFormat
= CF_FILE_ATTRIBUTES_ARRAY
;
674 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
675 m_formats
[index
].lindex
= -1;
676 m_formats
[index
].ptd
= nullptr;
677 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
680 if (containsExistingFiles
&& localonly
)
682 m_formats
[index
].cfFormat
= CF_HDROP
;
683 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
684 m_formats
[index
].lindex
= -1;
685 m_formats
[index
].ptd
= nullptr;
686 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
689 else if (containsExistingFiles
)
691 m_formats
[index
].cfFormat
= CF_FILECONTENTS
;
692 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
693 m_formats
[index
].lindex
= -1;
694 m_formats
[index
].ptd
= nullptr;
695 m_formats
[index
].tymed
= TYMED_ISTREAM
;
698 m_formats
[index
].cfFormat
= CF_FILEDESCRIPTOR
;
699 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
700 m_formats
[index
].lindex
= -1;
701 m_formats
[index
].ptd
= nullptr;
702 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
705 // clear possible leftovers
706 while (index
< GITDATAOBJECT_NUMFORMATS
)
708 m_formats
[index
].cfFormat
= 0;
709 m_formats
[index
].dwAspect
= 0;
710 m_formats
[index
].lindex
= -1;
711 m_formats
[index
].ptd
= nullptr;
712 m_formats
[index
].tymed
= 0;
717 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector
<FORMATETC
>& vec
, bool localonly
, bool containsExistingFiles
)
720 , m_localonly(localonly
)
721 , m_containsExistingFiles(containsExistingFiles
)
723 for (size_t i
= 0; i
< vec
.size(); ++i
)
724 m_vecFormatEtc
.push_back(vec
[i
]);
725 Init(localonly
, containsExistingFiles
);
728 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector
<FORMATETC
*>& vec
, bool localonly
, bool containsExistingFiles
)
731 , m_localonly(localonly
)
732 , m_containsExistingFiles(containsExistingFiles
)
734 for (size_t i
= 0; i
< vec
.size(); ++i
)
735 m_vecFormatEtc
.push_back(*vec
[i
]);
736 Init(localonly
, containsExistingFiles
);
739 STDMETHODIMP
CGitEnumFormatEtc::QueryInterface(REFIID refiid
, void** ppv
)
744 if (IID_IUnknown
== refiid
|| IID_IEnumFORMATETC
== refiid
)
745 *ppv
= static_cast<IEnumFORMATETC
*>(this);
747 return E_NOINTERFACE
;
753 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::AddRef()
755 return ++m_cRefCount
;
758 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::Release()
761 if (m_cRefCount
== 0)
769 STDMETHODIMP
CGitEnumFormatEtc::Next(ULONG celt
, LPFORMATETC lpFormatEtc
, ULONG
* pceltFetched
)
773 if (!pceltFetched
&& celt
!= 1) // pceltFetched can be NULL only for 1 item request
781 if (m_iCur
>= GITDATAOBJECT_NUMFORMATS
)
784 ULONG cReturn
= celt
;
786 while (m_iCur
< (GITDATAOBJECT_NUMFORMATS
+ m_vecFormatEtc
.size()) && cReturn
> 0)
788 if (m_iCur
< GITDATAOBJECT_NUMFORMATS
)
789 *lpFormatEtc
++ = m_formats
[m_iCur
++];
791 *lpFormatEtc
++ = m_vecFormatEtc
[m_iCur
++ - GITDATAOBJECT_NUMFORMATS
];
796 *pceltFetched
= celt
- cReturn
;
798 return (cReturn
== 0) ? S_OK
: S_FALSE
;
801 STDMETHODIMP
CGitEnumFormatEtc::Skip(ULONG celt
)
803 if ((m_iCur
+ int(celt
)) >= (GITDATAOBJECT_NUMFORMATS
+ m_vecFormatEtc
.size()))
809 STDMETHODIMP
CGitEnumFormatEtc::Reset()
815 STDMETHODIMP
CGitEnumFormatEtc::Clone(IEnumFORMATETC
** ppCloneEnumFormatEtc
)
817 if (!ppCloneEnumFormatEtc
)
820 CGitEnumFormatEtc
*newEnum
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_localonly
, m_containsExistingFiles
);
822 return E_OUTOFMEMORY
;
825 newEnum
->m_iCur
= m_iCur
;
826 *ppCloneEnumFormatEtc
= newEnum
;