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