1 /*
2  * PROJECT:     mydocs
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     MyDocs implementation
5  * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 #include "precomp.hpp"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(mydocs);
11 
12 static CLIPFORMAT g_cfHIDA = 0;
13 
14 CMyDocsDropHandler::CMyDocsDropHandler()
15 {
16     InterlockedIncrement(&g_ModuleRefCnt);
17 }
18 
19 CMyDocsDropHandler::~CMyDocsDropHandler()
20 {
21     InterlockedDecrement(&g_ModuleRefCnt);
22 }
23 
24 // IDropTarget
25 STDMETHODIMP
26 CMyDocsDropHandler::DragEnter(IDataObject *pDataObject, DWORD dwKeyState,
27                                 POINTL pt, DWORD *pdwEffect)
28 {
29     TRACE("(%p)\n", this);
30 
31     *pdwEffect &= DROPEFFECT_COPY; // Copy only
32 
33     return S_OK;
34 }
35 
36 STDMETHODIMP
37 CMyDocsDropHandler::DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
38 {
39     TRACE("(%p)\n", this);
40 
41     *pdwEffect &= DROPEFFECT_COPY; // Copy only
42 
43     return S_OK;
44 }
45 
46 STDMETHODIMP CMyDocsDropHandler::DragLeave()
47 {
48     TRACE("(%p)\n", this);
49     return S_OK;
50 }
51 
52 STDMETHODIMP
53 CMyDocsDropHandler::Drop(IDataObject *pDataObject, DWORD dwKeyState,
54                          POINTL pt, DWORD *pdwEffect)
55 {
56     TRACE("(%p)\n", this);
57 
58     if (!pDataObject)
59     {
60         ERR("pDataObject is NULL\n");
61         *pdwEffect = 0;
62         DragLeave();
63         return E_POINTER;
64     }
65 
66     CComPtr<IShellFolder> pDesktop;
67     HRESULT hr = SHGetDesktopFolder(&pDesktop);
68     if (FAILED_UNEXPECTEDLY(hr))
69         return hr;
70 
71     // get the clipboard format
72     if (g_cfHIDA == 0)
73         g_cfHIDA = ::RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
74 
75     // Retrieve an HIDA (handle of IDA)
76     STGMEDIUM medium;
77     FORMATETC fmt = { g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
78     hr = pDataObject->GetData(&fmt, &medium);
79     if (FAILED_UNEXPECTEDLY(hr))
80     {
81         *pdwEffect = 0;
82         DragLeave();
83         return E_FAIL;
84     }
85 
86     // lock HIDA
87     LPIDA pida = reinterpret_cast<LPIDA>(GlobalLock(medium.hGlobal));
88     UINT iItem, cItems = pida->cidl;
89 
90     // get the path of "My Documents"
91     WCHAR szzDir[MAX_PATH + 1];
92     SHGetSpecialFolderPathW(NULL, szzDir, CSIDL_PERSONAL, FALSE);
93     szzDir[lstrlenW(szzDir) + 1] = 0; // ends with double NULs
94 
95     // for all source items
96     CStringW strSrcList;
97     WCHAR szSrc[MAX_PATH];
98     PCIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pida);
99     for (iItem = 0; iItem < cItems; ++iItem)
100     {
101         // query source pidl
102         PCITEMID_CHILD pidlChild = HIDA_GetPIDLItem(pida, iItem);
103         CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlParent, pidlChild));
104 
105         // can get path?
106         szSrc[0] = 0;
107         if (!SHGetPathFromIDListW(pidl, szSrc))
108         {
109             // try to retrieve path from desktop
110             STRRET strret;
111             hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret);
112             if (FAILED_UNEXPECTEDLY(hr))
113                 break;
114             hr = StrRetToBufW(&strret, pidl, szSrc, _countof(szSrc));
115             if (FAILED_UNEXPECTEDLY(hr))
116                 break;
117             if (!PathFileExistsW(szSrc))
118                 break;
119         }
120 
121         if (iItem > 0)
122             strSrcList += L'|'; // separator is '|'
123         strSrcList += szSrc;
124     }
125 
126     // unlock HIDA
127     GlobalUnlock(medium.hGlobal);
128 
129     if (iItem != cItems)
130     {
131         // source not found
132         CStringW strText;
133         strText.Format(IDS_NOSRCFILEFOUND, szSrc[0] ? szSrc : L"(null)");
134         MessageBoxW(NULL, strText, NULL, MB_ICONERROR);
135 
136         *pdwEffect = 0;
137         DragLeave();
138         return E_FAIL;
139     }
140 
141     strSrcList += L"||"; // double separators
142 
143     // lock the buffer
144     LPWSTR pszzSrcList = strSrcList.GetBuffer();
145 
146     // convert every separator to a NUL
147     INT cch = strSrcList.GetLength();
148     for (INT i = 0; i < cch; ++i)
149     {
150         if (pszzSrcList[i] == L'|')
151             pszzSrcList[i] = L'\0';
152     }
153 
154     // copy them
155     SHFILEOPSTRUCTW fileop = { NULL };
156     fileop.wFunc = FO_COPY;
157     fileop.pFrom = pszzSrcList;
158     fileop.pTo = szzDir;
159     fileop.fFlags = FOF_ALLOWUNDO | FOF_FILESONLY | FOF_MULTIDESTFILES | FOF_NOCONFIRMMKDIR;
160     SHFileOperationW(&fileop);
161 
162     // unlock buffer
163     strSrcList.ReleaseBuffer();
164 
165     DragLeave();
166     return hr;
167 }
168 
169 // IPersistFile
170 STDMETHODIMP CMyDocsDropHandler::GetCurFile(LPOLESTR *ppszFileName)
171 {
172     FIXME("(%p)\n", this);
173     return E_NOTIMPL;
174 }
175 
176 STDMETHODIMP CMyDocsDropHandler::IsDirty()
177 {
178     FIXME("(%p)\n", this);
179     return E_NOTIMPL;
180 }
181 
182 STDMETHODIMP CMyDocsDropHandler::Load(LPCOLESTR pszFileName, DWORD dwMode)
183 {
184     return S_OK;
185 }
186 
187 STDMETHODIMP CMyDocsDropHandler::Save(LPCOLESTR pszFileName, BOOL fRemember)
188 {
189     FIXME("(%p)\n", this);
190     return E_NOTIMPL;
191 }
192 
193 STDMETHODIMP CMyDocsDropHandler::SaveCompleted(LPCOLESTR pszFileName)
194 {
195     FIXME("(%p)\n", this);
196     return E_NOTIMPL;
197 }
198 
199 // IPersist
200 STDMETHODIMP CMyDocsDropHandler::GetClassID(CLSID * lpClassId)
201 {
202     TRACE("(%p)\n", this);
203 
204     if (!lpClassId)
205     {
206         ERR("lpClassId is NULL\n");
207         return E_POINTER;
208     }
209 
210     *lpClassId = CLSID_MyDocsDropHandler;
211 
212     return S_OK;
213 }
214