1 #include "StdAfx.h"
2
3 #include "../../../Common/IntToString.h"
4 #include "../../../Common/StringConvert.h"
5
6 #include "../../../Windows/COM.h"
7 #include "../../../Windows/Clipboard.h"
8 #include "../../../Windows/Menu.h"
9 #include "../../../Windows/PropVariant.h"
10 #include "../../../Windows/PropVariantConv.h"
11
12 #include "../../PropID.h"
13 #include "../Common/PropIDUtils.h"
14 #include "../Explorer/ContextMenu.h"
15
16 #include "App.h"
17 #include "FormatUtils.h"
18 #include "LangUtils.h"
19 #include "ListViewDialog.h"
20 #include "MyLoadMenu.h"
21 #include "PropertyName.h"
22
23 #include "PropertyNameRes.h"
24 #include "resource.h"
25
26 // #define SHOW_DEBUG_PANEL_MENU
27
28 using namespace NWindows;
29
30 extern
31 LONG g_DllRefCount;
32 LONG g_DllRefCount = 0;
33
34 static const UINT kSevenZipStartMenuID = kMenuCmdID_Plugin_Start;
35 static const UINT kSystemStartMenuID = kMenuCmdID_Plugin_Start + 400;
36
37
38 #ifdef SHOW_DEBUG_PANEL_MENU
Print_Ptr(void * p,const char * s)39 static void Print_Ptr(void *p, const char *s)
40 {
41 char temp[32];
42 ConvertUInt64ToHex((UInt64)(void *)p, temp);
43 AString m;
44 m += temp;
45 m.Add_Space();
46 m += s;
47 OutputDebugStringA(m);
48 }
49 #define ODS(sz) { Print_Ptr(this, sz); }
50 #define ODS_U(s) { OutputDebugStringW(s); }
51 #else
52 #define ODS(sz)
53 #define ODS_U(s)
54 #endif
55
56
InvokeSystemCommand(const char * command)57 void CPanel::InvokeSystemCommand(const char *command)
58 {
59 NCOM::CComInitializer comInitializer;
60 if (!IsFsOrPureDrivesFolder())
61 return;
62 CRecordVector<UInt32> operatedIndices;
63 Get_ItemIndices_Operated(operatedIndices);
64 if (operatedIndices.IsEmpty())
65 return;
66 CMyComPtr<IContextMenu> contextMenu;
67 if (CreateShellContextMenu(operatedIndices, contextMenu) != S_OK)
68 return;
69
70 CMINVOKECOMMANDINFO ci;
71 ZeroMemory(&ci, sizeof(ci));
72 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
73 ci.hwnd = GetParent();
74 ci.lpVerb = command;
75 contextMenu->InvokeCommand(&ci);
76 }
77
78 static const char * const kSeparator = "------------------------";
79 static const char * const kSeparatorSmall = "----------------";
80
81 extern UString ConvertSizeToString(UInt64 value) throw();
82 bool IsSizeProp(UINT propID) throw();
83
84 UString GetOpenArcErrorMessage(UInt32 errorFlags);
85
86
AddListAscii(CListViewDialog & dialog,const char * s)87 static void AddListAscii(CListViewDialog &dialog, const char *s)
88 {
89 dialog.Strings.Add((UString)s);
90 dialog.Values.AddNew();
91 }
92
AddSeparator(CListViewDialog & dialog)93 static void AddSeparator(CListViewDialog &dialog)
94 {
95 AddListAscii(dialog, kSeparator);
96 }
97
AddSeparatorSmall(CListViewDialog & dialog)98 static void AddSeparatorSmall(CListViewDialog &dialog)
99 {
100 AddListAscii(dialog, kSeparatorSmall);
101 }
102
AddPropertyPair(const UString & name,const UString & val,CListViewDialog & dialog)103 static void AddPropertyPair(const UString &name, const UString &val, CListViewDialog &dialog)
104 {
105 dialog.Strings.Add(name);
106 dialog.Values.Add(val);
107 }
108
109
AddPropertyString(PROPID propID,const wchar_t * nameBSTR,const NCOM::CPropVariant & prop,CListViewDialog & dialog)110 static void AddPropertyString(PROPID propID, const wchar_t *nameBSTR,
111 const NCOM::CPropVariant &prop, CListViewDialog &dialog)
112 {
113 if (prop.vt != VT_EMPTY)
114 {
115 UString val;
116
117 if (propID == kpidErrorFlags ||
118 propID == kpidWarningFlags)
119 {
120 UInt32 flags = GetOpenArcErrorFlags(prop);
121 if (flags == 0)
122 return;
123 if (flags != 0)
124 val = GetOpenArcErrorMessage(flags);
125 }
126
127 if (val.IsEmpty())
128 {
129 if ((prop.vt == VT_UI8 || prop.vt == VT_UI4 || prop.vt == VT_UI2) && IsSizeProp(propID))
130 {
131 UInt64 v = 0;
132 ConvertPropVariantToUInt64(prop, v);
133 val = ConvertSizeToString(v);
134 }
135 else
136 ConvertPropertyToString2(val, prop, propID, 9); // we send 9 - is ns precision
137 }
138
139 if (!val.IsEmpty())
140 {
141 if (propID == kpidErrorType)
142 {
143 AddPropertyPair(L"Open WARNING:", L"Cannot open the file as expected archive type", dialog);
144 }
145 AddPropertyPair(GetNameOfProperty(propID, nameBSTR), val, dialog);
146 }
147 }
148 }
149
150
AddPropertyString(PROPID propID,UInt64 val,CListViewDialog & dialog)151 static void AddPropertyString(PROPID propID, UInt64 val, CListViewDialog &dialog)
152 {
153 NCOM::CPropVariant prop = val;
154 AddPropertyString(propID, NULL, prop, dialog);
155 }
156
157
158 static const Byte kSpecProps[] =
159 {
160 kpidPath,
161 kpidType,
162 kpidErrorType,
163 kpidError,
164 kpidErrorFlags,
165 kpidWarning,
166 kpidWarningFlags,
167 kpidOffset,
168 kpidPhySize,
169 kpidTailSize
170 };
171
Properties()172 void CPanel::Properties()
173 {
174 CMyComPtr<IGetFolderArcProps> getFolderArcProps;
175 _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
176 if (!getFolderArcProps)
177 {
178 InvokeSystemCommand("properties");
179 return;
180 }
181
182 {
183 CListViewDialog message;
184 // message.DeleteIsAllowed = false;
185 // message.SelectFirst = false;
186
187 CRecordVector<UInt32> operatedIndices;
188 Get_ItemIndices_Operated(operatedIndices);
189
190 if (operatedIndices.Size() == 1)
191 {
192 UInt32 index = operatedIndices[0];
193 // message += "Item:\n");
194 UInt32 numProps;
195 if (_folder->GetNumberOfProperties(&numProps) == S_OK)
196 {
197 for (UInt32 i = 0; i < numProps; i++)
198 {
199 CMyComBSTR name;
200 PROPID propID;
201 VARTYPE varType;
202
203 if (_folder->GetPropertyInfo(i, &name, &propID, &varType) != S_OK)
204 continue;
205
206 NCOM::CPropVariant prop;
207 if (_folder->GetProperty(index, propID, &prop) != S_OK)
208 continue;
209 AddPropertyString(propID, name, prop, message);
210 }
211 }
212
213
214 if (_folderRawProps)
215 {
216 _folderRawProps->GetNumRawProps(&numProps);
217 for (UInt32 i = 0; i < numProps; i++)
218 {
219 CMyComBSTR name;
220 PROPID propID;
221 if (_folderRawProps->GetRawPropInfo(i, &name, &propID) != S_OK)
222 continue;
223
224 const void *data;
225 UInt32 dataSize;
226 UInt32 propType;
227 if (_folderRawProps->GetRawProp(index, propID, &data, &dataSize, &propType) != S_OK)
228 continue;
229
230 if (dataSize != 0)
231 {
232 AString s;
233 if (propID == kpidNtSecure)
234 ConvertNtSecureToString((const Byte *)data, dataSize, s);
235 else
236 {
237 const unsigned kMaxDataSize = 1 << 8;
238 if (dataSize > kMaxDataSize)
239 {
240 s += "data:";
241 s.Add_UInt32(dataSize);
242 }
243 else
244 {
245 char temp[kMaxDataSize * 2 + 2];
246 if (dataSize <= 8 && (propID == kpidCRC || propID == kpidChecksum))
247 ConvertDataToHex_Upper(temp, (const Byte *)data, dataSize);
248 else
249 ConvertDataToHex_Lower(temp, (const Byte *)data, dataSize);
250 s += temp;
251 }
252 }
253 AddPropertyPair(GetNameOfProperty(propID, name), (UString)s.Ptr(), message);
254 }
255 }
256 }
257
258 AddSeparator(message);
259 }
260 else if (operatedIndices.Size() >= 1)
261 {
262 UInt64 packSize = 0;
263 UInt64 unpackSize = 0;
264 UInt64 numFiles = 0;
265 UInt64 numDirs = 0;
266
267 FOR_VECTOR (i, operatedIndices)
268 {
269 const UInt32 index = operatedIndices[i];
270 unpackSize += GetItemSize(index);
271 packSize += GetItem_UInt64Prop(index, kpidPackSize);
272 if (IsItem_Folder(index))
273 {
274 numDirs++;
275 numDirs += GetItem_UInt64Prop(index, kpidNumSubDirs);
276 numFiles += GetItem_UInt64Prop(index, kpidNumSubFiles);
277 }
278 else
279 numFiles++;
280 }
281 {
282 wchar_t temp[32];
283 ConvertUInt32ToString(operatedIndices.Size(), temp);
284 AddPropertyPair(L"", MyFormatNew(g_App.LangString_N_SELECTED_ITEMS, temp), message);
285 }
286
287 if (numDirs != 0)
288 AddPropertyString(kpidNumSubDirs, numDirs, message);
289 if (numFiles != 0)
290 AddPropertyString(kpidNumSubFiles, numFiles, message);
291 AddPropertyString(kpidSize, unpackSize, message);
292 AddPropertyString(kpidPackSize, packSize, message);
293
294 AddSeparator(message);
295 }
296
297
298 /*
299 AddLangString(message, IDS_PROP_FILE_TYPE);
300 message += kPropValueSeparator;
301 message += GetFolderTypeID();
302 message.Add_LF();
303 */
304
305 {
306 NCOM::CPropVariant prop;
307 if (_folder->GetFolderProperty(kpidPath, &prop) == S_OK)
308 {
309 AddPropertyString(kpidName, L"Path", prop, message);
310 }
311 }
312
313 CMyComPtr<IFolderProperties> folderProperties;
314 _folder.QueryInterface(IID_IFolderProperties, &folderProperties);
315 if (folderProperties)
316 {
317 UInt32 numProps;
318 if (folderProperties->GetNumberOfFolderProperties(&numProps) == S_OK)
319 {
320 for (UInt32 i = 0; i < numProps; i++)
321 {
322 CMyComBSTR name;
323 PROPID propID;
324 VARTYPE vt;
325 if (folderProperties->GetFolderPropertyInfo(i, &name, &propID, &vt) != S_OK)
326 continue;
327 NCOM::CPropVariant prop;
328 if (_folder->GetFolderProperty(propID, &prop) != S_OK)
329 continue;
330 AddPropertyString(propID, name, prop, message);
331 }
332 }
333 }
334
335 if (getFolderArcProps)
336 {
337 CMyComPtr<IFolderArcProps> getProps;
338 getFolderArcProps->GetFolderArcProps(&getProps);
339 if (getProps)
340 {
341 UInt32 numLevels;
342 if (getProps->GetArcNumLevels(&numLevels) != S_OK)
343 numLevels = 0;
344 for (UInt32 level2 = 0; level2 < numLevels; level2++)
345 {
346 {
347 UInt32 level = numLevels - 1 - level2;
348 UInt32 numProps;
349 if (getProps->GetArcNumProps(level, &numProps) == S_OK)
350 {
351 const int kNumSpecProps = Z7_ARRAY_SIZE(kSpecProps);
352
353 AddSeparator(message);
354
355 for (Int32 i = -(int)kNumSpecProps; i < (Int32)numProps; i++)
356 {
357 CMyComBSTR name;
358 PROPID propID;
359 VARTYPE vt;
360 if (i < 0)
361 propID = kSpecProps[i + kNumSpecProps];
362 else if (getProps->GetArcPropInfo(level, (UInt32)i, &name, &propID, &vt) != S_OK)
363 continue;
364 NCOM::CPropVariant prop;
365 if (getProps->GetArcProp(level, propID, &prop) != S_OK)
366 continue;
367 AddPropertyString(propID, name, prop, message);
368 }
369 }
370 }
371
372 if (level2 < numLevels - 1)
373 {
374 const UInt32 level = numLevels - 1 - level2;
375 UInt32 numProps;
376 if (getProps->GetArcNumProps2(level, &numProps) == S_OK)
377 {
378 AddSeparatorSmall(message);
379 for (UInt32 i = 0; i < numProps; i++)
380 {
381 CMyComBSTR name;
382 PROPID propID;
383 VARTYPE vt;
384 if (getProps->GetArcPropInfo2(level, i, &name, &propID, &vt) != S_OK)
385 continue;
386 NCOM::CPropVariant prop;
387 if (getProps->GetArcProp2(level, propID, &prop) != S_OK)
388 continue;
389 AddPropertyString(propID, name, prop, message);
390 }
391 }
392 }
393 }
394
395 {
396 // we ERROR message for NonOpen level
397 bool needSep = true;
398 const int kNumSpecProps = Z7_ARRAY_SIZE(kSpecProps);
399 for (Int32 i = -(int)kNumSpecProps; i < 0; i++)
400 {
401 CMyComBSTR name;
402 const PROPID propID = kSpecProps[i + kNumSpecProps];
403 NCOM::CPropVariant prop;
404 if (getProps->GetArcProp(numLevels, propID, &prop) != S_OK)
405 continue;
406 if (needSep)
407 {
408 AddSeparator(message);
409 AddSeparator(message);
410 needSep = false;
411 }
412 AddPropertyString(propID, name, prop, message);
413 }
414 }
415
416 }
417 }
418
419 message.Title = LangString(IDS_PROPERTIES);
420 message.NumColumns = 2;
421 message.Create(GetParent());
422 }
423 }
424
425
426
EditCut()427 void CPanel::EditCut()
428 {
429 // InvokeSystemCommand("cut");
430 }
431
EditCopy()432 void CPanel::EditCopy()
433 {
434 /*
435 CMyComPtr<IGetFolderArcProps> getFolderArcProps;
436 _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
437 if (!getFolderArcProps)
438 {
439 InvokeSystemCommand("copy");
440 return;
441 }
442 */
443 UString s;
444 CRecordVector<UInt32> indices;
445 Get_ItemIndices_Selected(indices);
446 FOR_VECTOR (i, indices)
447 {
448 if (i != 0)
449 s += "\xD\n";
450 s += GetItemName(indices[i]);
451 }
452 ClipboardSetText(_mainWindow, s);
453 }
454
EditPaste()455 void CPanel::EditPaste()
456 {
457 /*
458 UStringVector names;
459 ClipboardGetFileNames(names);
460 CopyFromNoAsk(names);
461 UString s;
462 for (int i = 0; i < names.Size(); i++)
463 {
464 s += L' ';
465 s += names[i];
466 }
467
468 MessageBoxW(0, s, L"", 0);
469 */
470
471 // InvokeSystemCommand("paste");
472 }
473
474
475
476 struct CFolderPidls
477 {
478 LPITEMIDLIST parent;
479 CRecordVector<LPITEMIDLIST> items;
480
CFolderPidlsCFolderPidls481 CFolderPidls(): parent(NULL) {}
~CFolderPidlsCFolderPidls482 ~CFolderPidls()
483 {
484 FOR_VECTOR (i, items)
485 CoTaskMemFree(items[i]);
486 CoTaskMemFree(parent);
487 }
488 };
489
490
ShellFolder_ParseDisplayName(IShellFolder * shellFolder,HWND hwnd,const UString & path,LPITEMIDLIST * ppidl)491 static HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder,
492 HWND hwnd, const UString &path, LPITEMIDLIST *ppidl)
493 {
494 ULONG eaten = 0;
495 return shellFolder->ParseDisplayName(hwnd, NULL,
496 path.Ptr_non_const(), &eaten, ppidl, NULL);
497 }
498
499
CreateShellContextMenu(const CRecordVector<UInt32> & operatedIndices,CMyComPtr<IContextMenu> & systemContextMenu)500 HRESULT CPanel::CreateShellContextMenu(
501 const CRecordVector<UInt32> &operatedIndices,
502 CMyComPtr<IContextMenu> &systemContextMenu)
503 {
504 ODS("==== CPanel::CreateShellContextMenu");
505 systemContextMenu.Release();
506 UString folderPath = GetFsPath();
507
508 CMyComPtr<IShellFolder> desktopFolder;
509 RINOK(::SHGetDesktopFolder(&desktopFolder))
510 if (!desktopFolder)
511 {
512 // ShowMessage("Failed to get Desktop folder");
513 return E_FAIL;
514 }
515
516 CFolderPidls pidls;
517 // NULL is allowed for parentHWND in ParseDisplayName()
518 const HWND parentHWND_for_ParseDisplayName = GetParent();
519 // if (folderPath.IsEmpty()), then ParseDisplayName returns pidls of "My Computer"
520 /* win10: ParseDisplayName() supports folder path with tail slash
521 ParseDisplayName() returns {
522 E_INVALIDARG : path with super path prefix "\\\\?\\"
523 ERROR_FILE_NOT_FOUND : path for network share (\\server\path1\long path2") larger than MAX_PATH
524 } */
525 const HRESULT res = ShellFolder_ParseDisplayName(desktopFolder,
526 parentHWND_for_ParseDisplayName,
527 folderPath, &pidls.parent);
528 if (res != S_OK)
529 {
530 ODS_U(folderPath);
531 if (res != E_INVALIDARG)
532 return res;
533 if (!NFile::NName::If_IsSuperPath_RemoveSuperPrefix(folderPath))
534 return res;
535 RINOK(ShellFolder_ParseDisplayName(desktopFolder,
536 parentHWND_for_ParseDisplayName,
537 folderPath, &pidls.parent))
538 }
539 if (!pidls.parent)
540 return E_FAIL;
541
542 /*
543 UString path2;
544 NShell::GetPathFromIDList(pidls.parent, path2);
545 ODS_U(path2);
546 */
547
548 if (operatedIndices.IsEmpty())
549 {
550 // how to get IContextMenu, if there are no selected files?
551 return E_FAIL;
552
553 /*
554 xp64 :
555 1) we can't use GetUIObjectOf() with (numItems == 0), it throws exception
556 2) we can't use desktopFolder->GetUIObjectOf() with absolute pidls of folder
557 context menu items are different in that case:
558 "Open / Explorer" for folder
559 "Delete" for "My Computer" icon
560 "Preperties" for "System"
561 */
562 /*
563 parentFolder = desktopFolder;
564 pidls.items.AddInReserved(pidls.parent);
565 pidls.parent = NULL;
566 */
567
568 // CreateViewObject() doesn't show all context menu items
569 /*
570 HRESULT res = parentFolder->CreateViewObject(
571 GetParent(), IID_IContextMenu, (void**)&systemContextMenu);
572 */
573 }
574
575 CMyComPtr<IShellFolder> parentFolder;
576 RINOK(desktopFolder->BindToObject(pidls.parent,
577 NULL, IID_IShellFolder, (void**)&parentFolder))
578 if (!parentFolder)
579 return E_FAIL;
580
581 ODS("==== CPanel::CreateShellContextMenu pidls START");
582
583 pidls.items.ClearAndReserve(operatedIndices.Size());
584 UString fileName;
585 FOR_VECTOR (i, operatedIndices)
586 {
587 fileName.Empty();
588 Add_ItemRelPath2_To_String(operatedIndices[i], fileName);
589 /* ParseDisplayName() in win10 returns:
590 E_INVALIDARG : if empty name, or path with dots only: "." , ".."
591 HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : if there is no such file
592 */
593 LPITEMIDLIST pidl = NULL;
594 RINOK(ShellFolder_ParseDisplayName(parentFolder,
595 parentHWND_for_ParseDisplayName,
596 fileName, &pidl))
597 if (!pidl)
598 return E_FAIL;
599 pidls.items.AddInReserved(pidl);
600 }
601
602 ODS("==== CPanel::CreateShellContextMenu pidls END");
603 // Get IContextMenu for items
604 RINOK(parentFolder->GetUIObjectOf(GetParent(),
605 pidls.items.Size(), (LPCITEMIDLIST *)(void *)pidls.items.ConstData(),
606 IID_IContextMenu, NULL, (void**)&systemContextMenu))
607 ODS("==== CPanel::CreateShellContextMenu GetUIObjectOf finished");
608 if (!systemContextMenu)
609 {
610 // ShowMessage("Unable to get context menu interface");
611 return E_FAIL;
612 }
613 return S_OK;
614 }
615
616
617 // #define SHOW_DEBUG_FM_CTX_MENU
618
619 #ifdef SHOW_DEBUG_FM_CTX_MENU
620
PrintHex(UString & s,UInt32 v)621 static void PrintHex(UString &s, UInt32 v)
622 {
623 char sz[32];
624 ConvertUInt32ToHex(v, sz);
625 s += sz;
626 }
627
PrintContextStr(UString & s,IContextMenu * ctxm,unsigned i,unsigned id,const char * name)628 static void PrintContextStr(UString &s, IContextMenu *ctxm, unsigned i, unsigned id, const char *name)
629 {
630 s += " | ";
631 s += name;
632 s += ": ";
633 UString s1;
634 {
635 char buf[256];
636 buf[0] = 0;
637 const HRESULT res = ctxm->GetCommandString(i, id,
638 NULL, buf, Z7_ARRAY_SIZE(buf) - 1);
639 if (res != S_OK)
640 {
641 PrintHex(s1, res);
642 s1.Add_Space();
643 }
644 s1 += GetUnicodeString(buf);
645 }
646 UString s2;
647 {
648 wchar_t buf2[256];
649 buf2[0] = 0;
650 const HRESULT res = ctxm->GetCommandString(i, id | GCS_UNICODE,
651 NULL, (char *)buf2, Z7_ARRAY_SIZE(buf2) - sizeof(wchar_t));
652 if (res != S_OK)
653 {
654 PrintHex(s2, res);
655 s2.Add_Space();
656 }
657 s2 += buf2;
658 }
659 s += s1;
660 if (s2.Compare(s1) != 0)
661 {
662 s += " Unicode: ";
663 s += s2;
664 }
665 }
666
PrintAllContextItems(IContextMenu * ctxm,unsigned num)667 static void PrintAllContextItems(IContextMenu *ctxm, unsigned num)
668 {
669 for (unsigned i = 0; i < num; i++)
670 {
671 UString s;
672 s.Add_UInt32(i);
673 s += ": ";
674 PrintContextStr(s, ctxm, i, GCS_VALIDATEA, "valid");
675 PrintContextStr(s, ctxm, i, GCS_VERBA, "verb");
676 PrintContextStr(s, ctxm, i, GCS_HELPTEXTA, "helptext");
677 OutputDebugStringW(s);
678 }
679 }
680
681 #endif
682
683
CreateSystemMenu(HMENU menuSpec,bool showExtendedVerbs,const CRecordVector<UInt32> & operatedIndices,CMyComPtr<IContextMenu> & systemContextMenu)684 void CPanel::CreateSystemMenu(HMENU menuSpec,
685 bool showExtendedVerbs,
686 const CRecordVector<UInt32> &operatedIndices,
687 CMyComPtr<IContextMenu> &systemContextMenu)
688 {
689 systemContextMenu.Release();
690
691 CreateShellContextMenu(operatedIndices, systemContextMenu);
692
693 if (!systemContextMenu)
694 return;
695
696 /*
697 // Set up a CMINVOKECOMMANDINFO structure.
698 CMINVOKECOMMANDINFO ci;
699 ZeroMemory(&ci, sizeof(ci));
700 ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
701 ci.hwnd = GetParent();
702 */
703
704 /*
705 if (Sender == GoBtn)
706 {
707 // Verbs that can be used are cut, paste,
708 // properties, delete, and so on.
709 String action;
710 if (CutRb->Checked)
711 action = "cut";
712 else if (CopyRb->Checked)
713 action = "copy";
714 else if (DeleteRb->Checked)
715 action = "delete";
716 else if (PropertiesRb->Checked)
717 action = "properties";
718
719 ci.lpVerb = action.c_str();
720 result = cm->InvokeCommand(&ci);
721 if (result)
722 ShowMessage(
723 "Error copying file to clipboard.");
724
725 }
726 else
727 */
728 {
729 // HMENU hMenu = CreatePopupMenu();
730 CMenu popupMenu;
731 CMenuDestroyer menuDestroyer(popupMenu);
732 if (!popupMenu.CreatePopup())
733 throw 210503;
734 const HMENU hMenu = popupMenu;
735 DWORD flags = CMF_EXPLORE;
736 if (showExtendedVerbs)
737 flags |= Z7_WIN_CMF_EXTENDEDVERBS;
738 ODS("=== systemContextMenu->QueryContextMenu START");
739 const HRESULT res = systemContextMenu->QueryContextMenu(hMenu, 0, kSystemStartMenuID, 0x7FFF, flags);
740 ODS("=== systemContextMenu->QueryContextMenu END");
741 if (SUCCEEDED(res))
742 {
743 #ifdef SHOW_DEBUG_FM_CTX_MENU
744 PrintAllContextItems(systemContextMenu, (unsigned)res);
745 #endif
746
747 CMenu menu;
748 menu.Attach(menuSpec);
749 CMenuItem menuItem;
750 menuItem.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
751 menuItem.fType = MFT_STRING;
752 menuItem.hSubMenu = popupMenu.Detach();
753 menuDestroyer.Disable();
754 LangString(IDS_SYSTEM, menuItem.StringValue);
755 menu.InsertItem(0, true, menuItem);
756 }
757 /*
758 if (Cmd < 100 && Cmd != 0)
759 {
760 ci.lpVerb = MAKEINTRESOURCE(Cmd - 1);
761 ci.lpParameters = "";
762 ci.lpDirectory = "";
763 ci.nShow = SW_SHOWNORMAL;
764 cm->InvokeCommand(&ci);
765 }
766 // If Cmd is > 100 then it's one of our
767 // inserted menu items.
768 else
769 // Find the menu item.
770 for (int i = 0; i < popupMenu1->Items->Count; i++)
771 {
772 TMenuItem* menu = popupMenu1->Items->Items[i];
773 // Call its OnClick handler.
774 if (menu->Command == Cmd - 100)
775 menu->OnClick(this);
776 }
777 // Release the memory allocated for the menu.
778 DestroyMenu(hMenu);
779 */
780 }
781 }
782
CreateFileMenu(HMENU menuSpec)783 void CPanel::CreateFileMenu(HMENU menuSpec)
784 {
785 CreateFileMenu(menuSpec, _sevenZipContextMenu, _systemContextMenu, true); // programMenu
786 }
787
CreateSevenZipMenu(HMENU menuSpec,bool showExtendedVerbs,const CRecordVector<UInt32> & operatedIndices,int firstDirIndex,CMyComPtr<IContextMenu> & sevenZipContextMenu)788 void CPanel::CreateSevenZipMenu(HMENU menuSpec,
789 bool showExtendedVerbs,
790 const CRecordVector<UInt32> &operatedIndices,
791 int firstDirIndex,
792 CMyComPtr<IContextMenu> &sevenZipContextMenu)
793 {
794 sevenZipContextMenu.Release();
795
796 CMenu menu;
797 menu.Attach(menuSpec);
798 // CMenuDestroyer menuDestroyer(menu);
799 // menu.CreatePopup();
800
801 CZipContextMenu *contextMenuSpec = new CZipContextMenu;
802 CMyComPtr<IContextMenu> contextMenu = contextMenuSpec;
803 // if (contextMenu.CoCreateInstance(CLSID_CZipContextMenu, IID_IContextMenu) == S_OK)
804 {
805 /*
806 CMyComPtr<IInitContextMenu> initContextMenu;
807 if (contextMenu.QueryInterface(IID_IInitContextMenu, &initContextMenu) != S_OK)
808 return;
809 */
810 ODS("=== FileName List Add START")
811 // for (unsigned y = 0; y < 10000; y++, contextMenuSpec->_fileNames.Clear())
812 GetFilePaths(operatedIndices, contextMenuSpec->_fileNames);
813 ODS("=== FileName List Add END")
814 contextMenuSpec->Init_For_7zFM();
815 contextMenuSpec->_attribs.FirstDirIndex = firstDirIndex;
816 {
817 DWORD flags = CMF_EXPLORE;
818 if (showExtendedVerbs)
819 flags |= Z7_WIN_CMF_EXTENDEDVERBS;
820 const HRESULT res = contextMenu->QueryContextMenu(menu,
821 0, // indexMenu
822 kSevenZipStartMenuID, // first
823 kSystemStartMenuID - 1, // last
824 flags);
825 ODS("=== contextMenu->QueryContextMenu END")
826 const bool sevenZipMenuCreated = SUCCEEDED(res);
827 if (sevenZipMenuCreated)
828 {
829 // if (res != 0)
830 {
831 // some "non-good" implementation of QueryContextMenu() could add some items to menu, but it return 0.
832 // so we still allow these items
833 sevenZipContextMenu = contextMenu;
834 #ifdef SHOW_DEBUG_FM_CTX_MENU
835 PrintAllContextItems(contextMenu, (unsigned)res);
836 #endif
837 }
838 }
839 else
840 {
841 // MessageBox_Error_HRESULT_Caption(res, L"QueryContextMenu");
842 }
843 // int code = HRESULT_CODE(res);
844 // int nextItemID = code;
845 }
846 }
847 }
848
IsReadOnlyFolder(IFolderFolder * folder)849 static bool IsReadOnlyFolder(IFolderFolder *folder)
850 {
851 if (!folder)
852 return false;
853
854 bool res = false;
855 {
856 NCOM::CPropVariant prop;
857 if (folder->GetFolderProperty(kpidReadOnly, &prop) == S_OK)
858 if (prop.vt == VT_BOOL)
859 res = VARIANT_BOOLToBool(prop.boolVal);
860 }
861 return res;
862 }
863
IsThereReadOnlyFolder() const864 bool CPanel::IsThereReadOnlyFolder() const
865 {
866 if (!_folderOperations)
867 return true;
868 if (IsReadOnlyFolder(_folder))
869 return true;
870 FOR_VECTOR (i, _parentFolders)
871 {
872 if (IsReadOnlyFolder(_parentFolders[i].ParentFolder))
873 return true;
874 }
875 return false;
876 }
877
CheckBeforeUpdate(UINT resourceID)878 bool CPanel::CheckBeforeUpdate(UINT resourceID)
879 {
880 if (!_folderOperations)
881 {
882 MessageBox_Error_UnsupportOperation();
883 // resourceID = resourceID;
884 // MessageBoxErrorForUpdate(E_NOINTERFACE, resourceID);
885 return false;
886 }
887
888 for (int i = (int)_parentFolders.Size(); i >= 0; i--)
889 {
890 IFolderFolder *folder;
891 if (i == (int)_parentFolders.Size())
892 folder = _folder;
893 else
894 folder = _parentFolders[i].ParentFolder;
895
896 if (!IsReadOnlyFolder(folder))
897 continue;
898
899 UString s;
900 AddLangString(s, resourceID);
901 s.Add_LF();
902 AddLangString(s, IDS_OPERATION_IS_NOT_SUPPORTED);
903 s.Add_LF();
904 if (i == 0)
905 s += GetFolderPath(folder);
906 else
907 s += _parentFolders[i - 1].VirtualPath;
908 s.Add_LF();
909 AddLangString(s, IDS_PROP_READ_ONLY);
910 MessageBox_Error(s);
911 return false;
912 }
913
914 return true;
915 }
916
CreateFileMenu(HMENU menuSpec,CMyComPtr<IContextMenu> & sevenZipContextMenu,CMyComPtr<IContextMenu> & systemContextMenu,bool programMenu)917 void CPanel::CreateFileMenu(HMENU menuSpec,
918 CMyComPtr<IContextMenu> &sevenZipContextMenu,
919 CMyComPtr<IContextMenu> &systemContextMenu,
920 bool programMenu)
921 {
922 sevenZipContextMenu.Release();
923 systemContextMenu.Release();
924
925 const bool showExtendedVerbs = IsKeyDown(VK_SHIFT);
926
927 CRecordVector<UInt32> operatedIndices;
928 Get_ItemIndices_Operated(operatedIndices);
929 const int firstDirIndex = FindDir_InOperatedList(operatedIndices);
930
931 CMenu menu;
932 menu.Attach(menuSpec);
933
934 if (!IsArcFolder())
935 {
936 CreateSevenZipMenu(menu, showExtendedVerbs, operatedIndices, firstDirIndex, sevenZipContextMenu);
937 // CreateSystemMenu is very slow if you call it inside ZIP archive with big number of files
938 // Windows probably can parse items inside ZIP archive.
939 if (g_App.ShowSystemMenu)
940 CreateSystemMenu(menu, showExtendedVerbs, operatedIndices, systemContextMenu);
941 }
942
943 /*
944 if (menu.GetItemCount() > 0)
945 menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)0);
946 */
947
948 CFileMenu fm;
949
950 fm.readOnly = IsThereReadOnlyFolder();
951 fm.isHashFolder = IsHashFolder();
952 fm.isFsFolder = Is_IO_FS_Folder();
953 fm.programMenu = programMenu;
954 fm.allAreFiles = (firstDirIndex == -1);
955 fm.numItems = operatedIndices.Size();
956
957 fm.isAltStreamsSupported = false;
958
959 if (fm.numItems == 1)
960 fm.FilePath = us2fs(GetItemFullPath(operatedIndices[0]));
961
962 if (_folderAltStreams)
963 {
964 if (operatedIndices.Size() <= 1)
965 {
966 UInt32 realIndex = (UInt32)(Int32)-1;
967 if (operatedIndices.Size() == 1)
968 realIndex = operatedIndices[0];
969 Int32 val = 0;
970 if (_folderAltStreams->AreAltStreamsSupported(realIndex, &val) == S_OK)
971 fm.isAltStreamsSupported = IntToBool(val);
972 }
973 }
974 else
975 {
976 if (fm.numItems == 0)
977 fm.isAltStreamsSupported = IsFSFolder();
978 else
979 fm.isAltStreamsSupported = IsFolder_with_FsItems();
980 }
981
982 fm.Load(menu, (unsigned)menu.GetItemCount());
983 }
984
InvokePluginCommand(unsigned id)985 bool CPanel::InvokePluginCommand(unsigned id)
986 {
987 return InvokePluginCommand(id, _sevenZipContextMenu, _systemContextMenu);
988 }
989
990 #if defined(_MSC_VER) && !defined(UNDER_CE)
991 #define use_CMINVOKECOMMANDINFOEX
992 /* CMINVOKECOMMANDINFOEX depends from (_WIN32_IE >= 0x0400) */
993 #endif
994
InvokePluginCommand(unsigned id,IContextMenu * sevenZipContextMenu,IContextMenu * systemContextMenu)995 bool CPanel::InvokePluginCommand(unsigned id,
996 IContextMenu *sevenZipContextMenu, IContextMenu *systemContextMenu)
997 {
998 UInt32 offset;
999 const bool isSystemMenu = (id >= kSystemStartMenuID);
1000 if (isSystemMenu)
1001 {
1002 if (!systemContextMenu)
1003 return false;
1004 offset = id - kSystemStartMenuID;
1005 }
1006 else
1007 {
1008 if (!sevenZipContextMenu)
1009 return false;
1010 offset = id - kSevenZipStartMenuID;
1011 }
1012
1013 #ifdef use_CMINVOKECOMMANDINFOEX
1014 CMINVOKECOMMANDINFOEX
1015 #else
1016 CMINVOKECOMMANDINFO
1017 #endif
1018 commandInfo;
1019
1020 memset(&commandInfo, 0, sizeof(commandInfo));
1021 commandInfo.cbSize = sizeof(commandInfo);
1022
1023 commandInfo.fMask = 0
1024 #ifdef use_CMINVOKECOMMANDINFOEX
1025 | CMIC_MASK_UNICODE
1026 #endif
1027 ;
1028
1029 commandInfo.hwnd = GetParent();
1030 commandInfo.lpVerb = (LPCSTR)(MAKEINTRESOURCE(offset));
1031 commandInfo.lpParameters = NULL;
1032 // 19.01: fixed CSysString to AString
1033 // MSDN suggest to send NULL: lpDirectory: This member is always NULL for menu items inserted by a Shell extension.
1034 const AString currentFolderA (GetAnsiString(_currentFolderPrefix));
1035 commandInfo.lpDirectory = (LPCSTR)(currentFolderA);
1036 commandInfo.nShow = SW_SHOW;
1037
1038 #ifdef use_CMINVOKECOMMANDINFOEX
1039
1040 commandInfo.lpParametersW = NULL;
1041 commandInfo.lpTitle = "";
1042
1043 /*
1044 system ContextMenu handler supports ContextMenu subhandlers.
1045 so InvokeCommand() converts (command_offset) from global number to subhandler number.
1046 XP-64 / win10:
1047 system ContextMenu converts (command_offset) in lpVerb only,
1048 and it keeps lpVerbW unchanged.
1049 also explorer.exe sends 0 in lpVerbW.
1050 We try to keep compatibility with Windows Explorer here.
1051 */
1052 commandInfo.lpVerbW = NULL;
1053
1054 const UString currentFolderUnicode = _currentFolderPrefix;
1055 commandInfo.lpDirectoryW = currentFolderUnicode;
1056 commandInfo.lpTitleW = L"";
1057 // commandInfo.ptInvoke.x = xPos;
1058 // commandInfo.ptInvoke.y = yPos;
1059 commandInfo.ptInvoke.x = 0;
1060 commandInfo.ptInvoke.y = 0;
1061
1062 #endif
1063
1064 HRESULT result;
1065 if (isSystemMenu)
1066 result = systemContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
1067 else
1068 result = sevenZipContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
1069 if (result == NOERROR)
1070 {
1071 KillSelection();
1072 return true;
1073 }
1074 else
1075 MessageBox_Error_HRESULT_Caption(result, L"InvokeCommand");
1076 return false;
1077 }
1078
OnContextMenu(HANDLE windowHandle,int xPos,int yPos)1079 bool CPanel::OnContextMenu(HANDLE windowHandle, int xPos, int yPos)
1080 {
1081 if (::GetParent((HWND)windowHandle) == _listView)
1082 {
1083 ShowColumnsContextMenu(xPos, yPos);
1084 return true;
1085 }
1086
1087 if (windowHandle != _listView)
1088 return false;
1089 /*
1090 POINT point;
1091 point.x = xPos;
1092 point.y = yPos;
1093 if (!_listView.ScreenToClient(&point))
1094 return false;
1095
1096 LVHITTESTINFO info;
1097 info.pt = point;
1098 int index = _listView.HitTest(&info);
1099 */
1100
1101 CRecordVector<UInt32> operatedIndices;
1102 Get_ItemIndices_Operated(operatedIndices);
1103
1104 // negative x,y are possible for multi-screen modes.
1105 // x=-1 && y=-1 for keyboard call (SHIFT+F10 and others).
1106 if (xPos == -1 && yPos == -1)
1107 {
1108 if (operatedIndices.Size() == 0)
1109 {
1110 xPos = 0;
1111 yPos = 0;
1112 }
1113 else
1114 {
1115 int itemIndex = _listView.GetNextItem(-1, LVNI_FOCUSED);
1116 if (itemIndex == -1)
1117 return false;
1118 RECT rect;
1119 if (!_listView.GetItemRect(itemIndex, &rect, LVIR_ICON))
1120 return false;
1121 xPos = (rect.left + rect.right) / 2;
1122 yPos = (rect.top + rect.bottom) / 2;
1123 }
1124 POINT point = {xPos, yPos};
1125 _listView.ClientToScreen(&point);
1126 xPos = point.x;
1127 yPos = point.y;
1128 }
1129
1130 CMenu menu;
1131 CMenuDestroyer menuDestroyer(menu);
1132 menu.CreatePopup();
1133
1134 CMyComPtr<IContextMenu> sevenZipContextMenu;
1135 CMyComPtr<IContextMenu> systemContextMenu;
1136 CreateFileMenu(menu, sevenZipContextMenu, systemContextMenu, false); // programMenu
1137
1138 const unsigned id = (unsigned)menu.Track(TPM_LEFTALIGN
1139 #ifndef UNDER_CE
1140 | TPM_RIGHTBUTTON
1141 #endif
1142 | TPM_RETURNCMD | TPM_NONOTIFY,
1143 xPos, yPos, _listView);
1144
1145 if (id == 0)
1146 return true;
1147
1148 if (id >= kMenuCmdID_Plugin_Start)
1149 {
1150 InvokePluginCommand(id, sevenZipContextMenu, systemContextMenu);
1151 return true;
1152 }
1153 if (ExecuteFileCommand(id))
1154 return true;
1155 return true;
1156 }
1157