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 "MyLoadMenu.h"
20 #include "PropertyName.h"
21 
22 #include "resource.h"
23 #include "PropertyNameRes.h"
24 
25 using namespace NWindows;
26 
27 LONG g_DllRefCount = 0;
28 
29 static const UINT kSevenZipStartMenuID = kMenuCmdID_Plugin_Start;
30 static const UINT kSystemStartMenuID = kMenuCmdID_Plugin_Start + 100;
31 
InvokeSystemCommand(const char * command)32 void CPanel::InvokeSystemCommand(const char *command)
33 {
34   NCOM::CComInitializer comInitializer;
35   if (!IsFsOrPureDrivesFolder())
36     return;
37   CRecordVector<UInt32> operatedIndices;
38   GetOperatedItemIndices(operatedIndices);
39   if (operatedIndices.IsEmpty())
40     return;
41   CMyComPtr<IContextMenu> contextMenu;
42   if (CreateShellContextMenu(operatedIndices, contextMenu) != S_OK)
43     return;
44 
45   CMINVOKECOMMANDINFO ci;
46   ZeroMemory(&ci, sizeof(ci));
47   ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
48   ci.hwnd = GetParent();
49   ci.lpVerb = command;
50   contextMenu->InvokeCommand(&ci);
51 }
52 
53 static const char * const kSeparator = "----------------------------\n";
54 static const char * const kSeparatorSmall = "----\n";
55 static const char * const kPropValueSeparator = ": ";
56 
57 extern UString ConvertSizeToString(UInt64 value) throw();
58 bool IsSizeProp(UINT propID) throw();
59 
60 UString GetOpenArcErrorMessage(UInt32 errorFlags);
61 
AddPropertyString(PROPID propID,const wchar_t * nameBSTR,const NCOM::CPropVariant & prop,UString & s)62 static void AddPropertyString(PROPID propID, const wchar_t *nameBSTR,
63     const NCOM::CPropVariant &prop, UString &s)
64 {
65   if (prop.vt != VT_EMPTY)
66   {
67     UString val;
68 
69     if (propID == kpidErrorFlags ||
70         propID == kpidWarningFlags)
71     {
72       UInt32 flags = GetOpenArcErrorFlags(prop);
73       if (flags == 0)
74         return;
75       if (flags != 0)
76         val = GetOpenArcErrorMessage(flags);
77     }
78     if (val.IsEmpty())
79     if ((prop.vt == VT_UI8 || prop.vt == VT_UI4 || prop.vt == VT_UI2) && IsSizeProp(propID))
80     {
81       UInt64 v = 0;
82       ConvertPropVariantToUInt64(prop, v);
83       val = ConvertSizeToString(v);
84     }
85     else
86       ConvertPropertyToString2(val, prop, propID);
87 
88     if (!val.IsEmpty())
89     {
90       s += GetNameOfProperty(propID, nameBSTR);
91       s += kPropValueSeparator;
92       /*
93       if (propID == kpidComment)
94         s.Add_LF();
95       */
96       s += val;
97       s.Add_LF();
98     }
99   }
100 }
101 
102 
AddPropertyString(PROPID propID,UInt64 val,UString & s)103 static void AddPropertyString(PROPID propID, UInt64 val, UString &s)
104 {
105   NCOM::CPropVariant prop = val;
106   AddPropertyString(propID, NULL, prop, s);
107 }
108 
109 
GetHex(Byte value)110 static inline char GetHex(Byte value)
111 {
112   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
113 }
114 
115 static const Byte kSpecProps[] =
116 {
117   kpidPath,
118   kpidType,
119   kpidErrorType,
120   kpidError,
121   kpidErrorFlags,
122   kpidWarning,
123   kpidWarningFlags,
124   kpidOffset,
125   kpidPhySize,
126   kpidTailSize
127 };
128 
Properties()129 void CPanel::Properties()
130 {
131   CMyComPtr<IGetFolderArcProps> getFolderArcProps;
132   _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
133   if (!getFolderArcProps)
134   {
135     InvokeSystemCommand("properties");
136     return;
137   }
138 
139   {
140     UString message;
141 
142     CRecordVector<UInt32> operatedIndices;
143     GetOperatedItemIndices(operatedIndices);
144 
145     if (operatedIndices.Size() == 1)
146     {
147       UInt32 index = operatedIndices[0];
148       // message += "Item:\n");
149       UInt32 numProps;
150       if (_folder->GetNumberOfProperties(&numProps) == S_OK)
151       {
152         for (UInt32 i = 0; i < numProps; i++)
153         {
154           CMyComBSTR name;
155           PROPID propID;
156           VARTYPE varType;
157 
158           if (_folder->GetPropertyInfo(i, &name, &propID, &varType) != S_OK)
159             continue;
160 
161           NCOM::CPropVariant prop;
162           if (_folder->GetProperty(index, propID, &prop) != S_OK)
163             continue;
164           AddPropertyString(propID, name, prop, message);
165         }
166       }
167 
168 
169       if (_folderRawProps)
170       {
171         _folderRawProps->GetNumRawProps(&numProps);
172         for (UInt32 i = 0; i < numProps; i++)
173         {
174           CMyComBSTR name;
175           PROPID propID;
176           if (_folderRawProps->GetRawPropInfo(i, &name, &propID) != S_OK)
177             continue;
178 
179           const void *data;
180           UInt32 dataSize;
181           UInt32 propType;
182           if (_folderRawProps->GetRawProp(index, propID, &data, &dataSize, &propType) != S_OK)
183             continue;
184 
185           if (dataSize != 0)
186           {
187             AString s;
188             if (propID == kpidNtSecure)
189               ConvertNtSecureToString((const Byte *)data, dataSize, s);
190             else
191             {
192               const UInt32 kMaxDataSize = 64;
193               if (dataSize > kMaxDataSize)
194               {
195                 s += "data:";
196                 s.Add_UInt32(dataSize);
197               }
198               else
199               {
200                 for (UInt32 k = 0; k < dataSize; k++)
201                 {
202                   Byte b = ((const Byte *)data)[k];
203                   s += GetHex((Byte)((b >> 4) & 0xF));
204                   s += GetHex((Byte)(b & 0xF));
205                 }
206               }
207             }
208             message += GetNameOfProperty(propID, name);
209             message += kPropValueSeparator;
210             message += s.Ptr();
211             message.Add_LF();
212           }
213         }
214       }
215 
216       message += kSeparator;
217     }
218     else if (operatedIndices.Size() >= 1)
219     {
220       UInt64 packSize = 0;
221       UInt64 unpackSize = 0;
222       UInt64 numFiles = 0;
223       UInt64 numDirs = 0;
224 
225       FOR_VECTOR (i, operatedIndices)
226       {
227         const UInt32 index = operatedIndices[i];
228         unpackSize += GetItemSize(index);
229         packSize += GetItem_UInt64Prop(index, kpidPackSize);
230         if (IsItem_Folder(index))
231         {
232           numDirs++;
233           numDirs += GetItem_UInt64Prop(index, kpidNumSubDirs);
234           numFiles += GetItem_UInt64Prop(index, kpidNumSubFiles);;
235         }
236         else
237           numFiles++;
238       }
239       {
240         wchar_t temp[32];
241         ConvertUInt32ToString(operatedIndices.Size(), temp);
242         message += MyFormatNew(g_App.LangString_N_SELECTED_ITEMS, temp);
243         message.Add_LF();
244       }
245 
246       if (numDirs != 0)
247         AddPropertyString(kpidNumSubDirs, numDirs, message);
248       if (numFiles != 0)
249         AddPropertyString(kpidNumSubFiles, numFiles, message);
250       AddPropertyString(kpidSize, unpackSize, message);
251       AddPropertyString(kpidPackSize, packSize, message);
252 
253       message += kSeparator;
254     }
255 
256 
257     /*
258     AddLangString(message, IDS_PROP_FILE_TYPE);
259     message += kPropValueSeparator;
260     message += GetFolderTypeID();
261     message.Add_LF();
262     */
263 
264     {
265       NCOM::CPropVariant prop;
266       if (_folder->GetFolderProperty(kpidPath, &prop) == S_OK)
267       {
268         AddPropertyString(kpidName, L"Path", prop, message);
269       }
270     }
271 
272     CMyComPtr<IFolderProperties> folderProperties;
273     _folder.QueryInterface(IID_IFolderProperties, &folderProperties);
274     if (folderProperties)
275     {
276       UInt32 numProps;
277       if (folderProperties->GetNumberOfFolderProperties(&numProps) == S_OK)
278       {
279         for (UInt32 i = 0; i < numProps; i++)
280         {
281           CMyComBSTR name;
282           PROPID propID;
283           VARTYPE vt;
284           if (folderProperties->GetFolderPropertyInfo(i, &name, &propID, &vt) != S_OK)
285             continue;
286           NCOM::CPropVariant prop;
287           if (_folder->GetFolderProperty(propID, &prop) != S_OK)
288             continue;
289           AddPropertyString(propID, name, prop, message);
290         }
291       }
292     }
293 
294     if (getFolderArcProps)
295     {
296       CMyComPtr<IFolderArcProps> getProps;
297       getFolderArcProps->GetFolderArcProps(&getProps);
298       if (getProps)
299       {
300         UInt32 numLevels;
301         if (getProps->GetArcNumLevels(&numLevels) != S_OK)
302           numLevels = 0;
303         for (UInt32 level2 = 0; level2 < numLevels; level2++)
304         {
305           {
306             UInt32 level = numLevels - 1 - level2;
307             UInt32 numProps;
308             if (getProps->GetArcNumProps(level, &numProps) == S_OK)
309             {
310               const int kNumSpecProps = ARRAY_SIZE(kSpecProps);
311 
312               message += kSeparator;
313 
314               for (Int32 i = -(int)kNumSpecProps; i < (Int32)numProps; i++)
315               {
316                 CMyComBSTR name;
317                 PROPID propID;
318                 VARTYPE vt;
319                 if (i < 0)
320                   propID = kSpecProps[i + kNumSpecProps];
321                 else if (getProps->GetArcPropInfo(level, i, &name, &propID, &vt) != S_OK)
322                   continue;
323                 NCOM::CPropVariant prop;
324                 if (getProps->GetArcProp(level, propID, &prop) != S_OK)
325                   continue;
326                 AddPropertyString(propID, name, prop, message);
327               }
328             }
329           }
330 
331           if (level2 != numLevels - 1)
332           {
333             UInt32 level = numLevels - 1 - level2;
334             UInt32 numProps;
335             if (getProps->GetArcNumProps2(level, &numProps) == S_OK)
336             {
337               message += kSeparatorSmall;
338               for (Int32 i = 0; i < (Int32)numProps; i++)
339               {
340                 CMyComBSTR name;
341                 PROPID propID;
342                 VARTYPE vt;
343                 if (getProps->GetArcPropInfo2(level, i, &name, &propID, &vt) != S_OK)
344                   continue;
345                 NCOM::CPropVariant prop;
346                 if (getProps->GetArcProp2(level, propID, &prop) != S_OK)
347                   continue;
348                 AddPropertyString(propID, name, prop, message);
349               }
350             }
351           }
352         }
353       }
354     }
355     ::MessageBoxW(*(this), message, LangString(IDS_PROPERTIES), MB_OK);
356   }
357 }
358 
EditCut()359 void CPanel::EditCut()
360 {
361   // InvokeSystemCommand("cut");
362 }
363 
EditCopy()364 void CPanel::EditCopy()
365 {
366   /*
367   CMyComPtr<IGetFolderArcProps> getFolderArcProps;
368   _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
369   if (!getFolderArcProps)
370   {
371     InvokeSystemCommand("copy");
372     return;
373   }
374   */
375   UString s;
376   CRecordVector<UInt32> indices;
377   GetSelectedItemsIndices(indices);
378   FOR_VECTOR (i, indices)
379   {
380     if (i != 0)
381       s += "\xD\n";
382     s += GetItemName(indices[i]);
383   }
384   ClipboardSetText(_mainWindow, s);
385 }
386 
EditPaste()387 void CPanel::EditPaste()
388 {
389   /*
390   UStringVector names;
391   ClipboardGetFileNames(names);
392   CopyFromNoAsk(names);
393   UString s;
394   for (int i = 0; i < names.Size(); i++)
395   {
396     s += L' ';
397     s += names[i];
398   }
399 
400   MessageBoxW(0, s, L"", 0);
401   */
402 
403   // InvokeSystemCommand("paste");
404 }
405 
406 
407 
408 struct CFolderPidls
409 {
410   LPITEMIDLIST parent;
411   CRecordVector<LPITEMIDLIST> items;
412 
CFolderPidlsCFolderPidls413   CFolderPidls(): parent(NULL) {}
~CFolderPidlsCFolderPidls414   ~CFolderPidls()
415   {
416     FOR_VECTOR (i, items)
417       CoTaskMemFree(items[i]);
418     CoTaskMemFree(parent);
419   }
420 };
421 
422 
CreateShellContextMenu(const CRecordVector<UInt32> & operatedIndices,CMyComPtr<IContextMenu> & systemContextMenu)423 HRESULT CPanel::CreateShellContextMenu(
424     const CRecordVector<UInt32> &operatedIndices,
425     CMyComPtr<IContextMenu> &systemContextMenu)
426 {
427   systemContextMenu.Release();
428   const UString folderPath = GetFsPath();
429 
430   CMyComPtr<IShellFolder> desktopFolder;
431   RINOK(::SHGetDesktopFolder(&desktopFolder));
432   if (!desktopFolder)
433   {
434     // ShowMessage("Failed to get Desktop folder");
435     return E_FAIL;
436   }
437 
438   CFolderPidls pidls;
439   DWORD eaten;
440 
441   // if (folderPath.IsEmpty()), then ParseDisplayName returns pidls of "My Computer"
442   RINOK(desktopFolder->ParseDisplayName(
443       GetParent(), NULL, (wchar_t *)(const wchar_t *)folderPath,
444       &eaten, &pidls.parent, NULL));
445 
446   /*
447   STRRET pName;
448   res = desktopFolder->GetDisplayNameOf(pidls.parent,  SHGDN_NORMAL, &pName);
449   WCHAR dir[MAX_PATH];
450   if (!SHGetPathFromIDListW(pidls.parent, dir))
451     dir[0] = 0;
452   */
453 
454   if (!pidls.parent)
455     return E_FAIL;
456 
457   if (operatedIndices.IsEmpty())
458   {
459     // how to get IContextMenu, if there are no selected files?
460     return E_FAIL;
461 
462     /*
463     xp64 :
464     1) we can't use GetUIObjectOf() with (numItems == 0), it throws exception
465     2) we can't use desktopFolder->GetUIObjectOf() with absolute pidls of folder
466         context menu items are different in that case:
467           "Open / Explorer" for folder
468           "Delete" for "My Computer" icon
469           "Preperties" for "System"
470     */
471     /*
472     parentFolder = desktopFolder;
473     pidls.items.AddInReserved(pidls.parent);
474     pidls.parent = NULL;
475     */
476 
477     // CreateViewObject() doesn't show all context menu items
478     /*
479     HRESULT res = parentFolder->CreateViewObject(
480         GetParent(), IID_IContextMenu, (void**)&systemContextMenu);
481     */
482   }
483 
484   CMyComPtr<IShellFolder> parentFolder;
485   RINOK(desktopFolder->BindToObject(pidls.parent,
486       NULL, IID_IShellFolder, (void**)&parentFolder));
487   if (!parentFolder)
488   {
489     // ShowMessage("Invalid file name");
490     return E_FAIL;
491   }
492 
493   pidls.items.ClearAndReserve(operatedIndices.Size());
494   FOR_VECTOR (i, operatedIndices)
495   {
496     LPITEMIDLIST pidl;
497     const UString fileName = GetItemRelPath2(operatedIndices[i]);
498     RINOK(parentFolder->ParseDisplayName(GetParent(), 0,
499         (wchar_t *)(const wchar_t *)fileName, &eaten, &pidl, 0));
500     pidls.items.AddInReserved(pidl);
501   }
502 
503   // Get IContextMenu for items
504 
505   RINOK(parentFolder->GetUIObjectOf(GetParent(), pidls.items.Size(),
506       (LPCITEMIDLIST *)&pidls.items.Front(), IID_IContextMenu, 0, (void**)&systemContextMenu));
507 
508   if (!systemContextMenu)
509   {
510     // ShowMessage("Unable to get context menu interface");
511     return E_FAIL;
512   }
513   return S_OK;
514 }
515 
516 
CreateSystemMenu(HMENU menuSpec,const CRecordVector<UInt32> & operatedIndices,CMyComPtr<IContextMenu> & systemContextMenu)517 void CPanel::CreateSystemMenu(HMENU menuSpec,
518     const CRecordVector<UInt32> &operatedIndices,
519     CMyComPtr<IContextMenu> &systemContextMenu)
520 {
521   systemContextMenu.Release();
522 
523   CreateShellContextMenu(operatedIndices, systemContextMenu);
524 
525   if (systemContextMenu == 0)
526     return;
527 
528   // Set up a CMINVOKECOMMANDINFO structure.
529   CMINVOKECOMMANDINFO ci;
530   ZeroMemory(&ci, sizeof(ci));
531   ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
532   ci.hwnd = GetParent();
533 
534   /*
535   if (Sender == GoBtn)
536   {
537     // Verbs that can be used are cut, paste,
538     // properties, delete, and so on.
539     String action;
540     if (CutRb->Checked)
541       action = "cut";
542     else if (CopyRb->Checked)
543       action = "copy";
544     else if (DeleteRb->Checked)
545       action = "delete";
546     else if (PropertiesRb->Checked)
547       action = "properties";
548 
549     ci.lpVerb = action.c_str();
550     result = cm->InvokeCommand(&ci);
551     if (result)
552       ShowMessage(
553       "Error copying file to clipboard.");
554 
555   }
556   else
557   */
558   {
559     // HMENU hMenu = CreatePopupMenu();
560     CMenu popupMenu;
561     // CMenuDestroyer menuDestroyer(popupMenu);
562     if (!popupMenu.CreatePopup())
563       throw 210503;
564 
565     HMENU hMenu = popupMenu;
566 
567     DWORD Flags = CMF_EXPLORE;
568     // Optionally the shell will show the extended
569     // context menu on some operating systems when
570     // the shift key is held down at the time the
571     // context menu is invoked. The following is
572     // commented out but you can uncommnent this
573     // line to show the extended context menu.
574     // Flags |= 0x00000080;
575     systemContextMenu->QueryContextMenu(hMenu, 0, kSystemStartMenuID, 0x7FFF, Flags);
576 
577 
578     {
579       CMenu menu;
580       menu.Attach(menuSpec);
581       CMenuItem menuItem;
582       menuItem.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
583       menuItem.fType = MFT_STRING;
584       menuItem.hSubMenu = popupMenu.Detach();
585       // menuDestroyer.Disable();
586       LangString(IDS_SYSTEM, menuItem.StringValue);
587       menu.InsertItem(0, true, menuItem);
588     }
589     /*
590     if (Cmd < 100 && Cmd != 0)
591     {
592       ci.lpVerb = MAKEINTRESOURCE(Cmd - 1);
593       ci.lpParameters = "";
594       ci.lpDirectory = "";
595       ci.nShow = SW_SHOWNORMAL;
596       cm->InvokeCommand(&ci);
597     }
598     // If Cmd is > 100 then it's one of our
599     // inserted menu items.
600     else
601       // Find the menu item.
602       for (int i = 0; i < popupMenu1->Items->Count; i++)
603       {
604         TMenuItem* menu = popupMenu1->Items->Items[i];
605         // Call its OnClick handler.
606         if (menu->Command == Cmd - 100)
607           menu->OnClick(this);
608       }
609       // Release the memory allocated for the menu.
610       DestroyMenu(hMenu);
611     */
612   }
613 }
614 
CreateFileMenu(HMENU menuSpec)615 void CPanel::CreateFileMenu(HMENU menuSpec)
616 {
617   CreateFileMenu(menuSpec, _sevenZipContextMenu, _systemContextMenu, true);
618 }
619 
CreateSevenZipMenu(HMENU menuSpec,const CRecordVector<UInt32> & operatedIndices,CMyComPtr<IContextMenu> & sevenZipContextMenu)620 void CPanel::CreateSevenZipMenu(HMENU menuSpec,
621     const CRecordVector<UInt32> &operatedIndices,
622     CMyComPtr<IContextMenu> &sevenZipContextMenu)
623 {
624   sevenZipContextMenu.Release();
625 
626   CMenu menu;
627   menu.Attach(menuSpec);
628   // CMenuDestroyer menuDestroyer(menu);
629   // menu.CreatePopup();
630 
631   bool sevenZipMenuCreated = false;
632 
633   CZipContextMenu *contextMenuSpec = new CZipContextMenu;
634   CMyComPtr<IContextMenu> contextMenu = contextMenuSpec;
635   // if (contextMenu.CoCreateInstance(CLSID_CZipContextMenu, IID_IContextMenu) == S_OK)
636   {
637     /*
638     CMyComPtr<IInitContextMenu> initContextMenu;
639     if (contextMenu.QueryInterface(IID_IInitContextMenu, &initContextMenu) != S_OK)
640       return;
641     */
642     UString currentFolderUnicode = GetFsPath();
643     UStringVector names;
644     unsigned i;
645     for (i = 0; i < operatedIndices.Size(); i++)
646       names.Add(currentFolderUnicode + GetItemRelPath2(operatedIndices[i]));
647     CRecordVector<const wchar_t *> namePointers;
648     for (i = 0; i < operatedIndices.Size(); i++)
649       namePointers.Add(names[i]);
650 
651     // NFile::NDirectory::MySetCurrentDirectory(currentFolderUnicode);
652     if (contextMenuSpec->InitContextMenu(currentFolderUnicode, &namePointers.Front(),
653         operatedIndices.Size()) == S_OK)
654     {
655       HRESULT res = contextMenu->QueryContextMenu(menu, 0, kSevenZipStartMenuID,
656           kSystemStartMenuID - 1, 0);
657       sevenZipMenuCreated = (HRESULT_SEVERITY(res) == SEVERITY_SUCCESS);
658       if (sevenZipMenuCreated)
659         sevenZipContextMenu = contextMenu;
660       // int code = HRESULT_CODE(res);
661       // int nextItemID = code;
662     }
663   }
664 }
665 
IsReadOnlyFolder(IFolderFolder * folder)666 static bool IsReadOnlyFolder(IFolderFolder *folder)
667 {
668   if (!folder)
669     return false;
670 
671   bool res = false;
672   {
673     NCOM::CPropVariant prop;
674     if (folder->GetFolderProperty(kpidReadOnly, &prop) == S_OK)
675       if (prop.vt == VT_BOOL)
676         res = VARIANT_BOOLToBool(prop.boolVal);
677   }
678   return res;
679 }
680 
IsThereReadOnlyFolder() const681 bool CPanel::IsThereReadOnlyFolder() const
682 {
683   if (!_folderOperations)
684     return true;
685   if (IsReadOnlyFolder(_folder))
686     return true;
687   FOR_VECTOR (i, _parentFolders)
688   {
689     if (IsReadOnlyFolder(_parentFolders[i].ParentFolder))
690       return true;
691   }
692   return false;
693 }
694 
CheckBeforeUpdate(UINT resourceID)695 bool CPanel::CheckBeforeUpdate(UINT resourceID)
696 {
697   if (!_folderOperations)
698   {
699     MessageBox_Error_UnsupportOperation();
700     // resourceID = resourceID;
701     // MessageBoxErrorForUpdate(E_NOINTERFACE, resourceID);
702     return false;
703   }
704 
705   for (int i = (int)_parentFolders.Size(); i >= 0; i--)
706   {
707     IFolderFolder *folder;
708     if (i == (int)_parentFolders.Size())
709       folder = _folder;
710     else
711       folder = _parentFolders[i].ParentFolder;
712 
713     if (!IsReadOnlyFolder(folder))
714       continue;
715 
716     UString s;
717     AddLangString(s, resourceID);
718     s.Add_LF();
719     AddLangString(s, IDS_OPERATION_IS_NOT_SUPPORTED);
720     s.Add_LF();
721     if (i == 0)
722       s += GetFolderPath(folder);
723     else
724       s += _parentFolders[i - 1].VirtualPath;
725     s.Add_LF();
726     AddLangString(s, IDS_PROP_READ_ONLY);
727     MessageBox_Error(s);
728     return false;
729   }
730 
731   return true;
732 }
733 
CreateFileMenu(HMENU menuSpec,CMyComPtr<IContextMenu> & sevenZipContextMenu,CMyComPtr<IContextMenu> & systemContextMenu,bool programMenu)734 void CPanel::CreateFileMenu(HMENU menuSpec,
735     CMyComPtr<IContextMenu> &sevenZipContextMenu,
736     CMyComPtr<IContextMenu> &systemContextMenu,
737     bool programMenu)
738 {
739   sevenZipContextMenu.Release();
740   systemContextMenu.Release();
741 
742   CRecordVector<UInt32> operatedIndices;
743   GetOperatedItemIndices(operatedIndices);
744 
745   CMenu menu;
746   menu.Attach(menuSpec);
747 
748   if (!IsArcFolder())
749   {
750     CreateSevenZipMenu(menu, operatedIndices, sevenZipContextMenu);
751     // CreateSystemMenu is very slow if you call it inside ZIP archive with big number of files
752     // Windows probably can parse items inside ZIP archive.
753     if (g_App.ShowSystemMenu)
754       CreateSystemMenu(menu, operatedIndices, systemContextMenu);
755   }
756 
757   /*
758   if (menu.GetItemCount() > 0)
759     menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)0);
760   */
761 
762   unsigned i;
763   for (i = 0; i < operatedIndices.Size(); i++)
764     if (IsItem_Folder(operatedIndices[i]))
765       break;
766   bool allAreFiles = (i == operatedIndices.Size());
767 
768   CFileMenu fm;
769 
770   fm.readOnly = IsThereReadOnlyFolder();
771   fm.isFsFolder = Is_IO_FS_Folder();
772   fm.programMenu = programMenu;
773   fm.allAreFiles = allAreFiles;
774   fm.numItems = operatedIndices.Size();
775 
776   fm.isAltStreamsSupported = false;
777 
778   if (_folderAltStreams)
779   {
780     if (operatedIndices.Size() <= 1)
781     {
782       Int32 realIndex = -1;
783       if (operatedIndices.Size() == 1)
784         realIndex = operatedIndices[0];
785       Int32 val = 0;
786       if (_folderAltStreams->AreAltStreamsSupported(realIndex, &val) == S_OK)
787         fm.isAltStreamsSupported = IntToBool(val);
788     }
789   }
790   else
791   {
792     if (fm.numItems == 0)
793       fm.isAltStreamsSupported = IsFSFolder();
794     else
795       fm.isAltStreamsSupported = IsFolder_with_FsItems();
796   }
797 
798   fm.Load(menu, menu.GetItemCount());
799 }
800 
InvokePluginCommand(int id)801 bool CPanel::InvokePluginCommand(int id)
802 {
803   return InvokePluginCommand(id, _sevenZipContextMenu, _systemContextMenu);
804 }
805 
806 #if defined(_MSC_VER) && !defined(UNDER_CE)
807 #define use_CMINVOKECOMMANDINFOEX
808 #endif
809 
InvokePluginCommand(int id,IContextMenu * sevenZipContextMenu,IContextMenu * systemContextMenu)810 bool CPanel::InvokePluginCommand(int id,
811     IContextMenu *sevenZipContextMenu, IContextMenu *systemContextMenu)
812 {
813   UInt32 offset;
814   bool isSystemMenu = (id >= kSystemStartMenuID);
815   if (isSystemMenu)
816     offset = id  - kSystemStartMenuID;
817   else
818     offset = id  - kSevenZipStartMenuID;
819 
820   #ifdef use_CMINVOKECOMMANDINFOEX
821     CMINVOKECOMMANDINFOEX
822   #else
823     CMINVOKECOMMANDINFO
824   #endif
825       commandInfo;
826 
827   memset(&commandInfo, 0, sizeof(commandInfo));
828   commandInfo.cbSize = sizeof(commandInfo);
829 
830   commandInfo.fMask = 0
831   #ifdef use_CMINVOKECOMMANDINFOEX
832     | CMIC_MASK_UNICODE
833   #endif
834     ;
835 
836   commandInfo.hwnd = GetParent();
837   commandInfo.lpVerb = (LPCSTR)(MAKEINTRESOURCE(offset));
838   commandInfo.lpParameters = NULL;
839   const CSysString currentFolderSys (GetSystemString(_currentFolderPrefix));
840   commandInfo.lpDirectory = (LPCSTR)(LPCTSTR)(currentFolderSys);
841   commandInfo.nShow = SW_SHOW;
842 
843   #ifdef use_CMINVOKECOMMANDINFOEX
844 
845   commandInfo.lpParametersW = NULL;
846   commandInfo.lpTitle = "";
847   commandInfo.lpVerbW = (LPCWSTR)(MAKEINTRESOURCEW(offset));
848   UString currentFolderUnicode = _currentFolderPrefix;
849   commandInfo.lpDirectoryW = currentFolderUnicode;
850   commandInfo.lpTitleW = L"";
851   // commandInfo.ptInvoke.x = xPos;
852   // commandInfo.ptInvoke.y = yPos;
853   commandInfo.ptInvoke.x = 0;
854   commandInfo.ptInvoke.y = 0;
855 
856   #endif
857 
858   HRESULT result;
859   if (isSystemMenu)
860     result = systemContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
861   else
862     result = sevenZipContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
863   if (result == NOERROR)
864   {
865     KillSelection();
866     return true;
867   }
868   return false;
869 }
870 
OnContextMenu(HANDLE windowHandle,int xPos,int yPos)871 bool CPanel::OnContextMenu(HANDLE windowHandle, int xPos, int yPos)
872 {
873   if (::GetParent((HWND)windowHandle) == _listView)
874   {
875     ShowColumnsContextMenu(xPos, yPos);
876     return true;
877   }
878 
879   if (windowHandle != _listView)
880     return false;
881   /*
882   POINT point;
883   point.x = xPos;
884   point.y = yPos;
885   if (!_listView.ScreenToClient(&point))
886     return false;
887 
888   LVHITTESTINFO info;
889   info.pt = point;
890   int index = _listView.HitTest(&info);
891   */
892 
893   CRecordVector<UInt32> operatedIndices;
894   GetOperatedItemIndices(operatedIndices);
895 
896   // negative x,y are possible for multi-screen modes.
897   // x=-1 && y=-1 for keyboard call (SHIFT+F10 and others).
898   if (xPos == -1 && yPos == -1)
899   {
900     if (operatedIndices.Size() == 0)
901     {
902       xPos = 0;
903       yPos = 0;
904     }
905     else
906     {
907       int itemIndex = _listView.GetNextItem(-1, LVNI_FOCUSED);
908       if (itemIndex == -1)
909         return false;
910       RECT rect;
911       if (!_listView.GetItemRect(itemIndex, &rect, LVIR_ICON))
912         return false;
913       xPos = (rect.left + rect.right) / 2;
914       yPos = (rect.top + rect.bottom) / 2;
915     }
916     POINT point = {xPos, yPos};
917     _listView.ClientToScreen(&point);
918     xPos = point.x;
919     yPos = point.y;
920   }
921 
922   CMenu menu;
923   CMenuDestroyer menuDestroyer(menu);
924   menu.CreatePopup();
925 
926   CMyComPtr<IContextMenu> sevenZipContextMenu;
927   CMyComPtr<IContextMenu> systemContextMenu;
928   CreateFileMenu(menu, sevenZipContextMenu, systemContextMenu, false);
929 
930   int result = menu.Track(TPM_LEFTALIGN
931       #ifndef UNDER_CE
932       | TPM_RIGHTBUTTON
933       #endif
934       | TPM_RETURNCMD | TPM_NONOTIFY,
935     xPos, yPos, _listView);
936 
937   if (result == 0)
938     return true;
939 
940   if (result >= kMenuCmdID_Plugin_Start)
941   {
942     InvokePluginCommand(result, sevenZipContextMenu, systemContextMenu);
943     return true;
944   }
945   if (ExecuteFileCommand(result))
946     return true;
947   return true;
948 }
949