xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/PanelDrag.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // PanelDrag.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef UNDER_CE
6 #include <winuserm.h>
7 #endif
8 
9 #include "../../../../C/7zVersion.h"
10 #include "../../../../C/CpuArch.h"
11 
12 #include "../../../Common/StringConvert.h"
13 #include "../../../Common/Wildcard.h"
14 
15 #include "../../../Windows/COM.h"
16 #include "../../../Windows/MemoryGlobal.h"
17 #include "../../../Windows/Menu.h"
18 #include "../../../Windows/FileDir.h"
19 #include "../../../Windows/FileName.h"
20 #include "../../../Windows/Shell.h"
21 
22 #include "../Common/ArchiveName.h"
23 #include "../Common/CompressCall.h"
24 #include "../Common/ExtractingFilePath.h"
25 
26 #include "MessagesDialog.h"
27 
28 #include "App.h"
29 #include "EnumFormatEtc.h"
30 #include "FormatUtils.h"
31 #include "LangUtils.h"
32 
33 #include "resource.h"
34 #include "../Explorer/resource.h"
35 
36 using namespace NWindows;
37 using namespace NFile;
38 using namespace NDir;
39 
40 #ifndef _UNICODE
41 extern bool g_IsNT;
42 #endif
43 
44 #define PRF(x)
45 #define PRF_W(x)
46 // #define PRF2(x)
47 #define PRF3(x)
48 #define PRF3_W(x)
49 #define PRF4(x)
50 // #define PRF4(x) OutputDebugStringA(x)
51 // #define PRF4_W(x) OutputDebugStringW(x)
52 
53 // #define SHOW_DEBUG_DRAG
54 
55 #ifdef SHOW_DEBUG_DRAG
56 
57 #define PRF_(x) { x; }
58 
Print_Point(const char * name,DWORD keyState,const POINTL & pt,DWORD effect)59 static void Print_Point(const char *name, DWORD keyState, const POINTL &pt, DWORD effect)
60 {
61   AString s (name);
62   s += " x=";  s.Add_UInt32((unsigned)pt.x);
63   s += " y=";  s.Add_UInt32((unsigned)pt.y);
64   s += " k=";  s.Add_UInt32(keyState);
65   s += " e=";  s.Add_UInt32(effect);
66   PRF4(s);
67 }
68 
69 #else
70 
71 #define PRF_(x)
72 
73 #endif
74 
75 
76 #define kTempDirPrefix  FTEXT("7zE")
77 
78 // all versions: k_Format_7zip_SetTargetFolder format to transfer folder path from target to source
79 static LPCTSTR const k_Format_7zip_SetTargetFolder = TEXT("7-Zip::SetTargetFolder");
80 // new v23 formats:
81 static LPCTSTR const k_Format_7zip_SetTransfer = TEXT("7-Zip::SetTransfer");
82 static LPCTSTR const k_Format_7zip_GetTransfer = TEXT("7-Zip::GetTransfer");
83 
84 /*
85   Win10: clipboard formats.
86   There are about 16K free ids (formats) per system that can be
87   registered with RegisterClipboardFormat() with different names.
88   Probably that 16K ids space is common for ids registering for both
89   formats: RegisterClipboardFormat(), and registered window classes:
90   RegisterClass(). But ids for window classes will be deleted from
91   the list after process finishing. And registered clipboard
92   formats probably will be deleted from the list only after reboot.
93 */
94 
95 // static bool const g_CreateArchive_for_Drag_from_7zip = false;
96 // static bool const g_CreateArchive_for_Drag_from_Explorer = true;
97     // = false; // for debug
98 
99 /*
100 How DoDragDrop() works:
101 {
102   IDropSource::QueryContinueDrag()  (keyState & MK_LBUTTON) != 0
103   IDropTarget::Enter()
104     IDropSource::GiveFeedback()
105   IDropTarget::DragOver()
106     IDropSource::GiveFeedback()
107 
108   for()
109   {
110     IDropSource::QueryContinueDrag()  (keyState & MK_LBUTTON) != 0
111     IDropTarget::DragOver()           (keyState & MK_LBUTTON) != 0
112       IDropSource::GiveFeedback()
113   }
114 
115   {
116     // DoDragDrop() in Win10 before calling // QueryContinueDrag()
117     // with (*(keyState & MK_LBUTTON) == 0) probably calls:
118     //   1) IDropTarget::DragOver() with same point values (x,y), but (keyState & MK_LBUTTON) != 0)
119     //   2) IDropSource::GiveFeedback().
120     // so DropSource can know exact GiveFeedback(effect) mode just before LBUTTON releasing.
121 
122     if (IDropSource::QueryContinueDrag() for (keyState & MK_LBUTTON) == 0
123       returns DRAGDROP_S_DROP), it will call
124     IDropTarget::Drop()
125   }
126   or
127   {
128     IDropSource::QueryContinueDrag()
129     IDropTarget::DragLeave()
130     IDropSource::GiveFeedback(0)
131   }
132   or
133   {
134     if (IDropSource::QueryContinueDrag()
135       returns DRAGDROP_S_CANCEL)
136     IDropTarget::DragLeave()
137   }
138 }
139 */
140 
141 
142 // ---------- CDropTarget ----------
143 
144 static const UInt32 k_Struct_Id_SetTranfer = 2;  // it's our selected id
145 static const UInt32 k_Struct_Id_GetTranfer = 3;  // it's our selected id
146 
147 static const UInt64 k_Program_Id = 1; // "7-Zip"
148 
149 enum E_Program_ISA
150 {
151   k_Program_ISA_x86   = 2,
152   k_Program_ISA_x64   = 3,
153   k_Program_ISA_armt  = 4,
154   k_Program_ISA_arm64 = 5,
155   k_Program_ISA_arm32 = 6,
156   k_Program_ISA_ia64  = 9
157 };
158 
159 #define k_Program_Ver ((MY_VER_MAJOR << 16) | MY_VER_MINOR)
160 
161 
162 // k_SourceFlags_* are flags that are sent from Source to Target
163 
164 static const UInt32 k_SourceFlags_DoNotProcessInTarget = 1 << 1;
165 /* Do not process in Target. Source will process operation instead of Target.
166    By default Target processes Drop opearation. */
167 // static const UInt32 k_SourceFlags_ProcessInTarget      = 1 << 2;
168 
169 static const UInt32 k_SourceFlags_DoNotWaitFinish   = 1 << 3;
170 static const UInt32 k_SourceFlags_WaitFinish        = 1 << 4;
171 /* usually Source needs WaitFinish, if temp files were created. */
172 
173 static const UInt32 k_SourceFlags_TempFiles         = 1 << 6;
174 static const UInt32 k_SourceFlags_NamesAreParent    = 1 << 7;
175 /* if returned path list for GetData(CF_HDROP) contains
176    path of parent temp folder instead of final paths of items
177    that will be extracted later from archive */
178 
179 static const UInt32 k_SourceFlags_SetTargetFolder   = 1 << 8;
180 /* SetData::("SetTargetFolder") was called (with empty or non-empty string) */
181 
182 static const UInt32 k_SourceFlags_SetTargetFolder_NonEmpty  = 1 << 9;
183 /* SetData::("SetTargetFolder") was called with non-empty string */
184 
185 static const UInt32 k_SourceFlags_NeedExtractOpToFs = 1 << 10;
186 
187 static const UInt32 k_SourceFlags_Copy_WasCalled = 1 << 11;
188 
189 static const UInt32 k_SourceFlags_LeftButton        = 1 << 14;
190 static const UInt32 k_SourceFlags_RightButton       = 1 << 15;
191 
192 
193 static const UInt32 k_TargetFlags_WasCanceled = 1 << 0;
194 static const UInt32 k_TargetFlags_MustBeProcessedBySource = 1 << 1;
195 static const UInt32 k_TargetFlags_WasProcessed    = 1 << 2;
196 static const UInt32 k_TargetFlags_DoNotWaitFinish = 1 << 3;
197 static const UInt32 k_TargetFlags_WaitFinish      = 1 << 4;
198 static const UInt32 k_TargetFlags_MenuWasShown    = 1 << 16;
199 
200 struct CDataObject_TransferBase
201 {
202   UInt32 Struct_Id;
203   UInt32 Struct_Size;
204 
205   UInt64 Program_Id;
206   UInt32 Program_Ver_Main;
207   UInt32 Program_Ver_Build;
208   UInt32 Program_ISA;
209   UInt32 Program_Flags;
210 
211   UInt32 ProcessId;
212   UInt32 _reserved1[7];
213 
214 protected:
215   void Init_Program();
216 };
217 
218 
Init_Program()219 void CDataObject_TransferBase::Init_Program()
220 {
221   Program_Id = k_Program_Id;
222   Program_ISA =
223     #if defined(MY_CPU_AMD64)
224       k_Program_ISA_x64
225     #elif defined(MY_CPU_X86)
226       k_Program_ISA_x86
227     #elif defined(MY_CPU_ARM64)
228       k_Program_ISA_arm64
229     #elif defined(MY_CPU_ARM32)
230       k_Program_ISA_arm32
231     #elif defined(MY_CPU_ARMT) || defined(MY_CPU_ARM)
232       k_Program_ISA_armt
233     #elif defined(MY_CPU_IA64)
234       k_Program_ISA_ia64
235     #else
236       0
237     #endif
238       ;
239   Program_Flags = sizeof(size_t);
240   Program_Ver_Main = k_Program_Ver;
241   // Program_Ver_Build = 0;
242   ProcessId = GetCurrentProcessId();
243 }
244 
245 
246 #if defined(__GNUC__) && !defined(__clang__)
247 /* 'void* memset(void*, int, size_t)' clearing an object
248     of non-trivial type 'struct CDataObject_SetTransfer' */
249 #pragma GCC diagnostic ignored "-Wclass-memaccess"
250 #endif
251 
252 
253 struct CDataObject_GetTransfer:
254 public CDataObject_TransferBase
255 {
256   UInt32 Flags;
257 
258   UInt32 _reserved2[11];
259 
CDataObject_GetTransferCDataObject_GetTransfer260   CDataObject_GetTransfer()
261   {
262     memset(this, 0, sizeof(*this));
263     Init_Program();
264     Struct_Id = k_Struct_Id_GetTranfer;
265     Struct_Size = sizeof(*this);
266   }
267 
CheckCDataObject_GetTransfer268   bool Check() const
269   {
270     return Struct_Size >= sizeof(*this) && Struct_Id == k_Struct_Id_GetTranfer;
271   }
272 };
273 
274 
275 enum Enum_FolderType
276 {
277   k_FolderType_None,
278   k_FolderType_Unknown = 1,
279   k_FolderType_Fs = 2,
280   k_FolderType_AltStreams = 3,
281   k_FolderType_Archive = 4
282 };
283 
284 struct CTargetTransferInfo
285 {
286   UInt32 Flags;
287   UInt32 FuncType;
288 
289   UInt32 KeyState;
290   UInt32 OkEffects;
291   POINTL Point;
292 
293   UInt32 Cmd_Effect;
294   UInt32 Cmd_Type;
295   UInt32 FolderType;
296   UInt32 _reserved3[3];
297 
CTargetTransferInfoCTargetTransferInfo298   CTargetTransferInfo()
299   {
300     memset(this, 0, sizeof(*this));
301   }
302 };
303 
304 struct CDataObject_SetTransfer:
305 public CDataObject_TransferBase
306 {
307   CTargetTransferInfo Target;
308 
InitCDataObject_SetTransfer309   void Init()
310   {
311     memset(this, 0, sizeof(*this));
312     Init_Program();
313     Struct_Id = k_Struct_Id_SetTranfer;
314     Struct_Size = sizeof(*this);
315   }
316 
CheckCDataObject_SetTransfer317   bool Check() const
318   {
319     return Struct_Size >= sizeof(*this) && Struct_Id == k_Struct_Id_SetTranfer;
320   }
321 };
322 
323 
324 
325 
326 
327 enum Enum_DragTargetMode
328 {
329   k_DragTargetMode_None   = 0,
330   k_DragTargetMode_Leave  = 1,
331   k_DragTargetMode_Enter  = 2,
332   k_DragTargetMode_Over   = 3,
333   k_DragTargetMode_Drop_Begin = 4,
334   k_DragTargetMode_Drop_End   = 5
335 };
336 
337 
338 // ---- menu ----
339 
340 namespace NDragMenu {
341 
342 enum Enum_CmdId
343 {
344   k_None          = 0,
345   k_Cancel        = 1,
346   k_Copy_Base     = 2, // to fs
347   k_Copy_ToArc    = 3,
348   k_AddToArc      = 4
349   /*
350   k_OpenArc       = 8,
351   k_TestArc       = 9,
352   k_ExtractFiles  = 10,
353   k_ExtractHere   = 11
354   */
355 };
356 
357 struct CCmdLangPair
358 {
359   unsigned CmdId_and_Flags;
360   unsigned LangId;
361 };
362 
363 static const UInt32 k_MenuFlags_CmdMask = (1 << 7) - 1;
364 static const UInt32 k_MenuFlag_Copy = 1 << 14;
365 static const UInt32 k_MenuFlag_Move = 1 << 15;
366 // #define IDS_CANCEL (IDCANCEL + 400)
367 #define IDS_CANCEL 402
368 
369 static const CCmdLangPair g_Pairs[] =
370 {
371   { k_Copy_Base  | k_MenuFlag_Copy,  IDS_COPY },
372   { k_Copy_Base  | k_MenuFlag_Move,  IDS_MOVE },
373   { k_Copy_ToArc | k_MenuFlag_Copy,  IDS_COPY_TO },
374   // { k_Copy_ToArc | k_MenuFlag_Move,  IDS_MOVE_TO }, // IDS_CONTEXT_COMPRESS_TO
375   // { k_OpenArc,      IDS_CONTEXT_OPEN },
376   // { k_ExtractFiles, IDS_CONTEXT_EXTRACT },
377   // { k_ExtractHere,  IDS_CONTEXT_EXTRACT_HERE },
378   // { k_TestArc,      IDS_CONTEXT_TEST },
379   { k_AddToArc   | k_MenuFlag_Copy,  IDS_CONTEXT_COMPRESS },
380   { k_Cancel, IDS_CANCEL }
381 };
382 
383 }
384 
385 
386 class CDropTarget Z7_final:
387   public IDropTarget,
388   public CMyUnknownImp
389 {
390   Z7_COM_UNKNOWN_IMP_1_MT(IDropTarget)
391   STDMETHOD(DragEnter)(IDataObject *dataObject, DWORD keyState, POINTL pt, DWORD *effect) Z7_override;
392   STDMETHOD(DragOver)(DWORD keyState, POINTL pt, DWORD *effect) Z7_override;
393   STDMETHOD(DragLeave)() Z7_override;
394   STDMETHOD(Drop)(IDataObject *dataObject, DWORD keyState, POINTL pt, DWORD *effect) Z7_override;
395 
396   bool m_IsRightButton;
397   bool m_GetTransfer_WasSuccess;
398   bool m_DropIsAllowed;      // = true, if data IDataObject can return CF_HDROP (so we can get list of paths)
399   bool m_PanelDropIsAllowed; // = false, if current target_panel is source_panel.
400                              // check it only if m_DropIsAllowed == true
401                              // we use it to show icon effect that drop is not allowed here.
402 
403   CMyComPtr<IDataObject> m_DataObject; // we set it in DragEnter()
404   UStringVector m_SourcePaths;
405 
406   // int m_DropHighlighted_SelectionIndex;
407   // int m_SubFolderIndex;      // realIndex of item in m_Panel list (if drop cursor to that item)
408   // UString m_DropHighlighted_SubFolderName;   // name of folder in m_Panel list (if drop cursor to that folder)
409 
410   CPanel *m_Panel;
411   bool m_IsAppTarget;        // true, if we want to drop to app window (not to panel)
412 
413   bool m_TargetPath_WasSent_ToDataObject;           // true, if TargetPath was sent
414   bool m_TargetPath_NonEmpty_WasSent_ToDataObject;  // true, if non-empty TargetPath was sent
415   bool m_Transfer_WasSent_ToDataObject;  // true, if Transfer was sent
416   UINT m_Format_7zip_SetTargetFolder;
417   UINT m_Format_7zip_SetTransfer;
418   UINT m_Format_7zip_GetTransfer;
419 
420   UInt32 m_ProcessId; // for sending
421 
422   bool IsItSameDrive() const;
423 
424   // void Try_QueryGetData(IDataObject *dataObject);
425   void LoadNames_From_DataObject(IDataObject *dataObject);
426 
427   UInt32 GetFolderType() const;
428   bool IsFsFolderPath() const;
429   DWORD GetEffect(DWORD keyState, POINTL pt, DWORD allowedEffect) const;
430   void RemoveSelection();
431   void PositionCursor(const POINTL &ptl);
432   UString GetTargetPath() const;
433   bool SendToSource_TargetPath_enable(IDataObject *dataObject, bool enablePath);
434   bool SendToSource_UInt32(IDataObject *dataObject, UINT format, UInt32 value);
435   bool SendToSource_TransferInfo(IDataObject *dataObject,
436       const CTargetTransferInfo &info);
437   void SendToSource_auto(IDataObject *dataObject,
438       const CTargetTransferInfo &info);
SendToSource_Drag(CTargetTransferInfo & info)439   void SendToSource_Drag(CTargetTransferInfo &info)
440   {
441     SendToSource_auto(m_DataObject, info);
442   }
443 
444   void ClearState();
445 
446 public:
447   CDropTarget();
448 
449   CApp *App;
450   int SrcPanelIndex;     // index of D&D source_panel
451   int TargetPanelIndex;  // what panel to use as target_panel of Application
452 };
453 
454 
455 
456 
457 // ---------- CDataObject ----------
458 
459 /*
460   Some programs (like Sticky Notes in Win10) do not like
461   virtual non-existing items (files/dirs) in CF_HDROP format.
462   So we use two versions of CF_HDROP data:
463     m_hGlobal_HDROP_Pre   : the list contains only destination path of temp directory.
464         That directory later will be filled with extracted items.
465     m_hGlobal_HDROP_Final : the list contains paths of all root items that
466         will be created in temp directory by archive extraction operation,
467         or the list of existing fs items, if source is filesystem directory.
468 
469   The DRAWBACK: some programs (like Edge in Win10) can use names from IDataObject::GetData()
470   call that was called before IDropSource::QueryContinueDrag() where we set (UseFinalGlobal = true)
471   So such programs will use non-relevant m_hGlobal_HDROP_Pre item,
472   instead of m_hGlobal_HDROP_Final items.
473 */
474 
475 class CDataObject Z7_final:
476   public IDataObject,
477   public CMyUnknownImp
478 {
479   Z7_COM_UNKNOWN_IMP_1_MT(IDataObject)
480 
481   Z7_COMWF_B GetData(LPFORMATETC pformatetcIn, LPSTGMEDIUM medium) Z7_override;
482   Z7_COMWF_B GetDataHere(LPFORMATETC pformatetc, LPSTGMEDIUM medium) Z7_override;
483   Z7_COMWF_B QueryGetData(LPFORMATETC pformatetc) Z7_override;
484 
GetCanonicalFormatEtc(LPFORMATETC,LPFORMATETC pformatetcOut)485   Z7_COMWF_B GetCanonicalFormatEtc(LPFORMATETC /* pformatetc */, LPFORMATETC pformatetcOut) Z7_override
486   {
487     if (!pformatetcOut)
488       return E_INVALIDARG;
489     pformatetcOut->ptd = NULL;
490     return E_NOTIMPL;
491   }
492 
493   Z7_COMWF_B SetData(LPFORMATETC etc, STGMEDIUM *medium, BOOL release) Z7_override;
494   Z7_COMWF_B EnumFormatEtc(DWORD drection, LPENUMFORMATETC *enumFormatEtc) Z7_override;
495 
DAdvise(FORMATETC *,DWORD,LPADVISESINK,DWORD *)496   Z7_COMWF_B DAdvise(FORMATETC * /* etc */, DWORD /* advf */, LPADVISESINK /* pAdvSink */, DWORD * /* pdwConnection */) Z7_override
497     { return OLE_E_ADVISENOTSUPPORTED; }
DUnadvise(DWORD)498   Z7_COMWF_B DUnadvise(DWORD /* dwConnection */) Z7_override
499     { return OLE_E_ADVISENOTSUPPORTED; }
EnumDAdvise(LPENUMSTATDATA * ppenumAdvise)500   Z7_COMWF_B EnumDAdvise(LPENUMSTATDATA *ppenumAdvise) Z7_override
501   {
502     if (ppenumAdvise)
503       *ppenumAdvise = NULL;
504     return OLE_E_ADVISENOTSUPPORTED;
505   }
506 
507   bool m_PerformedDropEffect_WasSet;
508   bool m_LogicalPerformedDropEffect_WasSet;
509   bool m_DestDirPrefix_FromTarget_WasSet;
510 public:
511   bool m_Transfer_WasSet;
512 private:
513   // GetData formats (source to target):
514   FORMATETC m_Etc;
515   // UINT m_Format_FileOpFlags;
516   // UINT m_Format_PreferredDropEffect;
517 
518   // SetData() formats (target to source):
519   // 7-Zip's format:
520   UINT m_Format_7zip_SetTargetFolder;
521   UINT m_Format_7zip_SetTransfer;
522   UINT m_Format_7zip_GetTransfer; // for GetData()
523 
524   UINT m_Format_PerformedDropEffect;
525   UINT m_Format_LogicalPerformedDropEffect;
526   UINT m_Format_DisableDragText;
527   UINT m_Format_IsShowingLayered;
528   UINT m_Format_IsShowingText;
529   UINT m_Format_DropDescription;
530   UINT m_Format_TargetCLSID;
531 
532   DWORD m_PerformedDropEffect;
533   DWORD m_LogicalPerformedDropEffect;
534 
535   void CopyFromPanelTo_Folder();
536   HRESULT SetData2(const FORMATETC *formatetc, const STGMEDIUM *medium);
537 
538 public:
539   bool IsRightButton;
540   bool IsTempFiles;
541 
542   bool UsePreGlobal;
543   bool DoNotProcessInTarget;
544 
545   bool NeedCall_Copy;
546   bool Copy_WasCalled;
547 
548   NMemory::CGlobal m_hGlobal_HDROP_Pre;
549   NMemory::CGlobal m_hGlobal_HDROP_Final;
550   // NMemory::CGlobal m_hGlobal_FileOpFlags;
551   // NMemory::CGlobal m_hGlobal_PreferredDropEffect;
552 
553   CPanel *Panel;
554   CRecordVector<UInt32> Indices;
555 
556   UString SrcDirPrefix_Temp; // FS directory with source files or Temp
557   UString DestDirPrefix_FromTarget;
558   /* destination Path that was sent by Target via SetData().
559      it can be altstreams prefix.
560      if (!DestDirPrefix_FromTarget.IsEmpty()) m_Panel->CompressDropFiles() was not called by Target.
561      So we must do drop actions in Source */
562   HRESULT Copy_HRESULT;
563   UStringVector Messages;
564 
565   CDataObject();
566 public:
567   CDataObject_SetTransfer m_Transfer;
568 };
569 
570 
571 // for old mingw:
572 #ifndef CFSTR_LOGICALPERFORMEDDROPEFFECT
573 #define CFSTR_LOGICALPERFORMEDDROPEFFECT    TEXT("Logical Performed DropEffect")
574 #endif
575 #ifndef CFSTR_TARGETCLSID
576 #define CFSTR_TARGETCLSID                   TEXT("TargetCLSID")                         // HGLOBAL with a CLSID of the drop target
577 #endif
578 
579 
580 
CDataObject()581 CDataObject::CDataObject()
582 {
583   // GetData formats (source to target):
584   // and we use CF_HDROP format to transfer file paths from source to target:
585   m_Etc.cfFormat = CF_HDROP;
586   m_Etc.ptd = NULL;
587   m_Etc.dwAspect = DVASPECT_CONTENT;
588   m_Etc.lindex = -1;
589   m_Etc.tymed = TYMED_HGLOBAL;
590 
591   // m_Format_FileOpFlags          = RegisterClipboardFormat(TEXT("FileOpFlags"));
592   // m_Format_PreferredDropEffect  = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT); // "Preferred DropEffect"
593 
594   // SetData() formats (target to source):
595   m_Format_7zip_SetTargetFolder = RegisterClipboardFormat(k_Format_7zip_SetTargetFolder);
596   m_Format_7zip_SetTransfer     = RegisterClipboardFormat(k_Format_7zip_SetTransfer);
597   m_Format_7zip_GetTransfer     = RegisterClipboardFormat(k_Format_7zip_GetTransfer);
598 
599   m_Format_PerformedDropEffect  = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT); // "Performed DropEffect"
600   m_Format_LogicalPerformedDropEffect = RegisterClipboardFormat(CFSTR_LOGICALPERFORMEDDROPEFFECT); // "Logical Performed DropEffect"
601   m_Format_DisableDragText      = RegisterClipboardFormat(TEXT("DisableDragText"));
602   m_Format_IsShowingLayered     = RegisterClipboardFormat(TEXT("IsShowingLayered"));
603   m_Format_IsShowingText        = RegisterClipboardFormat(TEXT("IsShowingText"));
604   m_Format_DropDescription      = RegisterClipboardFormat(TEXT("DropDescription"));
605   m_Format_TargetCLSID          = RegisterClipboardFormat(CFSTR_TARGETCLSID);
606 
607   m_PerformedDropEffect = 0;
608   m_LogicalPerformedDropEffect = 0;
609 
610   m_PerformedDropEffect_WasSet = false;
611   m_LogicalPerformedDropEffect_WasSet = false;
612 
613   m_DestDirPrefix_FromTarget_WasSet = false;
614   m_Transfer_WasSet = false;
615 
616   IsRightButton = false;
617   IsTempFiles = false;
618 
619   UsePreGlobal = false;
620   DoNotProcessInTarget = false;
621 
622   NeedCall_Copy = false;
623   Copy_WasCalled = false;
624 
625   Copy_HRESULT = S_OK;
626 }
627 
628 
629 
CopyFromPanelTo_Folder()630 void CDataObject::CopyFromPanelTo_Folder()
631 {
632   try
633   {
634     CCopyToOptions options;
635     options.folder = SrcDirPrefix_Temp;
636     /* 15.13: fixed problem with mouse cursor for password window.
637        DoDragDrop() probably calls SetCapture() to some hidden window.
638        But it's problem, if we show some modal window, like MessageBox.
639        So we return capture to our window.
640        If you know better way to solve the problem, please notify 7-Zip developer.
641     */
642     // MessageBoxW(*Panel, L"test", L"test", 0);
643     /* HWND oldHwnd = */ SetCapture(*Panel);
644     Copy_WasCalled = true;
645     Copy_HRESULT = E_FAIL;
646     Copy_HRESULT = Panel->CopyTo(options, Indices, &Messages);
647     // do we need to restore capture?
648     // ReleaseCapture();
649     // oldHwnd = SetCapture(oldHwnd);
650   }
651   catch(...)
652   {
653     Copy_HRESULT = E_FAIL;
654   }
655 }
656 
657 
658 #ifdef SHOW_DEBUG_DRAG
659 
PrintFormat2(AString & s,unsigned format)660 static void PrintFormat2(AString &s, unsigned format)
661 {
662   s += " ";
663   s += "= format=";
664   s.Add_UInt32(format);
665   s += " ";
666   const int k_len = 512;
667   CHAR temp[k_len];
668   if (GetClipboardFormatNameA(format, temp, k_len) && strlen(temp) != 0)
669     s += temp;
670 }
671 
PrintFormat(const char * title,unsigned format)672 static void PrintFormat(const char *title, unsigned format)
673 {
674   AString s (title);
675   PrintFormat2(s, format);
676   PRF4(s);
677 }
678 
PrintFormat_AndData(const char * title,unsigned format,const void * data,size_t size)679 static void PrintFormat_AndData(const char *title, unsigned format, const void *data, size_t size)
680 {
681   AString s (title);
682   PrintFormat2(s, format);
683   s += " size=";
684   s.Add_UInt32((UInt32)size);
685   for (size_t i = 0; i < size && i < 16; i++)
686   {
687     s += " ";
688     s.Add_UInt32(((const Byte *)data)[i]);
689   }
690   PRF4(s);
691 }
692 
PrintFormat_GUIDToStringW(const void * p)693 static void PrintFormat_GUIDToStringW(const void *p)
694 {
695   const GUID *guid = (const GUID *)p;
696   UString s;
697   const unsigned kSize = 48;
698   StringFromGUID2(*guid, s.GetBuf(kSize), kSize);
699   s.ReleaseBuf_CalcLen(kSize);
700   PRF3_W(s);
701 }
702 
703 // Vista
704 typedef enum
705 {
706   MY_DROPIMAGE_INVALID  = -1,                // no image preference (use default)
707   MY_DROPIMAGE_NONE     = 0,                 // red "no" circle
708   MY_DROPIMAGE_COPY     = DROPEFFECT_COPY,   // plus for copy
709   MY_DROPIMAGE_MOVE     = DROPEFFECT_MOVE,   // movement arrow for move
710   MY_DROPIMAGE_LINK     = DROPEFFECT_LINK,   // link arrow for link
711   MY_DROPIMAGE_LABEL    = 6,                 // tag icon to indicate metadata will be changed
712   MY_DROPIMAGE_WARNING  = 7,                 // yellow exclamation, something is amiss with the operation
713   MY_DROPIMAGE_NOIMAGE  = 8                  // no image at all
714 } MY_DROPIMAGETYPE;
715 
716 typedef struct {
717   MY_DROPIMAGETYPE type;
718   WCHAR szMessage[MAX_PATH];
719   WCHAR szInsert[MAX_PATH];
720 } MY_DROPDESCRIPTION;
721 
722 #endif
723 
724 
725 /*
726 IDataObject::SetData(LPFORMATETC etc, STGMEDIUM *medium, BOOL release)
727 ======================================================================
728 
729   Main purpose of CDataObject is to transfer data from source to target
730   of drag and drop operation.
731   But also CDataObject can be used to transfer data in backward direction
732   from target to source (even if target and source are different processes).
733   There are some predefined Explorer's formats to transfer some data from target to source.
734   And 7-Zip uses 7-Zip's format k_Format_7zip_SetTargetFolder to transfer
735   destination directory path from target to source.
736 
737   Our CDataObject::SetData() function here is used only to transfer data from target to source.
738   Usual source_to_target data is filled to m_hGlobal_* objects directly without SetData() calling.
739 
740 The main problem of SetData() is ownership of medium for (release == TRUE) case.
741 
742 SetData(,, release = TRUE) from different processes (DropSource and DropTarget)
743 ===============================================================================
744 {
745   MS DOCs about (STGMEDIUM *medium) ownership:
746     The data object called does not take ownership of the data
747     until it has successfully received it and no error code is returned.
748 
749   Each of processes (Source and Target) has own copy of medium allocated.
750   Windows code creates proxy IDataObject object in Target process to transferr
751   SetData() call between Target and Source processes via special proxies:
752     DropTarget ->
753     proxy_DataObject_in_Target ->
754     proxy_in_Source ->
755     DataObject_in_Source
756   when Target calls SetData() with proxy_DataObject_in_Target,
757   the system and proxy_in_Source
758    - allocates proxy-medium-in-Source process
759    - copies medium data from Target to that proxy-medium-in-Source
760    - sends proxy-medium-in-Source to DataObject_in_Source->SetData().
761 
762   after returning from SetData() to Target process:
763     Win10 proxy_DataObject_in_Target releases original medium in Target process,
764     only if SetData() in Source returns S_OK. It's consistent with DOCs above.
765 
766   for unsupported cfFormat:
767   [DropSource is 7-Zip 22.01 (old) : (etc->cfFormat != m_Format_7zip_SetTargetFolder && release == TRUE)]
768   (DropSource is WinRAR case):
769   Source doesn't release medium and returns error (for example, E_NOTIMPL)
770   {
771     Then Win10 proxy_in_Source also doesn't release proxy-medium-in-Source.
772     So there is memory leak in Source process.
773     Probably Win10 proxy_in_Source tries to avoid possible double releasing
774     that can be more fatal than memory leak.
775 
776     Then Win10 proxy_DataObject_in_Target also doesn't release
777     original medium, that was allocated by DropTarget.
778     So if DropTarget also doesn't release medium, there is memory leak in
779     DropTarget process too.
780     DropTarget is Win10-Explorer probably doesn't release medium in that case.
781   }
782 
783   [DropSource is 7-Zip 22.01 (old) : (etc->cfFormat == m_Format_7zip_SetTargetFolder && release == TRUE)]
784   DropSource returns S_OK and doesn't release medium:
785   {
786     then there is memory leak in DropSource process only.
787   }
788 
789   (DropSource is 7-Zip v23 (new)):
790   (DropSource is Win10-Explorer case)
791   {
792     Win10-Explorer-DropSource probably always releases medium,
793     and then it always returns S_OK.
794     So Win10 proxy_DataObject_in_Target also releases
795     original medium, that was allocated by DropTarget.
796     So there is no memory leak in Source and Target processes.
797   }
798 
799   if (DropTarget is Win10-Explorer)
800   {
801     Explorer Target uses SetData(,, (release = TRUE)) and
802     Explorer Target probably doesn't free memory after SetData(),
803       even if SetData(,, (release = TRUE)) returns E_NOTIMPL;
804   }
805 
806   if (DropSource is Win10-Explorer)
807   {
808     (release == FALSE) doesn't work, and SetData() returns E_NOTIMPL;
809     (release == TRUE)  works, and SetData() returns S_OK, and
810                        it returns S_OK even for formats unsupported by Explorer.
811   }
812 
813   To be more compatible with DOCs and Win10-Explorer and to avoid memory leaks,
814   we use the following scheme for our IDataObject::SetData(,, release == TRUE)
815   in DropSource code:
816   if (release == TRUE) { our SetData() always releases medium
817       with ReleaseStgMedium() and returns S_OK; }
818   The DRAWBACK of that scheme:
819     The caller always receives S_OK,
820     so the caller doesn't know about any error in SetData() in that case.
821 
822 for 7zip-Target to 7zip-Source calls:
823   we use (release == FALSE)
824   So we avoid (release == TRUE) memory leak problems,
825   and we can get real return code from SetData().
826 
827 for 7zip-Target to Explorer-Source calls:
828   we use (release == TRUE).
829   beacuse Explorer-Source doesn't accept (release == FALSE).
830 }
831 */
832 
833 /*
834 https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/shell/datascenarios.md
835 CFSTR_PERFORMEDDROPEFFECT:
836   is used by the target to inform the data object through its
837   IDataObject::SetData method of the outcome of a data transfer.
838 CFSTR_PREFERREDDROPEFFECT:
839   is used by the source to specify whether its preferred method of data transfer is move or copy.
840 */
841 
SetData(LPFORMATETC etc,STGMEDIUM * medium,BOOL release)842 Z7_COMWF_B CDataObject::SetData(LPFORMATETC etc, STGMEDIUM *medium, BOOL release)
843 {
844   try {
845   const HRESULT hres = SetData2(etc, medium);
846   // PrintFormat(release ? "SetData RELEASE=TRUE" : "SetData RELEASE=FALSE" , etc->cfFormat);
847   if (release)
848   {
849     /*
850     const DWORD tymed = medium->tymed;
851     IUnknown *pUnkForRelease = medium->pUnkForRelease;
852     */
853     // medium->tymed = NULL; // for debug
854     // return E_NOTIMPL;  // for debug
855     ReleaseStgMedium(medium);
856     /* ReleaseStgMedium() will change STGMEDIUM::tymed to (TYMED_NULL = 0).
857        but we also can clear (medium.hGlobal = NULL),
858        to prevent some incorrect releasing, if the caller will try to release the data  */
859     /*
860     if (medium->tymed == TYMED_NULL && tymed == TYMED_HGLOBAL && !pUnkForRelease)
861       medium->hGlobal = NULL;
862     */
863     // do we need return S_OK; for (tymed != TYMED_HGLOBAL) cases ?
864     /* we return S_OK here to shows that we take ownership of the data in (medium),
865        so the caller will not try to release (medium) */
866     return S_OK; // to be more compatible with Win10-Explorer and DOCs.
867   }
868   return hres;
869   } catch(...) { return E_FAIL; }
870 }
871 
872 
873 
SetData2(const FORMATETC * etc,const STGMEDIUM * medium)874 HRESULT CDataObject::SetData2(const FORMATETC *etc, const STGMEDIUM *medium)
875 {
876   // PRF3("== CDataObject::SetData()");
877 
878   HRESULT hres = S_OK;
879 
880   if (etc->cfFormat == 0)
881     return DV_E_FORMATETC;
882   if (etc->tymed != TYMED_HGLOBAL)
883     return E_NOTIMPL; // DV_E_TYMED;
884   if (etc->dwAspect != DVASPECT_CONTENT)
885     return E_NOTIMPL; // DV_E_DVASPECT;
886   if (medium->tymed != TYMED_HGLOBAL)
887     return E_NOTIMPL; // DV_E_TYMED;
888 
889   if (!medium->hGlobal)
890     return S_OK;
891 
892   if (etc->cfFormat == m_Format_7zip_SetTargetFolder)
893   {
894     DestDirPrefix_FromTarget.Empty();
895     m_DestDirPrefix_FromTarget_WasSet = true;
896   }
897   else if (etc->cfFormat == m_Format_7zip_SetTransfer)
898     m_Transfer_WasSet = false;
899 
900   const size_t size = GlobalSize(medium->hGlobal);
901   // GlobalLock() can return NULL, if memory block has a zero size
902   if (size == 0)
903     return S_OK;
904   const void *src = (const Byte *)GlobalLock(medium->hGlobal);
905   if (!src)
906     return E_FAIL;
907 
908   PRF_(PrintFormat_AndData("SetData", etc->cfFormat, src, size))
909 
910   if (etc->cfFormat == m_Format_7zip_SetTargetFolder)
911   {
912     /* this is our registered k_Format_7zip_SetTargetFolder format.
913        so it's call from 7-zip's CDropTarget */
914     /* 7-zip's CDropTarget calls SetData() for m_Format_7zip_SetTargetFolder
915        with (release == FALSE) */
916     const size_t num = size / sizeof(wchar_t);
917     if (size != num * sizeof(wchar_t))
918       return E_FAIL;
919     // if (num == 0) return S_OK;
920     // GlobalLock() can return NULL, if memory block has a zero-byte size
921     const wchar_t *s = (const wchar_t *)src;
922     UString &dest = DestDirPrefix_FromTarget;
923     for (size_t i = 0; i < num; i++)
924     {
925       const wchar_t c = s[i];
926       if (c == 0)
927         break;
928       dest += c;
929     }
930     // PRF_(PrintFormat_AndData("SetData", etc->cfFormat, src, size))
931     PRF3_W(DestDirPrefix_FromTarget);
932   }
933   else if (etc->cfFormat == m_Format_7zip_SetTransfer)
934   {
935     /* 7-zip's CDropTarget calls SetData() for m_Format_7zip_SetTransfer
936        with (release == FALSE) */
937     if (size < sizeof(CDataObject_SetTransfer))
938       return E_FAIL;
939     const CDataObject_SetTransfer *t = (const CDataObject_SetTransfer *)src;
940     if (!t->Check())
941       return E_FAIL;
942     m_Transfer = *t;
943     if (t->Target.FuncType != k_DragTargetMode_Leave)
944       m_Transfer_WasSet = true;
945     bool needProcessBySource = !DestDirPrefix_FromTarget.IsEmpty();
946     if (t->Target.FuncType == k_DragTargetMode_Drop_Begin)
947     {
948       if (t->Target.Cmd_Type != NDragMenu::k_Copy_Base
949           // || t->Target.Cmd_Effect != DROPEFFECT_COPY
950           )
951         needProcessBySource = false;
952     }
953     if (t->Target.FuncType == k_DragTargetMode_Drop_End)
954     {
955       if (t->Target.Flags & k_TargetFlags_MustBeProcessedBySource)
956         needProcessBySource = true;
957       else if (t->Target.Flags & k_TargetFlags_WasProcessed)
958         needProcessBySource = false;
959     }
960     DoNotProcessInTarget = needProcessBySource;
961   }
962   else
963   {
964     // SetData() from Explorer Target:
965     if (etc->cfFormat == m_Format_PerformedDropEffect)
966     {
967       m_PerformedDropEffect_WasSet = false;
968       if (size == sizeof(DWORD))
969       {
970         m_PerformedDropEffect = *(const DWORD *)src;
971         m_PerformedDropEffect_WasSet = true;
972       }
973     }
974     else if (etc->cfFormat == m_Format_LogicalPerformedDropEffect)
975     {
976       m_LogicalPerformedDropEffect_WasSet = false;
977       if (size == sizeof(DWORD))
978       {
979         m_LogicalPerformedDropEffect = *(const DWORD *)src;
980         m_LogicalPerformedDropEffect_WasSet = true;
981       }
982     }
983     else if (etc->cfFormat == m_Format_DropDescription)
984     {
985       // drop description contains only name of dest folder without full path
986       #ifdef SHOW_DEBUG_DRAG
987       if (size == sizeof(MY_DROPDESCRIPTION))
988       {
989         // const MY_DROPDESCRIPTION *s = (const MY_DROPDESCRIPTION *)src;
990         // PRF3_W(s->szMessage);
991         // PRF3_W(s->szInsert);
992       }
993       #endif
994     }
995     else if (etc->cfFormat == m_Format_TargetCLSID)
996     {
997       // it's called after call QueryContinueDrag() (keyState & MK_LBUTTON) == 0
998       // Shell File System Folder (explorer) guid: F3364BA0-65B9-11CE-A9BA-00AA004AE837
999       #ifdef SHOW_DEBUG_DRAG
1000       if (size == 16)
1001       {
1002         PrintFormat_GUIDToStringW((const Byte *)src);
1003       }
1004       #endif
1005     }
1006     else if (etc->cfFormat == m_Format_DisableDragText)
1007     {
1008       // (size == 4) (UInt32 value)
1009       //    value==0 : if drag to folder item or folder
1010       //    value==1 : if drag to file or non list_view */
1011     }
1012     else if (
1013         etc->cfFormat == m_Format_IsShowingLayered ||
1014         etc->cfFormat == m_Format_IsShowingText)
1015     {
1016       // (size == 4) (UInt32 value) value==0 :
1017     }
1018     else
1019       hres = DV_E_FORMATETC;
1020     // hres = E_NOTIMPL; // for debug
1021     // hres = DV_E_FORMATETC; // for debug
1022   }
1023 
1024   GlobalUnlock(medium->hGlobal);
1025   return hres;
1026 }
1027 
1028 
1029 
DuplicateGlobalMem(HGLOBAL srcGlobal)1030 static HGLOBAL DuplicateGlobalMem(HGLOBAL srcGlobal)
1031 {
1032   /* GlobalSize() returns 0: If the specified handle
1033      is not valid or if the object has been discarded */
1034   const SIZE_T size = GlobalSize(srcGlobal);
1035   if (size == 0)
1036     return NULL;
1037   // GlobalLock() can return NULL, if memory block has a zero-byte size
1038   const void *src = GlobalLock(srcGlobal);
1039   if (!src)
1040     return NULL;
1041   HGLOBAL destGlobal = GlobalAlloc(GHND | GMEM_SHARE, size);
1042   if (destGlobal)
1043   {
1044     void *dest = GlobalLock(destGlobal);
1045     if (!dest)
1046     {
1047       GlobalFree(destGlobal);
1048       destGlobal = NULL;
1049     }
1050     else
1051     {
1052       memcpy(dest, src, size);
1053       GlobalUnlock(destGlobal);
1054     }
1055   }
1056   GlobalUnlock(srcGlobal);
1057   return destGlobal;
1058 }
1059 
1060 
Medium_CopyFrom(LPSTGMEDIUM medium,const void * data,size_t size)1061 static bool Medium_CopyFrom(LPSTGMEDIUM medium, const void *data, size_t size)
1062 {
1063   medium->tymed = TYMED_NULL;
1064   medium->pUnkForRelease = NULL;
1065   medium->hGlobal = NULL;
1066   const HGLOBAL global = GlobalAlloc(GHND | GMEM_SHARE, size);
1067   if (!global)
1068     return false;
1069   void *dest = GlobalLock(global);
1070   if (!dest)
1071   {
1072     GlobalFree(global);
1073     return false;
1074   }
1075   memcpy(dest, data, size);
1076   GlobalUnlock(global);
1077   medium->hGlobal = global;
1078   medium->tymed = TYMED_HGLOBAL;
1079   return true;
1080 }
1081 
1082 
GetData(LPFORMATETC etc,LPSTGMEDIUM medium)1083 Z7_COMWF_B CDataObject::GetData(LPFORMATETC etc, LPSTGMEDIUM medium)
1084 {
1085   try {
1086   PRF_(PrintFormat("-- GetData", etc->cfFormat))
1087 
1088   medium->tymed = TYMED_NULL;
1089   medium->pUnkForRelease = NULL;
1090   medium->hGlobal = NULL;
1091 
1092   if (NeedCall_Copy && !Copy_WasCalled)
1093     CopyFromPanelTo_Folder();
1094 
1095   // PRF3("+ CDataObject::GetData");
1096   // PrintFormat(etc->cfFormat);
1097   HGLOBAL global;
1098   RINOK(QueryGetData(etc))
1099 
1100   /*
1101   if (etc->cfFormat == m_Format_FileOpFlags)
1102     global = m_hGlobal_FileOpFlags;
1103   else if (etc->cfFormat == m_Format_PreferredDropEffect)
1104   {
1105     // Explorer requests PreferredDropEffect only if Move/Copy selection is possible:
1106     //   Shift is not pressed and Ctrl is not pressed
1107     PRF3("------ CDataObject::GetData() PreferredDropEffect");
1108     global = m_hGlobal_PreferredDropEffect;
1109   }
1110   else
1111   */
1112   if (etc->cfFormat == m_Etc.cfFormat) // CF_HDROP
1113     global = UsePreGlobal ? m_hGlobal_HDROP_Pre : m_hGlobal_HDROP_Final;
1114   else if (etc->cfFormat == m_Format_7zip_GetTransfer)
1115   {
1116     CDataObject_GetTransfer transfer;
1117     if (m_DestDirPrefix_FromTarget_WasSet)
1118     {
1119       transfer.Flags |= k_SourceFlags_SetTargetFolder;
1120     }
1121     if (!DestDirPrefix_FromTarget.IsEmpty())
1122     {
1123       transfer.Flags |= k_SourceFlags_SetTargetFolder_NonEmpty;
1124     }
1125     if (IsTempFiles)
1126     {
1127       transfer.Flags |= k_SourceFlags_TempFiles;
1128       transfer.Flags |= k_SourceFlags_WaitFinish;
1129       transfer.Flags |= k_SourceFlags_NeedExtractOpToFs;
1130       if (UsePreGlobal)
1131         transfer.Flags |= k_SourceFlags_NamesAreParent;
1132     }
1133     else
1134       transfer.Flags |= k_SourceFlags_DoNotWaitFinish;
1135 
1136     if (IsRightButton)
1137       transfer.Flags |= k_SourceFlags_RightButton;
1138     else
1139       transfer.Flags |= k_SourceFlags_LeftButton;
1140 
1141     if (DoNotProcessInTarget)
1142       transfer.Flags |= k_SourceFlags_DoNotProcessInTarget;
1143     if (Copy_WasCalled)
1144       transfer.Flags |= k_SourceFlags_Copy_WasCalled;
1145 
1146     if (Medium_CopyFrom(medium, &transfer, sizeof(transfer)))
1147       return S_OK;
1148     return E_OUTOFMEMORY;
1149   }
1150   else
1151     return DV_E_FORMATETC;
1152 
1153   if (!global)
1154     return DV_E_FORMATETC;
1155   medium->tymed = m_Etc.tymed;
1156   medium->hGlobal = DuplicateGlobalMem(global);
1157   if (!medium->hGlobal)
1158     return E_OUTOFMEMORY;
1159   return S_OK;
1160   } catch(...) { return E_FAIL; }
1161 }
1162 
GetDataHere(LPFORMATETC,LPSTGMEDIUM)1163 Z7_COMWF_B CDataObject::GetDataHere(LPFORMATETC /* etc */, LPSTGMEDIUM /* medium */)
1164 {
1165   PRF3("CDataObject::GetDataHere()");
1166   // Seems Windows doesn't call it, so we will not implement it.
1167   return E_UNEXPECTED;
1168 }
1169 
1170 
1171 /*
1172   IDataObject::QueryGetData() Determines whether the data object is capable of
1173   rendering the data as specified. Objects attempting a paste or drop
1174   operation can call this method before calling IDataObject::GetData
1175   to get an indication of whether the operation may be successful.
1176 
1177   The client of a data object calls QueryGetData to determine whether
1178   passing the specified FORMATETC structure to a subsequent call to
1179   IDataObject::GetData is likely to be successful.
1180 
1181   we check Try_QueryGetData with CF_HDROP
1182 */
1183 
QueryGetData(LPFORMATETC etc)1184 Z7_COMWF_B CDataObject::QueryGetData(LPFORMATETC etc)
1185 {
1186   PRF3("-- CDataObject::QueryGetData()");
1187   if (    etc->cfFormat == m_Etc.cfFormat // CF_HDROP
1188       ||  etc->cfFormat == m_Format_7zip_GetTransfer
1189       // || (etc->cfFormat == m_Format_FileOpFlags && (HGLOBAL)m_hGlobal_FileOpFlags)
1190       // || (etc->cfFormat == m_Format_PreferredDropEffect && (HGLOBAL)m_hGlobal_PreferredDropEffect)
1191       )
1192   {
1193   }
1194   else
1195     return DV_E_FORMATETC;
1196   if (etc->dwAspect != m_Etc.dwAspect)
1197     return DV_E_DVASPECT;
1198   /* GetData(): It is possible to specify more than one medium by using the Boolean OR
1199      operator, allowing the method to choose the best medium among those specified. */
1200   if ((etc->tymed & m_Etc.tymed) == 0)
1201     return DV_E_TYMED;
1202   return S_OK;
1203 }
1204 
EnumFormatEtc(DWORD direction,LPENUMFORMATETC FAR * enumFormatEtc)1205 Z7_COMWF_B CDataObject::EnumFormatEtc(DWORD direction, LPENUMFORMATETC FAR* enumFormatEtc)
1206 {
1207   // we don't enumerate for DATADIR_SET. Seems it can work without it.
1208   if (direction != DATADIR_GET)
1209     return E_NOTIMPL;
1210   // we don't enumerate for m_Format_FileOpFlags also. Seems it can work without it.
1211   return CreateEnumFormatEtc(1, &m_Etc, enumFormatEtc);
1212 }
1213 
1214 
1215 
1216 ////////////////////////////////////////////////////////
1217 
1218 class CDropSource Z7_final:
1219   public IDropSource,
1220   public CMyUnknownImp
1221 {
1222   Z7_COM_UNKNOWN_IMP_1_MT(IDropSource)
1223   STDMETHOD(QueryContinueDrag)(BOOL escapePressed, DWORD keyState) Z7_override;
1224   STDMETHOD(GiveFeedback)(DWORD effect) Z7_override;
1225 
1226   DWORD m_Effect;
1227 public:
1228   CDataObject *DataObjectSpec;
1229   CMyComPtr<IDataObject> DataObject;
1230 
1231   HRESULT DragProcessing_HRESULT;
1232   bool DragProcessing_WasFinished;
1233 
CDropSource()1234   CDropSource():
1235       m_Effect(DROPEFFECT_NONE),
1236       // Panel(NULL),
1237       DragProcessing_HRESULT(S_OK),
1238       DragProcessing_WasFinished(false)
1239       {}
1240 };
1241 
1242 // static bool g_Debug = 0;
1243 
1244 
QueryContinueDrag(BOOL escapePressed,DWORD keyState)1245 Z7_COMWF_B CDropSource::QueryContinueDrag(BOOL escapePressed, DWORD keyState)
1246 {
1247   // try {
1248 
1249   /* Determines whether a drag-and-drop operation should be continued, canceled, or completed.
1250      escapePressed : Indicates whether the Esc key has been pressed
1251        since the previous call to QueryContinueDrag
1252        or to DoDragDrop if this is the first call to QueryContinueDrag:
1253          TRUE  : the end user has pressed the escape key;
1254          FALSE : it has not been pressed.
1255      keyState : The current state of the keyboard modifier keys on the keyboard.
1256       Possible values can be a combination of any of the flags:
1257       MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
1258   */
1259   #ifdef SHOW_DEBUG_DRAG
1260   {
1261     AString s ("CDropSource::QueryContinueDrag()");
1262     s.Add_Space();
1263     s += "keystate=";
1264     s.Add_UInt32(keyState);
1265     PRF4(s);
1266   }
1267   #endif
1268 
1269   /*
1270   if ((keyState & MK_LBUTTON) == 0)
1271   {
1272     // PRF4("CDropSource::QueryContinueDrag() (keyState & MK_LBUTTON) == 0");
1273     g_Debug = true;
1274   }
1275   else
1276   {
1277     // PRF4("CDropSource::QueryContinueDrag() (keyState & MK_LBUTTON) != 0");
1278   }
1279   */
1280 
1281   if (escapePressed)
1282   {
1283     // The drag operation should be canceled with no drop operation occurring.
1284     DragProcessing_WasFinished = true;
1285     DragProcessing_HRESULT = DRAGDROP_S_CANCEL;
1286     return DRAGDROP_S_CANCEL;
1287   }
1288 
1289   if (DragProcessing_WasFinished)
1290     return DragProcessing_HRESULT;
1291 
1292   if ((keyState & MK_RBUTTON) != 0)
1293   {
1294     if (!DataObjectSpec->IsRightButton)
1295     {
1296       DragProcessing_WasFinished = true;
1297       DragProcessing_HRESULT = DRAGDROP_S_CANCEL;
1298       return DRAGDROP_S_CANCEL;
1299     }
1300     return S_OK;
1301   }
1302 
1303   if ((keyState & MK_LBUTTON) != 0)
1304   {
1305     if (DataObjectSpec->IsRightButton)
1306     {
1307       DragProcessing_WasFinished = true;
1308       DragProcessing_HRESULT = DRAGDROP_S_CANCEL;
1309       return DRAGDROP_S_CANCEL;
1310     }
1311     /* The drag operation should continue. This result occurs if no errors are detected,
1312        the mouse button starting the drag-and-drop operation has not been released,
1313        and the Esc key has not been detected. */
1314     return S_OK;
1315   }
1316   {
1317     // the mouse button starting the drag-and-drop operation has been released.
1318 
1319     /* Win10 probably calls DragOver()/GiveFeedback() just before LBUTTON releasing.
1320        so m_Effect is effect returned by DropTarget::DragOver()
1321        just before LBUTTON releasing.
1322        So here we can use Effect sent to last GiveFeedback() */
1323 
1324     if (m_Effect == DROPEFFECT_NONE)
1325     {
1326       DragProcessing_WasFinished = true;
1327       DragProcessing_HRESULT = DRAGDROP_S_CANCEL;
1328       // Drop target cannot accept the data. So we cancel drag and drop
1329       // maybe return DRAGDROP_S_DROP also OK here ?
1330       // return DRAGDROP_S_DROP; // for debug
1331       return DRAGDROP_S_CANCEL;
1332     }
1333 
1334     // we switch to real names for items that will be created in temp folder
1335     DataObjectSpec->UsePreGlobal = false;
1336     DataObjectSpec->Copy_HRESULT = S_OK;
1337     // MoveMode = (((keyState & MK_SHIFT) != 0) && MoveIsAllowed);
1338     /*
1339     if (DataObjectSpec->IsRightButton)
1340       return DRAGDROP_S_DROP;
1341     */
1342 
1343     if (DataObjectSpec->IsTempFiles)
1344     {
1345       if (!DataObjectSpec->DestDirPrefix_FromTarget.IsEmpty())
1346       {
1347         /* we know the destination Path.
1348            So we can copy or extract items later in Source with simpler code. */
1349         DataObjectSpec->DoNotProcessInTarget = true;
1350         // return DRAGDROP_S_CANCEL;
1351       }
1352       else
1353       {
1354         DataObjectSpec->NeedCall_Copy = true;
1355         /*
1356         if (Copy_HRESULT != S_OK || !Messages.IsEmpty())
1357         {
1358           DragProcessing_WasFinished = true;
1359           DragProcessing_HRESULT = DRAGDROP_S_CANCEL;
1360           return DRAGDROP_S_CANCEL;
1361         }
1362         */
1363       }
1364     }
1365     DragProcessing_HRESULT = DRAGDROP_S_DROP;
1366     DragProcessing_WasFinished = true;
1367     return DRAGDROP_S_DROP;
1368   }
1369   // } catch(...) { return E_FAIL; }
1370 }
1371 
1372 
GiveFeedback(DWORD effect)1373 Z7_COMWF_B CDropSource::GiveFeedback(DWORD effect)
1374 {
1375   // PRF3("CDropSource::GiveFeedback");
1376   /* Enables a source application to give visual feedback to the end user
1377      during a drag-and-drop operation by providing the DoDragDrop function
1378      with an enumeration value specifying the visual effect.
1379   in (effect):
1380      The DROPEFFECT value returned by the most recent call to
1381         IDropTarget::DragEnter,
1382         IDropTarget::DragOver,
1383      or DROPEFFECT_NONE after IDropTarget::DragLeave.
1384     0: DROPEFFECT_NONE
1385     1: DROPEFFECT_COPY
1386     2: DROPEFFECT_MOVE
1387     4: DROPEFFECT_LINK
1388     0x80000000: DROPEFFECT_SCROLL
1389     The dwEffect parameter can include DROPEFFECT_SCROLL, indicating that the
1390     source should put up the drag-scrolling variation of the appropriate pointer.
1391   */
1392   m_Effect = effect;
1393 
1394  #ifdef SHOW_DEBUG_DRAG
1395   AString w ("GiveFeedback effect=");
1396   if (effect & DROPEFFECT_SCROLL)
1397     w += " SCROLL ";
1398   w.Add_UInt32(effect & ~DROPEFFECT_SCROLL);
1399   // if (g_Debug)
1400   PRF4(w);
1401  #endif
1402 
1403   /* S_OK : no special drag and drop cursors.
1404             Maybe it's for case where we created custom custom cursors.
1405      DRAGDROP_S_USEDEFAULTCURSORS: Indicates successful completion of the method,
1406        and requests OLE to update the cursor using the OLE-provided default cursors. */
1407   // return S_OK; // for debug
1408   return DRAGDROP_S_USEDEFAULTCURSORS;
1409 }
1410 
1411 
1412 
1413 /*
1414 static bool Global_SetUInt32(NMemory::CGlobal &hg, const UInt32 v)
1415 {
1416   if (!hg.Alloc(GHND | GMEM_SHARE, sizeof(v)))
1417     return false;
1418   NMemory::CGlobalLock dropLock(hg);
1419   *(UInt32 *)dropLock.GetPointer() = v;
1420   return true;
1421 }
1422 */
1423 
CopyNamesToHGlobal(NMemory::CGlobal & hgDrop,const UStringVector & names)1424 static bool CopyNamesToHGlobal(NMemory::CGlobal &hgDrop, const UStringVector &names)
1425 {
1426   size_t totalLen = 1;
1427 
1428   #ifndef _UNICODE
1429   if (!g_IsNT)
1430   {
1431     AStringVector namesA;
1432     unsigned i;
1433     for (i = 0; i < names.Size(); i++)
1434       namesA.Add(GetSystemString(names[i]));
1435     for (i = 0; i < namesA.Size(); i++)
1436       totalLen += namesA[i].Len() + 1;
1437 
1438     if (!hgDrop.Alloc(GHND | GMEM_SHARE, totalLen * sizeof(CHAR) + sizeof(DROPFILES)))
1439       return false;
1440 
1441     NMemory::CGlobalLock dropLock(hgDrop);
1442     DROPFILES *dropFiles = (DROPFILES *)dropLock.GetPointer();
1443     if (!dropFiles)
1444       return false;
1445     dropFiles->fNC = FALSE;
1446     dropFiles->pt.x = 0;
1447     dropFiles->pt.y = 0;
1448     dropFiles->pFiles = sizeof(DROPFILES);
1449     dropFiles->fWide = FALSE;
1450     CHAR *p = (CHAR *) (void *) ((BYTE *)dropFiles + sizeof(DROPFILES));
1451     for (i = 0; i < namesA.Size(); i++)
1452     {
1453       const AString &s = namesA[i];
1454       const unsigned fullLen = s.Len() + 1;
1455       MyStringCopy(p, (const char *)s);
1456       p += fullLen;
1457       totalLen -= fullLen;
1458     }
1459     *p = 0;
1460   }
1461   else
1462   #endif
1463   {
1464     unsigned i;
1465     for (i = 0; i < names.Size(); i++)
1466       totalLen += names[i].Len() + 1;
1467 
1468     if (!hgDrop.Alloc(GHND | GMEM_SHARE, totalLen * sizeof(WCHAR) + sizeof(DROPFILES)))
1469       return false;
1470 
1471     NMemory::CGlobalLock dropLock(hgDrop);
1472     DROPFILES *dropFiles = (DROPFILES *)dropLock.GetPointer();
1473     if (!dropFiles)
1474       return false;
1475     /* fNC:
1476         TRUE  : pt specifies the screen coordinates of a point in a window's nonclient area.
1477         FALSE : pt specifies the client coordinates of a point in the client area.
1478     */
1479     dropFiles->fNC = FALSE;
1480     dropFiles->pt.x = 0;
1481     dropFiles->pt.y = 0;
1482     dropFiles->pFiles = sizeof(DROPFILES);
1483     dropFiles->fWide = TRUE;
1484     WCHAR *p = (WCHAR *) (void *) ((BYTE *)dropFiles + sizeof(DROPFILES));
1485     for (i = 0; i < names.Size(); i++)
1486     {
1487       const UString &s = names[i];
1488       const unsigned fullLen = s.Len() + 1;
1489       MyStringCopy(p, (const WCHAR *)s);
1490       p += fullLen;
1491       totalLen -= fullLen;
1492     }
1493     *p = 0;
1494   }
1495   // if (totalLen != 1) return false;
1496   return true;
1497 }
1498 
1499 
OnDrag(LPNMLISTVIEW,bool isRightButton)1500 void CPanel::OnDrag(LPNMLISTVIEW /* nmListView */, bool isRightButton)
1501 {
1502   PRF("CPanel::OnDrag");
1503   if (!DoesItSupportOperations())
1504     return;
1505 
1506   CDisableTimerProcessing disableTimerProcessing2(*this);
1507 
1508   CRecordVector<UInt32> indices;
1509   Get_ItemIndices_Operated(indices);
1510   if (indices.Size() == 0)
1511     return;
1512 
1513   // CSelectedState selState;
1514   // SaveSelectedState(selState);
1515 
1516   const bool isFSFolder = IsFSFolder();
1517   // why we don't allow drag with rightButton from archive?
1518   if (!isFSFolder && isRightButton)
1519     return;
1520 
1521   UString dirPrefix;
1522   CTempDir tempDirectory;
1523 
1524   CDataObject *dataObjectSpec = new CDataObject;
1525   CMyComPtr<IDataObject> dataObject = dataObjectSpec;
1526   dataObjectSpec->IsRightButton = isRightButton;
1527 
1528   {
1529     /* we can change confirmation mode and another options.
1530        Explorer target requests that FILEOP_FLAGS value. */
1531     /*
1532     const FILEOP_FLAGS fopFlags =
1533         FOF_NOCONFIRMATION
1534       | FOF_NOCONFIRMMKDIR
1535       | FOF_NOERRORUI
1536       | FOF_SILENT;
1537       // | FOF_SIMPLEPROGRESS; // it doesn't work as expected in Win10
1538     Global_SetUInt32(dataObjectSpec->m_hGlobal_FileOpFlags, fopFlags);
1539     // dataObjectSpec->m_hGlobal_FileOpFlags.Free(); // for debug : disable these options
1540     */
1541   }
1542   {
1543     /* we can change Preferred DropEffect.
1544        Explorer target requests that FILEOP_FLAGS value. */
1545     /*
1546     const DWORD effect = DROPEFFECT_MOVE; // DROPEFFECT_COPY;
1547     Global_SetUInt32(dataObjectSpec->m_hGlobal_PreferredDropEffect, effect);
1548     */
1549   }
1550   if (isFSFolder)
1551   {
1552     dirPrefix = GetFsPath(); // why this in 22.01 ?
1553     dataObjectSpec->UsePreGlobal = false;
1554     // dataObjectSpec->IsTempFiles = false;
1555   }
1556   else
1557   {
1558     if (!tempDirectory.Create(kTempDirPrefix))
1559     {
1560       MessageBox_Error(L"Can't create temp folder");
1561       return;
1562     }
1563     dirPrefix = fs2us(tempDirectory.GetPath());
1564     {
1565       UStringVector names;
1566       names.Add(dirPrefix);
1567       dataObjectSpec->IsTempFiles = true;
1568       dataObjectSpec->UsePreGlobal = true;
1569       if (!CopyNamesToHGlobal(dataObjectSpec->m_hGlobal_HDROP_Pre, names))
1570         return;
1571     }
1572     NFile::NName::NormalizeDirPathPrefix(dirPrefix);
1573     /*
1574     {
1575       FString path2 = dirPrefix;
1576       path2 += "1.txt";
1577       CopyFileW(L"C:\\1\\1.txt", path2, FALSE);
1578     }
1579     */
1580   }
1581 
1582   {
1583     UStringVector names;
1584     // names variable is     USED for drag and drop from 7-zip to Explorer or to 7-zip archive folder.
1585     // names variable is NOT USED for drag and drop from 7-zip to 7-zip File System folder.
1586     FOR_VECTOR (i, indices)
1587     {
1588       const UInt32 index = indices[i];
1589       UString s;
1590       if (isFSFolder)
1591         s = GetItemRelPath(index);
1592       else
1593       {
1594         s = GetItemName(index);
1595         /*
1596         // We use (keepAndReplaceEmptyPrefixes = true) in CAgentFolder::Extract
1597         // So the following code is not required.
1598         // Maybe we also can change IFolder interface and send some flag also.
1599         if (s.IsEmpty())
1600         {
1601           // Correct_FsFile_Name("") returns "_".
1602           // If extracting code removes empty folder prefixes from path (as it was in old version),
1603           // Explorer can't find "_" folder in temp folder.
1604           // We can ask Explorer to copy parent temp folder "7zE" instead.
1605           names.Clear();
1606           names.Add(dirPrefix2);
1607           break;
1608         }
1609         */
1610         s = Get_Correct_FsFile_Name(s);
1611       }
1612       names.Add(dirPrefix + s);
1613     }
1614     if (!CopyNamesToHGlobal(dataObjectSpec->m_hGlobal_HDROP_Final, names))
1615       return;
1616   }
1617 
1618   CDropSource *dropSourceSpec = new CDropSource;
1619   CMyComPtr<IDropSource> dropSource = dropSourceSpec;
1620   dataObjectSpec->Panel = this;
1621   dataObjectSpec->Indices = indices;
1622   dataObjectSpec->SrcDirPrefix_Temp = dirPrefix;
1623 
1624   dropSourceSpec->DataObjectSpec = dataObjectSpec;
1625   dropSourceSpec->DataObject = dataObjectSpec;
1626 
1627 
1628   /*
1629   CTime - file creation timestamp.
1630   There are two operations in Windows with Drag and Drop:
1631     COPY_OPERATION : icon with    Plus sign : CTime will be set as current_time.
1632     MOVE_OPERATION : icon without Plus sign : CTime will be preserved.
1633 
1634   Note: if we call DoDragDrop() with (effectsOK = DROPEFFECT_MOVE), then
1635     it will use MOVE_OPERATION and CTime will be preserved.
1636     But MoveFile() function doesn't preserve CTime, if different volumes are used.
1637     Why it's so?
1638     Does DoDragDrop() use some another function (not MoveFile())?
1639 
1640   if (effectsOK == DROPEFFECT_COPY) it works as COPY_OPERATION
1641 
1642   if (effectsOK == DROPEFFECT_MOVE) drag works as MOVE_OPERATION
1643 
1644   if (effectsOK == (DROPEFFECT_COPY | DROPEFFECT_MOVE))
1645   {
1646     if we drag file to same volume, then Windows suggests:
1647        CTRL      - COPY_OPERATION
1648        [default] - MOVE_OPERATION
1649 
1650     if we drag file to another volume, then Windows suggests
1651        [default] - COPY_OPERATION
1652        SHIFT     - MOVE_OPERATION
1653   }
1654 
1655   We want to use MOVE_OPERATION for extracting from archive (open in 7-Zip) to Explorer:
1656   It has the following advantages:
1657     1) it uses fast MOVE_OPERATION instead of slow COPY_OPERATION and DELETE, if same volume.
1658     2) it preserves CTime
1659 
1660   Some another programs support only COPY_OPERATION.
1661   So we can use (DROPEFFECT_COPY | DROPEFFECT_MOVE)
1662 
1663   Also another program can return from DoDragDrop() before
1664   files using. But we delete temp folder after DoDragDrop(),
1665   and another program can't open input files in that case.
1666 
1667   We create objects:
1668     IDropSource *dropSource
1669     IDataObject *dataObject
1670   if DropTarget is 7-Zip window, then 7-Zip's
1671     IDropTarget::DragOver() sets DestDirPrefix_FromTarget in IDataObject.
1672   and
1673     IDropSource::QueryContinueDrag() sets DoNotProcessInTarget, if DestDirPrefix_FromTarget is not empty.
1674   So we can detect destination path after DoDragDrop().
1675   Now we don't know any good way to detect destination path for D&D to Explorer.
1676   */
1677 
1678   /*
1679   DWORD effectsOK = DROPEFFECT_COPY;
1680   if (moveIsAllowed)
1681     effectsOK |= DROPEFFECT_MOVE;
1682   */
1683   const bool moveIsAllowed = isFSFolder;
1684   _panelCallback->DragBegin();
1685   PRF("=== DoDragDrop()");
1686   DWORD effect = 0;
1687   // 18.04: was changed
1688   const DWORD effectsOK = DROPEFFECT_MOVE | DROPEFFECT_COPY;
1689   // effectsOK |= (1 << 8); // for debug
1690   HRESULT res = ::DoDragDrop(dataObject, dropSource, effectsOK, &effect);
1691   PRF("=== After DoDragDrop()");
1692   _panelCallback->DragEnd();
1693 
1694   /*
1695     Win10 drag and drop to Explorer:
1696       DoDragDrop() output variables:
1697        for MOVE operation:
1698        {
1699          effect == DROPEFFECT_NONE;
1700          dropSourceSpec->m_PerformedDropEffect == DROPEFFECT_MOVE;
1701        }
1702        for COPY operation:
1703        {
1704          effect == DROPEFFECT_COPY;
1705          dropSourceSpec->m_PerformedDropEffect == DROPEFFECT_COPY;
1706        }
1707     DOCs: The source inspects the two values that can be returned by the target.
1708        If both are set to DROPEFFECT_MOVE, it completes the unoptimized move
1709        by deleting the original data. Otherwise, the target did an optimized
1710        move and the original data has been deleted.
1711 
1712     We didn't see "unoptimized move" case (two values of DROPEFFECT_MOVE),
1713     where we still need to delete source files.
1714     So we don't delete files after DoDragDrop().
1715 
1716     Also DOCs say for "optimized move":
1717       The target also calls the data object's IDataObject::SetData method and passes
1718       it a CFSTR_PERFORMEDDROPEFFECT format identifier set to DROPEFFECT_NONE.
1719     but actually in Win10 we always have
1720       (dropSourceSpec->m_PerformedDropEffect == DROPEFFECT_MOVE)
1721     for any MOVE operation.
1722   */
1723 
1724   const bool canceled = (res == DRAGDROP_S_CANCEL);
1725 
1726   CDisableNotify disableNotify(*this);
1727 
1728   if (res == DRAGDROP_S_DROP)
1729   {
1730     /* DRAGDROP_S_DROP is returned. It means that
1731          - IDropTarget::Drop() was called,
1732          - IDropTarget::Drop() returned (ret_code >= 0)
1733     */
1734     res = dataObjectSpec->Copy_HRESULT;
1735     bool need_Process = dataObjectSpec->DoNotProcessInTarget;
1736     if (dataObjectSpec->m_Transfer_WasSet)
1737     {
1738       if (dataObjectSpec->m_Transfer.Target.FuncType == k_DragTargetMode_Drop_End)
1739       {
1740         if (dataObjectSpec->m_Transfer.Target.Flags & k_TargetFlags_MustBeProcessedBySource)
1741           need_Process = true;
1742       }
1743     }
1744 
1745     if (need_Process)
1746       if (!dataObjectSpec->DestDirPrefix_FromTarget.IsEmpty())
1747       {
1748         if (!NFile::NName::IsAltStreamPrefixWithColon(dataObjectSpec->DestDirPrefix_FromTarget))
1749           NFile::NName::NormalizeDirPathPrefix(dataObjectSpec->DestDirPrefix_FromTarget);
1750         CCopyToOptions options;
1751         options.folder = dataObjectSpec->DestDirPrefix_FromTarget;
1752         // if MOVE is not allowed, we just use COPY operation
1753         /* it was 7-zip's Target that set non-empty dataObjectSpec->DestDirPrefix_FromTarget.
1754            it means that target didn't completed operation,
1755            and we can use (effect) value returned by target via DoDragDrop().
1756            as indicator of type of operation
1757         */
1758         // options.moveMode = (moveIsAllowed && effect == DROPEFFECT_MOVE) // before v23.00:
1759         options.moveMode = moveIsAllowed;
1760         if (moveIsAllowed)
1761         {
1762           if (dataObjectSpec->m_Transfer_WasSet)
1763             options.moveMode = (
1764               dataObjectSpec->m_Transfer.Target.Cmd_Effect == DROPEFFECT_MOVE);
1765           else
1766             options.moveMode = (effect == DROPEFFECT_MOVE);
1767             // we expect (DROPEFFECT_MOVE) as indicator of move operation for Drag&Drop MOVE ver 22.01.
1768         }
1769         res = CopyTo(options, indices, &dataObjectSpec->Messages);
1770       }
1771     /*
1772     if (effect & DROPEFFECT_MOVE)
1773       RefreshListCtrl(selState);
1774     */
1775   }
1776   else
1777   {
1778     // we ignore E_UNEXPECTED that is returned if we drag file to printer
1779     if (res != DRAGDROP_S_CANCEL
1780         && res != S_OK
1781         && res != E_UNEXPECTED)
1782       MessageBox_Error_HRESULT(res);
1783     res = dataObjectSpec->Copy_HRESULT;
1784   }
1785 
1786   if (!dataObjectSpec->Messages.IsEmpty())
1787   {
1788     CMessagesDialog messagesDialog;
1789     messagesDialog.Messages = &dataObjectSpec->Messages;
1790     messagesDialog.Create((*this));
1791   }
1792 
1793   if (res != S_OK && res != E_ABORT)
1794   {
1795     // we restore Notify before MessageBox_Error_HRESULT. So we will see files selection
1796     disableNotify.Restore();
1797     // SetFocusToList();
1798     MessageBox_Error_HRESULT(res);
1799   }
1800   if (res == S_OK && dataObjectSpec->Messages.IsEmpty() && !canceled)
1801     KillSelection();
1802 }
1803 
1804 
1805 
1806 
1807 
CDropTarget()1808 CDropTarget::CDropTarget():
1809       m_IsRightButton(false),
1810       m_GetTransfer_WasSuccess(false),
1811       m_DropIsAllowed(false),
1812       m_PanelDropIsAllowed(false),
1813       // m_DropHighlighted_SelectionIndex(-1),
1814       // m_SubFolderIndex(-1),
1815       m_Panel(NULL),
1816       m_IsAppTarget(false),
1817       m_TargetPath_WasSent_ToDataObject(false),
1818       m_TargetPath_NonEmpty_WasSent_ToDataObject(false),
1819       m_Transfer_WasSent_ToDataObject(false),
1820       App(NULL),
1821       SrcPanelIndex(-1),
1822       TargetPanelIndex(-1)
1823 {
1824   m_Format_7zip_SetTargetFolder = RegisterClipboardFormat(k_Format_7zip_SetTargetFolder);
1825   m_Format_7zip_SetTransfer     = RegisterClipboardFormat(k_Format_7zip_SetTransfer);
1826   m_Format_7zip_GetTransfer     = RegisterClipboardFormat(k_Format_7zip_GetTransfer);
1827 
1828   m_ProcessId = GetCurrentProcessId();
1829   // m_TransactionId = ((UInt64)m_ProcessId << 32) + 1;
1830   // ClearState();
1831 }
1832 
1833 // clear internal state
ClearState()1834 void CDropTarget::ClearState()
1835 {
1836   m_DataObject.Release();
1837   m_SourcePaths.Clear();
1838 
1839   m_IsRightButton = false;
1840 
1841   m_GetTransfer_WasSuccess = false;
1842   m_DropIsAllowed = false;
1843 
1844   m_PanelDropIsAllowed = false;
1845   // m_SubFolderIndex = -1;
1846   // m_DropHighlighted_SubFolderName.Empty();
1847   m_Panel = NULL;
1848   m_IsAppTarget = false;
1849   m_TargetPath_WasSent_ToDataObject = false;
1850   m_TargetPath_NonEmpty_WasSent_ToDataObject = false;
1851   m_Transfer_WasSent_ToDataObject = false;
1852 }
1853 
1854 /*
1855   IDataObject::QueryGetData() Determines whether the data object is capable of
1856   rendering the data as specified. Objects attempting a paste or drop
1857   operation can call this method before calling IDataObject::GetData
1858   to get an indication of whether the operation may be successful.
1859 
1860   The client of a data object calls QueryGetData to determine whether
1861   passing the specified FORMATETC structure to a subsequent call to
1862   IDataObject::GetData is likely to be successful.
1863 
1864   We check Try_QueryGetData with CF_HDROP
1865 */
1866 /*
1867 void CDropTarget::Try_QueryGetData(IDataObject *dataObject)
1868 {
1869   FORMATETC etc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1870   m_DropIsAllowed = (dataObject->QueryGetData(&etc) == S_OK);
1871 }
1872 */
1873 
ListView_SetItemState_DropHighlighted(NControl::CListView & listView,int index,bool highlighted)1874 static void ListView_SetItemState_DropHighlighted(
1875     NControl::CListView &listView, int index, bool highlighted)
1876 {
1877   // LVIS_DROPHILITED : The item is highlighted as a drag-and-drop target
1878   /*
1879   LVITEM item;
1880   item.mask = LVIF_STATE;
1881   item.iItem = index;
1882   item.iSubItem = 0;
1883   item.state = enable ? LVIS_DROPHILITED : 0;
1884   item.stateMask = LVIS_DROPHILITED;
1885   item.pszText = NULL;
1886   listView.SetItem(&item);
1887   */
1888   listView.SetItemState(index, highlighted ? LVIS_DROPHILITED : 0, LVIS_DROPHILITED);
1889 }
1890 
1891 // Removes DropHighlighted state in ListView item, if it was set before
RemoveSelection()1892 void CDropTarget::RemoveSelection()
1893 {
1894   if (m_Panel)
1895   {
1896     m_Panel->m_DropHighlighted_SubFolderName.Empty();
1897     if (m_Panel->m_DropHighlighted_SelectionIndex >= 0)
1898     {
1899       ListView_SetItemState_DropHighlighted(m_Panel->_listView,
1900           m_Panel->m_DropHighlighted_SelectionIndex, false);
1901       m_Panel->m_DropHighlighted_SelectionIndex = -1;
1902     }
1903   }
1904 }
1905 
1906 #ifdef UNDER_CE
1907 #define ChildWindowFromPointEx(hwndParent, pt, uFlags) ChildWindowFromPoint(hwndParent, pt)
1908 #endif
1909 
1910 
1911 /*
1912    PositionCursor() function sets m_Panel under cursor drop, and
1913    m_SubFolderIndex/m_DropHighlighted_SubFolderName, if drop to some folder in Panel list.
1914 */
1915 /*
1916 PositionCursor() uses as input variables:
1917   m_DropIsAllowed must be set before PositionCursor()
1918   if (m_DropHighlighted_SelectionIndex >= 0 && m_Panel) it uses m_Panel and removes previous selection
1919 PositionCursor() sets
1920   m_PanelDropIsAllowed
1921   m_Panel
1922   m_IsAppTarget
1923   m_SubFolderIndex
1924   m_DropHighlighted_SubFolderName
1925   m_DropHighlighted_SelectionIndex
1926 */
PositionCursor(const POINTL & ptl)1927 void CDropTarget::PositionCursor(const POINTL &ptl)
1928 {
1929   RemoveSelection();
1930 
1931   // m_SubFolderIndex = -1;
1932   // m_DropHighlighted_SubFolderName.Empty();
1933   m_IsAppTarget = true;
1934   m_Panel = NULL;
1935   m_PanelDropIsAllowed = false;
1936 
1937   if (!m_DropIsAllowed)
1938     return;
1939 
1940   POINT pt;
1941   pt.x = ptl.x;
1942   pt.y = ptl.y;
1943   {
1944     POINT pt2 = pt;
1945     if (App->_window.ScreenToClient(&pt2))
1946       for (unsigned i = 0; i < kNumPanelsMax; i++)
1947         if (App->IsPanelVisible(i))
1948         {
1949           CPanel *panel = &App->Panels[i];
1950           if (panel->IsEnabled())
1951           if (::ChildWindowFromPointEx(App->_window, pt2,
1952               CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) == (HWND)*panel)
1953           {
1954             m_Panel = panel;
1955             m_IsAppTarget = false;
1956             if ((int)i == SrcPanelIndex)
1957               return; // we don't allow to drop to source panel
1958             break;
1959           }
1960         }
1961   }
1962 
1963   m_PanelDropIsAllowed = true;
1964 
1965   if (!m_Panel)
1966   {
1967     if (TargetPanelIndex >= 0)
1968       m_Panel = &App->Panels[TargetPanelIndex];
1969     // we don't need to find item in panel
1970     return;
1971   }
1972 
1973   // we will try to find and highlight drop folder item in listView under cursor
1974   /*
1975   m_PanelDropIsAllowed = m_Panel->DoesItSupportOperations();
1976   if (!m_PanelDropIsAllowed)
1977     return;
1978   */
1979   /* now we don't allow drop to subfolder under cursor, if dest panel is archive.
1980      Another code must be fixed for that case, where we must use m_SubFolderIndex/m_DropHighlighted_SubFolderName */
1981   if (!m_Panel->IsFsOrPureDrivesFolder())
1982     return;
1983 
1984   if (::WindowFromPoint(pt) != (HWND)m_Panel->_listView)
1985     return;
1986 
1987   LVHITTESTINFO info;
1988   m_Panel->_listView.ScreenToClient(&pt);
1989   info.pt = pt;
1990   const int index = ListView_HitTest(m_Panel->_listView, &info);
1991   if (index < 0)
1992     return;
1993   const unsigned realIndex = m_Panel->GetRealItemIndex(index);
1994   if (realIndex == kParentIndex)
1995     return;
1996   if (!m_Panel->IsItem_Folder(realIndex))
1997     return;
1998   // m_SubFolderIndex = (int)realIndex;
1999   m_Panel->m_DropHighlighted_SubFolderName = m_Panel->GetItemName(realIndex);
2000   ListView_SetItemState_DropHighlighted(m_Panel->_listView, index, true);
2001   m_Panel->m_DropHighlighted_SelectionIndex = index;
2002 }
2003 
2004 
2005 /* returns true, if !m_IsAppTarget
2006    and target is FS folder or altStream folder
2007 */
2008 
GetFolderType() const2009 UInt32 CDropTarget::GetFolderType() const
2010 {
2011   if (m_IsAppTarget || !m_Panel)
2012     return k_FolderType_None;
2013   if (m_Panel->IsFSFolder() ||
2014       (m_Panel->IsFSDrivesFolder()
2015        && m_Panel->m_DropHighlighted_SelectionIndex >= 0))
2016     return k_FolderType_Fs;
2017   if (m_Panel->IsAltStreamsFolder())
2018     return k_FolderType_AltStreams;
2019   if (m_Panel->IsArcFolder())
2020     return k_FolderType_Archive;
2021   return k_FolderType_Unknown;
2022 }
2023 
IsFsFolderPath() const2024 bool CDropTarget::IsFsFolderPath() const
2025 {
2026   if (m_IsAppTarget || !m_Panel)
2027     return false;
2028   if (m_Panel->IsFSFolder())
2029     return true;
2030   if (m_Panel->IsAltStreamsFolder())
2031     return true;
2032   return m_Panel->IsFSDrivesFolder() &&
2033       m_Panel->m_DropHighlighted_SelectionIndex >= 0;
2034 }
2035 
2036 
2037 #define INIT_FORMATETC_HGLOBAL(type) { (type), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }
2038 
DataObject_GetData_GetTransfer(IDataObject * dataObject,UINT a_Format_7zip_GetTransfer,CDataObject_GetTransfer & transfer)2039 static bool DataObject_GetData_GetTransfer(IDataObject *dataObject,
2040     UINT a_Format_7zip_GetTransfer, CDataObject_GetTransfer &transfer)
2041 {
2042   FORMATETC etc = INIT_FORMATETC_HGLOBAL((CLIPFORMAT)a_Format_7zip_GetTransfer);
2043   NCOM::CStgMedium medium;
2044   const HRESULT res = dataObject->GetData(&etc, &medium);
2045   if (res != S_OK)
2046     return false;
2047   if (medium.tymed != TYMED_HGLOBAL)
2048     return false;
2049   const size_t size = GlobalSize(medium.hGlobal);
2050   if (size < sizeof(transfer))
2051     return false;
2052   NMemory::CGlobalLock dropLock(medium.hGlobal);
2053   const CDataObject_GetTransfer *t = (const CDataObject_GetTransfer *)dropLock.GetPointer();
2054   if (!t)
2055     return false;
2056   if (!t->Check()) // isSetData
2057     return false;
2058   transfer = *t;
2059   return true;
2060 }
2061 
2062 /*
2063   returns true, if all m_SourcePaths[] items are same drive
2064   as destination drop path in m_Panel
2065 */
IsItSameDrive() const2066 bool CDropTarget::IsItSameDrive() const
2067 {
2068   if (!m_Panel)
2069     return false;
2070   if (!IsFsFolderPath())
2071     return false;
2072 
2073   UString drive;
2074 
2075   if (m_Panel->IsFSFolder())
2076   {
2077     drive = m_Panel->GetDriveOrNetworkPrefix();
2078     if (drive.IsEmpty())
2079       return false;
2080   }
2081   else if (m_Panel->IsFSDrivesFolder()
2082       && m_Panel->m_DropHighlighted_SelectionIndex >= 0)
2083   {
2084     drive = m_Panel->m_DropHighlighted_SubFolderName;
2085     drive.Add_PathSepar();
2086   }
2087   else
2088     return false;
2089 
2090   if (m_SourcePaths.Size() == 0)
2091     return false;
2092 
2093   FOR_VECTOR (i, m_SourcePaths)
2094   {
2095     if (!m_SourcePaths[i].IsPrefixedBy_NoCase(drive))
2096       return false;
2097   }
2098   return true;
2099 }
2100 
2101 
2102 /*
2103   There are 2 different actions, when we drag to 7-Zip:
2104   1) if target panel is "7-Zip" FS and any of the 2 cases:
2105      - Drag from any non "7-Zip" program;
2106      or
2107      - Drag from "7-Zip" to non-panel area of "7-Zip".
2108      We want to create new archive for that operation with "Add to Archive" window.
2109   2) all another operations work as usual file COPY/MOVE
2110     - Drag from "7-Zip" FS to "7-Zip" FS.
2111         COPY/MOVE are supported.
2112     - Drag to open archive in 7-Zip.
2113         We want to update archive.
2114         We replace COPY to MOVE.
2115     - Drag from "7-Zip" archive to "7-Zip" FS.
2116         We replace COPY to MOVE.
2117 */
2118 
2119 // we try to repeat Explorer's effects.
2120 // out: 0 - means that use default effect
GetEffect_ForKeys(DWORD keyState)2121 static DWORD GetEffect_ForKeys(DWORD keyState)
2122 {
2123   if (keyState & MK_CONTROL)
2124   {
2125     if (keyState & MK_ALT)
2126       return 0;
2127     if (keyState & MK_SHIFT)
2128       return DROPEFFECT_LINK; // CONTROL + SHIFT
2129     return DROPEFFECT_COPY;   // CONTROL
2130   }
2131   // no CONTROL
2132   if (keyState & MK_SHIFT)
2133   {
2134     if (keyState & MK_ALT)
2135       return 0;
2136     return DROPEFFECT_MOVE; // SHIFT
2137   }
2138   // no CONTROL, no SHIFT
2139   if (keyState & MK_ALT)
2140     return DROPEFFECT_LINK; // ALT
2141   return 0;
2142 }
2143 
2144 
2145 /* GetEffect() uses m_TargetPath_WasSentToDataObject
2146    to disale MOVE operation, if Source is not 7-Zip
2147 */
GetEffect(DWORD keyState,POINTL,DWORD allowedEffect) const2148 DWORD CDropTarget::GetEffect(DWORD keyState, POINTL /* pt */, DWORD allowedEffect) const
2149 {
2150   // (DROPEFFECT_NONE == 0)
2151   if (!m_DropIsAllowed || !m_PanelDropIsAllowed)
2152     return 0;
2153   if (!IsFsFolderPath() || !m_TargetPath_WasSent_ToDataObject)
2154   {
2155     // we don't allow MOVE, if Target is archive or Source is not 7-Zip
2156     // disabled for debug:
2157     // allowedEffect &= ~DROPEFFECT_MOVE;
2158   }
2159   DWORD effect;
2160   {
2161     effect = GetEffect_ForKeys(keyState);
2162     if (effect == DROPEFFECT_LINK)
2163       return 0;
2164     effect &= allowedEffect;
2165   }
2166   if (effect == 0)
2167   {
2168     if (allowedEffect & DROPEFFECT_COPY)
2169       effect = DROPEFFECT_COPY;
2170     if (allowedEffect & DROPEFFECT_MOVE)
2171     {
2172       /* MOVE operation can be optimized. So MOVE is preferred way
2173          for default action, if Source and Target are at same drive */
2174       if (IsItSameDrive())
2175         effect = DROPEFFECT_MOVE;
2176     }
2177   }
2178   return effect;
2179 }
2180 
2181 
2182 /* returns:
2183     - target folder path prefix, if target is FS folder
2184     - empty string, if target is not FS folder
2185 */
GetTargetPath() const2186 UString CDropTarget::GetTargetPath() const
2187 {
2188   if (!IsFsFolderPath())
2189     return UString();
2190   UString path = m_Panel->GetFsPath();
2191   if (/* m_SubFolderIndex >= 0 && */
2192       !m_Panel->m_DropHighlighted_SubFolderName.IsEmpty())
2193   {
2194     path += m_Panel->m_DropHighlighted_SubFolderName;
2195     path.Add_PathSepar();
2196   }
2197   return path;
2198 }
2199 
2200 
2201 /*
2202 if IDropSource is Win10-Explorer
2203 --------------------------------
2204   As in MS DOCs:
2205   The source inspects the two (effect) values that can be returned by the target:
2206     1) SetData(CFSTR_PERFORMEDDROPEFFECT)
2207     2) returned value  (*effect) by
2208         CDropTarget::Drop(IDataObject *dataObject, DWORD keyState,
2209         POINTL pt, DWORD *effect)
2210   If both are set to DROPEFFECT_MOVE, Explorer completes the unoptimized move by deleting
2211   the original data.
2212   // Otherwise, the target did an optimized move and the original data has been deleted.
2213 */
2214 
2215 
2216 /*
2217   Send targetPath from target to dataObject (to Source)
2218   input: set (enablePath = false) to send empty path
2219   returns true,  if SetData()         returns S_OK : (source is 7-zip)
2220   returns false, if SetData() doesn't return  S_OK : (source is Explorer)
2221 */
SendToSource_TargetPath_enable(IDataObject * dataObject,bool enablePath)2222 bool CDropTarget::SendToSource_TargetPath_enable(IDataObject *dataObject, bool enablePath)
2223 {
2224   m_TargetPath_NonEmpty_WasSent_ToDataObject = false;
2225   UString path;
2226   if (enablePath)
2227     path = GetTargetPath();
2228   PRF("CDropTarget::SetPath");
2229   PRF_W(path);
2230   if (!dataObject || m_Format_7zip_SetTargetFolder == 0)
2231     return false;
2232   FORMATETC etc = INIT_FORMATETC_HGLOBAL((CLIPFORMAT)m_Format_7zip_SetTargetFolder);
2233   STGMEDIUM medium;
2234   medium.tymed = etc.tymed;
2235   medium.pUnkForRelease = NULL;
2236   const size_t num = path.Len() + 1; // + (1 << 19) // for debug
2237   medium.hGlobal = GlobalAlloc(GHND | GMEM_SHARE, num * sizeof(wchar_t));
2238   if (!medium.hGlobal)
2239     return false;
2240   // Sleep(1000);
2241   wchar_t *dest = (wchar_t *)GlobalLock(medium.hGlobal);
2242   // Sleep(1000);
2243   bool res = false;
2244   if (dest)
2245   {
2246     MyStringCopy(dest, (const wchar_t *)path);
2247     GlobalUnlock(medium.hGlobal);
2248     // OutputDebugString("m_DataObject->SetData");
2249     const BOOL release = FALSE; // that way is more simple for correct releasing.
2250         // TRUE; // for debug : is not good for some cases.
2251     /* If DropSource is Win10-Explorer, dataObject->SetData() returns E_NOTIMPL; */
2252     const HRESULT hres = dataObject->SetData(&etc, &medium, release);
2253     // Sleep(1000);
2254     res = (hres == S_OK);
2255   }
2256 
2257   ReleaseStgMedium(&medium);
2258   if (res && !path.IsEmpty())
2259     m_TargetPath_NonEmpty_WasSent_ToDataObject = true;
2260   // Sleep(1000);
2261   return res;
2262 }
2263 
2264 
SendToSource_auto(IDataObject * dataObject,const CTargetTransferInfo & info)2265 void CDropTarget::SendToSource_auto(IDataObject *dataObject,
2266     const CTargetTransferInfo &info)
2267 {
2268   /* we try to send target path to Source.
2269      If Source is 7-Zip, then it will accept k_Format_7zip_SetTargetFolder.
2270      That sent path will be non-Empty, if this target is FS folder and drop is allowed */
2271   bool need_Send = false;
2272   if (   info.FuncType == k_DragTargetMode_Enter
2273       || info.FuncType == k_DragTargetMode_Over
2274       || (info.FuncType == k_DragTargetMode_Drop_Begin
2275         // && targetOp_Cmd != NDragMenu::k_None
2276         && info.Cmd_Type != NDragMenu::k_Cancel))
2277   // if (!g_CreateArchive_for_Drag_from_7zip)
2278       need_Send = m_DropIsAllowed && m_PanelDropIsAllowed && IsFsFolderPath();
2279   m_TargetPath_WasSent_ToDataObject = SendToSource_TargetPath_enable(dataObject, need_Send);
2280   SendToSource_TransferInfo(dataObject, info);
2281 }
2282 
2283 
SendToSource_TransferInfo(IDataObject * dataObject,const CTargetTransferInfo & info)2284 bool CDropTarget::SendToSource_TransferInfo(IDataObject *dataObject,
2285     const CTargetTransferInfo &info)
2286 {
2287   m_Transfer_WasSent_ToDataObject = false;
2288   PRF("CDropTarget::SendToSource_TransferInfo");
2289 
2290   if (!dataObject || m_Format_7zip_SetTransfer == 0)
2291     return false;
2292   FORMATETC etc = INIT_FORMATETC_HGLOBAL((CLIPFORMAT)m_Format_7zip_SetTransfer);
2293   STGMEDIUM medium;
2294   medium.tymed = etc.tymed;
2295   medium.pUnkForRelease = NULL;
2296   CDataObject_SetTransfer transfer;
2297   const size_t size = sizeof(transfer); // + (1 << 19) // for debug
2298   // OutputDebugString("GlobalAlloc");
2299   medium.hGlobal = GlobalAlloc(GHND | GMEM_SHARE, size);
2300   // Sleep(1000);
2301   if (!medium.hGlobal)
2302     return false;
2303   // OutputDebugString("GlobalLock");
2304   void *dest = (wchar_t *)GlobalLock(medium.hGlobal);
2305   // Sleep(1000);
2306   bool res = false;
2307   if (dest)
2308   {
2309     transfer.Init();
2310     transfer.Target = info;
2311 
2312     memcpy(dest, &transfer, sizeof(transfer));
2313     GlobalUnlock(medium.hGlobal);
2314     // OutputDebugString("m_DataObject->SetData");
2315     const BOOL release = FALSE; // that way is more simple for correct releasing.
2316         // TRUE; // for debug : is not good for some cases
2317     const HRESULT hres = dataObject->SetData(&etc, &medium, release);
2318     res = (hres == S_OK);
2319   }
2320 
2321   ReleaseStgMedium(&medium);
2322   if (res)
2323     m_Transfer_WasSent_ToDataObject = true;
2324   return res;
2325 }
2326 
2327 
SendToSource_UInt32(IDataObject * dataObject,UINT format,UInt32 value)2328 bool CDropTarget::SendToSource_UInt32(IDataObject *dataObject, UINT format, UInt32 value)
2329 {
2330   PRF("CDropTarget::Send_UInt32 (Performed)");
2331 
2332   if (!dataObject || format == 0)
2333     return false;
2334   FORMATETC etc = INIT_FORMATETC_HGLOBAL((CLIPFORMAT)format);
2335   STGMEDIUM medium;
2336   medium.tymed = etc.tymed;
2337   medium.pUnkForRelease = NULL;
2338   const size_t size = 4;
2339   medium.hGlobal = GlobalAlloc(GHND | GMEM_SHARE, size);
2340   if (!medium.hGlobal)
2341     return false;
2342   void *dest = GlobalLock(medium.hGlobal);
2343   bool res = false;
2344   if (dest)
2345   {
2346     *(UInt32 *)dest = value;
2347     GlobalUnlock(medium.hGlobal);
2348     // OutputDebugString("m_DataObject->SetData");
2349     const BOOL release = TRUE;
2350         // FALSE; // for debug
2351     /* If DropSource is Win10-Explorer, then (release == FALSE) doesn't work
2352        and dataObject->SetData() returns E_NOTIMPL;
2353        So we use release = TRUE; here */
2354     const HRESULT hres = dataObject->SetData(&etc, &medium, release);
2355     // we return here without calling ReleaseStgMedium().
2356     return (hres == S_OK);
2357     // Sleep(1000);
2358     /*
2359       if (we use release = TRUE), we expect that
2360           - SetData() will release medium, and
2361           - SetData() will set STGMEDIUM::tymed to (TYMED_NULL = 0).
2362       but some "incorrect" SetData() implementations can keep STGMEDIUM::tymed unchanged.
2363       And it's not safe to call ReleaseStgMedium() here for that case,
2364       because DropSource also could release medium.
2365       We can reset (medium.tymed = TYMED_NULL) manually here to disable
2366       unsafe medium releasing in ReleaseStgMedium().
2367     */
2368     /*
2369     if (release)
2370     {
2371       medium.tymed = TYMED_NULL;
2372       medium.pUnkForRelease = NULL;
2373       medium.hGlobal = NULL;
2374     }
2375     res = (hres == S_OK);
2376     */
2377   }
2378   ReleaseStgMedium(&medium);
2379   return res;
2380 }
2381 
2382 
LoadNames_From_DataObject(IDataObject * dataObject)2383 void CDropTarget::LoadNames_From_DataObject(IDataObject *dataObject)
2384 {
2385   // "\\\\.\\" prefix is possible for long names
2386   m_DropIsAllowed = NShell::DataObject_GetData_HDROP_or_IDLIST_Names(dataObject, m_SourcePaths) == S_OK;
2387 }
2388 
2389 
DragEnter(IDataObject * dataObject,DWORD keyState,POINTL pt,DWORD * effect)2390 Z7_COMWF_B CDropTarget::DragEnter(IDataObject *dataObject, DWORD keyState, POINTL pt, DWORD *effect)
2391 {
2392   /* *(effect):
2393        - on input  : value of the dwOKEffects parameter of the DoDragDrop() function.
2394        - on return : must contain one of the DROPEFFECT flags, which indicates
2395                      what the result of the drop operation would be.
2396      (pt): the current cursor coordinates in screen coordinates.
2397   */
2398   PRF_(Print_Point("CDropTarget::DragEnter", keyState, pt, *effect))
2399   try {
2400 
2401   if ((keyState & (MK_RBUTTON | MK_MBUTTON)) != 0)
2402     m_IsRightButton = true;
2403 
2404   LoadNames_From_DataObject(dataObject);
2405   // Try_QueryGetData(dataObject);
2406   // we will use (m_DataObject) later in DragOver() and DragLeave().
2407   m_DataObject = dataObject;
2408   // return DragOver(keyState, pt, effect);
2409   PositionCursor(pt);
2410   CTargetTransferInfo target;
2411   target.FuncType = k_DragTargetMode_Enter;
2412   target.KeyState = keyState;
2413   target.Point = pt;
2414   target.OkEffects = *effect;
2415   SendToSource_Drag(target);
2416 
2417   CDataObject_GetTransfer transfer;
2418   m_GetTransfer_WasSuccess = DataObject_GetData_GetTransfer(
2419       dataObject, m_Format_7zip_GetTransfer, transfer);
2420   if (m_GetTransfer_WasSuccess)
2421   {
2422     if (transfer.Flags & k_SourceFlags_LeftButton)
2423       m_IsRightButton = false;
2424     else if (transfer.Flags & k_SourceFlags_RightButton)
2425       m_IsRightButton = true;
2426   }
2427 
2428   *effect = GetEffect(keyState, pt, *effect);
2429   return S_OK;
2430   } catch(...) { return E_FAIL; }
2431 }
2432 
2433 
DragOver(DWORD keyState,POINTL pt,DWORD * effect)2434 Z7_COMWF_B CDropTarget::DragOver(DWORD keyState, POINTL pt, DWORD *effect)
2435 {
2436   PRF_(Print_Point("CDropTarget::DragOver", keyState, pt, *effect))
2437   /*
2438     For efficiency reasons, a data object is not passed in IDropTarget::DragOver.
2439     The data object passed in the most recent call to IDropTarget::DragEnter
2440     is available and can be used.
2441 
2442     When IDropTarget::DragOver has completed its operation, the DoDragDrop
2443     function calls IDropSource::GiveFeedback so the source application can display
2444     the appropriate visual feedback to the user.
2445   */
2446   /*
2447     we suppose that it's unexpected that (keyState) shows that mouse
2448     button is not pressed, because such cases will be processed by
2449     IDropSource::QueryContinueDrag() that returns DRAGDROP_S_DROP or DRAGDROP_S_CANCEL.
2450     So DragOver() will not be called.
2451   */
2452 
2453   if ((keyState & MK_LBUTTON) == 0)
2454   {
2455     PRF4("CDropTarget::DragOver() (keyState & MK_LBUTTON) == 0");
2456     // g_Debug = true;
2457   }
2458 
2459   try {
2460   /* we suppose that source names were not changed after DragEnter()
2461      so we don't request GetNames_From_DataObject() for each call of DragOver() */
2462   PositionCursor(pt);
2463   CTargetTransferInfo target;
2464   target.FuncType = k_DragTargetMode_Over;
2465   target.KeyState = keyState;
2466   target.Point = pt;
2467   target.OkEffects = *effect;
2468   SendToSource_Drag(target);
2469   *effect = GetEffect(keyState, pt, *effect);
2470   // *effect = 1 << 8; // for debug
2471   return S_OK;
2472   } catch(...) { return E_FAIL; }
2473 }
2474 
2475 
DragLeave()2476 Z7_COMWF_B CDropTarget::DragLeave()
2477 {
2478   PRF4("CDropTarget::DragLeave");
2479   try {
2480   RemoveSelection();
2481   // we send empty TargetPath to 7-Zip Source to clear value of TargetPath that was sent before
2482 
2483   CTargetTransferInfo target;
2484   target.FuncType = k_DragTargetMode_Leave;
2485   /*
2486   target.KeyState = 0;
2487   target.Point = pt;
2488   pt.x = 0; // -1
2489   pt.y = 0; // -1
2490   target.Effect = 0;
2491   */
2492   SendToSource_Drag(target);
2493   ClearState();
2494   return S_OK;
2495   } catch(...) { return E_FAIL; }
2496 }
2497 
2498 
2499 static unsigned Drag_OnContextMenu(int xPos, int yPos, UInt32 cmdFlags);
2500 
2501 /*
2502   We suppose that there was DragEnter/DragOver for same (POINTL pt) before Drop().
2503   But we can work without DragEnter/DragOver too.
2504 */
Drop(IDataObject * dataObject,DWORD keyState,POINTL pt,DWORD * effect)2505 Z7_COMWF_B CDropTarget::Drop(IDataObject *dataObject, DWORD keyState,
2506     POINTL pt, DWORD *effect)
2507 {
2508   PRF_(Print_Point("CDropTarget::Drop", keyState, pt, *effect))
2509   /* Drop() is called after SourceDrop::QueryContinueDrag() returned DRAGDROP_S_DROP.
2510      So it's possible that Source have done some operations already.
2511   */
2512   HRESULT hres = S_OK;
2513   bool needDrop_by_Source = false;
2514   DWORD opEffect = DROPEFFECT_NONE;
2515 
2516   try {
2517   // we don't need m_DataObject reference anymore, because we use local (dataObject)
2518   m_DataObject.Release();
2519 
2520   /* in normal case : we called LoadNames_From_DataObject() in DragEnter() already.
2521      But if by some reason DragEnter() was not called,
2522      we need to call LoadNames_From_DataObject() before PositionCursor().
2523   */
2524   if (!m_DropIsAllowed) LoadNames_From_DataObject(dataObject);
2525   PositionCursor(pt);
2526 
2527   CPanel::CDisableTimerProcessing2 disableTimerProcessing(m_Panel);
2528   // CDisableNotify disableNotify2(m_Panel);
2529 
2530   UInt32 cmd = NDragMenu::k_None;
2531   UInt32 cmdEffect = DROPEFFECT_NONE;
2532   bool menu_WasShown = false;
2533   if (m_IsRightButton && m_Panel)
2534   {
2535     UInt32 flagsMask;
2536     if (m_Panel->IsArcFolder())
2537       flagsMask = (UInt32)1 << NDragMenu::k_Copy_ToArc;
2538     else
2539     {
2540       flagsMask = (UInt32)1 << NDragMenu::k_AddToArc;
2541       if (IsFsFolderPath())
2542         flagsMask |= (UInt32)1 << NDragMenu::k_Copy_Base;
2543     }
2544     // flagsMask |= (UInt32)1 << NDragMenu::k_Cancel;
2545     const UInt32 cmd32 = Drag_OnContextMenu(pt.x, pt.y, flagsMask);
2546     cmd = cmd32 & NDragMenu::k_MenuFlags_CmdMask;
2547     if (cmd32 & NDragMenu::k_MenuFlag_Copy)
2548       cmdEffect = DROPEFFECT_COPY;
2549     else if (cmd32 & NDragMenu::k_MenuFlag_Move)
2550       cmdEffect = DROPEFFECT_MOVE;
2551     opEffect = cmdEffect;
2552     menu_WasShown = true;
2553   }
2554   else
2555   {
2556     opEffect = GetEffect(keyState, pt, *effect);
2557     if (m_IsAppTarget)
2558       cmd = NDragMenu::k_AddToArc;
2559     else if (m_Panel)
2560     {
2561       if (IsFsFolderPath())
2562       {
2563         const bool is7zip = m_TargetPath_WasSent_ToDataObject;
2564         bool createNewArchive = false;
2565         if (is7zip)
2566           createNewArchive = false; // g_CreateArchive_for_Drag_from_7zip;
2567         else
2568           createNewArchive = true; // g_CreateArchive_for_Drag_from_Explorer;
2569 
2570         if (createNewArchive)
2571           cmd = NDragMenu::k_AddToArc;
2572         else
2573         {
2574           if (opEffect != 0)
2575             cmd = NDragMenu::k_Copy_Base;
2576           cmdEffect = opEffect;
2577         }
2578       }
2579       else
2580       {
2581         /* if we are inside open archive:
2582            if archive support operations         -> we will call operations
2583            if archive doesn't support operations -> we will create new archove
2584         */
2585         if (m_Panel->IsArcFolder()
2586             || m_Panel->DoesItSupportOperations())
2587         {
2588           cmd = NDragMenu::k_Copy_ToArc;
2589           // we don't want move to archive operation here.
2590           // so we force to DROPEFFECT_COPY.
2591           if (opEffect != DROPEFFECT_NONE)
2592             opEffect = DROPEFFECT_COPY;
2593           cmdEffect = opEffect;
2594         }
2595         else
2596           cmd = NDragMenu::k_AddToArc;
2597       }
2598     }
2599   }
2600 
2601   if (cmd == 0)
2602     cmd = NDragMenu::k_AddToArc;
2603 
2604   if (cmd == NDragMenu::k_AddToArc)
2605   {
2606     opEffect = DROPEFFECT_COPY;
2607     cmdEffect = DROPEFFECT_COPY;
2608   }
2609 
2610   if (m_Panel)
2611   if (cmd == NDragMenu::k_Copy_ToArc)
2612   {
2613     const UString title = LangString(IDS_CONFIRM_FILE_COPY);
2614     UString s = LangString(cmdEffect == DROPEFFECT_MOVE ?
2615         IDS_MOVE_TO : IDS_COPY_TO);
2616     s.Add_LF();
2617     // s += "\'";
2618     s += m_Panel->_currentFolderPrefix;
2619     // s += "\'";
2620     s.Add_LF();
2621     AddLangString(s, IDS_WANT_TO_COPY_FILES);
2622     s += " ?";
2623     const int res = ::MessageBoxW(*m_Panel, s, title, MB_YESNOCANCEL | MB_ICONQUESTION);
2624     if (res != IDYES)
2625       cmd = NDragMenu::k_Cancel;
2626   }
2627 
2628   CTargetTransferInfo target;
2629   target.FuncType = k_DragTargetMode_Drop_Begin;
2630   target.KeyState = keyState;
2631   target.Point = pt;
2632   target.OkEffects = *effect;
2633   target.Flags = 0;
2634 
2635   target.Cmd_Effect = cmdEffect;
2636   target.Cmd_Type = cmd;
2637   target.FolderType = GetFolderType();
2638 
2639   if (cmd == NDragMenu::k_Cancel)
2640     target.Flags |= k_TargetFlags_WasCanceled;
2641   if (menu_WasShown)
2642     target.Flags |= k_TargetFlags_MenuWasShown;
2643 
2644   SendToSource_auto(dataObject, target);
2645 
2646   CDataObject_GetTransfer transfer;
2647   m_GetTransfer_WasSuccess = DataObject_GetData_GetTransfer(
2648       dataObject, m_Format_7zip_GetTransfer, transfer);
2649 
2650   /* The Source (for example, 7-zip) could change file names when drop was confirmed.
2651      So we must reload source file paths here */
2652   if (cmd != NDragMenu::k_Cancel)
2653     LoadNames_From_DataObject(dataObject);
2654 
2655   if (cmd == NDragMenu::k_Cancel)
2656   {
2657     opEffect = DROPEFFECT_NONE;
2658     cmdEffect = DROPEFFECT_NONE;
2659   }
2660   else
2661   {
2662     if (m_GetTransfer_WasSuccess)
2663       needDrop_by_Source = ((transfer.Flags & k_SourceFlags_DoNotProcessInTarget) != 0);
2664     if (!needDrop_by_Source)
2665     {
2666       bool moveMode = (cmdEffect == DROPEFFECT_MOVE);
2667       bool needDrop = false;
2668       if (m_IsRightButton && m_Panel)
2669         needDrop = true;
2670       if (m_DropIsAllowed && m_PanelDropIsAllowed)
2671       {
2672         /* if non-empty TargetPath was sent successfully to DataObject,
2673            then the Source is 7-Zip, and that 7zip-Source can copy to FS operation.
2674            So we can disable Drop operation here for such case.
2675         */
2676         needDrop_by_Source = (cmd != NDragMenu::k_AddToArc
2677             && m_TargetPath_WasSent_ToDataObject
2678             && m_TargetPath_NonEmpty_WasSent_ToDataObject);
2679         needDrop = !(needDrop_by_Source);
2680       }
2681       if (needDrop)
2682       {
2683         UString path = GetTargetPath();
2684         if (m_IsAppTarget && m_Panel)
2685           if (m_Panel->IsFSFolder())
2686             path = m_Panel->GetFsPath();
2687 
2688         UInt32 sourceFlags = 0;
2689         if (m_GetTransfer_WasSuccess)
2690           sourceFlags = transfer.Flags;
2691 
2692         if (menu_WasShown)
2693           target.Flags |= k_TargetFlags_MenuWasShown;
2694 
2695         target.Flags |= k_TargetFlags_WasProcessed;
2696 
2697         RemoveSelection();
2698         // disableTimerProcessing.Restore();
2699         m_Panel->CompressDropFiles(m_SourcePaths, path,
2700             (cmd == NDragMenu::k_AddToArc),  // createNewArchive,
2701             moveMode, sourceFlags,
2702             target.Flags
2703             );
2704       }
2705     }
2706   } // end of if (cmd != NDragMenu::k_Cancel)
2707   {
2708     /* note that, if (we send CFSTR_PERFORMEDDROPEFFECT as DROPEFFECT_MOVE
2709             and Drop() returns (*effect == DROPEFFECT_MOVE), then
2710        Win10-Explorer-Source will try to remove files just after Drop() exit.
2711        But our CompressFiles() could be run without waiting finishing.
2712        DOCs say, that we must send CFSTR_PERFORMEDDROPEFFECT
2713          - DROPEFFECT_NONE : for   optimized move
2714          - DROPEFFECT_MOVE : for unoptimized move.
2715        But actually Win10-Explorer-Target sends (DROPEFFECT_MOVE) for move operation.
2716        And it still works as in optimized mode, because "unoptimized" deleting by Source will be performed
2717        if both conditions are met:
2718           1) DROPEFFECT_MOVE is sent to (CFSTR_PERFORMEDDROPEFFECT) and
2719           2) (*effect == DROPEFFECT_MOVE) is returend by Drop().
2720        We don't want to send DROPEFFECT_MOVE here to protect from
2721        deleting file by Win10-Explorer.
2722        We are not sure that allfile fieree processed by move.
2723     */
2724 
2725     // for debug: we test the case when source tries to delete original files
2726     // bool res;
2727     // only CFSTR_PERFORMEDDROPEFFECT affects file removing in Win10-Explorer.
2728     // res = SendToSource_UInt32(dataObject, RegisterClipboardFormat(CFSTR_LOGICALPERFORMEDDROPEFFECT), DROPEFFECT_MOVE); // for debug
2729     /* res = */ SendToSource_UInt32(dataObject,
2730         RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT),
2731         cmd == NDragMenu::k_Cancel ? DROPEFFECT_NONE : DROPEFFECT_COPY);
2732     // res = res;
2733   }
2734   RemoveSelection();
2735 
2736   target.FuncType = k_DragTargetMode_Drop_End;
2737   target.Cmd_Type = cmd;
2738   if (needDrop_by_Source)
2739     target.Flags |= k_TargetFlags_MustBeProcessedBySource;
2740 
2741   SendToSource_TransferInfo(dataObject, target);
2742   } catch(...) { hres = E_FAIL; }
2743 
2744   ClearState();
2745   // *effect |= (1 << 10); // for debug
2746   // *effect = DROPEFFECT_COPY; // for debug
2747 
2748   /*
2749     if we return (*effect == DROPEFFECT_MOVE) here,
2750     Explorer-Source at some conditions can treat it as (unoptimized move) mode,
2751     and Explorer-Source will remove source files after DoDragDrop()
2752     in that (unoptimized move) mode.
2753     We want to avoid such (unoptimized move) cases.
2754     So we don't return (*effect == DROPEFFECT_MOVE), here if Source is not 7-Zip.
2755     If source is 7-Zip that will do acual opeartion, then we can return DROPEFFECT_MOVE.
2756   */
2757   if (hres != S_OK || (opEffect == DROPEFFECT_MOVE && !needDrop_by_Source))
2758   {
2759     // opEffect = opEffect;
2760     // opEffect = DROPEFFECT_NONE; // for debug disabled
2761   }
2762 
2763   *effect = opEffect;
2764   /* if (hres <  0), DoDragDrop() also will return (hres).
2765      if (hres >= 0), DoDragDrop() will return DRAGDROP_S_DROP;
2766   */
2767   return hres;
2768 }
2769 
2770 
2771 
2772 // ---------- CPanel ----------
2773 
2774 
Is_Path1_Prefixed_by_Path2(const UString & path,const UString & prefix)2775 static bool Is_Path1_Prefixed_by_Path2(const UString &path, const UString &prefix)
2776 {
2777   const unsigned len = prefix.Len();
2778   if (path.Len() < len)
2779     return false;
2780   return CompareFileNames(path.Left(len), prefix) == 0;
2781 }
2782 
IsFolderInTemp(const UString & path)2783 static bool IsFolderInTemp(const UString &path)
2784 {
2785   FString tempPathF;
2786   if (!MyGetTempPath(tempPathF))
2787     return false;
2788   const UString tempPath = fs2us(tempPathF);
2789   if (tempPath.IsEmpty())
2790     return false;
2791   return Is_Path1_Prefixed_by_Path2(path, tempPath);
2792 }
2793 
AreThereNamesFromTemp(const UStringVector & filePaths)2794 static bool AreThereNamesFromTemp(const UStringVector &filePaths)
2795 {
2796   FString tempPathF;
2797   if (!MyGetTempPath(tempPathF))
2798     return false;
2799   const UString tempPath = fs2us(tempPathF);
2800   if (tempPath.IsEmpty())
2801     return false;
2802   FOR_VECTOR (i, filePaths)
2803     if (Is_Path1_Prefixed_by_Path2(filePaths[i], tempPath))
2804       return true;
2805   return false;
2806 }
2807 
2808 
2809 /*
2810   empty folderPath means create new Archive to path of first fileName.
2811   createNewArchive == true : show "Add to archive ..." dialog with external program
2812     folderPath.IsEmpty()  : create archive in folder of filePaths[0].
2813   createNewArchive == false :
2814     folderPath.IsEmpty()  : copy to archive folder that is open in panel
2815     !folderPath.IsEmpty()  : CopyFsItems() to folderPath.
2816 */
CompressDropFiles(const UStringVector & filePaths,const UString & folderPath,bool createNewArchive,bool moveMode,UInt32 sourceFlags,UInt32 & targetFlags)2817 void CPanel::CompressDropFiles(
2818     const UStringVector &filePaths,
2819     const UString &folderPath,
2820     bool createNewArchive,
2821     bool moveMode,
2822     UInt32 sourceFlags,
2823     UInt32 &targetFlags
2824     )
2825 {
2826   if (filePaths.Size() == 0)
2827     return;
2828   // createNewArchive = false; // for debug
2829 
2830   if (createNewArchive)
2831   {
2832     UString folderPath2 = folderPath;
2833     // folderPath2.Empty(); // for debug
2834     if (folderPath2.IsEmpty())
2835     {
2836       {
2837         FString folderPath2F;
2838         GetOnlyDirPrefix(us2fs(filePaths.Front()), folderPath2F);
2839         folderPath2 = fs2us(folderPath2F);
2840       }
2841       if (IsFolderInTemp(folderPath2))
2842       {
2843         /* we don't want archive to be created in temp directory.
2844            so we change the path to root folder (non-temp) */
2845         folderPath2 = ROOT_FS_FOLDER;
2846       }
2847     }
2848 
2849     UString arcName_base;
2850     const UString arcName = CreateArchiveName(filePaths,
2851         false, // isHash
2852         NULL,  // CFileInfo *fi
2853         arcName_base);
2854 
2855     bool needWait;
2856     if (sourceFlags & k_SourceFlags_WaitFinish)
2857       needWait = true;
2858     else if (sourceFlags & k_SourceFlags_DoNotWaitFinish)
2859       needWait = false;
2860     else if (sourceFlags & k_SourceFlags_TempFiles)
2861       needWait = true;
2862     else
2863       needWait = AreThereNamesFromTemp(filePaths);
2864 
2865     targetFlags |= (needWait ?
2866         k_TargetFlags_WaitFinish :
2867         k_TargetFlags_DoNotWaitFinish);
2868 
2869     CompressFiles(folderPath2, arcName,
2870         L"",      // arcType
2871         true,     // addExtension
2872         filePaths,
2873         false,    // email
2874         true,     // showDialog
2875         needWait);
2876   }
2877   else
2878   {
2879     targetFlags |= k_TargetFlags_WaitFinish;
2880     if (!folderPath.IsEmpty())
2881     {
2882       CCopyToOptions options;
2883       options.moveMode = moveMode;
2884       options.folder = folderPath;
2885       options.showErrorMessages = true; // showErrorMessages is not used for this operation
2886       options.NeedRegistryZone = false;
2887       options.ZoneIdMode = NExtract::NZoneIdMode::kNone;
2888       // maybe we need more options here: FIXME
2889       /* HRESULT hres = */ CopyFsItems(options,
2890           filePaths,
2891           NULL // UStringVector *messages
2892           );
2893       // hres = hres;
2894     }
2895     else
2896     {
2897       CopyFromNoAsk(moveMode, filePaths);
2898     }
2899   }
2900 }
2901 
2902 
2903 
Drag_OnContextMenu(int xPos,int yPos,UInt32 cmdFlags)2904 static unsigned Drag_OnContextMenu(int xPos, int yPos, UInt32 cmdFlags)
2905 {
2906   CMenu menu;
2907   CMenuDestroyer menuDestroyer(menu);
2908   /*
2909     Esc key in shown menu doesn't work if we call Drag_OnContextMenu from ::Drop().
2910     We call SetFocus() tp solve that problem.
2911     But the focus will be changed to Target Window after Drag and Drop.
2912     Is it OK to use SetFocus() here ?
2913     Is there another way to enable Esc key ?
2914   */
2915   // _listView.SetFocus(); // for debug
2916   ::SetFocus(g_HWND);
2917   menu.CreatePopup();
2918   /*
2919   int defaultCmd; // = NDragMenu::k_Move;
2920   defaultCmd = NDragMenu::k_None;
2921   */
2922   for (unsigned i = 0; i < Z7_ARRAY_SIZE(NDragMenu::g_Pairs); i++)
2923   {
2924     const NDragMenu::CCmdLangPair &pair = NDragMenu::g_Pairs[i];
2925     const UInt32 cmdAndFlags = pair.CmdId_and_Flags;
2926     const UInt32 cmdId = cmdAndFlags & NDragMenu::k_MenuFlags_CmdMask;
2927     if (cmdId != NDragMenu::k_Cancel)
2928     if ((cmdFlags & ((UInt32)1 << cmdId)) == 0)
2929       continue;
2930     const UINT flags = MF_STRING;
2931     /*
2932     if (prop.IsVisible)
2933       flags |= MF_CHECKED;
2934     if (i == 0)
2935       flags |= MF_GRAYED;
2936     */
2937     // MF_DEFAULT doesn't work
2938     // if (i == 2) flags |= MF_DEFAULT;
2939     // if (i == 4) flags |= MF_HILITE;
2940     // if (cmd == defaultCmd) flags |= MF_HILITE;
2941     UString name = LangString(pair.LangId);
2942     if (name.IsEmpty())
2943     {
2944       if (cmdId == NDragMenu::k_Cancel)
2945         name = "Cancel";
2946       else
2947         name.Add_UInt32(pair.LangId);
2948     }
2949     if (cmdId == NDragMenu::k_Copy_ToArc)
2950     {
2951       // UString destPath = _currentFolderPrefix;
2952       /*
2953       UString destPath = LangString(IDS_CONTEXT_ARCHIVE);
2954       name = MyFormatNew(name, destPath);
2955       */
2956       name.Add_Space();
2957       AddLangString(name, IDS_CONTEXT_ARCHIVE);
2958     }
2959     if (cmdId == NDragMenu::k_Cancel)
2960       menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)NULL);
2961     menu.AppendItem(flags, cmdAndFlags, name);
2962   }
2963   /*
2964   if (defaultCmd != 0)
2965     SetMenuDefaultItem(menu, (unsigned)defaultCmd,
2966         FALSE); // byPos
2967   */
2968   int menuResult = menu.Track(
2969       TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY,
2970       xPos, yPos,
2971       g_HWND
2972       // _listView // for debug
2973       );
2974   /* menu.Track() return value is zero, if the user cancels
2975      the menu without making a selection, or if an error occurs */
2976   if (menuResult <= 0)
2977     menuResult = NDragMenu::k_Cancel;
2978   return (unsigned)menuResult;
2979 }
2980 
2981 
2982 
CreateDragTarget()2983 void CApp::CreateDragTarget()
2984 {
2985   _dropTargetSpec = new CDropTarget();
2986   _dropTarget = _dropTargetSpec;
2987   _dropTargetSpec->App = (this);
2988 }
2989 
SetFocusedPanel(unsigned index)2990 void CApp::SetFocusedPanel(unsigned index)
2991 {
2992   LastFocusedPanel = index;
2993   _dropTargetSpec->TargetPanelIndex = (int)LastFocusedPanel;
2994 }
2995 
DragBegin(unsigned panelIndex)2996 void CApp::DragBegin(unsigned panelIndex)
2997 {
2998   _dropTargetSpec->TargetPanelIndex = (int)(NumPanels > 1 ? 1 - panelIndex : panelIndex);
2999   _dropTargetSpec->SrcPanelIndex = (int)panelIndex;
3000 }
3001 
DragEnd()3002 void CApp::DragEnd()
3003 {
3004   _dropTargetSpec->TargetPanelIndex = (int)LastFocusedPanel;
3005   _dropTargetSpec->SrcPanelIndex = -1;
3006 }
3007