summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/envshell.cpp164
-rw-r--r--src/envshell.h6
-rw-r--r--src/filetree.cpp30
-rw-r--r--src/filetree.h3
-rw-r--r--src/mainwindow.ui3
-rw-r--r--src/shared/directoryentry.h2
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);