From ebb855dc1421e89813a1f4a74a4963d76aba9747 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 May 2019 03:10:37 -0400 Subject: ExploreFile() will select the file in explorer when the path is a file replaced more ShellExecute() calls with ExploreFile() --- src/overwriteinfodialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/overwriteinfodialog.cpp') diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index 3d82cf17..e9d7ce06 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "ui_overwriteinfodialog.h" #include "report.h" #include "utility.h" +#include "organizercore.h" #include #include #include @@ -263,7 +264,7 @@ void OverwriteInfoDialog::createDirectoryTriggered() void OverwriteInfoDialog::on_explorerButton_clicked() { - ::ShellExecuteW(nullptr, L"explore", ToWString(m_ModInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ExploreFile(m_ModInfo->absolutePath()); } void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos) -- cgit v1.3.1 From 76708b9694070bcaa5775a097ae73ffc163ca31b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 May 2019 08:34:52 -0400 Subject: put explore and open functions in namespace shell previewDataFile() was duplicated in both MainWindow and ModInfoDialog, moved to OrganizerCore added preview menu item to filetree better logging when shell operations fail --- src/downloadmanager.cpp | 8 +- src/mainwindow.cpp | 98 ++++--------------- src/modinfodialog.cpp | 133 ++++++++++---------------- src/modinfodialog.h | 12 ++- src/organizercore.cpp | 228 +++++++++++++++++++++++++++++++++++++++++--- src/organizercore.h | 15 ++- src/overwriteinfodialog.cpp | 2 +- 7 files changed, 311 insertions(+), 185 deletions(-) (limited to 'src/overwriteinfodialog.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 686092b5..1412df51 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1037,7 +1037,7 @@ void DownloadManager::openFile(int index) return; } - ExploreFile(m_OutputDirectory); + shell::ExploreFile(m_OutputDirectory); return; } @@ -1051,18 +1051,18 @@ void DownloadManager::openInDownloadsFolder(int index) const auto path = getFilePath(index); if (QFile::exists(path)) { - ExploreFile(path); + shell::ExploreFile(path); return; } else { const auto unfinished = path + ".unfinished"; if (QFile::exists(unfinished)) { - ExploreFile(unfinished); + shell::ExploreFile(unfinished); return; } } - ExploreFile(m_OutputDirectory); + shell::ExploreFile(m_OutputDirectory); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ce6620c0..94cbe9b1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3241,12 +3241,12 @@ void MainWindow::openExplorer_clicked() if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - ExploreFile(info->absolutePath()); + shell::ExploreFile(info->absolutePath()); } } else { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - ExploreFile(modInfo->absolutePath()); + shell::ExploreFile(modInfo->absolutePath()); } } @@ -3261,14 +3261,14 @@ void MainWindow::openOriginExplorer_clicked() continue; } ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - ExploreFile(modInfo->absolutePath()); + shell::ExploreFile(modInfo->absolutePath()); } } else { QModelIndex idx = selection->currentIndex(); QString fileName = idx.data().toString(); ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); - ExploreFile(modInfo->absolutePath()); + shell::ExploreFile(modInfo->absolutePath()); } } @@ -3283,7 +3283,7 @@ void MainWindow::openExplorer_activated() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - ExploreFile(modInfo->absolutePath()); + shell::ExploreFile(modInfo->absolutePath()); } } @@ -3304,7 +3304,7 @@ void MainWindow::openExplorer_activated() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - ExploreFile(modInfo->absolutePath()); + shell::ExploreFile(modInfo->absolutePath()); } } } @@ -4271,61 +4271,61 @@ void MainWindow::disableVisibleMods() void MainWindow::openInstanceFolder() { QString dataPath = qApp->property("dataPath").toString(); - ExploreFile(dataPath); + shell::ExploreFile(dataPath); } void MainWindow::openLogsFolder() { QString logsPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()); - ExploreFile(logsPath); + shell::ExploreFile(logsPath); } void MainWindow::openInstallFolder() { - ExploreFile(qApp->applicationDirPath()); + shell::ExploreFile(qApp->applicationDirPath()); } void MainWindow::openPluginsFolder() { QString pluginsPath = QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()); - ExploreFile(pluginsPath); + shell::ExploreFile(pluginsPath); } void MainWindow::openProfileFolder() { - ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); + shell::ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); } void MainWindow::openIniFolder() { if (m_OrganizerCore.currentProfile()->localSettingsEnabled()) { - ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); + shell::ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); } else { - ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); + shell::ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); } } void MainWindow::openDownloadsFolder() { - ExploreFile(m_OrganizerCore.settings().getDownloadDirectory()); + shell::ExploreFile(m_OrganizerCore.settings().getDownloadDirectory()); } void MainWindow::openModsFolder() { - ExploreFile(m_OrganizerCore.settings().getModDirectory()); + shell::ExploreFile(m_OrganizerCore.settings().getModDirectory()); } void MainWindow::openGameFolder() { - ExploreFile(m_OrganizerCore.managedGame()->gameDirectory()); + shell::ExploreFile(m_OrganizerCore.managedGame()->gameDirectory()); } void MainWindow::openMyGamesFolder() { - ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); + shell::ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); } @@ -5304,67 +5304,7 @@ void MainWindow::disableSelectedMods_clicked() void MainWindow::previewDataFile() { QString fileName = QDir::fromNativeSeparators(m_ContextItem->data(0, Qt::UserRole).toString()); - - // what we have is an absolute path to the file in its actual location (for the primary origin) - // what we want is the path relative to the virtual data directory - - // we need to look in the virtual directory for the file to make sure the info is up to date. - - // check if the file comes from the actual data folder instead of a mod - QDir gameDirectory = m_OrganizerCore.managedGame()->dataDirectory().absolutePath(); - QString relativePath = gameDirectory.relativeFilePath(fileName); - QDir dirRelativePath = gameDirectory.relativeFilePath(fileName); - // if the file is on a different drive the dirRelativePath will actually be an absolute path so we make sure that is not the case - if (!dirRelativePath.isAbsolute() && !relativePath.startsWith("..")) { - fileName = relativePath; - } - else { - // crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory - int offset = m_OrganizerCore.settings().getModDirectory().size() + 1; - offset = fileName.indexOf("/", offset); - fileName = fileName.mid(offset + 1); - } - - - - const FileEntry::Ptr file = m_OrganizerCore.directoryStructure()->searchFile(ToWString(fileName), nullptr); - - if (file.get() == nullptr) { - reportError(tr("file not found: %1").arg(qUtf8Printable(fileName))); - return; - } - - // set up preview dialog - PreviewDialog preview(fileName); - auto addFunc = [&] (int originId) { - FilesOrigin &origin = m_OrganizerCore.directoryStructure()->getOriginByID(originId); - QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; - if (QFile::exists(filePath)) { - // it's very possible the file doesn't exist, because it's inside an archive. we don't support that - QWidget *wid = m_PluginContainer.previewGenerator().genPreview(filePath); - if (wid == nullptr) { - reportError(tr("failed to generate preview for %1").arg(filePath)); - } else { - preview.addVariant(ToQString(origin.getName()), wid); - } - } - }; - - addFunc(file->getOrigin()); - for (auto alt : file->getAlternatives()) { - addFunc(alt.first); - } - if (preview.numVariants() > 0) { - QSettings &settings = m_OrganizerCore.settings().directInterface(); - QString key = QString("geometry/%1").arg(preview.objectName()); - if (settings.contains(key)) { - preview.restoreGeometry(settings.value(key).toByteArray()); - } - preview.exec(); - settings.setValue(key, preview.saveGeometry()); - } else { - QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas.")); - } + m_OrganizerCore.previewFileWithAlternatives(this, fileName); } void MainWindow::openDataFile() @@ -5374,7 +5314,7 @@ void MainWindow::openDataFile() } QFileInfo targetInfo(m_ContextItem->data(0, Qt::UserRole).toString()); - m_OrganizerCore.executeFile(this, targetInfo); + m_OrganizerCore.executeFileVirtualized(this, targetInfo); } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 9bd36f7e..c3ef7c47 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -322,9 +322,9 @@ bool ExpanderWidget::opened() const ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), m_ThumbnailMapper(this), m_RequestStarted(false), - m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_RenameAction(nullptr), - m_DeleteAction(nullptr), m_HideAction(nullptr), m_UnhideAction(nullptr), - m_Directory(directory), m_Origin(nullptr), + m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_PreviewAction(nullptr), + m_RenameAction(nullptr), m_DeleteAction(nullptr), m_HideAction(nullptr), + m_UnhideAction(nullptr), m_Directory(directory), m_Origin(nullptr), m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) { ui->setupUi(this); @@ -483,16 +483,18 @@ void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo) m_NewFolderAction = new QAction(tr("&New Folder"), ui->fileTree); m_OpenAction = new QAction(tr("&Open"), ui->fileTree); + m_PreviewAction = new QAction(tr("&Preview"), ui->fileTree); m_RenameAction = new QAction(tr("&Rename"), ui->fileTree); m_DeleteAction = new QAction(tr("&Delete"), ui->fileTree); m_HideAction = new QAction(tr("&Hide"), ui->fileTree); m_UnhideAction = new QAction(tr("&Unhide"), ui->fileTree); - QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); - QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); - QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); - QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); - QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); + connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); + connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); + connect(m_PreviewAction, SIGNAL(triggered()), this, SLOT(previewTriggered())); + connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); + connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); + connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); } @@ -1244,12 +1246,11 @@ bool ModInfoDialog::recursiveDelete(const QModelIndex &index) void ModInfoDialog::on_openInExplorerButton_clicked() { - ExploreFile(m_ModInfo->absolutePath()); + shell::ExploreFile(m_ModInfo->absolutePath()); } void ModInfoDialog::deleteFile(const QModelIndex &index) { - bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) : m_FileSystemModel->remove(index); if (!res) { @@ -1406,21 +1407,29 @@ void ModInfoDialog::changeFiletreeVisibility(bool hide) } -void ModInfoDialog::openFile(const QModelIndex &index) +void ModInfoDialog::openTriggered() { - QString fileName = m_FileSystemModel->filePath(index); + if (m_FileSelection.size() == 1) { + const auto index = m_FileSelection.at(0); + if (!index.isValid()) { + return; + } - HINSTANCE res = ::ShellExecuteW(nullptr, L"open", ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); - if ((unsigned long long)res <= 32) { - qCritical("failed to invoke %s: %d", qUtf8Printable(fileName), res); + QString fileName = m_FileSystemModel->filePath(index); + shell::OpenFile(fileName); } } - -void ModInfoDialog::openTriggered() +void ModInfoDialog::previewTriggered() { - foreach(QModelIndex idx, m_FileSelection) { - openFile(idx); + if (m_FileSelection.size() == 1) { + const auto index = m_FileSelection.at(0); + if (!index.isValid()) { + return; + } + + QString fileName = m_FileSystemModel->filePath(index); + m_OrganizerCore->previewFile(this, m_ModInfo->name(), fileName); } } @@ -1464,6 +1473,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) if (selectionModel->hasSelection()) { bool enableOpen = true; + bool enablePreview = true; bool enableRename = true; bool enableDelete = true; bool enableHide = true; @@ -1484,10 +1494,15 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) if (!hasFiles) { enableOpen = false; + enablePreview = false; } const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); + if (!canPreviewFile(false, fileName)) { + enablePreview = false; + } + if (!canHideFile(false, fileName)) { enableHide = false; } @@ -1499,6 +1514,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) // this is a multiple selection, don't show open action so users don't open // a thousand files enableOpen = false; + enablePreview = false; enableRename = false; if (m_FileSelection.size() < max_scan_for_visibility) { @@ -1530,6 +1546,10 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) menu.addAction(m_OpenAction); } + if (enablePreview) { + menu.addAction(m_PreviewAction); + } + if (enableRename) { menu.addAction(m_RenameAction); } @@ -1755,7 +1775,7 @@ void ModInfoDialog::openDataFile(const QTreeWidgetItem* item) } QFileInfo targetInfo(item->data(0, Qt::UserRole).toString()); - m_OrganizerCore->executeFile(this, targetInfo); + m_OrganizerCore->executeFileVirtualized(this, targetInfo); } void ModInfoDialog::previewDataFile(const QTreeWidgetItem* item) @@ -1765,63 +1785,17 @@ void ModInfoDialog::previewDataFile(const QTreeWidgetItem* item) } QString fileName = QDir::fromNativeSeparators(item->data(0, Qt::UserRole).toString()); + m_OrganizerCore->previewFileWithAlternatives(this, fileName); +} - // what we have is an absolute path to the file in its actual location (for the primary origin) - // what we want is the path relative to the virtual data directory - - // we need to look in the virtual directory for the file to make sure the info is up to date. - - // check if the file comes from the actual data folder instead of a mod - QDir gameDirectory = m_OrganizerCore->managedGame()->dataDirectory().absolutePath(); - QString relativePath = gameDirectory.relativeFilePath(fileName); - QDir direRelativePath = gameDirectory.relativeFilePath(fileName); - // if the file is on a different drive the dirRelativePath will actually be an absolute path so we make sure that is not the case - if (!direRelativePath.isAbsolute() && !relativePath.startsWith("..")) { - fileName = relativePath; - } - else { - // crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory - int offset = m_OrganizerCore->settings().getModDirectory().size() + 1; - offset = fileName.indexOf("/", offset); - fileName = fileName.mid(offset + 1); - } - - - - const FileEntry::Ptr file = m_OrganizerCore->directoryStructure()->searchFile(ToWString(fileName), nullptr); - - if (file.get() == nullptr) { - reportError(tr("file not found: %1").arg(qUtf8Printable(fileName))); - return; - } - - // set up preview dialog - PreviewDialog preview(fileName); - auto addFunc = [&](int originId) { - FilesOrigin &origin = m_OrganizerCore->directoryStructure()->getOriginByID(originId); - QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; - if (QFile::exists(filePath)) { - // it's very possible the file doesn't exist, because it's inside an archive. we don't support that - QWidget *wid = m_PluginContainer->previewGenerator().genPreview(filePath); - if (wid == nullptr) { - reportError(tr("failed to generate preview for %1").arg(filePath)); - } - else { - preview.addVariant(ToQString(origin.getName()), wid); - } - } - }; +bool ModInfoDialog::canPreviewFile(bool isArchive, const QString& filename) const +{ + if (isArchive) { + return false; + } - addFunc(file->getOrigin()); - for (auto alt : file->getAlternatives()) { - addFunc(alt.first); - } - if (preview.numVariants() > 0) { - preview.exec(); - } - else { - QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas.")); - } + const auto ext = QFileInfo(filename).suffix(); + return m_PluginContainer->previewGenerator().previewSupported(ext); } bool ModInfoDialog::canHideFile(bool isArchive, const QString& filename) const @@ -1866,12 +1840,9 @@ bool ModInfoDialog::canUnhideConflictItem(const QTreeWidgetItem* item) const bool ModInfoDialog::canPreviewConflictItem(const QTreeWidgetItem* item) const { - const QString fileName = item->data(0, Qt::UserRole).toString(); - if (!m_PluginContainer->previewGenerator().previewSupported(QFileInfo(fileName).suffix())) { - return false; - } - - return true; + return canPreviewFile( + item->data(1, Qt::UserRole + 2).toBool(), + item->data(0, Qt::UserRole).toString()); } void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &pos) diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 6bf30a52..c0730afa 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -313,7 +313,6 @@ private: QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); - void openFile(const QModelIndex &index); void saveIniTweaks(); void saveCategories(QTreeWidgetItem *currentNode); void saveCurrentTextFile(); @@ -348,10 +347,11 @@ private slots: void delete_activated(); - void deleteTriggered(); - void renameTriggered(); - void openTriggered(); void createDirectoryTriggered(); + void openTriggered(); + void previewTriggered(); + void renameTriggered(); + void deleteTriggered(); void hideTriggered(); void unhideTriggered(); @@ -416,6 +416,7 @@ private: QAction *m_NewFolderAction; QAction *m_OpenAction; + QAction *m_PreviewAction; QAction *m_RenameAction; QAction *m_DeleteAction; QAction *m_HideAction; @@ -439,11 +440,12 @@ private: bool canUnhideConflictItem(const QTreeWidgetItem* item) const; bool canPreviewConflictItem(const QTreeWidgetItem* item) const; - void previewDataFile(const QTreeWidgetItem* item); void openDataFile(const QTreeWidgetItem* item); + void previewDataFile(const QTreeWidgetItem* item); void changeConflictFilesVisibility(bool hide); void changeFiletreeVisibility(bool hide); + bool canPreviewFile(bool isArchive, const QString& filename) const; bool canHideFile(bool isArchive, const QString& filename) const; bool canUnhideFile(bool isArchive, const QString& filename) const; }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index d228dfe1..3a6b810e 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -35,6 +35,7 @@ #include "instancemanager.h" #include #include "helper.h" +#include "previewdialog.h" #include #include @@ -331,16 +332,103 @@ bool GetFileExecutionContext( } -bool ExploreDirectory(const QFileInfo& info) +const char* ShellExecuteError(int i) { - const auto path = QDir::toNativeSeparators(info.absoluteFilePath()); - const auto ws_path = path.toStdWString(); + switch (i) { + case 0: + return "The operating system is out of memory or resources"; + + case ERROR_FILE_NOT_FOUND: + return "The specified file was not found"; + + case ERROR_PATH_NOT_FOUND: + return "The specified path was not found"; + + case ERROR_BAD_FORMAT: + return "The .exe file is invalid (non-Win32 .exe or error in .exe image)"; + + case SE_ERR_ACCESSDENIED: + return "The operating system denied access to the specified file"; + + case SE_ERR_ASSOCINCOMPLETE: + return "The file name association is incomplete or invalid"; + + case SE_ERR_DDEBUSY: + return "The DDE transaction could not be completed because other DDE " + "transactions were being processed"; + + case SE_ERR_DDEFAIL: + return "The DDE transaction failed"; + + case SE_ERR_DDETIMEOUT: + return "The DDE transaction could not be completed because the request " + "timed out"; + + case SE_ERR_DLLNOTFOUND: + return "The specified DLL was not found"; + + case SE_ERR_NOASSOC: + return "There is no application associated with the given file name " + "extension"; + + case SE_ERR_OOM: + return "There was not enough memory to complete the operation"; + + case SE_ERR_SHARE: + return "A sharing violation occurred"; + + default: + return "Unknown error"; + } +} + +void LogShellFailure( + const wchar_t* operation, const wchar_t* file, const wchar_t* params, + HINSTANCE h) +{ + const auto code = static_cast(reinterpret_cast(h)); + + QString s = "failed to invoke"; + + if (operation) { + s += " " + QString::fromWCharArray(operation); + } + + if (file) { + s += " " + QString::fromWCharArray(file); + } + if (params) { + s += " " + QString::fromWCharArray(params); + } + + qCritical( + "failed to invoke %s: %s (error %d)", + s, ShellExecuteError(code), code); +} + +bool ShellExecuteWrapper( + const wchar_t* operation, const wchar_t* file, const wchar_t* params) +{ const auto h = ::ShellExecuteW( - nullptr, L"explore", ws_path.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + 0, operation, file, params, nullptr, SW_SHOWNORMAL); // anything <= 32 is not an actual HINSTANCE and signals failure - return (h > reinterpret_cast(32)); + if (h <= reinterpret_cast(32)) + { + LogShellFailure(operation, file, params, h); + return false; + } + + return true; +} + +bool ExploreDirectory(const QFileInfo& info) +{ + const auto path = QDir::toNativeSeparators(info.absoluteFilePath()); + const auto ws_path = path.toStdWString(); + + return ShellExecuteWrapper(L"explore", ws_path.c_str(), nullptr); } bool ExploreFileInDirectory(const QFileInfo& info) @@ -349,14 +437,13 @@ bool ExploreFileInDirectory(const QFileInfo& info) const auto params = "/select,\"" + path + "\""; const auto ws_params = params.toStdWString(); - const auto h = ::ShellExecuteW( - nullptr, nullptr, L"explorer", ws_params.c_str(), nullptr, SW_SHOWNORMAL); - - // anything <= 32 is not an actual HINSTANCE and signals failure - return (h > reinterpret_cast(32)); + return ShellExecuteWrapper(nullptr, L"explorer", ws_params.c_str()); } +namespace shell +{ + bool ExploreFile(const QFileInfo& info) { if (info.isFile()) { @@ -385,6 +472,14 @@ bool ExploreFile(const QDir& dir) return ExploreFile(QFileInfo(dir.absolutePath())); } +bool OpenFile(const QString& path) +{ + const auto ws_path = path.toStdWString(); + return ShellExecuteWrapper(L"open", ws_path.c_str(), nullptr); +} + +} // namespace shell + OrganizerCore::OrganizerCore(const QSettings &initSettings) : m_UserInterface(nullptr) @@ -1339,7 +1434,8 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const return res; } -bool OrganizerCore::executeFile(QWidget* parent, const QFileInfo& targetInfo) +bool OrganizerCore::executeFileVirtualized( + QWidget* parent, const QFileInfo& targetInfo) { QFileInfo binaryInfo; QString arguments; @@ -1372,6 +1468,116 @@ bool OrganizerCore::executeFile(QWidget* parent, const QFileInfo& targetInfo) return false; } +bool OrganizerCore::previewFileWithAlternatives( + QWidget* parent, QString fileName) +{ + // what we have is an absolute path to the file in its actual location (for the primary origin) + // what we want is the path relative to the virtual data directory + + // we need to look in the virtual directory for the file to make sure the info is up to date. + + // check if the file comes from the actual data folder instead of a mod + QDir gameDirectory = managedGame()->dataDirectory().absolutePath(); + QString relativePath = gameDirectory.relativeFilePath(fileName); + QDir dirRelativePath = gameDirectory.relativeFilePath(fileName); + + // if the file is on a different drive the dirRelativePath will actually be an + // absolute path so we make sure that is not the case + if (!dirRelativePath.isAbsolute() && !relativePath.startsWith("..")) { + fileName = relativePath; + } + else { + // crude: we search for the next slash after the base mod directory to skip + // everything up to the data-relative directory + int offset = settings().getModDirectory().size() + 1; + offset = fileName.indexOf("/", offset); + fileName = fileName.mid(offset + 1); + } + + + + const FileEntry::Ptr file = directoryStructure()->searchFile(ToWString(fileName), nullptr); + + if (file.get() == nullptr) { + reportError(tr("file not found: %1").arg(qUtf8Printable(fileName))); + return false; + } + + // set up preview dialog + PreviewDialog preview(fileName); + auto addFunc = [&](int originId) { + FilesOrigin &origin = directoryStructure()->getOriginByID(originId); + QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; + if (QFile::exists(filePath)) { + // it's very possible the file doesn't exist, because it's inside an archive. we don't support that + QWidget *wid = m_PluginContainer->previewGenerator().genPreview(filePath); + if (wid == nullptr) { + reportError(tr("failed to generate preview for %1").arg(filePath)); + } + else { + preview.addVariant(ToQString(origin.getName()), wid); + } + } + }; + + addFunc(file->getOrigin()); + for (auto alt : file->getAlternatives()) { + addFunc(alt.first); + } + + if (preview.numVariants() > 0) { + QSettings &s = settings().directInterface(); + QString key = QString("geometry/%1").arg(preview.objectName()); + if (s.contains(key)) { + preview.restoreGeometry(s.value(key).toByteArray()); + } + + preview.exec(); + + s.setValue(key, preview.saveGeometry()); + + return true; + } + else { + QMessageBox::information( + parent, tr("Sorry"), + tr("Sorry, can't preview anything. This function currently does not support extracting from bsas.")); + + return false; + } +} + +bool OrganizerCore::previewFile( + QWidget* parent, const QString& originName, const QString& path) +{ + if (!QFile::exists(path)) { + reportError(tr("File '%1' not found.").arg(path)); + return false; + } + + PreviewDialog preview(path); + + QWidget *wid = m_PluginContainer->previewGenerator().genPreview(path); + if (wid == nullptr) { + reportError(tr("Failed to generate preview for %1").arg(path)); + return false; + } + + preview.addVariant(originName, wid); + + QSettings &s = settings().directInterface(); + QString key = QString("geometry/%1").arg(preview.objectName()); + if (s.contains(key)) { + preview.restoreGeometry(s.value(key).toByteArray()); + } + + preview.exec(); + + s.setValue(key, preview.saveGeometry()); + + return true; +} + void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, diff --git a/src/organizercore.h b/src/organizercore.h index 3f773277..41f0fd46 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -67,9 +67,14 @@ bool GetFileExecutionContext( QWidget* parent, const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments, FileExecutionTypes& type); -bool ExploreFile(const QFileInfo& info); -bool ExploreFile(const QString& path); -bool ExploreFile(const QDir& dir); +namespace shell +{ + bool ExploreFile(const QFileInfo& info); + bool ExploreFile(const QString& path); + bool ExploreFile(const QDir& dir); + + bool OpenFile(const QString& path); +} class OrganizerCore : public QObject, public MOBase::IPluginDiagnose @@ -156,7 +161,9 @@ public: void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } - bool executeFile(QWidget* parent, const QFileInfo& targetInfo); + bool executeFileVirtualized(QWidget* parent, const QFileInfo& targetInfo); + bool previewFileWithAlternatives(QWidget* parent, QString filename); + bool previewFile(QWidget* parent, const QString& originName, const QString& path); void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir ¤tDirectory = QDir(), diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index e9d7ce06..d6764852 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -264,7 +264,7 @@ void OverwriteInfoDialog::createDirectoryTriggered() void OverwriteInfoDialog::on_explorerButton_clicked() { - ExploreFile(m_ModInfo->absolutePath()); + shell::ExploreFile(m_ModInfo->absolutePath()); } void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos) -- cgit v1.3.1 From c82c7af678c088a6b94fc8a4a0f31fc9498e8220 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 26 May 2019 04:00:54 -0400 Subject: changed rest of ShellExecuteW() calls to use shell::Execute(), shell::OpenLink() or shell::OpenFile() --- src/downloadmanager.cpp | 4 ++-- src/modinfodialog.cpp | 3 +-- src/motddialog.cpp | 3 ++- src/organizercore.cpp | 14 ++++++++++++++ src/organizercore.h | 3 +++ src/overwriteinfodialog.cpp | 7 +------ src/problemsdialog.cpp | 3 ++- src/selfupdater.cpp | 19 ++++++------------- src/settings.cpp | 23 ++++++++++++----------- 9 files changed, 43 insertions(+), 36 deletions(-) (limited to 'src/overwriteinfodialog.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 1412df51..ecc3cfd6 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1030,10 +1030,10 @@ void DownloadManager::openFile(int index) reportError(tr("OpenFile: invalid download index %1").arg(index)); return; } + QDir path = QDir(m_OutputDirectory); if (path.exists(getFileName(index))) { - - ::ShellExecuteW(nullptr, L"open", ToWString(QDir::toNativeSeparators(getFilePath(index))).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + shell::OpenFile(getFilePath(index)); return; } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c3ef7c47..19a03ea7 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -1075,10 +1075,9 @@ void ModInfoDialog::linkClicked(const QUrl &url) //Ideally we'd ask the mod for the game and the web service then pass the game //and URL to the web service if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { - emit linkActivated(url.toString()); } else { - ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + shell::OpenLink(url); } } diff --git a/src/motddialog.cpp b/src/motddialog.cpp index 96d88542..9be41d96 100644 --- a/src/motddialog.cpp +++ b/src/motddialog.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "bbcode.h" #include "utility.h" #include "ui_motddialog.h" +#include "organizercore.h" #include MotDDialog::MotDDialog(const QString &message, QWidget *parent) @@ -43,5 +44,5 @@ void MotDDialog::on_okButton_clicked() void MotDDialog::linkClicked(const QUrl &url) { - ::ShellExecuteW(nullptr, L"open", MOBase::ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + shell::OpenLink(url); } diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 3a6b810e..a10c23d7 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -478,6 +478,20 @@ bool OpenFile(const QString& path) return ShellExecuteWrapper(L"open", ws_path.c_str(), nullptr); } +bool OpenLink(const QUrl& url) +{ + const auto ws_url = url.toString().toStdWString(); + return ShellExecuteWrapper(L"open", ws_url.c_str(), nullptr); +} + +bool Execute(const QString& program, const QString& params) +{ + const auto program_ws = program.toStdWString(); + const auto params_ws = params.toStdWString(); + + return ShellExecuteWrapper(L"open", program_ws.c_str(), params_ws.c_str()); +} + } // namespace shell diff --git a/src/organizercore.h b/src/organizercore.h index 41f0fd46..c9434e92 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -74,6 +74,9 @@ namespace shell bool ExploreFile(const QDir& dir); bool OpenFile(const QString& path); + bool OpenLink(const QUrl& url); + + bool Execute(const QString& program, const QString& params); } diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index d6764852..9b3e55df 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -218,12 +218,7 @@ void OverwriteInfoDialog::renameTriggered() void OverwriteInfoDialog::openFile(const QModelIndex &index) { - QString fileName = m_FileSystemModel->filePath(index); - - HINSTANCE res = ::ShellExecuteW(nullptr, L"open", ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); - if ((INT_PTR)res <= 32) { - qCritical("failed to invoke %s: %d", qUtf8Printable(fileName), res); - } + shell::OpenFile(m_FileSystemModel->filePath(index)); } diff --git a/src/problemsdialog.cpp b/src/problemsdialog.cpp index 795baab0..56109d34 100644 --- a/src/problemsdialog.cpp +++ b/src/problemsdialog.cpp @@ -1,5 +1,6 @@ #include "problemsdialog.h" #include "ui_problemsdialog.h" +#include "organizercore.h" #include #include #include @@ -87,5 +88,5 @@ void ProblemsDialog::startFix() void ProblemsDialog::urlClicked(const QUrl &url) { - ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + shell::OpenLink(url); } diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 4c0f9a8d..271c621b 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -31,6 +31,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "bbcode.h" #include "plugincontainer.h" +#include "organizercore.h" #include #include #include @@ -178,7 +179,7 @@ void SelfUpdater::startUpdate() tr("New update available (%1)") .arg(m_UpdateCandidate["tag_name"].toString()), tr("Do you want to install update? All your mods and setup will be left untouched.\nSelect Show Details option to see the full change-log."), QMessageBox::Yes | QMessageBox::Cancel, m_Parent); - + query.setDetailedText(m_UpdateCandidate["body"].toString()); query.button(QMessageBox::Yes)->setText(tr("Install")); @@ -329,22 +330,14 @@ void SelfUpdater::downloadCancel() void SelfUpdater::installUpdate() { - const QString mopath - = QDir::fromNativeSeparators(qApp->property("dataPath").toString()); - - std::wstring parameters = ToWString("/DIR=\"" + qApp->applicationDirPath() + "\" "); + const QString parameters = "/DIR=\"" + qApp->applicationDirPath() + "\" "; - HINSTANCE res = ::ShellExecuteW( - nullptr, L"open", m_UpdateFile.fileName().toStdWString().c_str(), parameters.c_str(), - nullptr, SW_SHOW); - - if (res > (HINSTANCE)32) { + if (shell::Execute(m_UpdateFile.fileName(), parameters)) { QCoreApplication::quit(); } else { - reportError(tr("Failed to start %1: %2") - .arg(m_UpdateFile.fileName()) - .arg((INT_PTR)res)); + reportError(tr("Failed to start %1").arg(m_UpdateFile.fileName())); } + m_UpdateFile.remove(); } diff --git a/src/settings.cpp b/src/settings.cpp index 231487bb..a844fdc9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -130,18 +130,19 @@ bool Settings::pluginBlacklisted(const QString &fileName) const void Settings::registerAsNXMHandler(bool force) { - std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); - std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); - std::wstring mode = force ? L"forcereg" : L"reg"; - std::wstring parameters = mode + L" " + m_GamePlugin->gameShortName().toStdWString(); - for (QString altGame : m_GamePlugin->validShortNames()) { - parameters += L"," + altGame.toStdWString(); + const auto nxmPath = QCoreApplication::applicationDirPath() + "/nxmhandler.exe"; + const auto executable = QCoreApplication::applicationFilePath(); + + QString mode = force ? "forcereg" : "reg"; + QString parameters = mode + " " + m_GamePlugin->gameShortName(); + for (const QString& altGame : m_GamePlugin->validShortNames()) { + parameters += "," + altGame; } - parameters += L" \"" + executable + L"\""; - HINSTANCE res = ::ShellExecuteW(nullptr, L"open", nxmPath.c_str(), parameters.c_str(), nullptr, SW_SHOWNORMAL); - if ((INT_PTR)res <= 32) { - QMessageBox::critical(nullptr, tr("Failed"), - tr("Sorry, failed to start the helper application")); + parameters += " \"" + executable + "\""; + + if (!shell::Execute(nxmPath, parameters)) { + QMessageBox::critical( + nullptr, tr("Failed"), tr("Failed to start the helper application")); } } -- cgit v1.3.1