1 /* 2 * Trash virtual folder support. The trashing engine is implemented in trash.c 3 * 4 * Copyright (C) 2006 Mikolaj Zalewski 5 * Copyright (C) 2009 Andrew Hill 6 * Copyright (C) 2017 Giannis Adamopoulos 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include <precomp.h> 24 25 WINE_DEFAULT_DEBUG_CHANNEL (shell); 26 27 class CRecyclerDropTarget : 28 public CComObjectRootEx<CComMultiThreadModelNoCS>, 29 public IDropTarget 30 { 31 private: 32 BOOL fAcceptFmt; /* flag for pending Drop */ 33 UINT cfShellIDList; 34 35 static HRESULT _DoDeleteDataObject(IDataObject *pda, DWORD fMask) 36 { 37 HRESULT hr; 38 STGMEDIUM medium; 39 FORMATETC formatetc; 40 InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL); 41 hr = pda->GetData(&formatetc, &medium); 42 if (FAILED_UNEXPECTEDLY(hr)) 43 return hr; 44 45 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); 46 if (!lpdf) 47 { 48 ERR("Error locking global\n"); 49 ReleaseStgMedium(&medium); 50 return E_FAIL; 51 } 52 53 /* Delete them */ 54 SHFILEOPSTRUCTW FileOp; 55 ZeroMemory(&FileOp, sizeof(FileOp)); 56 FileOp.wFunc = FO_DELETE; 57 FileOp.pFrom = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);; 58 if ((fMask & CMIC_MASK_SHIFT_DOWN) == 0) 59 FileOp.fFlags = FOF_ALLOWUNDO; 60 TRACE("Deleting file (just the first) = %s, allowundo: %d\n", debugstr_w(FileOp.pFrom), (FileOp.fFlags == FOF_ALLOWUNDO)); 61 62 int res = SHFileOperationW(&FileOp); 63 if (res) 64 { 65 ERR("SHFileOperation failed with 0x%x\n", res); 66 hr = E_FAIL; 67 } 68 69 GlobalUnlock(medium.hGlobal); 70 ReleaseStgMedium(&medium); 71 72 return hr; 73 } 74 75 struct DeleteThreadData { 76 IStream *s; 77 DWORD fMask; 78 }; 79 80 static DWORD WINAPI _DoDeleteThreadProc(LPVOID lpParameter) 81 { 82 DeleteThreadData *data = static_cast<DeleteThreadData*>(lpParameter); 83 CoInitialize(NULL); 84 IDataObject *pDataObject; 85 HRESULT hr = CoGetInterfaceAndReleaseStream (data->s, IID_PPV_ARG(IDataObject, &pDataObject)); 86 if (SUCCEEDED(hr)) 87 { 88 _DoDeleteDataObject(pDataObject, data->fMask); 89 } 90 pDataObject->Release(); 91 CoUninitialize(); 92 HeapFree(GetProcessHeap(), 0, data); 93 return 0; 94 } 95 96 static void _DoDeleteAsync(IDataObject *pda, DWORD fMask) 97 { 98 DeleteThreadData *data = static_cast<DeleteThreadData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(DeleteThreadData))); 99 data->fMask = fMask; 100 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pda, &data->s); 101 SHCreateThread(_DoDeleteThreadProc, data, NULL, NULL); 102 } 103 104 public: 105 CRecyclerDropTarget() 106 { 107 fAcceptFmt = FALSE; 108 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); 109 } 110 111 STDMETHODIMP 112 DragEnter(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override 113 { 114 TRACE("Recycle bin drag over (%p)\n", this); 115 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */ 116 fAcceptFmt = TRUE; 117 118 *pdwEffect = DROPEFFECT_MOVE; 119 return S_OK; 120 } 121 122 STDMETHODIMP 123 DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override 124 { 125 TRACE("(%p)\n", this); 126 127 if (!pdwEffect) 128 return E_INVALIDARG; 129 130 *pdwEffect = DROPEFFECT_MOVE; 131 132 return S_OK; 133 } 134 135 STDMETHODIMP 136 DragLeave() override 137 { 138 TRACE("(%p)\n", this); 139 140 fAcceptFmt = FALSE; 141 142 return S_OK; 143 } 144 145 STDMETHODIMP 146 Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override 147 { 148 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect); 149 150 FORMATETC fmt; 151 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 152 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); 153 154 /* Handle cfShellIDList Drop objects here, otherwise send the appropriate message to other software */ 155 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 156 { 157 DWORD fMask = 0; 158 159 if ((grfKeyState & MK_SHIFT) == MK_SHIFT) 160 fMask |= CMIC_MASK_SHIFT_DOWN; 161 162 _DoDeleteAsync(pDataObject, fMask); 163 } 164 else 165 { 166 /* 167 * TODO call SetData on the data object with format CFSTR_TARGETCLSID 168 * set to the Recycle Bin's class identifier CLSID_RecycleBin. 169 */ 170 } 171 return S_OK; 172 } 173 174 DECLARE_NOT_AGGREGATABLE(CRecyclerDropTarget) 175 176 DECLARE_PROTECT_FINAL_CONSTRUCT() 177 178 BEGIN_COM_MAP(CRecyclerDropTarget) 179 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) 180 END_COM_MAP() 181 }; 182 183 HRESULT CRecyclerDropTarget_CreateInstance(REFIID riid, LPVOID * ppvOut) 184 { 185 return ShellObjectCreator<CRecyclerDropTarget>(riid, ppvOut); 186 } 187