summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-01-22 02:54:29 -0500
committerisanae <14251494+isanae@users.noreply.github.com>2020-02-04 03:33:22 -0500
commit2a0e78e3cf0c1106a1fb7e470148f6e0b093b2b1 (patch)
treeaa0ce69d111a22d232dc1589fa707855bddc53b0 /src
parent6005da618775d545c664371f53571a75eacab7f2 (diff)
fixed bad path for alternate origins
finished ShellMenuCollection, had to split a bunch of things
Diffstat (limited to 'src')
-rw-r--r--src/envshell.cpp499
-rw-r--r--src/envshell.h36
-rw-r--r--src/filetree.cpp60
-rw-r--r--src/shared/directoryentry.cpp14
-rw-r--r--src/shared/directoryentry.h7
5 files changed, 358 insertions, 258 deletions
diff --git a/src/envshell.cpp b/src/envshell.cpp
index 58948d31..e01bb804 100644
--- a/src/envshell.cpp
+++ b/src/envshell.cpp
@@ -62,17 +62,12 @@ struct IdlsFreer
class WndProcFilter : public QAbstractNativeEventFilter
{
public:
- WndProcFilter(QMainWindow* mw, IContextMenu* cm)
- : m_mw(mw), m_cm(cm), m_cm2(nullptr), m_cm3(nullptr)
- {
- createInterfaces();
- }
+ using function_type = std::function<
+ bool (HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out)>;
- ~WndProcFilter()
+ WndProcFilter(function_type f)
+ : m_f(std::move(f))
{
- if (auto* sb=m_mw->statusBar()) {
- sb->clearMessage();
- }
}
bool nativeEventFilter(const QByteArray& type, void* m, long* lresultOut) override
@@ -82,194 +77,116 @@ public:
return false;
}
- if (msg->message == WM_MENUSELECT) {
- HANDLE_WM_MENUSELECT(msg->hwnd, msg->wParam, msg->lParam, onMenuSelect);
- return true;
- }
-
- if (m_cm3) {
- LRESULT lresult = 0;
+ LRESULT lr = 0;
- const auto r = m_cm3->HandleMenuMsg2(
- msg->message, msg->wParam, msg->lParam, &lresult);
+ const bool r = m_f(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lr);
- if (SUCCEEDED(r)) {
- if (lresultOut) {
- *lresultOut = lresult;
- }
-
- return true;
- }
+ if (lresultOut) {
+ *lresultOut = lr;
}
- if (m_cm2) {
- const auto r = m_cm2->HandleMenuMsg(
- msg->message, msg->wParam, msg->lParam);
-
- if (SUCCEEDED(r)) {
- if (lresultOut) {
- *lresultOut = 0;
- }
-
- return true;
- }
- }
-
- return false;
+ return r;
}
private:
- QMainWindow* m_mw;
- IContextMenu* m_cm;
- COMPtr<IContextMenu2> m_cm2;
- COMPtr<IContextMenu3> m_cm3;
-
- void createInterfaces()
- {
- if (!m_cm) {
- return;
- }
-
- IContextMenu2* cm2 = nullptr;
- if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) {
- m_cm2.reset(cm2);
- }
-
- IContextMenu3* cm3 = nullptr;
- if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) {
- m_cm3.reset(cm3);
- }
- }
+ function_type m_f;
+};
- // adapted from
- // https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723
- //
- void onMenuSelect(
- HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
- {
- if (m_cm && item >= QCM_FIRST && item <= QCM_LAST) {
- WCHAR szBuf[MAX_PATH];
- const auto r = IContextMenu_GetCommandString(
- m_cm, item - QCM_FIRST, GCS_HELPTEXTW, NULL, szBuf, MAX_PATH);
- if (FAILED(r)) {
- lstrcpynW(szBuf, L"No help available.", MAX_PATH);
- }
- if (m_mw) {
- if (auto* sb=m_mw->statusBar()) {
- sb->showMessage(QString::fromWCharArray(szBuf));
- }
- }
- }
+HWND getHWND(QMainWindow* mw)
+{
+ if (mw) {
+ return (HWND)mw->winId();
+ } else {
+ return 0;
}
+}
- // adapted from
- // https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723
- //
- HRESULT IContextMenu_GetCommandString(
- IContextMenu *pcm, UINT_PTR idCmd, UINT uFlags,
- UINT *pwReserved, LPWSTR pszName, UINT cchMax)
- {
- // Callers are expected to be using Unicode.
- if (!(uFlags & GCS_UNICODE)) {
- return E_INVALIDARG;
- }
+// adapted from
+// https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723
+//
+HRESULT IContextMenu_GetCommandString(
+ IContextMenu *pcm, UINT_PTR idCmd, UINT uFlags,
+ UINT *pwReserved, LPWSTR pszName, UINT cchMax)
+{
+ // Callers are expected to be using Unicode.
+ if (!(uFlags & GCS_UNICODE)) {
+ return E_INVALIDARG;
+ }
- // Some context menu handlers have off-by-one bugs and will
- // overflow the output buffer. Let’s artificially reduce the
- // buffer size so a one-character overflow won’t corrupt memory.
- if (cchMax <= 1) {
- return E_FAIL;
- }
+ // Some context menu handlers have off-by-one bugs and will
+ // overflow the output buffer. Let’s artificially reduce the
+ // buffer size so a one-character overflow won’t corrupt memory.
+ if (cchMax <= 1) {
+ return E_FAIL;
+ }
- cchMax--;
+ cchMax--;
- // First try the Unicode message. Preset the output buffer
- // with a known value because some handlers return S_OK without
- // doing anything.
- pszName[0] = L'\0';
+ // First try the Unicode message. Preset the output buffer
+ // with a known value because some handlers return S_OK without
+ // doing anything.
+ pszName[0] = L'\0';
- HRESULT hr = pcm->GetCommandString(
- idCmd, uFlags, pwReserved, (LPSTR)pszName, cchMax);
+ HRESULT hr = pcm->GetCommandString(
+ idCmd, uFlags, pwReserved, (LPSTR)pszName, cchMax);
- if (SUCCEEDED(hr) && pszName[0] == L'\0') {
- // Rats, a buggy IContextMenu handler that returned success
- // even though it failed.
- hr = E_NOTIMPL;
- }
+ if (SUCCEEDED(hr) && pszName[0] == L'\0') {
+ // Rats, a buggy IContextMenu handler that returned success
+ // even though it failed.
+ hr = E_NOTIMPL;
+ }
- if (FAILED(hr)) {
- // try again with ANSI – pad the buffer with one extra character
- // to compensate for context menu handlers that overflow by
- // one character.
- LPSTR pszAnsi = (LPSTR)LocalAlloc(
- LMEM_FIXED, (cchMax + 1) * sizeof(CHAR));
+ if (FAILED(hr)) {
+ // try again with ANSI – pad the buffer with one extra character
+ // to compensate for context menu handlers that overflow by
+ // one character.
+ LPSTR pszAnsi = (LPSTR)LocalAlloc(
+ LMEM_FIXED, (cchMax + 1) * sizeof(CHAR));
- if (pszAnsi) {
- pszAnsi[0] = '\0';
+ if (pszAnsi) {
+ pszAnsi[0] = '\0';
- hr = pcm->GetCommandString(
- idCmd, uFlags & ~GCS_UNICODE, pwReserved, pszAnsi, cchMax);
+ hr = pcm->GetCommandString(
+ idCmd, uFlags & ~GCS_UNICODE, pwReserved, pszAnsi, cchMax);
- if (SUCCEEDED(hr) && pszAnsi[0] == '\0') {
- // Rats, a buggy IContextMenu handler that returned success
- // even though it failed.
- hr = E_NOTIMPL;
- }
+ if (SUCCEEDED(hr) && pszAnsi[0] == '\0') {
+ // Rats, a buggy IContextMenu handler that returned success
+ // even though it failed.
+ hr = E_NOTIMPL;
+ }
- if (SUCCEEDED(hr)) {
- if (MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pszName, cchMax) == 0) {
- hr = E_FAIL;
- }
+ if (SUCCEEDED(hr)) {
+ if (MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pszName, cchMax) == 0) {
+ hr = E_FAIL;
}
-
- LocalFree(pszAnsi);
-
- } else {
- hr = E_OUTOFMEMORY;
}
- }
-
- return hr;
- }
-};
+ LocalFree(pszAnsi);
-QMainWindow* getMainWindow(QWidget* w)
-{
- QWidget* p = w;
-
- while (p) {
- if (auto* mw=dynamic_cast<QMainWindow*>(p)) {
- return mw;
+ } else {
+ hr = E_OUTOFMEMORY;
}
-
- p = p->parentWidget();
}
- return nullptr;
+ return hr;
}
-HWND getHWND(QMainWindow* mw)
+
+ShellMenu::ShellMenu(QMainWindow* mw)
+ : m_mw(mw)
{
- if (mw) {
- return (HWND)mw->winId();
- } else {
- return 0;
- }
}
-
void ShellMenu::addFile(QFileInfo fi)
{
m_files.emplace_back(std::move(fi));
}
-void ShellMenu::exec(QWidget* parent, const QPoint& pos)
+void ShellMenu::exec(const QPoint& pos)
{
- auto* mw = getMainWindow(parent);
HMENU menu = getMenu();
if (!menu) {
return;
@@ -277,12 +194,29 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos)
try
{
- const int cmd = runMenu(mw, m_cm.get(), m_menu.get(), pos);
+ const auto hwnd = getHWND(m_mw);
+
+ auto filter = std::make_unique<WndProcFilter>(
+ [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) {
+ return wndProc(h, m, wp, lp, out);
+ });
+
+ QCoreApplication::instance()->installNativeEventFilter(filter.get());
+
+ const int cmd = TrackPopupMenuEx(
+ menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr);
+
+ if (m_mw) {
+ if (auto* sb=m_mw->statusBar()) {
+ sb->clearMessage();
+ }
+ }
+
if (cmd <= 0) {
return;
}
- invoke(mw, pos, cmd - QCM_FIRST, m_cm.get());
+ invoke(pos, cmd - QCM_FIRST);
}
catch(MenuFailed& e)
{
@@ -301,13 +235,67 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos)
HMENU ShellMenu::getMenu()
{
if (!m_menu) {
- createMenu();
+ create();
}
return m_menu.get();
}
-void ShellMenu::createMenu()
+bool ShellMenu::wndProc(HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out)
+{
+ if (m == WM_MENUSELECT) {
+ HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect);
+ return true;
+ }
+
+ if (m_cm3) {
+ const auto r = m_cm3->HandleMenuMsg2(m, wp, lp, out);
+
+ if (SUCCEEDED(r)) {
+ return true;
+ }
+ }
+
+ if (m_cm2) {
+ const auto r = m_cm2->HandleMenuMsg(m, wp, lp);
+
+ if (SUCCEEDED(r)) {
+ if (out) {
+ *out = 0;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// adapted from
+// https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723
+//
+void ShellMenu::onMenuSelect(
+ HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
+{
+ if (m_cm && item >= QCM_FIRST && item <= QCM_LAST) {
+ WCHAR szBuf[MAX_PATH];
+
+ const auto r = IContextMenu_GetCommandString(
+ m_cm.get(), item - QCM_FIRST, GCS_HELPTEXTW, NULL, szBuf, MAX_PATH);
+
+ if (FAILED(r)) {
+ lstrcpynW(szBuf, L"No help available.", MAX_PATH);
+ }
+
+ if (m_mw) {
+ if (auto* sb=m_mw->statusBar()) {
+ sb->showMessage(QString::fromWCharArray(szBuf));
+ }
+ }
+ }
+}
+
+void ShellMenu::create()
{
if (m_files.empty()) {
log::warn("showShellMenu(): no files given");
@@ -326,8 +314,9 @@ void ShellMenu::createMenu()
IdlsFreer freer(idls);
auto array = createItemArray(idls);
- m_cm = createContextMenu(array.get());
- m_menu = createMenu(m_cm.get());
+
+ createContextMenu(array.get());
+ createPopupMenu(m_cm.get());
}
catch(DummyMenu& dm)
{
@@ -375,44 +364,6 @@ HMenuPtr ShellMenu::createDummyMenu(const QString& what)
}
}
-COMPtr<IShellItem> ShellMenu::createShellItem(const std::wstring& path)
-{
- IShellItem* item = nullptr;
-
- auto r = SHCreateItemFromParsingName(
- path.c_str(), nullptr, IID_IShellItem, (void**)&item);
-
- if (FAILED(r)) {
- throw MenuFailed(r, "SHCreateItemFromParsingName failed");
- }
-
- return COMPtr<IShellItem>(item);
-}
-
-COMPtr<IPersistIDList> ShellMenu::getPersistIDList(IShellItem* item)
-{
- IPersistIDList* idl = nullptr;
- auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl);
-
- if (FAILED(r)) {
- throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed");
- }
-
- return COMPtr<IPersistIDList>(idl);
-}
-
-CoTaskMemPtr<LPITEMIDLIST> ShellMenu::getIDList(IPersistIDList* pidlist)
-{
- LPITEMIDLIST absIdl = nullptr;
- auto r = pidlist->GetIDList(&absIdl);
-
- if (FAILED(r)) {
- throw MenuFailed(r, "GetIDList failed");
- }
-
- return CoTaskMemPtr<LPITEMIDLIST>(absIdl);
-}
-
std::vector<LPCITEMIDLIST> ShellMenu::createIdls(
const std::vector<QFileInfo>& files)
{
@@ -455,7 +406,7 @@ COMPtr<IShellItemArray> ShellMenu::createItemArray(
return COMPtr<IShellItemArray>(array);
}
-COMPtr<IContextMenu> ShellMenu::createContextMenu(IShellItemArray* array)
+void ShellMenu::createContextMenu(IShellItemArray* array)
{
IContextMenu* cm = nullptr;
@@ -466,10 +417,24 @@ COMPtr<IContextMenu> ShellMenu::createContextMenu(IShellItemArray* array)
throw MenuFailed(r, "BindToHandler failed");
}
- return COMPtr<IContextMenu>(cm);
+ m_cm.reset(cm);
+
+ {
+ IContextMenu2* cm2 = nullptr;
+ if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) {
+ m_cm2.reset(cm2);
+ }
+ }
+
+ {
+ IContextMenu3* cm3 = nullptr;
+ if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) {
+ m_cm3.reset(cm3);
+ }
+ }
}
-HMenuPtr ShellMenu::createMenu(IContextMenu* cm)
+void ShellMenu::createPopupMenu(IContextMenu* cm)
{
HMENU hmenu = CreatePopupMenu();
if (!hmenu) {
@@ -484,24 +449,50 @@ HMenuPtr ShellMenu::createMenu(IContextMenu* cm)
throw MenuFailed(r, "QueryContextMenu failed");
}
- return HMenuPtr(hmenu);
+ m_menu.reset(hmenu);
}
-int ShellMenu::runMenu(
- QMainWindow* mw, IContextMenu* cm, HMENU menu, const QPoint& p)
+COMPtr<IShellItem> ShellMenu::createShellItem(const std::wstring& path)
{
- const auto hwnd = getHWND(mw);
+ IShellItem* item = nullptr;
- auto filter = std::make_unique<WndProcFilter>(mw, cm);
- QCoreApplication::instance()->installNativeEventFilter(filter.get());
+ auto r = SHCreateItemFromParsingName(
+ path.c_str(), nullptr, IID_IShellItem, (void**)&item);
+
+ if (FAILED(r)) {
+ throw MenuFailed(r, "SHCreateItemFromParsingName failed");
+ }
+
+ return COMPtr<IShellItem>(item);
+}
+
+COMPtr<IPersistIDList> ShellMenu::getPersistIDList(IShellItem* item)
+{
+ IPersistIDList* idl = nullptr;
+ auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl);
+
+ if (FAILED(r)) {
+ throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed");
+ }
- return TrackPopupMenuEx(menu, TPM_RETURNCMD, p.x(), p.y(), hwnd, nullptr);
+ return COMPtr<IPersistIDList>(idl);
}
-void ShellMenu::invoke(
- QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm)
+CoTaskMemPtr<LPITEMIDLIST> ShellMenu::getIDList(IPersistIDList* pidlist)
{
- const auto hwnd = getHWND(mw);
+ LPITEMIDLIST absIdl = nullptr;
+ auto r = pidlist->GetIDList(&absIdl);
+
+ if (FAILED(r)) {
+ throw MenuFailed(r, "GetIDList failed");
+ }
+
+ return CoTaskMemPtr<LPITEMIDLIST>(absIdl);
+}
+
+void ShellMenu::invoke(const QPoint& p, int cmd)
+{
+ const auto hwnd = getHWND(m_mw);
CMINVOKECOMMANDINFOEX info = {};
@@ -525,7 +516,7 @@ void ShellMenu::invoke(
info.fMask |= CMIC_MASK_CONTROL_DOWN;
}
- const auto r = cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
+ const auto r = m_cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
if (FAILED(r)) {
throw MenuFailed(r, fmt::format("InvokeCommand failed, verb={}", cmd));
@@ -533,12 +524,17 @@ void ShellMenu::invoke(
}
+ShellMenuCollection::ShellMenuCollection(QMainWindow* mw)
+ : m_mw(mw), m_active(nullptr)
+{
+}
+
void ShellMenuCollection::add(QString name, ShellMenu m)
{
m_menus.push_back({name, std::move(m)});
}
-void ShellMenuCollection::exec(QWidget* parent, const QPoint& pos)
+void ShellMenuCollection::exec(const QPoint& pos)
{
HMENU menu = ::CreatePopupMenu();
if (!menu) {
@@ -572,10 +568,77 @@ void ShellMenuCollection::exec(QWidget* parent, const QPoint& pos)
}
}
- auto* mw = getMainWindow(parent);
- auto hwnd = getHWND(mw);
+ auto hwnd = getHWND(m_mw);
+
+ auto filter = std::make_unique<WndProcFilter>(
+ [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) {
+ return wndProc(h, m, wp, lp, out);
+ });
+
+ QCoreApplication::instance()->installNativeEventFilter(filter.get());
+
+ const int cmd = TrackPopupMenuEx(
+ menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr);
+
+ if (m_mw) {
+ if (auto* sb=m_mw->statusBar()) {
+ sb->clearMessage();
+ }
+ }
+
+ if (cmd <= 0) {
+ return;
+ }
+
+ if (!m_active) {
+ log::debug("SMC: command {} selected without active submenu");
+ return;
+ }
+
+ const auto realCmd = cmd - QCM_FIRST;
+
+ log::debug("SMC: invoking {} on {}", realCmd, m_active->name);
+ m_active->menu.invoke(pos, realCmd);
+}
+
+bool ShellMenuCollection::wndProc(
+ HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out)
+{
+ if (m == WM_MENUSELECT) {
+ auto* oldActive = m_active;
+ m_active = nullptr;
- TrackPopupMenuEx(menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr);
+ HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect);
+
+ if (!m_active && oldActive) {
+ // this was not a top level, forward to active
+ m_active = oldActive;
+ } else if (m_active && m_active == oldActive) {
+ // same top level menu was selected twice, ignore
+ return true;
+ } else if (m_active && m_active != oldActive) {
+ // new top level selected
+ log::debug("SMC: switching to {}", m_active->name);
+ }
+ }
+
+ if (!m_active) {
+ // no active menu, forward it to the default handler
+ return false;
+ }
+
+ return m_active->menu.wndProc(h, m, wp, lp, out);
+}
+
+void ShellMenuCollection::onMenuSelect(
+ HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
+{
+ for (auto&& m : m_menus) {
+ if (m.menu.getMenu() == hmenuPopup) {
+ m_active = &m;
+ break;
+ }
+ }
}
} // namespace
diff --git a/src/envshell.h b/src/envshell.h
index 79dce552..52125a5c 100644
--- a/src/envshell.h
+++ b/src/envshell.h
@@ -11,7 +11,7 @@ namespace env
class ShellMenu
{
public:
- ShellMenu() = default;
+ ShellMenu(QMainWindow* mw);
// noncopyable
ShellMenu(const ShellMenu&) = delete;
@@ -21,35 +21,44 @@ public:
void addFile(QFileInfo fi);
- void exec(QWidget* parent, const QPoint& pos);
+ void exec(const QPoint& pos);
HMENU getMenu();
+ bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out);
+ void invoke(const QPoint& p, int cmd);
private:
+ QMainWindow* m_mw;
std::vector<QFileInfo> m_files;
COMPtr<IContextMenu> m_cm;
+ COMPtr<IContextMenu2> m_cm2;
+ COMPtr<IContextMenu3> m_cm3;
HMenuPtr m_menu;
- void createMenu();
+ void create();
+
+ std::vector<LPCITEMIDLIST> createIdls(const std::vector<QFileInfo>& files);
+ COMPtr<IShellItemArray> createItemArray(std::vector<LPCITEMIDLIST>& idls);
+
+ void createContextMenu(IShellItemArray* array);
+ void createPopupMenu(IContextMenu* cm);
COMPtr<IShellItem> createShellItem(const std::wstring& path);
COMPtr<IPersistIDList> getPersistIDList(IShellItem* item);
CoTaskMemPtr<LPITEMIDLIST> getIDList(IPersistIDList* pidlist);
- std::vector<LPCITEMIDLIST> createIdls(const std::vector<QFileInfo>& files);
- COMPtr<IShellItemArray> createItemArray(std::vector<LPCITEMIDLIST>& idls);
- COMPtr<IContextMenu> createContextMenu(IShellItemArray* array);
- HMenuPtr createMenu(IContextMenu* cm);
HMenuPtr createDummyMenu(const QString& what);
- int runMenu(QMainWindow* mw, IContextMenu* cm, HMENU menu, const QPoint& p);
- void invoke(QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm);
+ void onMenuSelect(
+ HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags);
};
class ShellMenuCollection
{
public:
+ ShellMenuCollection(QMainWindow* mw);
+
void add(QString name, ShellMenu m);
- void exec(QWidget* parent, const QPoint& pos);
+ void exec(const QPoint& pos);
private:
struct MenuInfo
@@ -58,7 +67,14 @@ private:
ShellMenu menu;
};
+ QMainWindow* m_mw;
std::vector<MenuInfo> m_menus;
+ MenuInfo* m_active;
+
+ bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out);
+
+ void onMenuSelect(
+ HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags);
};
} // namespace
diff --git a/src/filetree.cpp b/src/filetree.cpp
index 543be82b..4e3db1f7 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -466,8 +466,25 @@ void FileTree::onContextMenu(const QPoint &pos)
menu.exec(m_tree->viewport()->mapToGlobal(pos));
}
+QMainWindow* getMainWindow(QWidget* w)
+{
+ QWidget* p = w;
+
+ while (p) {
+ if (auto* mw=dynamic_cast<QMainWindow*>(p)) {
+ return mw;
+ }
+
+ p = p->parentWidget();
+ }
+
+ return nullptr;
+}
+
void FileTree::showShellMenu(QPoint pos)
{
+ auto* mw = getMainWindow(m_tree);
+
// menus by origin
std::map<int, env::ShellMenu> menus;
@@ -477,7 +494,12 @@ void FileTree::showShellMenu(QPoint pos)
continue;
}
- menus[item->originID()].addFile(item->realPath());
+ auto itor = menus.find(item->originID());
+ if (itor == menus.end()) {
+ itor = menus.emplace(item->originID(), mw).first;
+ }
+
+ itor->second.addFile(item->realPath());
if (item->isConflicted()) {
const auto file = m_core.directoryStructure()->searchFile(
@@ -499,35 +521,21 @@ void FileTree::showShellMenu(QPoint pos)
}
for (auto&& alt : alts) {
- auto* dir = file->getParent();
- if (!dir) {
- log::error(
- "file {} from origin {} has no parent",
- item->dataRelativeFilePath(), alt.first);
-
- continue;
+ auto itor = menus.find(alt.first);
+ if (itor == menus.end()) {
+ itor = menus.emplace(alt.first, mw).first;
}
- const auto* origin = dir->findOriginByID(alt.first);
- if (!origin) {
+ const auto fullPath = file->getFullPath(alt.first);
+ if (fullPath.empty()) {
log::error(
- "origin {} for file {} cannot be found",
- alt.first, item->dataRelativeFilePath());
-
- continue;
- }
-
- const auto originFile = origin->findFile(file->getIndex());
- if (!originFile) {
- log::error(
- "file {} not found in origin {} ({})",
- item->dataRelativeFilePath(), origin->getName(), file->getIndex());
+ "file {} not found in origin {}",
+ item->dataRelativeFilePath(), alt.first);
continue;
}
- menus[alt.first].addFile(
- QString::fromStdWString(originFile->getFullPath()));
+ itor->second.addFile(QString::fromStdWString(fullPath));
}
}
}
@@ -538,9 +546,9 @@ void FileTree::showShellMenu(QPoint pos)
}
else if (menus.size() == 1) {
auto& menu = menus.begin()->second;
- menu.exec(m_tree, m_tree->viewport()->mapToGlobal(pos));
+ menu.exec(m_tree->viewport()->mapToGlobal(pos));
} else {
- env::ShellMenuCollection mc;
+ env::ShellMenuCollection mc(mw);
for (auto&& m : menus) {
const auto* origin = m_core.directoryStructure()->findOriginByID(m.first);
@@ -552,7 +560,7 @@ void FileTree::showShellMenu(QPoint pos)
mc.add(QString::fromStdWString(origin->getName()), std::move(m.second));
}
- mc.exec(m_tree, m_tree->viewport()->mapToGlobal(pos));
+ mc.exec(m_tree->viewport()->mapToGlobal(pos));
}
}
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 460431a4..4181098c 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -354,12 +354,20 @@ bool FileEntry::isFromArchive(std::wstring archiveName) const
return false;
}
-std::wstring FileEntry::getFullPath() const
+std::wstring FileEntry::getFullPath(int originID) const
{
- bool ignore = false;
+ if (originID == -1) {
+ bool ignore = false;
+ originID = getOrigin(ignore);
+ }
// base directory for origin
- std::wstring result = m_Parent->getOriginByID(getOrigin(ignore)).getPath();
+ const auto* o = m_Parent->findOriginByID(originID);
+ if (!o) {
+ return {};
+ }
+
+ std::wstring result = o->getPath();
// all intermediate directories
recurseParents(result, m_Parent);
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 69eb7574..6102c88f 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -127,7 +127,12 @@ public:
}
bool isFromArchive(std::wstring archiveName = L"") const;
- std::wstring getFullPath() const;
+
+ // if originID is -1, uses the main origin; if this file doesn't exist in the
+ // given origin, returns an empty string
+ //
+ std::wstring getFullPath(int originID=-1) const;
+
std::wstring getRelativePath() const;
DirectoryEntry *getParent()