From ed4eb2aae8fcf493c07a02cbca3df14f00c5e130 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 9 May 2019 12:04:03 -0500 Subject: Update NXM link handling to support premium link and validate user info --- src/downloadmanager.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/downloadmanager.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 25542ef3..78162f11 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -628,6 +628,7 @@ void DownloadManager::addNXMDownload(const QString &url) info->nexusKey = nxmInfo.key(); info->nexusExpires = nxmInfo.expires(); + info->nexusDownloadUser = nxmInfo.userId(); QObject *test = info; m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.game(), nxmInfo.modId(), nxmInfo.fileId(), this, qVariantFromValue(test), "")); -- cgit v1.3.1 From 2d58ad346c9e8840d3864080d62999e686962a76 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 23 May 2019 22:24:57 -0500 Subject: Add support for new Nexus API game codes to NXM link handling --- src/downloadmanager.cpp | 19 +- src/organizer_en.ts | 962 ++++++++++++++++++++++++------------------------ 2 files changed, 505 insertions(+), 476 deletions(-) (limited to 'src/downloadmanager.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 78162f11..d4a6c37f 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -549,10 +549,21 @@ void DownloadManager::addNXMDownload(const QString &url) NXMUrl nxmInfo(url); QStringList validGames; + MOBase::IPluginGame* foundGame = nullptr; validGames.append(m_ManagedGame->gameShortName()); validGames.append(m_ManagedGame->validShortNames()); + for (auto game : validGames) { + MOBase::IPluginGame* gamePlugin = m_OrganizerCore->getGame(game); + if ( + nxmInfo.game().compare(gamePlugin->gameShortName(), Qt::CaseInsensitive) == 0 || + nxmInfo.game().compare(gamePlugin->gameNexusName(), Qt::CaseInsensitive) == 0 + ) { + foundGame = gamePlugin; + break; + } + } qDebug("add nxm download: %s", qUtf8Printable(url)); - if (!validGames.contains(nxmInfo.game(), Qt::CaseInsensitive)) { + if (foundGame == nullptr) { qDebug("download requested for wrong game (game: %s, url: %s)", qUtf8Printable(m_ManagedGame->gameShortName()), qUtf8Printable(nxmInfo.game())); QMessageBox::information(nullptr, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO " "has been set up for \"%2\".").arg(nxmInfo.game()).arg(m_ManagedGame->gameShortName()), QMessageBox::Ok); @@ -560,7 +571,7 @@ void DownloadManager::addNXMDownload(const QString &url) } for (auto tuple : m_PendingDownloads) { - if (std::get<0>(tuple).compare(nxmInfo.game(), Qt::CaseInsensitive) == 0, std::get<1>(tuple) == nxmInfo.modId() && std::get<2>(tuple) == nxmInfo.fileId()) { + if (std::get<0>(tuple).compare(foundGame->gameShortName(), Qt::CaseInsensitive) == 0, std::get<1>(tuple) == nxmInfo.modId() && std::get<2>(tuple) == nxmInfo.fileId()) { QString debugStr("download requested is already queued (mod: %1, file: %2)"); QString infoStr(tr("There is already a download queued for this file.\n\nMod %1\nFile %2")); @@ -620,7 +631,7 @@ void DownloadManager::addNXMDownload(const QString &url) emit aboutToUpdate(); - m_PendingDownloads.append(std::make_tuple(nxmInfo.game(), nxmInfo.modId(), nxmInfo.fileId())); + m_PendingDownloads.append(std::make_tuple(foundGame->gameShortName(), nxmInfo.modId(), nxmInfo.fileId())); emit update(-1); emit downloadAdded(); @@ -631,7 +642,7 @@ void DownloadManager::addNXMDownload(const QString &url) info->nexusDownloadUser = nxmInfo.userId(); QObject *test = info; - m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.game(), nxmInfo.modId(), nxmInfo.fileId(), this, qVariantFromValue(test), "")); + m_RequestIDs.insert(m_NexusInterface->requestFileInfo(foundGame->gameShortName(), nxmInfo.modId(), nxmInfo.fileId(), this, qVariantFromValue(test), "")); } diff --git a/src/organizer_en.ts b/src/organizer_en.ts index f38044de..24a58c81 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -323,93 +323,108 @@ p, li { white-space: pre-wrap; } - Size + Mod name - Status + Version + Nexus ID + + + + + Size + + + + + Status + + + + Filetime - + < game %1 mod %2 file %3 > - + Unknown - + Pending - + Started - + Canceling - + Pausing - + Canceled - + Paused - + Error - - - + + + Fetching Info - + Downloaded - + Installed - + Uninstalled - + Pending download - + Information missing, please select "Query Info" from the context menu to re-retrieve. @@ -417,145 +432,145 @@ p, li { white-space: pre-wrap; } DownloadListWidget - + Install - + Query Info - + Visit on Nexus - + Open File - - - + + + Show in Folder - - + + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Resume - + Delete Installed... - + Delete Uninstalled... - + Delete All... - + Hide Installed... - + Hide Uninstalled... - + Hide All... - + Un-Hide All... - - + + Delete Files? - + This will permanently delete the selected download. - + This will remove all finished downloads from this list and from disk. - + This will remove all installed downloads from this list and from disk. - + This will remove all uninstalled downloads from this list and from disk. - + Are you sure? - + This will remove all finished downloads from this list (but NOT from disk). - + This will remove all installed downloads from this list (but NOT from disk). - + This will remove all uninstalled downloads from this list (but NOT from disk). @@ -588,17 +603,17 @@ p, li { white-space: pre-wrap; } - + Wrong Game - + The download link is for a mod for "%1" but this instance of MO has been set up for "%2". - + There is already a download queued for this file. Mod %1 @@ -606,12 +621,12 @@ File %2 - + Already Queued - + There is already a download started for this file. Mod %1: %2 @@ -619,266 +634,266 @@ File %3: %4 - + Already Started - - + + remove: invalid download index %1 - + failed to delete %1 - + failed to delete meta file for %1 - + restore: invalid download index: %1 - + cancel: invalid download index %1 - + pause: invalid download index %1 - + resume: invalid download index %1 - + resume (int): invalid download index %1 - + No known download urls. Sorry, this download can't be resumed. - - + + query: invalid download index %1 - + Please enter the nexus mod id - + Mod ID: - + Please select the source game code for %1 - + VisitNexus: invalid download index %1 - + Nexus ID for this Mod is unknown - + OpenFile: invalid download index %1 - + OpenFileInDownloadsFolder: invalid download index %1 - + get pending: invalid download index %1 - + get path: invalid download index %1 - + Main - + Update - + Optional - + Old - + Miscellaneous - + Deleted - + Unknown - + display name: invalid download index %1 - + file name: invalid download index %1 - + file time: invalid download index %1 - + file size: invalid download index %1 - + progress: invalid download index %1 - + state: invalid download index %1 - + infocomplete: invalid download index %1 - - + + mod id: invalid download index %1 - + ishidden: invalid download index %1 - + file info: invalid download index %1 - + mark installed: invalid download index %1 - + mark uninstalled: invalid download index %1 - + Memory allocation error (in processing progress event). - + Memory allocation error (in processing downloaded data). - + Information updated - - + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? - + No file on Nexus matches the selected file by name. Please manually choose the correct one. - + No download server available. Please try again later. - + Failed to request file info from nexus: %1 - + Warning: Content type is: %1 - + Download header content length: %1 downloaded file size: %2 - + Download failed: %1 (%2) - + We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers. - + failed to re-open %1 - + Unable to write download to drive (return %1). Check the drive's available storage. @@ -1568,7 +1583,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -1759,8 +1774,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1955,7 +1970,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1993,7 +2008,7 @@ p, li { white-space: pre-wrap; } - + Endorse Mod Organizer @@ -2061,8 +2076,8 @@ Error: %1 - - + + Endorse @@ -2182,733 +2197,733 @@ Error: %1 - + Notice: Your current MO version (%1) is lower than the previously used one (%2). The GUI may not downgrade gracefully, so you may experience oddities. However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Mod Backup> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - - + + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + Failed to create backup. - + You need to be logged in with Nexus to resume a download - - - - + + + + You need to be logged in with Nexus to endorse - - + + Endorsing multiple mods will take a while. Please wait... - - + + Unendorsing multiple mods will take a while. Please wait... - - - + + + You need to be logged in with Nexus to track - + Failed to display overwrite dialog: %1 - + Opening Nexus Links - + You are trying to open %1 links to Nexus Mods. Are you sure you want to do this? - + Nexus ID for this Mod is unknown - + Opening Web Pages - + You are trying to open %1 Web Pages. Are you sure you want to do this? - + Web page for this mod is unknown - + <table cellspacing="5"><tr><th>Type</th><th>All</th><th>Visible</th><tr><td>Enabled mods:&emsp;</td><td align=right>%1 / %2</td><td align=right>%3 / %4</td></tr><tr><td>Unmanaged/DLCs:&emsp;</td><td align=right>%5</td><td align=right>%6</td></tr><tr><td>Mod backups:&emsp;</td><td align=right>%7</td><td align=right>%8</td></tr><tr><td>Separators:&emsp;</td><td align=right>%9</td><td align=right>%10</td></tr></table> - + <table cellspacing="6"><tr><th>Type</th><th>Active </th><th>Total</th></tr><tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr><tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr><tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr><tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr><tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr></table> - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + Create Separator... - + This will create a new separator. Please enter a name: - + A separator with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Move successful. - - + + Are you sure? - + About to recursively delete: - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Notes_column - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open INIs folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check for updates - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Move content to Mod... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - + Select Color... - + Reset Color - + Change versioning scheme - + Force-check updates - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - + Won't endorse - + Endorsement state unknown - + Start tracking - + Stop tracking - + Tracked state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2916,12 +2931,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2929,22 +2944,22 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Restarting MO - + Changing the managed game directory requires restarting MO. Any pending downloads will be paused. @@ -2952,373 +2967,373 @@ Click OK to restart MO now. - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - - + + Set Priority - + Set the priority of the selected plugins - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Abstain from Endorsing Mod Organizer - + Are you sure you want to abstain from endorsing Mod Organizer 2? You will have to visit the mod page on the %1 Nexus site to change your mind. - - + + Thank you for endorsing MO2! :) - - + + Please reconsider endorsing MO2 on Nexus! - + Thank you! - + Thank you for your endorsement! - + Okay. - + This mod will not be endorsed and will no longer ask you to endorse. - + Mod ID %1 no longer seems to be available on Nexus. - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + Open Origin in Explorer - + Open Origin Info... - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -3581,63 +3596,64 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + The following conflicted files are provided by this mod - - + + + File - + Overwritten Mods - + The following conflicted files are provided by other mods - + Providing Mod - - Non-Conflicted files + + The following files have no conflicts - + Categories - + Primary Category - + Nexus Info - + Mod ID - + Mod ID for this mod on Nexus. - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -3646,22 +3662,22 @@ p, li { white-space: pre-wrap; } - + Source Game - + Source game for this mod. - + <html><head/><body><p>Source game for this mod. This determines where the mod was downloaded from and decides where to fetch info, version updates, and send endorsements. Changing this will likely require you to enter a new Mod ID.</p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -3670,76 +3686,76 @@ p, li { white-space: pre-wrap; } - + Version - + Refresh - + Refresh all information from Nexus. - + Description - + about:blank - + Endorse - + Web page URL (only used if invalid NexusID) : - + Notes - - - + + + Enter comments about the mod here. These are displayed in the notes column of the mod list. - - - + + + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. - + Filetree - + Open Mod in Explorer - + A directory view of this mod - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -3749,258 +3765,260 @@ p, li { white-space: pre-wrap; } - + Previous - + Next - + Close - + &Delete - + &Rename - + &Hide - + &Unhide - + &Open - + &New Folder - - + + Save changes? - - + + Save changes to "%1"? - + File Exists - + A file with that name exists, please enter a new one - + failed to move file - + failed to create directory "optional" - + Info requested, please wait - + Main - + Update - + Optional - + Old - + Miscellaneous - + Deleted - + Unknown - + Current Version: %1 - + No update available - + <div style="text-align: center;"><h1>Uh oh!</h1><p>Sorry, there is no description available for this mod. :(</p></div> - + <a href="%1">Visit on Nexus</a> - + Failed to delete %1 - - - - + + + + Confirm - - + + Are sure you want to delete "%1"? - - + + Are sure you want to delete the selected files? - - + + New Folder - + Failed to create "%1" - + Select binary - + Binary - + file not found: %1 - + failed to generate preview for %1 - + Sorry - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Hide - + Un-Hide - - + + + Open/Execute - - + + + Preview - + Name - + Please enter a name - - + + Error - + Invalid name. Must be a valid file name - + A tweak by that name exists - + Create Tweak @@ -5780,7 +5798,7 @@ If the folder was still in use, restart MO and try again. - + <Manage...> -- cgit v1.3.1 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/downloadmanager.cpp | 25 ++++++++++++------------- src/modinfodialog.cpp | 2 +- src/organizercore.cpp | 44 +++++++++++++++++++++++++++++++++++++++----- src/organizercore.h | 2 +- src/overwriteinfodialog.cpp | 3 ++- 5 files changed, 55 insertions(+), 21 deletions(-) (limited to 'src/downloadmanager.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 78162f11..686092b5 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1037,7 +1037,7 @@ void DownloadManager::openFile(int index) return; } - ::ShellExecuteW(nullptr, L"explore", ToWString(QDir::toNativeSeparators(m_OutputDirectory)).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ExploreFile(m_OutputDirectory); return; } @@ -1047,23 +1047,22 @@ void DownloadManager::openInDownloadsFolder(int index) reportError(tr("OpenFileInDownloadsFolder: invalid download index %1").arg(index)); return; } - QString params = "/select,\""; - QDir path = QDir(m_OutputDirectory); - if (path.exists(getFileName(index))) { - params = params + QDir::toNativeSeparators(getFilePath(index)) + "\""; - ::ShellExecuteW(nullptr, nullptr, L"explorer", ToWString(params).c_str(), nullptr, SW_SHOWNORMAL); - return; - } - else if (path.exists(getFileName(index) + ".unfinished")) { - params = params + QDir::toNativeSeparators(getFilePath(index)+".unfinished") + "\""; + const auto path = getFilePath(index); - ::ShellExecuteW(nullptr, nullptr, L"explorer", ToWString(params).c_str(), nullptr, SW_SHOWNORMAL); + if (QFile::exists(path)) { + ExploreFile(path); return; } + else { + const auto unfinished = path + ".unfinished"; + if (QFile::exists(unfinished)) { + ExploreFile(unfinished); + return; + } + } - ::ShellExecuteW(nullptr, L"explore", ToWString(QDir::toNativeSeparators(m_OutputDirectory)).c_str(), nullptr, nullptr, SW_SHOWNORMAL); - return; + ExploreFile(m_OutputDirectory); } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 83c0169a..5fb3e9cf 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -1242,7 +1242,7 @@ bool ModInfoDialog::recursiveDelete(const QModelIndex &index) void ModInfoDialog::on_openInExplorerButton_clicked() { - ::ShellExecuteW(nullptr, L"explore", ToWString(m_ModInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ExploreFile(m_ModInfo->absolutePath()); } void ModInfoDialog::deleteFile(const QModelIndex &index) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2542545f..d228dfe1 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -330,25 +330,59 @@ bool GetFileExecutionContext( } } -bool ExploreFile(const QString& path) + +bool ExploreDirectory(const QFileInfo& info) { - const auto ws = path.toStdWString(); + const auto path = QDir::toNativeSeparators(info.absoluteFilePath()); + const auto ws_path = path.toStdWString(); const auto h = ::ShellExecuteW( - nullptr, L"explore", ws.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + nullptr, L"explore", ws_path.c_str(), nullptr, nullptr, SW_SHOWNORMAL); // anything <= 32 is not an actual HINSTANCE and signals failure return (h > reinterpret_cast(32)); } +bool ExploreFileInDirectory(const QFileInfo& info) +{ + const auto path = QDir::toNativeSeparators(info.absoluteFilePath()); + 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)); +} + + bool ExploreFile(const QFileInfo& info) { - return ExploreFile(info.absolutePath()); + if (info.isFile()) { + return ExploreFileInDirectory(info); + } else if (info.isDir()) { + return ExploreDirectory(info); + } else { + // try the parent directory + const auto parent = info.dir(); + + if (parent.exists()) { + return ExploreDirectory(parent.absolutePath()); + } + } + + return false; +} + +bool ExploreFile(const QString& path) +{ + return ExploreFile(QFileInfo(path)); } bool ExploreFile(const QDir& dir) { - return ExploreFile(dir.absolutePath()); + return ExploreFile(QFileInfo(dir.absolutePath())); } diff --git a/src/organizercore.h b/src/organizercore.h index 103443eb..3f773277 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -67,8 +67,8 @@ bool GetFileExecutionContext( QWidget* parent, const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments, FileExecutionTypes& type); -bool ExploreFile(const QString& path); bool ExploreFile(const QFileInfo& info); +bool ExploreFile(const QString& path); bool ExploreFile(const QDir& dir); 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/downloadmanager.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/downloadmanager.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