xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/PanelMenu.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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