mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 15:14:59 -06:00
1177 lines
30 KiB
C++
Executable File
1177 lines
30 KiB
C++
Executable File
#include "StdAfx.h"
|
|
|
|
#include "../../../Common/IntToString.h"
|
|
#include "../../../Common/StringConvert.h"
|
|
|
|
#include "../../../Windows/COM.h"
|
|
#include "../../../Windows/Clipboard.h"
|
|
#include "../../../Windows/Menu.h"
|
|
#include "../../../Windows/PropVariant.h"
|
|
#include "../../../Windows/PropVariantConv.h"
|
|
|
|
#include "../../PropID.h"
|
|
#include "../Common/PropIDUtils.h"
|
|
#include "../Explorer/ContextMenu.h"
|
|
|
|
#include "App.h"
|
|
#include "FormatUtils.h"
|
|
#include "LangUtils.h"
|
|
#include "ListViewDialog.h"
|
|
#include "MyLoadMenu.h"
|
|
#include "PropertyName.h"
|
|
|
|
#include "PropertyNameRes.h"
|
|
#include "resource.h"
|
|
|
|
// #define SHOW_DEBUG_PANEL_MENU
|
|
|
|
using namespace NWindows;
|
|
|
|
extern
|
|
LONG g_DllRefCount;
|
|
LONG g_DllRefCount = 0;
|
|
|
|
static const UINT kSevenZipStartMenuID = kMenuCmdID_Plugin_Start;
|
|
static const UINT kSystemStartMenuID = kMenuCmdID_Plugin_Start + 400;
|
|
|
|
|
|
#ifdef SHOW_DEBUG_PANEL_MENU
|
|
static void Print_Ptr(void *p, const char *s)
|
|
{
|
|
char temp[32];
|
|
ConvertUInt64ToHex((UInt64)(void *)p, temp);
|
|
AString m;
|
|
m += temp;
|
|
m.Add_Space();
|
|
m += s;
|
|
OutputDebugStringA(m);
|
|
}
|
|
#define ODS(sz) { Print_Ptr(this, sz); }
|
|
#define ODS_U(s) { OutputDebugStringW(s); }
|
|
#else
|
|
#define ODS(sz)
|
|
#define ODS_U(s)
|
|
#endif
|
|
|
|
|
|
void CPanel::InvokeSystemCommand(const char *command)
|
|
{
|
|
NCOM::CComInitializer comInitializer;
|
|
if (!IsFsOrPureDrivesFolder())
|
|
return;
|
|
CRecordVector<UInt32> operatedIndices;
|
|
Get_ItemIndices_Operated(operatedIndices);
|
|
if (operatedIndices.IsEmpty())
|
|
return;
|
|
CMyComPtr<IContextMenu> contextMenu;
|
|
if (CreateShellContextMenu(operatedIndices, contextMenu) != S_OK)
|
|
return;
|
|
|
|
CMINVOKECOMMANDINFO ci;
|
|
ZeroMemory(&ci, sizeof(ci));
|
|
ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
|
|
ci.hwnd = GetParent();
|
|
ci.lpVerb = command;
|
|
contextMenu->InvokeCommand(&ci);
|
|
}
|
|
|
|
static const char * const kSeparator = "------------------------";
|
|
static const char * const kSeparatorSmall = "----------------";
|
|
|
|
extern UString ConvertSizeToString(UInt64 value) throw();
|
|
bool IsSizeProp(UINT propID) throw();
|
|
|
|
UString GetOpenArcErrorMessage(UInt32 errorFlags);
|
|
|
|
|
|
static void AddListAscii(CListViewDialog &dialog, const char *s)
|
|
{
|
|
dialog.Strings.Add((UString)s);
|
|
dialog.Values.AddNew();
|
|
}
|
|
|
|
static void AddSeparator(CListViewDialog &dialog)
|
|
{
|
|
AddListAscii(dialog, kSeparator);
|
|
}
|
|
|
|
static void AddSeparatorSmall(CListViewDialog &dialog)
|
|
{
|
|
AddListAscii(dialog, kSeparatorSmall);
|
|
}
|
|
|
|
static void AddPropertyPair(const UString &name, const UString &val, CListViewDialog &dialog)
|
|
{
|
|
dialog.Strings.Add(name);
|
|
dialog.Values.Add(val);
|
|
}
|
|
|
|
|
|
static void AddPropertyString(PROPID propID, const wchar_t *nameBSTR,
|
|
const NCOM::CPropVariant &prop, CListViewDialog &dialog)
|
|
{
|
|
if (prop.vt != VT_EMPTY)
|
|
{
|
|
UString val;
|
|
|
|
if (propID == kpidErrorFlags ||
|
|
propID == kpidWarningFlags)
|
|
{
|
|
UInt32 flags = GetOpenArcErrorFlags(prop);
|
|
if (flags == 0)
|
|
return;
|
|
if (flags != 0)
|
|
val = GetOpenArcErrorMessage(flags);
|
|
}
|
|
|
|
if (val.IsEmpty())
|
|
{
|
|
if ((prop.vt == VT_UI8 || prop.vt == VT_UI4 || prop.vt == VT_UI2) && IsSizeProp(propID))
|
|
{
|
|
UInt64 v = 0;
|
|
ConvertPropVariantToUInt64(prop, v);
|
|
val = ConvertSizeToString(v);
|
|
}
|
|
else
|
|
ConvertPropertyToString2(val, prop, propID, 9); // we send 9 - is ns precision
|
|
}
|
|
|
|
if (!val.IsEmpty())
|
|
{
|
|
if (propID == kpidErrorType)
|
|
{
|
|
AddPropertyPair(L"Open WARNING:", L"Cannot open the file as expected archive type", dialog);
|
|
}
|
|
AddPropertyPair(GetNameOfProperty(propID, nameBSTR), val, dialog);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void AddPropertyString(PROPID propID, UInt64 val, CListViewDialog &dialog)
|
|
{
|
|
NCOM::CPropVariant prop = val;
|
|
AddPropertyString(propID, NULL, prop, dialog);
|
|
}
|
|
|
|
|
|
static inline unsigned GetHex_Upper(unsigned v)
|
|
{
|
|
return (v < 10) ? ('0' + v) : ('A' + (v - 10));
|
|
}
|
|
|
|
static inline unsigned GetHex_Lower(unsigned v)
|
|
{
|
|
return (v < 10) ? ('0' + v) : ('a' + (v - 10));
|
|
}
|
|
|
|
static const Byte kSpecProps[] =
|
|
{
|
|
kpidPath,
|
|
kpidType,
|
|
kpidErrorType,
|
|
kpidError,
|
|
kpidErrorFlags,
|
|
kpidWarning,
|
|
kpidWarningFlags,
|
|
kpidOffset,
|
|
kpidPhySize,
|
|
kpidTailSize
|
|
};
|
|
|
|
void CPanel::Properties()
|
|
{
|
|
CMyComPtr<IGetFolderArcProps> getFolderArcProps;
|
|
_folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
|
|
if (!getFolderArcProps)
|
|
{
|
|
InvokeSystemCommand("properties");
|
|
return;
|
|
}
|
|
|
|
{
|
|
CListViewDialog message;
|
|
// message.DeleteIsAllowed = false;
|
|
// message.SelectFirst = false;
|
|
|
|
CRecordVector<UInt32> operatedIndices;
|
|
Get_ItemIndices_Operated(operatedIndices);
|
|
|
|
if (operatedIndices.Size() == 1)
|
|
{
|
|
UInt32 index = operatedIndices[0];
|
|
// message += "Item:\n");
|
|
UInt32 numProps;
|
|
if (_folder->GetNumberOfProperties(&numProps) == S_OK)
|
|
{
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
PROPID propID;
|
|
VARTYPE varType;
|
|
|
|
if (_folder->GetPropertyInfo(i, &name, &propID, &varType) != S_OK)
|
|
continue;
|
|
|
|
NCOM::CPropVariant prop;
|
|
if (_folder->GetProperty(index, propID, &prop) != S_OK)
|
|
continue;
|
|
AddPropertyString(propID, name, prop, message);
|
|
}
|
|
}
|
|
|
|
|
|
if (_folderRawProps)
|
|
{
|
|
_folderRawProps->GetNumRawProps(&numProps);
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
PROPID propID;
|
|
if (_folderRawProps->GetRawPropInfo(i, &name, &propID) != S_OK)
|
|
continue;
|
|
|
|
const void *data;
|
|
UInt32 dataSize;
|
|
UInt32 propType;
|
|
if (_folderRawProps->GetRawProp(index, propID, &data, &dataSize, &propType) != S_OK)
|
|
continue;
|
|
|
|
if (dataSize != 0)
|
|
{
|
|
AString s;
|
|
if (propID == kpidNtSecure)
|
|
ConvertNtSecureToString((const Byte *)data, dataSize, s);
|
|
else
|
|
{
|
|
const UInt32 kMaxDataSize = 64;
|
|
if (dataSize > kMaxDataSize)
|
|
{
|
|
s += "data:";
|
|
s.Add_UInt32(dataSize);
|
|
}
|
|
else
|
|
{
|
|
const bool needUpper = (dataSize <= 8)
|
|
&& (propID == kpidCRC || propID == kpidChecksum);
|
|
for (UInt32 k = 0; k < dataSize; k++)
|
|
{
|
|
const Byte b = ((const Byte *)data)[k];
|
|
if (needUpper)
|
|
{
|
|
s += (char)GetHex_Upper((b >> 4) & 0xF);
|
|
s += (char)GetHex_Upper(b & 0xF);
|
|
}
|
|
else
|
|
{
|
|
s += (char)GetHex_Lower((b >> 4) & 0xF);
|
|
s += (char)GetHex_Lower(b & 0xF);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AddPropertyPair(GetNameOfProperty(propID, name), (UString)s.Ptr(), message);
|
|
}
|
|
}
|
|
}
|
|
|
|
AddSeparator(message);
|
|
}
|
|
else if (operatedIndices.Size() >= 1)
|
|
{
|
|
UInt64 packSize = 0;
|
|
UInt64 unpackSize = 0;
|
|
UInt64 numFiles = 0;
|
|
UInt64 numDirs = 0;
|
|
|
|
FOR_VECTOR (i, operatedIndices)
|
|
{
|
|
const UInt32 index = operatedIndices[i];
|
|
unpackSize += GetItemSize(index);
|
|
packSize += GetItem_UInt64Prop(index, kpidPackSize);
|
|
if (IsItem_Folder(index))
|
|
{
|
|
numDirs++;
|
|
numDirs += GetItem_UInt64Prop(index, kpidNumSubDirs);
|
|
numFiles += GetItem_UInt64Prop(index, kpidNumSubFiles);
|
|
}
|
|
else
|
|
numFiles++;
|
|
}
|
|
{
|
|
wchar_t temp[32];
|
|
ConvertUInt32ToString(operatedIndices.Size(), temp);
|
|
AddPropertyPair(L"", MyFormatNew(g_App.LangString_N_SELECTED_ITEMS, temp), message);
|
|
}
|
|
|
|
if (numDirs != 0)
|
|
AddPropertyString(kpidNumSubDirs, numDirs, message);
|
|
if (numFiles != 0)
|
|
AddPropertyString(kpidNumSubFiles, numFiles, message);
|
|
AddPropertyString(kpidSize, unpackSize, message);
|
|
AddPropertyString(kpidPackSize, packSize, message);
|
|
|
|
AddSeparator(message);
|
|
}
|
|
|
|
|
|
/*
|
|
AddLangString(message, IDS_PROP_FILE_TYPE);
|
|
message += kPropValueSeparator;
|
|
message += GetFolderTypeID();
|
|
message.Add_LF();
|
|
*/
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
if (_folder->GetFolderProperty(kpidPath, &prop) == S_OK)
|
|
{
|
|
AddPropertyString(kpidName, L"Path", prop, message);
|
|
}
|
|
}
|
|
|
|
CMyComPtr<IFolderProperties> folderProperties;
|
|
_folder.QueryInterface(IID_IFolderProperties, &folderProperties);
|
|
if (folderProperties)
|
|
{
|
|
UInt32 numProps;
|
|
if (folderProperties->GetNumberOfFolderProperties(&numProps) == S_OK)
|
|
{
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
PROPID propID;
|
|
VARTYPE vt;
|
|
if (folderProperties->GetFolderPropertyInfo(i, &name, &propID, &vt) != S_OK)
|
|
continue;
|
|
NCOM::CPropVariant prop;
|
|
if (_folder->GetFolderProperty(propID, &prop) != S_OK)
|
|
continue;
|
|
AddPropertyString(propID, name, prop, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (getFolderArcProps)
|
|
{
|
|
CMyComPtr<IFolderArcProps> getProps;
|
|
getFolderArcProps->GetFolderArcProps(&getProps);
|
|
if (getProps)
|
|
{
|
|
UInt32 numLevels;
|
|
if (getProps->GetArcNumLevels(&numLevels) != S_OK)
|
|
numLevels = 0;
|
|
for (UInt32 level2 = 0; level2 < numLevels; level2++)
|
|
{
|
|
{
|
|
UInt32 level = numLevels - 1 - level2;
|
|
UInt32 numProps;
|
|
if (getProps->GetArcNumProps(level, &numProps) == S_OK)
|
|
{
|
|
const int kNumSpecProps = Z7_ARRAY_SIZE(kSpecProps);
|
|
|
|
AddSeparator(message);
|
|
|
|
for (Int32 i = -(int)kNumSpecProps; i < (Int32)numProps; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
PROPID propID;
|
|
VARTYPE vt;
|
|
if (i < 0)
|
|
propID = kSpecProps[i + kNumSpecProps];
|
|
else if (getProps->GetArcPropInfo(level, (UInt32)i, &name, &propID, &vt) != S_OK)
|
|
continue;
|
|
NCOM::CPropVariant prop;
|
|
if (getProps->GetArcProp(level, propID, &prop) != S_OK)
|
|
continue;
|
|
AddPropertyString(propID, name, prop, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (level2 < numLevels - 1)
|
|
{
|
|
const UInt32 level = numLevels - 1 - level2;
|
|
UInt32 numProps;
|
|
if (getProps->GetArcNumProps2(level, &numProps) == S_OK)
|
|
{
|
|
AddSeparatorSmall(message);
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
PROPID propID;
|
|
VARTYPE vt;
|
|
if (getProps->GetArcPropInfo2(level, i, &name, &propID, &vt) != S_OK)
|
|
continue;
|
|
NCOM::CPropVariant prop;
|
|
if (getProps->GetArcProp2(level, propID, &prop) != S_OK)
|
|
continue;
|
|
AddPropertyString(propID, name, prop, message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// we ERROR message for NonOpen level
|
|
bool needSep = true;
|
|
const int kNumSpecProps = Z7_ARRAY_SIZE(kSpecProps);
|
|
for (Int32 i = -(int)kNumSpecProps; i < 0; i++)
|
|
{
|
|
CMyComBSTR name;
|
|
const PROPID propID = kSpecProps[i + kNumSpecProps];
|
|
NCOM::CPropVariant prop;
|
|
if (getProps->GetArcProp(numLevels, propID, &prop) != S_OK)
|
|
continue;
|
|
if (needSep)
|
|
{
|
|
AddSeparator(message);
|
|
AddSeparator(message);
|
|
needSep = false;
|
|
}
|
|
AddPropertyString(propID, name, prop, message);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
message.Title = LangString(IDS_PROPERTIES);
|
|
message.NumColumns = 2;
|
|
message.Create(GetParent());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPanel::EditCut()
|
|
{
|
|
// InvokeSystemCommand("cut");
|
|
}
|
|
|
|
void CPanel::EditCopy()
|
|
{
|
|
/*
|
|
CMyComPtr<IGetFolderArcProps> getFolderArcProps;
|
|
_folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
|
|
if (!getFolderArcProps)
|
|
{
|
|
InvokeSystemCommand("copy");
|
|
return;
|
|
}
|
|
*/
|
|
UString s;
|
|
CRecordVector<UInt32> indices;
|
|
Get_ItemIndices_Selected(indices);
|
|
FOR_VECTOR (i, indices)
|
|
{
|
|
if (i != 0)
|
|
s += "\xD\n";
|
|
s += GetItemName(indices[i]);
|
|
}
|
|
ClipboardSetText(_mainWindow, s);
|
|
}
|
|
|
|
void CPanel::EditPaste()
|
|
{
|
|
/*
|
|
UStringVector names;
|
|
ClipboardGetFileNames(names);
|
|
CopyFromNoAsk(names);
|
|
UString s;
|
|
for (int i = 0; i < names.Size(); i++)
|
|
{
|
|
s += L' ';
|
|
s += names[i];
|
|
}
|
|
|
|
MessageBoxW(0, s, L"", 0);
|
|
*/
|
|
|
|
// InvokeSystemCommand("paste");
|
|
}
|
|
|
|
|
|
|
|
struct CFolderPidls
|
|
{
|
|
LPITEMIDLIST parent;
|
|
CRecordVector<LPITEMIDLIST> items;
|
|
|
|
CFolderPidls(): parent(NULL) {}
|
|
~CFolderPidls()
|
|
{
|
|
FOR_VECTOR (i, items)
|
|
CoTaskMemFree(items[i]);
|
|
CoTaskMemFree(parent);
|
|
}
|
|
};
|
|
|
|
|
|
static HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder,
|
|
HWND hwnd, const UString &path, LPITEMIDLIST *ppidl)
|
|
{
|
|
ULONG eaten = 0;
|
|
return shellFolder->ParseDisplayName(hwnd, NULL,
|
|
path.Ptr_non_const(), &eaten, ppidl, NULL);
|
|
}
|
|
|
|
|
|
HRESULT CPanel::CreateShellContextMenu(
|
|
const CRecordVector<UInt32> &operatedIndices,
|
|
CMyComPtr<IContextMenu> &systemContextMenu)
|
|
{
|
|
ODS("==== CPanel::CreateShellContextMenu");
|
|
systemContextMenu.Release();
|
|
UString folderPath = GetFsPath();
|
|
|
|
CMyComPtr<IShellFolder> desktopFolder;
|
|
RINOK(::SHGetDesktopFolder(&desktopFolder))
|
|
if (!desktopFolder)
|
|
{
|
|
// ShowMessage("Failed to get Desktop folder");
|
|
return E_FAIL;
|
|
}
|
|
|
|
CFolderPidls pidls;
|
|
// NULL is allowed for parentHWND in ParseDisplayName()
|
|
const HWND parentHWND_for_ParseDisplayName = GetParent();
|
|
// if (folderPath.IsEmpty()), then ParseDisplayName returns pidls of "My Computer"
|
|
/* win10: ParseDisplayName() supports folder path with tail slash
|
|
ParseDisplayName() returns {
|
|
E_INVALIDARG : path with super path prefix "\\\\?\\"
|
|
ERROR_FILE_NOT_FOUND : path for network share (\\server\path1\long path2") larger than MAX_PATH
|
|
} */
|
|
const HRESULT res = ShellFolder_ParseDisplayName(desktopFolder,
|
|
parentHWND_for_ParseDisplayName,
|
|
folderPath, &pidls.parent);
|
|
if (res != S_OK)
|
|
{
|
|
ODS_U(folderPath);
|
|
if (res != E_INVALIDARG)
|
|
return res;
|
|
if (!NFile::NName::If_IsSuperPath_RemoveSuperPrefix(folderPath))
|
|
return res;
|
|
RINOK(ShellFolder_ParseDisplayName(desktopFolder,
|
|
parentHWND_for_ParseDisplayName,
|
|
folderPath, &pidls.parent))
|
|
}
|
|
if (!pidls.parent)
|
|
return E_FAIL;
|
|
|
|
/*
|
|
UString path2;
|
|
NShell::GetPathFromIDList(pidls.parent, path2);
|
|
ODS_U(path2);
|
|
*/
|
|
|
|
if (operatedIndices.IsEmpty())
|
|
{
|
|
// how to get IContextMenu, if there are no selected files?
|
|
return E_FAIL;
|
|
|
|
/*
|
|
xp64 :
|
|
1) we can't use GetUIObjectOf() with (numItems == 0), it throws exception
|
|
2) we can't use desktopFolder->GetUIObjectOf() with absolute pidls of folder
|
|
context menu items are different in that case:
|
|
"Open / Explorer" for folder
|
|
"Delete" for "My Computer" icon
|
|
"Preperties" for "System"
|
|
*/
|
|
/*
|
|
parentFolder = desktopFolder;
|
|
pidls.items.AddInReserved(pidls.parent);
|
|
pidls.parent = NULL;
|
|
*/
|
|
|
|
// CreateViewObject() doesn't show all context menu items
|
|
/*
|
|
HRESULT res = parentFolder->CreateViewObject(
|
|
GetParent(), IID_IContextMenu, (void**)&systemContextMenu);
|
|
*/
|
|
}
|
|
|
|
CMyComPtr<IShellFolder> parentFolder;
|
|
RINOK(desktopFolder->BindToObject(pidls.parent,
|
|
NULL, IID_IShellFolder, (void**)&parentFolder))
|
|
if (!parentFolder)
|
|
return E_FAIL;
|
|
|
|
ODS("==== CPanel::CreateShellContextMenu pidls START");
|
|
|
|
pidls.items.ClearAndReserve(operatedIndices.Size());
|
|
UString fileName;
|
|
FOR_VECTOR (i, operatedIndices)
|
|
{
|
|
fileName.Empty();
|
|
Add_ItemRelPath2_To_String(operatedIndices[i], fileName);
|
|
/* ParseDisplayName() in win10 returns:
|
|
E_INVALIDARG : if empty name, or path with dots only: "." , ".."
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : if there is no such file
|
|
*/
|
|
LPITEMIDLIST pidl = NULL;
|
|
RINOK(ShellFolder_ParseDisplayName(parentFolder,
|
|
parentHWND_for_ParseDisplayName,
|
|
fileName, &pidl))
|
|
if (!pidl)
|
|
return E_FAIL;
|
|
pidls.items.AddInReserved(pidl);
|
|
}
|
|
|
|
ODS("==== CPanel::CreateShellContextMenu pidls END");
|
|
// Get IContextMenu for items
|
|
RINOK(parentFolder->GetUIObjectOf(GetParent(),
|
|
pidls.items.Size(), (LPCITEMIDLIST *)(void *)&pidls.items.Front(),
|
|
IID_IContextMenu, NULL, (void**)&systemContextMenu))
|
|
ODS("==== CPanel::CreateShellContextMenu GetUIObjectOf finished");
|
|
if (!systemContextMenu)
|
|
{
|
|
// ShowMessage("Unable to get context menu interface");
|
|
return E_FAIL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// #define SHOW_DEBUG_FM_CTX_MENU
|
|
|
|
#ifdef SHOW_DEBUG_FM_CTX_MENU
|
|
|
|
static void PrintHex(UString &s, UInt32 v)
|
|
{
|
|
char sz[32];
|
|
ConvertUInt32ToHex(v, sz);
|
|
s += sz;
|
|
}
|
|
|
|
static void PrintContextStr(UString &s, IContextMenu *ctxm, unsigned i, unsigned id, const char *name)
|
|
{
|
|
s += " | ";
|
|
s += name;
|
|
s += ": ";
|
|
UString s1;
|
|
{
|
|
char buf[256];
|
|
buf[0] = 0;
|
|
const HRESULT res = ctxm->GetCommandString(i, id,
|
|
NULL, buf, Z7_ARRAY_SIZE(buf) - 1);
|
|
if (res != S_OK)
|
|
{
|
|
PrintHex(s1, res);
|
|
s1.Add_Space();
|
|
}
|
|
s1 += GetUnicodeString(buf);
|
|
}
|
|
UString s2;
|
|
{
|
|
wchar_t buf2[256];
|
|
buf2[0] = 0;
|
|
const HRESULT res = ctxm->GetCommandString(i, id | GCS_UNICODE,
|
|
NULL, (char *)buf2, Z7_ARRAY_SIZE(buf2) - sizeof(wchar_t));
|
|
if (res != S_OK)
|
|
{
|
|
PrintHex(s2, res);
|
|
s2.Add_Space();
|
|
}
|
|
s2 += buf2;
|
|
}
|
|
s += s1;
|
|
if (s2.Compare(s1) != 0)
|
|
{
|
|
s += " Unicode: ";
|
|
s += s2;
|
|
}
|
|
}
|
|
|
|
static void PrintAllContextItems(IContextMenu *ctxm, unsigned num)
|
|
{
|
|
for (unsigned i = 0; i < num; i++)
|
|
{
|
|
UString s;
|
|
s.Add_UInt32(i);
|
|
s += ": ";
|
|
PrintContextStr(s, ctxm, i, GCS_VALIDATEA, "valid");
|
|
PrintContextStr(s, ctxm, i, GCS_VERBA, "verb");
|
|
PrintContextStr(s, ctxm, i, GCS_HELPTEXTA, "helptext");
|
|
OutputDebugStringW(s);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void CPanel::CreateSystemMenu(HMENU menuSpec,
|
|
bool showExtendedVerbs,
|
|
const CRecordVector<UInt32> &operatedIndices,
|
|
CMyComPtr<IContextMenu> &systemContextMenu)
|
|
{
|
|
systemContextMenu.Release();
|
|
|
|
CreateShellContextMenu(operatedIndices, systemContextMenu);
|
|
|
|
if (!systemContextMenu)
|
|
return;
|
|
|
|
/*
|
|
// Set up a CMINVOKECOMMANDINFO structure.
|
|
CMINVOKECOMMANDINFO ci;
|
|
ZeroMemory(&ci, sizeof(ci));
|
|
ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
|
|
ci.hwnd = GetParent();
|
|
*/
|
|
|
|
/*
|
|
if (Sender == GoBtn)
|
|
{
|
|
// Verbs that can be used are cut, paste,
|
|
// properties, delete, and so on.
|
|
String action;
|
|
if (CutRb->Checked)
|
|
action = "cut";
|
|
else if (CopyRb->Checked)
|
|
action = "copy";
|
|
else if (DeleteRb->Checked)
|
|
action = "delete";
|
|
else if (PropertiesRb->Checked)
|
|
action = "properties";
|
|
|
|
ci.lpVerb = action.c_str();
|
|
result = cm->InvokeCommand(&ci);
|
|
if (result)
|
|
ShowMessage(
|
|
"Error copying file to clipboard.");
|
|
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// HMENU hMenu = CreatePopupMenu();
|
|
CMenu popupMenu;
|
|
CMenuDestroyer menuDestroyer(popupMenu);
|
|
if (!popupMenu.CreatePopup())
|
|
throw 210503;
|
|
const HMENU hMenu = popupMenu;
|
|
DWORD flags = CMF_EXPLORE;
|
|
if (showExtendedVerbs)
|
|
flags |= Z7_WIN_CMF_EXTENDEDVERBS;
|
|
ODS("=== systemContextMenu->QueryContextMenu START");
|
|
const HRESULT res = systemContextMenu->QueryContextMenu(hMenu, 0, kSystemStartMenuID, 0x7FFF, flags);
|
|
ODS("=== systemContextMenu->QueryContextMenu END");
|
|
if (SUCCEEDED(res))
|
|
{
|
|
#ifdef SHOW_DEBUG_FM_CTX_MENU
|
|
PrintAllContextItems(systemContextMenu, (unsigned)res);
|
|
#endif
|
|
|
|
CMenu menu;
|
|
menu.Attach(menuSpec);
|
|
CMenuItem menuItem;
|
|
menuItem.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
|
|
menuItem.fType = MFT_STRING;
|
|
menuItem.hSubMenu = popupMenu.Detach();
|
|
menuDestroyer.Disable();
|
|
LangString(IDS_SYSTEM, menuItem.StringValue);
|
|
menu.InsertItem(0, true, menuItem);
|
|
}
|
|
/*
|
|
if (Cmd < 100 && Cmd != 0)
|
|
{
|
|
ci.lpVerb = MAKEINTRESOURCE(Cmd - 1);
|
|
ci.lpParameters = "";
|
|
ci.lpDirectory = "";
|
|
ci.nShow = SW_SHOWNORMAL;
|
|
cm->InvokeCommand(&ci);
|
|
}
|
|
// If Cmd is > 100 then it's one of our
|
|
// inserted menu items.
|
|
else
|
|
// Find the menu item.
|
|
for (int i = 0; i < popupMenu1->Items->Count; i++)
|
|
{
|
|
TMenuItem* menu = popupMenu1->Items->Items[i];
|
|
// Call its OnClick handler.
|
|
if (menu->Command == Cmd - 100)
|
|
menu->OnClick(this);
|
|
}
|
|
// Release the memory allocated for the menu.
|
|
DestroyMenu(hMenu);
|
|
*/
|
|
}
|
|
}
|
|
|
|
void CPanel::CreateFileMenu(HMENU menuSpec)
|
|
{
|
|
CreateFileMenu(menuSpec, _sevenZipContextMenu, _systemContextMenu, true); // programMenu
|
|
}
|
|
|
|
void CPanel::CreateSevenZipMenu(HMENU menuSpec,
|
|
bool showExtendedVerbs,
|
|
const CRecordVector<UInt32> &operatedIndices,
|
|
int firstDirIndex,
|
|
CMyComPtr<IContextMenu> &sevenZipContextMenu)
|
|
{
|
|
sevenZipContextMenu.Release();
|
|
|
|
CMenu menu;
|
|
menu.Attach(menuSpec);
|
|
// CMenuDestroyer menuDestroyer(menu);
|
|
// menu.CreatePopup();
|
|
|
|
CZipContextMenu *contextMenuSpec = new CZipContextMenu;
|
|
CMyComPtr<IContextMenu> contextMenu = contextMenuSpec;
|
|
// if (contextMenu.CoCreateInstance(CLSID_CZipContextMenu, IID_IContextMenu) == S_OK)
|
|
{
|
|
/*
|
|
CMyComPtr<IInitContextMenu> initContextMenu;
|
|
if (contextMenu.QueryInterface(IID_IInitContextMenu, &initContextMenu) != S_OK)
|
|
return;
|
|
*/
|
|
ODS("=== FileName List Add START")
|
|
// for (unsigned y = 0; y < 10000; y++, contextMenuSpec->_fileNames.Clear())
|
|
GetFilePaths(operatedIndices, contextMenuSpec->_fileNames);
|
|
ODS("=== FileName List Add END")
|
|
contextMenuSpec->Init_For_7zFM();
|
|
contextMenuSpec->_attribs.FirstDirIndex = firstDirIndex;
|
|
{
|
|
DWORD flags = CMF_EXPLORE;
|
|
if (showExtendedVerbs)
|
|
flags |= Z7_WIN_CMF_EXTENDEDVERBS;
|
|
const HRESULT res = contextMenu->QueryContextMenu(menu,
|
|
0, // indexMenu
|
|
kSevenZipStartMenuID, // first
|
|
kSystemStartMenuID - 1, // last
|
|
flags);
|
|
ODS("=== contextMenu->QueryContextMenu END")
|
|
const bool sevenZipMenuCreated = SUCCEEDED(res);
|
|
if (sevenZipMenuCreated)
|
|
{
|
|
// if (res != 0)
|
|
{
|
|
// some "non-good" implementation of QueryContextMenu() could add some items to menu, but it return 0.
|
|
// so we still allow these items
|
|
sevenZipContextMenu = contextMenu;
|
|
#ifdef SHOW_DEBUG_FM_CTX_MENU
|
|
PrintAllContextItems(contextMenu, (unsigned)res);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MessageBox_Error_HRESULT_Caption(res, L"QueryContextMenu");
|
|
}
|
|
// int code = HRESULT_CODE(res);
|
|
// int nextItemID = code;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsReadOnlyFolder(IFolderFolder *folder)
|
|
{
|
|
if (!folder)
|
|
return false;
|
|
|
|
bool res = false;
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
if (folder->GetFolderProperty(kpidReadOnly, &prop) == S_OK)
|
|
if (prop.vt == VT_BOOL)
|
|
res = VARIANT_BOOLToBool(prop.boolVal);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool CPanel::IsThereReadOnlyFolder() const
|
|
{
|
|
if (!_folderOperations)
|
|
return true;
|
|
if (IsReadOnlyFolder(_folder))
|
|
return true;
|
|
FOR_VECTOR (i, _parentFolders)
|
|
{
|
|
if (IsReadOnlyFolder(_parentFolders[i].ParentFolder))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPanel::CheckBeforeUpdate(UINT resourceID)
|
|
{
|
|
if (!_folderOperations)
|
|
{
|
|
MessageBox_Error_UnsupportOperation();
|
|
// resourceID = resourceID;
|
|
// MessageBoxErrorForUpdate(E_NOINTERFACE, resourceID);
|
|
return false;
|
|
}
|
|
|
|
for (int i = (int)_parentFolders.Size(); i >= 0; i--)
|
|
{
|
|
IFolderFolder *folder;
|
|
if (i == (int)_parentFolders.Size())
|
|
folder = _folder;
|
|
else
|
|
folder = _parentFolders[i].ParentFolder;
|
|
|
|
if (!IsReadOnlyFolder(folder))
|
|
continue;
|
|
|
|
UString s;
|
|
AddLangString(s, resourceID);
|
|
s.Add_LF();
|
|
AddLangString(s, IDS_OPERATION_IS_NOT_SUPPORTED);
|
|
s.Add_LF();
|
|
if (i == 0)
|
|
s += GetFolderPath(folder);
|
|
else
|
|
s += _parentFolders[i - 1].VirtualPath;
|
|
s.Add_LF();
|
|
AddLangString(s, IDS_PROP_READ_ONLY);
|
|
MessageBox_Error(s);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPanel::CreateFileMenu(HMENU menuSpec,
|
|
CMyComPtr<IContextMenu> &sevenZipContextMenu,
|
|
CMyComPtr<IContextMenu> &systemContextMenu,
|
|
bool programMenu)
|
|
{
|
|
sevenZipContextMenu.Release();
|
|
systemContextMenu.Release();
|
|
|
|
const bool showExtendedVerbs = IsKeyDown(VK_SHIFT);
|
|
|
|
CRecordVector<UInt32> operatedIndices;
|
|
Get_ItemIndices_Operated(operatedIndices);
|
|
const int firstDirIndex = FindDir_InOperatedList(operatedIndices);
|
|
|
|
CMenu menu;
|
|
menu.Attach(menuSpec);
|
|
|
|
if (!IsArcFolder())
|
|
{
|
|
CreateSevenZipMenu(menu, showExtendedVerbs, operatedIndices, firstDirIndex, sevenZipContextMenu);
|
|
// CreateSystemMenu is very slow if you call it inside ZIP archive with big number of files
|
|
// Windows probably can parse items inside ZIP archive.
|
|
if (g_App.ShowSystemMenu)
|
|
CreateSystemMenu(menu, showExtendedVerbs, operatedIndices, systemContextMenu);
|
|
}
|
|
|
|
/*
|
|
if (menu.GetItemCount() > 0)
|
|
menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)0);
|
|
*/
|
|
|
|
CFileMenu fm;
|
|
|
|
fm.readOnly = IsThereReadOnlyFolder();
|
|
fm.isHashFolder = IsHashFolder();
|
|
fm.isFsFolder = Is_IO_FS_Folder();
|
|
fm.programMenu = programMenu;
|
|
fm.allAreFiles = (firstDirIndex == -1);
|
|
fm.numItems = operatedIndices.Size();
|
|
|
|
fm.isAltStreamsSupported = false;
|
|
|
|
if (fm.numItems == 1)
|
|
fm.FilePath = us2fs(GetItemFullPath(operatedIndices[0]));
|
|
|
|
if (_folderAltStreams)
|
|
{
|
|
if (operatedIndices.Size() <= 1)
|
|
{
|
|
UInt32 realIndex = (UInt32)(Int32)-1;
|
|
if (operatedIndices.Size() == 1)
|
|
realIndex = operatedIndices[0];
|
|
Int32 val = 0;
|
|
if (_folderAltStreams->AreAltStreamsSupported(realIndex, &val) == S_OK)
|
|
fm.isAltStreamsSupported = IntToBool(val);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fm.numItems == 0)
|
|
fm.isAltStreamsSupported = IsFSFolder();
|
|
else
|
|
fm.isAltStreamsSupported = IsFolder_with_FsItems();
|
|
}
|
|
|
|
fm.Load(menu, (unsigned)menu.GetItemCount());
|
|
}
|
|
|
|
bool CPanel::InvokePluginCommand(unsigned id)
|
|
{
|
|
return InvokePluginCommand(id, _sevenZipContextMenu, _systemContextMenu);
|
|
}
|
|
|
|
#if defined(_MSC_VER) && !defined(UNDER_CE)
|
|
#define use_CMINVOKECOMMANDINFOEX
|
|
/* CMINVOKECOMMANDINFOEX depends from (_WIN32_IE >= 0x0400) */
|
|
#endif
|
|
|
|
bool CPanel::InvokePluginCommand(unsigned id,
|
|
IContextMenu *sevenZipContextMenu, IContextMenu *systemContextMenu)
|
|
{
|
|
UInt32 offset;
|
|
const bool isSystemMenu = (id >= kSystemStartMenuID);
|
|
if (isSystemMenu)
|
|
{
|
|
if (!systemContextMenu)
|
|
return false;
|
|
offset = id - kSystemStartMenuID;
|
|
}
|
|
else
|
|
{
|
|
if (!sevenZipContextMenu)
|
|
return false;
|
|
offset = id - kSevenZipStartMenuID;
|
|
}
|
|
|
|
#ifdef use_CMINVOKECOMMANDINFOEX
|
|
CMINVOKECOMMANDINFOEX
|
|
#else
|
|
CMINVOKECOMMANDINFO
|
|
#endif
|
|
commandInfo;
|
|
|
|
memset(&commandInfo, 0, sizeof(commandInfo));
|
|
commandInfo.cbSize = sizeof(commandInfo);
|
|
|
|
commandInfo.fMask = 0
|
|
#ifdef use_CMINVOKECOMMANDINFOEX
|
|
| CMIC_MASK_UNICODE
|
|
#endif
|
|
;
|
|
|
|
commandInfo.hwnd = GetParent();
|
|
commandInfo.lpVerb = (LPCSTR)(MAKEINTRESOURCE(offset));
|
|
commandInfo.lpParameters = NULL;
|
|
// 19.01: fixed CSysString to AString
|
|
// MSDN suggest to send NULL: lpDirectory: This member is always NULL for menu items inserted by a Shell extension.
|
|
const AString currentFolderA (GetAnsiString(_currentFolderPrefix));
|
|
commandInfo.lpDirectory = (LPCSTR)(currentFolderA);
|
|
commandInfo.nShow = SW_SHOW;
|
|
|
|
#ifdef use_CMINVOKECOMMANDINFOEX
|
|
|
|
commandInfo.lpParametersW = NULL;
|
|
commandInfo.lpTitle = "";
|
|
|
|
/*
|
|
system ContextMenu handler supports ContextMenu subhandlers.
|
|
so InvokeCommand() converts (command_offset) from global number to subhandler number.
|
|
XP-64 / win10:
|
|
system ContextMenu converts (command_offset) in lpVerb only,
|
|
and it keeps lpVerbW unchanged.
|
|
also explorer.exe sends 0 in lpVerbW.
|
|
We try to keep compatibility with Windows Explorer here.
|
|
*/
|
|
commandInfo.lpVerbW = NULL;
|
|
|
|
const UString currentFolderUnicode = _currentFolderPrefix;
|
|
commandInfo.lpDirectoryW = currentFolderUnicode;
|
|
commandInfo.lpTitleW = L"";
|
|
// commandInfo.ptInvoke.x = xPos;
|
|
// commandInfo.ptInvoke.y = yPos;
|
|
commandInfo.ptInvoke.x = 0;
|
|
commandInfo.ptInvoke.y = 0;
|
|
|
|
#endif
|
|
|
|
HRESULT result;
|
|
if (isSystemMenu)
|
|
result = systemContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
|
|
else
|
|
result = sevenZipContextMenu->InvokeCommand(LPCMINVOKECOMMANDINFO(&commandInfo));
|
|
if (result == NOERROR)
|
|
{
|
|
KillSelection();
|
|
return true;
|
|
}
|
|
else
|
|
MessageBox_Error_HRESULT_Caption(result, L"InvokeCommand");
|
|
return false;
|
|
}
|
|
|
|
bool CPanel::OnContextMenu(HANDLE windowHandle, int xPos, int yPos)
|
|
{
|
|
if (::GetParent((HWND)windowHandle) == _listView)
|
|
{
|
|
ShowColumnsContextMenu(xPos, yPos);
|
|
return true;
|
|
}
|
|
|
|
if (windowHandle != _listView)
|
|
return false;
|
|
/*
|
|
POINT point;
|
|
point.x = xPos;
|
|
point.y = yPos;
|
|
if (!_listView.ScreenToClient(&point))
|
|
return false;
|
|
|
|
LVHITTESTINFO info;
|
|
info.pt = point;
|
|
int index = _listView.HitTest(&info);
|
|
*/
|
|
|
|
CRecordVector<UInt32> operatedIndices;
|
|
Get_ItemIndices_Operated(operatedIndices);
|
|
|
|
// negative x,y are possible for multi-screen modes.
|
|
// x=-1 && y=-1 for keyboard call (SHIFT+F10 and others).
|
|
if (xPos == -1 && yPos == -1)
|
|
{
|
|
if (operatedIndices.Size() == 0)
|
|
{
|
|
xPos = 0;
|
|
yPos = 0;
|
|
}
|
|
else
|
|
{
|
|
int itemIndex = _listView.GetNextItem(-1, LVNI_FOCUSED);
|
|
if (itemIndex == -1)
|
|
return false;
|
|
RECT rect;
|
|
if (!_listView.GetItemRect(itemIndex, &rect, LVIR_ICON))
|
|
return false;
|
|
xPos = (rect.left + rect.right) / 2;
|
|
yPos = (rect.top + rect.bottom) / 2;
|
|
}
|
|
POINT point = {xPos, yPos};
|
|
_listView.ClientToScreen(&point);
|
|
xPos = point.x;
|
|
yPos = point.y;
|
|
}
|
|
|
|
CMenu menu;
|
|
CMenuDestroyer menuDestroyer(menu);
|
|
menu.CreatePopup();
|
|
|
|
CMyComPtr<IContextMenu> sevenZipContextMenu;
|
|
CMyComPtr<IContextMenu> systemContextMenu;
|
|
CreateFileMenu(menu, sevenZipContextMenu, systemContextMenu, false); // programMenu
|
|
|
|
const unsigned id = (unsigned)menu.Track(TPM_LEFTALIGN
|
|
#ifndef UNDER_CE
|
|
| TPM_RIGHTBUTTON
|
|
#endif
|
|
| TPM_RETURNCMD | TPM_NONOTIFY,
|
|
xPos, yPos, _listView);
|
|
|
|
if (id == 0)
|
|
return true;
|
|
|
|
if (id >= kMenuCmdID_Plugin_Start)
|
|
{
|
|
InvokePluginCommand(id, sevenZipContextMenu, systemContextMenu);
|
|
return true;
|
|
}
|
|
if (ExecuteFileCommand(id))
|
|
return true;
|
|
return true;
|
|
}
|