diff options
| -rw-r--r-- | src/envshell.cpp | 164 | ||||
| -rw-r--r-- | src/envshell.h | 6 | ||||
| -rw-r--r-- | src/filetree.cpp | 30 | ||||
| -rw-r--r-- | src/filetree.h | 3 | ||||
| -rw-r--r-- | src/mainwindow.ui | 3 | ||||
| -rw-r--r-- | src/shared/directoryentry.h | 2 |
6 files changed, 153 insertions, 55 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 diff --git a/src/envshell.h b/src/envshell.h index f30495e0..3be53841 100644 --- a/src/envshell.h +++ b/src/envshell.h @@ -7,7 +7,11 @@ namespace env { -void showShellMenu(QWidget* parent, const QFileInfo& file, const QPoint& pos); +void showShellMenu( + QWidget* parent, const QFileInfo& file, const QPoint& pos); + +void showShellMenu( + QWidget* parent, const std::vector<QFileInfo>& files, const QPoint& pos); } diff --git a/src/filetree.cpp b/src/filetree.cpp index 3c99ab05..d7dbc6e6 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -442,18 +442,8 @@ void FileTree::onContextMenu(const QPoint &pos) const auto m = QApplication::keyboardModifiers(); if (m & Qt::ShiftModifier) { - if (auto* item=singleSelection()) { - if (!item->isDirectory()) { - const auto file = m_core.directoryStructure()->searchFile( - item->dataRelativeFilePath().toStdWString(), nullptr); - - if (file) { - const QFileInfo fi(QString::fromStdWString(file->getFullPath())); - env::showShellMenu(m_tree, fi, m_tree->viewport()->mapToGlobal(pos)); - return; - } - } - } + showShellMenu(pos); + return; } QMenu menu; @@ -476,6 +466,22 @@ void FileTree::onContextMenu(const QPoint &pos) menu.exec(m_tree->viewport()->mapToGlobal(pos)); } +void FileTree::showShellMenu(QPoint pos) +{ + std::vector<QFileInfo> files; + + for (auto&& index : m_tree->selectionModel()->selectedRows()) { + auto* item = m_model->itemFromIndex(index); + if (!item) { + continue; + } + + files.push_back(item->realPath()); + } + + env::showShellMenu(m_tree, files, m_tree->viewport()->mapToGlobal(pos)); +} + void FileTree::addDirectoryMenus(QMenu&, FileTreeItem&) { // noop diff --git a/src/filetree.h b/src/filetree.h index 40b5b2ff..39c9d0c6 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -48,9 +48,10 @@ private: FileTreeModel* m_model; FileTreeItem* singleSelection(); + void onExpandedChanged(const QModelIndex& index, bool expanded); void onContextMenu(const QPoint &pos); - void showShellMenu(const MOShared::FileEntry& file, QPoint pos); + void showShellMenu(QPoint pos); void addDirectoryMenus(QMenu& menu, FileTreeItem& item); void addFileMenus(QMenu& menu, const MOShared::FileEntry& file, int originID); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index b27122ef..da9f949c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1085,6 +1085,9 @@ p, li { white-space: pre-wrap; } <property name="alternatingRowColors"> <bool>true</bool> </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> <property name="uniformRowHeights"> <bool>true</bool> </property> diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index f1d3ba03..b5a1dced 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -416,7 +416,7 @@ public: // path containing the file
//
const FileEntry::Ptr searchFile(
- const std::wstring &path, const DirectoryEntry **directory) const;
+ const std::wstring &path, const DirectoryEntry **directory=nullptr) const;
void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime);
|
