1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016 - 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
= (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
)
38 , m_bInOperation(FALSE
)
41 , m_iStripLength(stripLength
)
43 ASSERT((m_revision
.IsEmpty() && m_iStripLength
== -1) || (!m_revision
.IsEmpty() && m_iStripLength
>= -1)); // m_iStripLength only possible if rev is set
44 m_containsExistingFiles
= false;
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(void)
92 STDMETHODIMP_(ULONG
) GitDataObject::Release(void)
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
< (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
< (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(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());
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
);
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
));
214 DWORD
* effect
= (DWORD
*)GlobalLock(data
);
215 (*effect
) = DROPEFFECT_COPY
;
217 pmedium
->hGlobal
= data
;
218 pmedium
->tymed
= TYMED_HGLOBAL
;
221 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_TEXT
))
224 // create the string from the path list
226 if (!m_gitPaths
.IsEmpty())
228 // create a single string where the URLs are separated by newlines
229 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
231 text
+= m_gitPaths
[i
].GetWinPathString();
235 CStringA texta
= CUnicodeUtils::GetUTF8(text
);
236 pmedium
->tymed
= TYMED_HGLOBAL
;
237 pmedium
->hGlobal
= GlobalAlloc(GHND
, (texta
.GetLength() + 1) * sizeof(char));
238 if (pmedium
->hGlobal
)
240 char* pMem
= (char*)GlobalLock(pmedium
->hGlobal
);
241 strcpy_s(pMem
, texta
.GetLength() + 1, (LPCSTR
)texta
);
242 GlobalUnlock(pmedium
->hGlobal
);
244 pmedium
->pUnkForRelease
= nullptr;
247 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && ((pformatetcIn
->cfFormat
== CF_UNICODETEXT
) || (pformatetcIn
->cfFormat
== CF_INETURL
) || (pformatetcIn
->cfFormat
== CF_SHELLURL
)))
249 // caller wants Unicode text
250 // create the string from the path list
252 if (!m_gitPaths
.IsEmpty())
254 // create a single string where the URLs are separated by newlines
255 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
257 if (pformatetcIn
->cfFormat
== CF_UNICODETEXT
)
258 text
+= m_gitPaths
[i
].GetWinPathString();
260 text
+= g_Git
.CombinePath(m_gitPaths
[i
]);
264 pmedium
->tymed
= TYMED_HGLOBAL
;
265 pmedium
->hGlobal
= GlobalAlloc(GHND
, (text
.GetLength() + 1) * sizeof(TCHAR
));
266 if (pmedium
->hGlobal
)
268 TCHAR
* pMem
= (TCHAR
*)GlobalLock(pmedium
->hGlobal
);
269 wcscpy_s(pMem
, text
.GetLength() + 1, (LPCTSTR
)text
);
270 GlobalUnlock(pmedium
->hGlobal
);
272 pmedium
->pUnkForRelease
= nullptr;
275 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_HDROP
))
279 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
281 if (m_gitPaths
[i
].m_Action
& (CTGitPath::LOGACTIONS_MISSING
| CTGitPath::LOGACTIONS_DELETED
) || m_gitPaths
[i
].IsDirectory())
284 nLength
+= g_Git
.CombinePath(m_gitPaths
[i
]).GetLength();
285 nLength
+= 1; // '\0' separator
288 int nBufferSize
= sizeof(DROPFILES
) + (nLength
+ 1) * sizeof(TCHAR
);
289 auto pBuffer
= std::make_unique
<char[]>(nBufferSize
);
290 SecureZeroMemory(pBuffer
.get(), nBufferSize
);
292 DROPFILES
* df
= (DROPFILES
*)pBuffer
.get();
293 df
->pFiles
= sizeof(DROPFILES
);
296 TCHAR
* pFilenames
= (TCHAR
*)(pBuffer
.get() + sizeof(DROPFILES
));
297 TCHAR
* pCurrentFilename
= pFilenames
;
299 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
301 if (m_gitPaths
[i
].m_Action
& (CTGitPath::LOGACTIONS_MISSING
| CTGitPath::LOGACTIONS_DELETED
) || m_gitPaths
[i
].IsDirectory())
303 CString str
= g_Git
.CombinePath(m_gitPaths
[i
]);
304 wcscpy_s(pCurrentFilename
, str
.GetLength() + 1, (LPCWSTR
)str
);
305 pCurrentFilename
+= str
.GetLength();
306 *pCurrentFilename
= '\0'; // separator between file names
309 *pCurrentFilename
= '\0'; // terminate array
311 pmedium
->tymed
= TYMED_HGLOBAL
;
312 pmedium
->hGlobal
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_MOVEABLE
| GMEM_DDESHARE
, nBufferSize
);
313 if (pmedium
->hGlobal
)
315 LPVOID pMem
= ::GlobalLock(pmedium
->hGlobal
);
317 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
318 GlobalUnlock(pmedium
->hGlobal
);
320 pmedium
->pUnkForRelease
= nullptr;
323 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_FILE_ATTRIBUTES_ARRAY
))
325 int nBufferSize
= sizeof(FILE_ATTRIBUTES_ARRAY
) + m_gitPaths
.GetCount() * sizeof(DWORD
);
326 auto pBuffer
= std::make_unique
<char[]>(nBufferSize
);
327 SecureZeroMemory(pBuffer
.get(), nBufferSize
);
329 FILE_ATTRIBUTES_ARRAY
* cf
= (FILE_ATTRIBUTES_ARRAY
*)pBuffer
.get();
330 cf
->cItems
= m_gitPaths
.GetCount();
331 cf
->dwProductFileAttributes
= DWORD_MAX
;
332 cf
->dwSumFileAttributes
= 0;
333 for (int i
= 0; i
< m_gitPaths
.GetCount(); ++i
)
335 DWORD fileattribs
= FILE_ATTRIBUTE_NORMAL
;
336 cf
->rgdwFileAttributes
[i
] = fileattribs
;
337 cf
->dwProductFileAttributes
&= fileattribs
;
338 cf
->dwSumFileAttributes
|= fileattribs
;
341 pmedium
->tymed
= TYMED_HGLOBAL
;
342 pmedium
->hGlobal
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_MOVEABLE
| GMEM_DDESHARE
, nBufferSize
);
343 if (pmedium
->hGlobal
)
345 LPVOID pMem
= ::GlobalLock(pmedium
->hGlobal
);
347 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
348 GlobalUnlock(pmedium
->hGlobal
);
350 pmedium
->pUnkForRelease
= nullptr;
354 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
356 if ((pformatetcIn
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
357 (pformatetcIn
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
358 (pformatetcIn
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
360 CopyMedium(pmedium
, m_vecStgMedium
[i
], m_vecFormatEtc
[i
]);
365 return DV_E_FORMATETC
;
368 STDMETHODIMP
GitDataObject::GetDataHere(FORMATETC
* /*pformatetc*/, STGMEDIUM
* /*pmedium*/)
373 STDMETHODIMP
GitDataObject::QueryGetData(FORMATETC
* pformatetc
)
378 if (!(DVASPECT_CONTENT
& pformatetc
->dwAspect
))
379 return DV_E_DVASPECT
;
381 if ((pformatetc
->tymed
& TYMED_ISTREAM
) &&
382 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
383 (pformatetc
->cfFormat
== CF_FILECONTENTS
) &&
384 m_containsExistingFiles
)
388 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
389 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
390 ((pformatetc
->cfFormat
== CF_TEXT
) || (pformatetc
->cfFormat
== CF_UNICODETEXT
) || (pformatetc
->cfFormat
== CF_PREFERREDDROPEFFECT
)))
394 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
395 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
396 (pformatetc
->cfFormat
== CF_FILEDESCRIPTOR
) &&
397 !m_revision
.IsEmpty() &&
398 m_containsExistingFiles
)
402 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
403 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
404 ((pformatetc
->cfFormat
== CF_HDROP
) || (pformatetc
->cfFormat
== CF_INETURL
) || (pformatetc
->cfFormat
== CF_SHELLURL
)) &&
405 m_revision
.IsEmpty() &&
406 m_containsExistingFiles
)
410 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
411 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
412 (pformatetc
->cfFormat
== CF_FILE_ATTRIBUTES_ARRAY
) &&
413 m_containsExistingFiles
)
418 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
420 if ((pformatetc
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
421 (pformatetc
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
422 (pformatetc
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
429 STDMETHODIMP
GitDataObject::GetCanonicalFormatEtc(FORMATETC
* /*pformatectIn*/, FORMATETC
* pformatetcOut
)
433 return DATA_S_SAMEFORMATETC
;
436 STDMETHODIMP
GitDataObject::SetData(FORMATETC
* pformatetc
, STGMEDIUM
* pmedium
, BOOL fRelease
)
438 if (!pformatetc
|| !pmedium
)
441 FORMATETC
* fetc
= new (std::nothrow
) FORMATETC
;
442 STGMEDIUM
* pStgMed
= new (std::nothrow
) STGMEDIUM
;
444 if (!fetc
|| !pStgMed
)
448 return E_OUTOFMEMORY
;
450 SecureZeroMemory(fetc
, sizeof(FORMATETC
));
451 SecureZeroMemory(pStgMed
, sizeof(STGMEDIUM
));
453 // do we already store this format?
454 for (size_t i
= 0; i
< m_vecFormatEtc
.size(); ++i
)
456 if ((pformatetc
->tymed
== m_vecFormatEtc
[i
]->tymed
) &&
457 (pformatetc
->dwAspect
== m_vecFormatEtc
[i
]->dwAspect
) &&
458 (pformatetc
->cfFormat
== m_vecFormatEtc
[i
]->cfFormat
))
460 // yes, this format is already in our object:
461 // we have to replace the existing format. To do that, we
462 // remove the format we already have
463 delete m_vecFormatEtc
[i
];
464 m_vecFormatEtc
.erase(m_vecFormatEtc
.begin() + i
);
465 ReleaseStgMedium(m_vecStgMedium
[i
]);
466 delete m_vecStgMedium
[i
];
467 m_vecStgMedium
.erase(m_vecStgMedium
.begin() + i
);
473 m_vecFormatEtc
.push_back(fetc
);
478 CopyMedium(pStgMed
, pmedium
, pformatetc
);
479 m_vecStgMedium
.push_back(pStgMed
);
484 STDMETHODIMP
GitDataObject::EnumFormatEtc(DWORD dwDirection
, IEnumFORMATETC
** ppenumFormatEtc
)
486 if (!ppenumFormatEtc
)
489 *ppenumFormatEtc
= nullptr;
493 *ppenumFormatEtc
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_revision
.IsEmpty(), m_containsExistingFiles
);
494 if (!*ppenumFormatEtc
)
495 return E_OUTOFMEMORY
;
496 (*ppenumFormatEtc
)->AddRef();
504 STDMETHODIMP
GitDataObject::DAdvise(FORMATETC
* /*pformatetc*/, DWORD
/*advf*/, IAdviseSink
* /*pAdvSink*/, DWORD
* /*pdwConnection*/)
506 return OLE_E_ADVISENOTSUPPORTED
;
509 STDMETHODIMP
GitDataObject::DUnadvise(DWORD
/*dwConnection*/)
514 HRESULT STDMETHODCALLTYPE
GitDataObject::EnumDAdvise(IEnumSTATDATA
** /*ppenumAdvise*/)
516 return OLE_E_ADVISENOTSUPPORTED
;
519 void GitDataObject::CopyMedium(STGMEDIUM
* pMedDest
, STGMEDIUM
* pMedSrc
, FORMATETC
* pFmtSrc
)
521 switch (pMedSrc
->tymed
)
524 pMedDest
->hGlobal
= (HGLOBAL
)OleDuplicateData(pMedSrc
->hGlobal
, pFmtSrc
->cfFormat
, 0);
527 pMedDest
->hBitmap
= (HBITMAP
)OleDuplicateData(pMedSrc
->hBitmap
, pFmtSrc
->cfFormat
, 0);
530 pMedDest
->hMetaFilePict
= (HMETAFILEPICT
)OleDuplicateData(pMedSrc
->hMetaFilePict
, pFmtSrc
->cfFormat
, 0);
533 pMedDest
->hEnhMetaFile
= (HENHMETAFILE
)OleDuplicateData(pMedSrc
->hEnhMetaFile
, pFmtSrc
->cfFormat
, 0);
536 pMedSrc
->lpszFileName
= (LPOLESTR
)OleDuplicateData(pMedSrc
->lpszFileName
, pFmtSrc
->cfFormat
, 0);
539 pMedDest
->pstm
= pMedSrc
->pstm
;
540 pMedSrc
->pstm
->AddRef();
543 pMedDest
->pstg
= pMedSrc
->pstg
;
544 pMedSrc
->pstg
->AddRef();
550 pMedDest
->tymed
= pMedSrc
->tymed
;
551 pMedDest
->pUnkForRelease
= nullptr;
552 if (pMedSrc
->pUnkForRelease
)
554 pMedDest
->pUnkForRelease
= pMedSrc
->pUnkForRelease
;
555 pMedSrc
->pUnkForRelease
->AddRef();
560 //////////////////////////////////////////////////////////////////////////
562 //////////////////////////////////////////////////////////////////////////
563 HRESULT STDMETHODCALLTYPE
GitDataObject::SetAsyncMode(BOOL fDoOpAsync
)
565 m_bIsAsync
= fDoOpAsync
;
569 HRESULT STDMETHODCALLTYPE
GitDataObject::GetAsyncMode(BOOL
* pfIsOpAsync
)
574 *pfIsOpAsync
= m_bIsAsync
;
579 HRESULT STDMETHODCALLTYPE
GitDataObject::StartOperation(IBindCtx
* /*pbcReserved*/)
581 m_bInOperation
= TRUE
;
585 HRESULT STDMETHODCALLTYPE
GitDataObject::InOperation(BOOL
* pfInAsyncOp
)
590 *pfInAsyncOp
= m_bInOperation
;
595 HRESULT STDMETHODCALLTYPE
GitDataObject::EndOperation(HRESULT
/*hResult*/, IBindCtx
* /*pbcReserved*/, DWORD
/*dwEffects*/)
597 m_bInOperation
= FALSE
;
601 HRESULT
GitDataObject::SetDropDescription(DROPIMAGETYPE image
, LPCTSTR format
, LPCTSTR insert
)
603 if (!format
|| !insert
)
606 FORMATETC fetc
= { 0 };
607 fetc
.cfFormat
= (CLIPFORMAT
)RegisterClipboardFormat(CFSTR_DROPDESCRIPTION
);
608 fetc
.dwAspect
= DVASPECT_CONTENT
;
610 fetc
.tymed
= TYMED_HGLOBAL
;
612 STGMEDIUM medium
= { 0 };
613 medium
.hGlobal
= GlobalAlloc(GHND
, sizeof(DROPDESCRIPTION
));
614 if (medium
.hGlobal
== 0)
615 return E_OUTOFMEMORY
;
617 DROPDESCRIPTION
* pDropDescription
= (DROPDESCRIPTION
*)GlobalLock(medium
.hGlobal
);
618 if (!pDropDescription
)
620 StringCchCopy(pDropDescription
->szInsert
, _countof(pDropDescription
->szInsert
), insert
);
621 StringCchCopy(pDropDescription
->szMessage
, _countof(pDropDescription
->szMessage
), format
);
622 pDropDescription
->type
= image
;
623 GlobalUnlock(medium
.hGlobal
);
624 return SetData(&fetc
, &medium
, TRUE
);
627 void CGitEnumFormatEtc::Init(bool localonly
, bool containsExistingFiles
)
630 m_formats
[index
].cfFormat
= CF_UNICODETEXT
;
631 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
632 m_formats
[index
].lindex
= -1;
633 m_formats
[index
].ptd
= nullptr;
634 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
637 m_formats
[index
].cfFormat
= CF_TEXT
;
638 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
639 m_formats
[index
].lindex
= -1;
640 m_formats
[index
].ptd
= nullptr;
641 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
644 m_formats
[index
].cfFormat
= CF_PREFERREDDROPEFFECT
;
645 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
646 m_formats
[index
].lindex
= -1;
647 m_formats
[index
].ptd
= nullptr;
648 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
651 if (containsExistingFiles
&& localonly
)
653 m_formats
[index
].cfFormat
= CF_INETURL
;
654 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
655 m_formats
[index
].lindex
= -1;
656 m_formats
[index
].ptd
= nullptr;
657 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
660 m_formats
[index
].cfFormat
= CF_SHELLURL
;
661 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
662 m_formats
[index
].lindex
= -1;
663 m_formats
[index
].ptd
= nullptr;
664 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
668 m_formats
[index
].cfFormat
= CF_FILE_ATTRIBUTES_ARRAY
;
669 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
670 m_formats
[index
].lindex
= -1;
671 m_formats
[index
].ptd
= nullptr;
672 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
675 if (containsExistingFiles
&& localonly
)
677 m_formats
[index
].cfFormat
= CF_HDROP
;
678 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
679 m_formats
[index
].lindex
= -1;
680 m_formats
[index
].ptd
= nullptr;
681 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
684 else if (containsExistingFiles
)
686 m_formats
[index
].cfFormat
= CF_FILECONTENTS
;
687 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
688 m_formats
[index
].lindex
= -1;
689 m_formats
[index
].ptd
= nullptr;
690 m_formats
[index
].tymed
= TYMED_ISTREAM
;
693 m_formats
[index
].cfFormat
= CF_FILEDESCRIPTOR
;
694 m_formats
[index
].dwAspect
= DVASPECT_CONTENT
;
695 m_formats
[index
].lindex
= -1;
696 m_formats
[index
].ptd
= nullptr;
697 m_formats
[index
].tymed
= TYMED_HGLOBAL
;
700 // clear possible leftovers
701 while (index
< GITDATAOBJECT_NUMFORMATS
)
703 m_formats
[index
].cfFormat
= 0;
704 m_formats
[index
].dwAspect
= 0;
705 m_formats
[index
].lindex
= -1;
706 m_formats
[index
].ptd
= nullptr;
707 m_formats
[index
].tymed
= 0;
712 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector
<FORMATETC
>& vec
, bool localonly
, bool containsExistingFiles
)
715 , m_localonly(localonly
)
716 , m_containsExistingFiles(containsExistingFiles
)
718 for (size_t i
= 0; i
< vec
.size(); ++i
)
719 m_vecFormatEtc
.push_back(vec
[i
]);
720 Init(localonly
, containsExistingFiles
);
723 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector
<FORMATETC
*>& vec
, bool localonly
, bool containsExistingFiles
)
726 , m_localonly(localonly
)
727 , m_containsExistingFiles(containsExistingFiles
)
729 for (size_t i
= 0; i
< vec
.size(); ++i
)
730 m_vecFormatEtc
.push_back(*vec
[i
]);
731 Init(localonly
, containsExistingFiles
);
734 STDMETHODIMP
CGitEnumFormatEtc::QueryInterface(REFIID refiid
, void** ppv
)
739 if (IID_IUnknown
== refiid
|| IID_IEnumFORMATETC
== refiid
)
740 *ppv
= static_cast<IEnumFORMATETC
*>(this);
742 return E_NOINTERFACE
;
748 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::AddRef(void)
750 return ++m_cRefCount
;
753 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::Release(void)
756 if (m_cRefCount
== 0)
764 STDMETHODIMP
CGitEnumFormatEtc::Next(ULONG celt
, LPFORMATETC lpFormatEtc
, ULONG
* pceltFetched
)
768 if (!pceltFetched
&& celt
!= 1) // pceltFetched can be NULL only for 1 item request
776 if (m_iCur
>= GITDATAOBJECT_NUMFORMATS
)
779 ULONG cReturn
= celt
;
781 while (m_iCur
< (GITDATAOBJECT_NUMFORMATS
+ m_vecFormatEtc
.size()) && cReturn
> 0)
783 if (m_iCur
< GITDATAOBJECT_NUMFORMATS
)
784 *lpFormatEtc
++ = m_formats
[m_iCur
++];
786 *lpFormatEtc
++ = m_vecFormatEtc
[m_iCur
++ - GITDATAOBJECT_NUMFORMATS
];
791 *pceltFetched
= celt
- cReturn
;
793 return (cReturn
== 0) ? S_OK
: S_FALSE
;
796 STDMETHODIMP
CGitEnumFormatEtc::Skip(ULONG celt
)
798 if ((m_iCur
+ int(celt
)) >= (GITDATAOBJECT_NUMFORMATS
+ m_vecFormatEtc
.size()))
804 STDMETHODIMP
CGitEnumFormatEtc::Reset(void)
810 STDMETHODIMP
CGitEnumFormatEtc::Clone(IEnumFORMATETC
** ppCloneEnumFormatEtc
)
812 if (!ppCloneEnumFormatEtc
)
815 CGitEnumFormatEtc
*newEnum
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_localonly
, m_containsExistingFiles
);
817 return E_OUTOFMEMORY
;
820 newEnum
->m_iCur
= m_iCur
;
821 *ppCloneEnumFormatEtc
= newEnum
;