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