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.
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
)
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(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
));
215 return E_OUTOFMEMORY
;
216 DWORD
* effect
= (DWORD
*)GlobalLock(data
);
218 return E_OUTOFMEMORY
;
219 (*effect
) = DROPEFFECT_COPY
;
221 pmedium
->hGlobal
= data
;
222 pmedium
->tymed
= TYMED_HGLOBAL
;
225 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_TEXT
))
228 // create the string from the path list
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();
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;
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
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();
264 text
+= g_Git
.CombinePath(m_gitPaths
[i
]);
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;
279 else if ((pformatetcIn
->tymed
& TYMED_HGLOBAL
) && (pformatetcIn
->dwAspect
== DVASPECT_CONTENT
) && (pformatetcIn
->cfFormat
== CF_HDROP
))
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())
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
);
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())
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
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
);
321 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
322 GlobalUnlock(pmedium
->hGlobal
);
324 pmedium
->pUnkForRelease
= nullptr;
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
);
351 memcpy(pMem
, pBuffer
.get(), nBufferSize
);
352 GlobalUnlock(pmedium
->hGlobal
);
354 pmedium
->pUnkForRelease
= nullptr;
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
]);
369 return DV_E_FORMATETC
;
372 STDMETHODIMP
GitDataObject::GetDataHere(FORMATETC
* /*pformatetc*/, STGMEDIUM
* /*pmedium*/)
377 STDMETHODIMP
GitDataObject::QueryGetData(FORMATETC
* pformatetc
)
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
)
392 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
393 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
394 ((pformatetc
->cfFormat
== CF_TEXT
) || (pformatetc
->cfFormat
== CF_UNICODETEXT
) || (pformatetc
->cfFormat
== CF_PREFERREDDROPEFFECT
)))
398 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
399 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
400 (pformatetc
->cfFormat
== CF_FILEDESCRIPTOR
) &&
401 !m_revision
.IsEmpty() &&
402 m_containsExistingFiles
)
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
)
414 if ((pformatetc
->tymed
& TYMED_HGLOBAL
) &&
415 (pformatetc
->dwAspect
== DVASPECT_CONTENT
) &&
416 (pformatetc
->cfFormat
== CF_FILE_ATTRIBUTES_ARRAY
) &&
417 m_containsExistingFiles
)
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
))
433 STDMETHODIMP
GitDataObject::GetCanonicalFormatEtc(FORMATETC
* /*pformatectIn*/, FORMATETC
* pformatetcOut
)
437 return DATA_S_SAMEFORMATETC
;
440 STDMETHODIMP
GitDataObject::SetData(FORMATETC
* pformatetc
, STGMEDIUM
* pmedium
, BOOL fRelease
)
442 if (!pformatetc
|| !pmedium
)
445 FORMATETC
* fetc
= new (std::nothrow
) FORMATETC
;
446 STGMEDIUM
* pStgMed
= new (std::nothrow
) STGMEDIUM
;
448 if (!fetc
|| !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
);
477 m_vecFormatEtc
.push_back(fetc
);
482 CopyMedium(pStgMed
, pmedium
, pformatetc
);
483 m_vecStgMedium
.push_back(pStgMed
);
488 STDMETHODIMP
GitDataObject::EnumFormatEtc(DWORD dwDirection
, IEnumFORMATETC
** ppenumFormatEtc
)
490 if (!ppenumFormatEtc
)
493 *ppenumFormatEtc
= nullptr;
497 *ppenumFormatEtc
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_revision
.IsEmpty(), m_containsExistingFiles
);
498 if (!*ppenumFormatEtc
)
499 return E_OUTOFMEMORY
;
500 (*ppenumFormatEtc
)->AddRef();
508 STDMETHODIMP
GitDataObject::DAdvise(FORMATETC
* /*pformatetc*/, DWORD
/*advf*/, IAdviseSink
* /*pAdvSink*/, DWORD
* /*pdwConnection*/)
510 return OLE_E_ADVISENOTSUPPORTED
;
513 STDMETHODIMP
GitDataObject::DUnadvise(DWORD
/*dwConnection*/)
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
)
528 pMedDest
->hGlobal
= (HGLOBAL
)OleDuplicateData(pMedSrc
->hGlobal
, pFmtSrc
->cfFormat
, 0);
531 pMedDest
->hBitmap
= (HBITMAP
)OleDuplicateData(pMedSrc
->hBitmap
, pFmtSrc
->cfFormat
, 0);
534 pMedDest
->hMetaFilePict
= (HMETAFILEPICT
)OleDuplicateData(pMedSrc
->hMetaFilePict
, pFmtSrc
->cfFormat
, 0);
537 pMedDest
->hEnhMetaFile
= (HENHMETAFILE
)OleDuplicateData(pMedSrc
->hEnhMetaFile
, pFmtSrc
->cfFormat
, 0);
540 pMedSrc
->lpszFileName
= (LPOLESTR
)OleDuplicateData(pMedSrc
->lpszFileName
, pFmtSrc
->cfFormat
, 0);
543 pMedDest
->pstm
= pMedSrc
->pstm
;
544 pMedSrc
->pstm
->AddRef();
547 pMedDest
->pstg
= pMedSrc
->pstg
;
548 pMedSrc
->pstg
->AddRef();
554 pMedDest
->tymed
= pMedSrc
->tymed
;
555 pMedDest
->pUnkForRelease
= nullptr;
556 if (pMedSrc
->pUnkForRelease
)
558 pMedDest
->pUnkForRelease
= pMedSrc
->pUnkForRelease
;
559 pMedSrc
->pUnkForRelease
->AddRef();
564 //////////////////////////////////////////////////////////////////////////
566 //////////////////////////////////////////////////////////////////////////
567 HRESULT STDMETHODCALLTYPE
GitDataObject::SetAsyncMode(BOOL fDoOpAsync
)
569 m_bIsAsync
= fDoOpAsync
;
573 HRESULT STDMETHODCALLTYPE
GitDataObject::GetAsyncMode(BOOL
* pfIsOpAsync
)
578 *pfIsOpAsync
= m_bIsAsync
;
583 HRESULT STDMETHODCALLTYPE
GitDataObject::StartOperation(IBindCtx
* /*pbcReserved*/)
585 m_bInOperation
= TRUE
;
589 HRESULT STDMETHODCALLTYPE
GitDataObject::InOperation(BOOL
* pfInAsyncOp
)
594 *pfInAsyncOp
= m_bInOperation
;
599 HRESULT STDMETHODCALLTYPE
GitDataObject::EndOperation(HRESULT
/*hResult*/, IBindCtx
* /*pbcReserved*/, DWORD
/*dwEffects*/)
601 m_bInOperation
= FALSE
;
605 HRESULT
GitDataObject::SetDropDescription(DROPIMAGETYPE image
, LPCTSTR format
, LPCTSTR insert
)
607 if (!format
|| !insert
)
610 FORMATETC fetc
= { 0 };
611 fetc
.cfFormat
= (CLIPFORMAT
)RegisterClipboardFormat(CFSTR_DROPDESCRIPTION
);
612 fetc
.dwAspect
= DVASPECT_CONTENT
;
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
)
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
)
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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;
716 CGitEnumFormatEtc::CGitEnumFormatEtc(const std::vector
<FORMATETC
>& vec
, bool localonly
, bool containsExistingFiles
)
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
)
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
)
743 if (IID_IUnknown
== refiid
|| IID_IEnumFORMATETC
== refiid
)
744 *ppv
= static_cast<IEnumFORMATETC
*>(this);
746 return E_NOINTERFACE
;
752 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::AddRef(void)
754 return ++m_cRefCount
;
757 STDMETHODIMP_(ULONG
) CGitEnumFormatEtc::Release(void)
760 if (m_cRefCount
== 0)
768 STDMETHODIMP
CGitEnumFormatEtc::Next(ULONG celt
, LPFORMATETC lpFormatEtc
, ULONG
* pceltFetched
)
772 if (!pceltFetched
&& celt
!= 1) // pceltFetched can be NULL only for 1 item request
780 if (m_iCur
>= GITDATAOBJECT_NUMFORMATS
)
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
++];
790 *lpFormatEtc
++ = m_vecFormatEtc
[m_iCur
++ - GITDATAOBJECT_NUMFORMATS
];
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()))
808 STDMETHODIMP
CGitEnumFormatEtc::Reset(void)
814 STDMETHODIMP
CGitEnumFormatEtc::Clone(IEnumFORMATETC
** ppCloneEnumFormatEtc
)
816 if (!ppCloneEnumFormatEtc
)
819 CGitEnumFormatEtc
*newEnum
= new (std::nothrow
) CGitEnumFormatEtc(m_vecFormatEtc
, m_localonly
, m_containsExistingFiles
);
821 return E_OUTOFMEMORY
;
824 newEnum
->m_iCur
= m_iCur
;
825 *ppCloneEnumFormatEtc
= newEnum
;