diff options
Diffstat (limited to 'src/envshell.cpp')
| -rw-r--r-- | src/envshell.cpp | 164 |
1 files changed, 124 insertions, 40 deletions
diff --git a/src/envshell.cpp b/src/envshell.cpp index ca392a9c..dcf337c5 100644 --- a/src/envshell.cpp +++ b/src/envshell.cpp @@ -24,6 +24,24 @@ public: }; +struct IdlsFreer +{ + const std::vector<LPCITEMIDLIST>& v; + + IdlsFreer(const std::vector<LPCITEMIDLIST>& v) + : v(v) + { + } + + ~IdlsFreer() + { + for (auto&& idl : v) { + ::CoTaskMemFree(const_cast<LPITEMIDLIST>(idl)); + } + } +}; + + class WndProcFilter : public QAbstractNativeEventFilter { public: @@ -191,48 +209,103 @@ private: -CoTaskMemPtr<LPITEMIDLIST> getIDL(const wchar_t* path) +QMainWindow* getMainWindow(QWidget* w) { - LPITEMIDLIST pidl; - SFGAOF sfgao; + QWidget* p = w; - const auto r = SHParseDisplayName(path, nullptr, &pidl, 0, &sfgao); + while (p) { + if (auto* mw=dynamic_cast<QMainWindow*>(p)) { + return mw; + } + + p = p->parentWidget(); + } + + return nullptr; +} + +COMPtr<IShellItem> 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, "SHParseDisplayName failed"); + throw MenuFailed(r, "SHCreateItemFromParsingName failed"); } - return CoTaskMemPtr<LPITEMIDLIST>(pidl); + return COMPtr<IShellItem>(item); } -std::pair<COMPtr<IShellFolder>, LPCITEMIDLIST> getShellFolder(LPITEMIDLIST idl) +COMPtr<IPersistIDList> getPersistIDList(IShellItem* item) { - IShellFolder* psf = nullptr; - LPCITEMIDLIST pidlChild = nullptr; + IPersistIDList* idl = nullptr; + auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl); + + if (FAILED(r)) { + throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed"); + } - const auto r = SHBindToParent( - idl, IID_IShellFolder, reinterpret_cast<void**>(&psf), &pidlChild); + return COMPtr<IPersistIDList>(idl); +} + +CoTaskMemPtr<LPITEMIDLIST> getIDList(IPersistIDList* pidlist) +{ + LPITEMIDLIST absIdl = nullptr; + auto r = pidlist->GetIDList(&absIdl); if (FAILED(r)) { - throw MenuFailed(r, "SHBindToParent failed"); + throw MenuFailed(r, "GetIDList failed"); } - return {COMPtr<IShellFolder>(psf), pidlChild}; + return CoTaskMemPtr<LPITEMIDLIST>(absIdl); } -COMPtr<IContextMenu> getContextMenu(IShellFolder* psf, LPCITEMIDLIST idl) +std::vector<LPCITEMIDLIST> createIdls( + const std::vector<QFileInfo>& files) { - IContextMenu* pcm = nullptr; + std::vector<LPCITEMIDLIST> idls; + + for (auto&& f : files) { + const auto path = QDir::toNativeSeparators(f.absoluteFilePath()).toStdWString(); - const auto r = psf->GetUIObjectOf( - 0, 1, &idl, IID_IContextMenu, nullptr, - reinterpret_cast<void**>(&pcm)); + auto item = createShellItem(path); + auto pidlist = getPersistIDList(item.get()); + auto absIdl = getIDList(pidlist.get()); + + idls.push_back(absIdl.release()); + } + + return idls; +} + +COMPtr<IShellItemArray> createItemArray( + std::vector<LPCITEMIDLIST>& idls) +{ + IShellItemArray* array = nullptr; + auto r = SHCreateShellItemArrayFromIDLists( + static_cast<UINT>(idls.size()), &idls[0], &array); + + if (FAILED(r)) { + throw MenuFailed(r, "SHCreateShellItemArrayFromIDLists failed"); + } + + return COMPtr<IShellItemArray>(array); +} + +COMPtr<IContextMenu> createContextMenu(IShellItemArray* array) +{ + IContextMenu* cm = nullptr; + + auto r = array->BindToHandler( + nullptr, BHID_SFUIObject, IID_IContextMenu, (void**)&cm); if (FAILED(r)) { - throw MenuFailed(r, "GetUIObjectOf failed"); + throw MenuFailed(r, "BindToHandler failed"); } - return COMPtr<IContextMenu>(pcm); + return COMPtr<IContextMenu>(cm); } HMenuPtr createMenu(IContextMenu* cm) @@ -296,31 +369,29 @@ void invoke(QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm) } } -QMainWindow* getMainWindow(QWidget* w) -{ - QWidget* p = w; - - while (p) { - if (auto* mw=dynamic_cast<QMainWindow*>(p)) { - return mw; - } - - p = p->parentWidget(); - } - return nullptr; -} - -void showShellMenu(QWidget* parent, const QFileInfo& file, const QPoint& pos) +void showShellMenu( + QWidget* parent, const std::vector<QFileInfo>& files, const QPoint& pos) { - const auto path = QDir::toNativeSeparators(file.absoluteFilePath()); + if (files.empty()) { + log::warn("showShellMenu(): no files given"); + return; + } try { auto* mw = getMainWindow(parent); - auto idl = getIDL(path.toStdWString().c_str()); - auto [sf, childIdl] = getShellFolder(idl.get()); - auto cm = getContextMenu(sf.get(), childIdl); + auto idls = createIdls(files); + + if (idls.empty()) { + log::error("no idls, can't create context menu"); + return; + } + + IdlsFreer freer(idls); + + auto array = createItemArray(idls); + auto cm = createContextMenu(array.get()); auto hmenu = createMenu(cm.get()); const int cmd = runMenu(mw, cm.get(), hmenu.get(), pos); @@ -332,8 +403,21 @@ void showShellMenu(QWidget* parent, const QFileInfo& file, const QPoint& pos) } catch(MenuFailed& e) { - log::error("can't create shell menu for '{}': {}", path, e.what()); + if (files.size() == 1) { + log::error( + "can't create shell menu for '{}': {}", + QDir::toNativeSeparators(files[0].absoluteFilePath()), e.what()); + } else { + log::error( + "can't create shell menu for {} files: {}", + files.size(), e.what()); + } } } +void showShellMenu(QWidget* parent, const QFileInfo& file, const QPoint& pos) +{ + showShellMenu(parent, std::vector{file}, pos); +} + } // namespace |
