diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-06 18:50:28 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-06 18:50:28 -0400 |
| commit | 9166bd9c5bf02484bd7486374e508ca304111f5a (patch) | |
| tree | 5103f120c7c2635004a523bf3001a1edfee09263 /src | |
| parent | dfd34bf9c0ad055e76c1b6f272c21eb0d79536ce (diff) | |
added new Shortcut class, moved stuff that was in MainWindow into it
added more error handling and logging, some was missing
removed some redundancy of setting the menu icons for add/remove, they're all set when clicking the button anyway
Diffstat (limited to 'src')
| -rw-r--r-- | src/mainwindow.cpp | 175 | ||||
| -rw-r--r-- | src/mainwindow.h | 12 | ||||
| -rw-r--r-- | src/shared/util.cpp | 320 | ||||
| -rw-r--r-- | src/shared/util.h | 96 |
4 files changed, 441 insertions, 162 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7681b482..f9e4138e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -220,6 +220,9 @@ MainWindow::MainWindow(QSettings &initSettings , m_PluginContainer(pluginContainer) , m_DidUpdateMasterList(false) , m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this)) + , m_LinkToolbar(nullptr) + , m_LinkDesktop(nullptr) + , m_LinkStartMenu(nullptr) { QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); QWebEngineProfile::defaultProfile()->setHttpCacheMaximumSize(52428800); @@ -335,9 +338,9 @@ MainWindow::MainWindow(QSettings &initSettings resizeLists(modListAdjusted, pluginListAdjusted); QMenu *linkMenu = new QMenu(this); - linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar and Menu"), this, SLOT(linkToolbar())); - linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop())); - linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu())); + m_LinkToolbar = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar and Menu"), this, SLOT(linkToolbar())); + m_LinkDesktop = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop())); + m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu())); ui->linkButton->setMenu(linkMenu); QMenu *listOptionsMenu = new QMenu(ui->listOptionsBtn); @@ -2397,88 +2400,6 @@ void MainWindow::on_startButton_clicked() { ui->startButton->setEnabled(true); } -static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments, - LPCSTR linkFileName, LPCWSTR description, - LPCTSTR iconFileName, int iconNumber, - LPCWSTR currentDirectory) -{ - HRESULT result = E_INVALIDARG; - if ((targetFileName != nullptr) && (wcslen(targetFileName) > 0) && - (arguments != nullptr) && - (linkFileName != nullptr) && (strlen(linkFileName) > 0) && - (description != nullptr) && - (currentDirectory != nullptr)) { - - IShellLink* shellLink; - result = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_IShellLink, (LPVOID*)&shellLink); - - if (!SUCCEEDED(result)) { - qCritical("failed to create IShellLink instance"); - return result; - } - - result = shellLink->SetPath(targetFileName); - if (!SUCCEEDED(result)) { - qCritical("failed to set target path %ls", targetFileName); - shellLink->Release(); - return result; - } - - result = shellLink->SetArguments(arguments); - if (!SUCCEEDED(result)) { - qCritical("failed to set arguments: %ls", arguments); - shellLink->Release(); - return result; - } - - if (wcslen(description) > 0) { - result = shellLink->SetDescription(description); - if (!SUCCEEDED(result)) { - qCritical("failed to set description: %ls", description); - shellLink->Release(); - return result; - } - } - - if (wcslen(currentDirectory) > 0) { - result = shellLink->SetWorkingDirectory(currentDirectory); - if (!SUCCEEDED(result)) { - qCritical("failed to set working directory: %ls", currentDirectory); - shellLink->Release(); - return result; - } - } - - if (iconFileName != nullptr) { - result = shellLink->SetIconLocation(iconFileName, iconNumber); - if (!SUCCEEDED(result)) { - qCritical("failed to load program icon: %ls %d", iconFileName, iconNumber); - shellLink->Release(); - return result; - } - } - - IPersistFile *persistFile; - result = shellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile); - if (SUCCEEDED(result)) { - wchar_t linkFileNameW[MAX_PATH]; - if (MultiByteToWideChar(CP_ACP, 0, linkFileName, -1, linkFileNameW, MAX_PATH) > 0) { - result = persistFile->Save(linkFileNameW, TRUE); - } else { - qCritical("failed to create link: %s", linkFileName); - } - persistFile->Release(); - } else { - qCritical("failed to create IPersistFile instance"); - } - - shellLink->Release(); - } - return result; -} - - bool MainWindow::modifyExecutablesDialog() { bool result = false; @@ -5144,74 +5065,39 @@ void MainWindow::on_savegameList_customContextMenuRequested(const QPoint &pos) void MainWindow::linkToolbar() { - Executable &exe(getSelectedExecutable()); + Executable& exe = getSelectedExecutable(); + exe.setShownOnToolbar(!exe.isShownOnToolbar()); - ui->linkButton->menu()->actions().at(static_cast<int>(ShortcutType::Toolbar))->setIcon(exe.isShownOnToolbar() ? QIcon(":/MO/gui/remove") : QIcon(":/MO/gui/link")); updatePinnedExecutables(); } -namespace { -QString getLinkfile(const QString &dir, const Executable &exec) +void MainWindow::linkDesktop() { - return QDir::fromNativeSeparators(dir) + "/" + exec.title() + ".lnk"; + env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::Desktop); } -QString getDesktopLinkfile(const Executable &exec) +void MainWindow::linkMenu() { - return getLinkfile(getDesktopDirectory(), exec); + env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::StartMenu); } -QString getStartMenuLinkfile(const Executable &exec) +void MainWindow::on_linkButton_pressed() { - return getLinkfile(getStartMenuDirectory(), exec); -} -} + const Executable& exe = getSelectedExecutable(); -void MainWindow::addWindowsLink(const ShortcutType mapping) -{ - const Executable &selectedExecutable(getSelectedExecutable()); - QString const linkName = getLinkfile(mapping == ShortcutType::Desktop ? getDesktopDirectory() : getStartMenuDirectory(), - selectedExecutable); + const QIcon addIcon(":/MO/gui/link"); + const QIcon removeIcon(":/MO/gui/remove"); - if (QFile::exists(linkName)) { - if (QFile::remove(linkName)) { - ui->linkButton->menu()->actions().at(static_cast<int>(mapping))->setIcon(QIcon(":/MO/gui/link")); - } else { - reportError(tr("failed to remove %1").arg(linkName)); - } - } else { - QFileInfo const exeInfo(qApp->applicationFilePath()); - // create link - QString executable = QDir::toNativeSeparators(selectedExecutable.binaryInfo().absoluteFilePath()); + env::Shortcut shortcut(exe); - std::wstring targetFile = ToWString(exeInfo.absoluteFilePath()); - std::wstring parameter = ToWString( - QString("\"moshortcut://%1:%2\"").arg(InstanceManager::instance().currentInstance(),selectedExecutable.title())); - std::wstring description = ToWString(QString("Run %1 with ModOrganizer").arg(selectedExecutable.title())); - std::wstring iconFile = ToWString(executable); - std::wstring currentDirectory = ToWString(QDir::toNativeSeparators(qApp->applicationDirPath())); + m_LinkToolbar->setIcon( + exe.isShownOnToolbar() ? removeIcon : addIcon); - if (CreateShortcut(targetFile.c_str() - , parameter.c_str() - , QDir::toNativeSeparators(linkName).toUtf8().constData() - , description.c_str() - , (selectedExecutable.usesOwnIcon() ? iconFile.c_str() : nullptr), 0 - , currentDirectory.c_str()) == 0) { - ui->linkButton->menu()->actions().at(static_cast<int>(mapping))->setIcon(QIcon(":/MO/gui/remove")); - } else { - reportError(tr("failed to create %1").arg(linkName)); - } - } -} + m_LinkDesktop->setIcon( + shortcut.exists(env::Shortcut::Desktop) ? removeIcon : addIcon); -void MainWindow::linkDesktop() -{ - addWindowsLink(ShortcutType::Desktop); -} - -void MainWindow::linkMenu() -{ - addWindowsLink(ShortcutType::StartMenu); + m_LinkStartMenu->setIcon( + shortcut.exists(env::Shortcut::StartMenu) ? removeIcon : addIcon); } void MainWindow::on_actionSettings_triggered() @@ -6476,21 +6362,6 @@ Executable &MainWindow::getSelectedExecutable() return m_OrganizerCore.executablesList()->get(name); } -void MainWindow::on_linkButton_pressed() -{ - const Executable &selectedExecutable(getSelectedExecutable()); - - const QIcon addIcon(":/MO/gui/link"); - const QIcon removeIcon(":/MO/gui/remove"); - - const QFileInfo linkDesktopFile(getDesktopLinkfile(selectedExecutable)); - const QFileInfo linkMenuFile(getStartMenuLinkfile(selectedExecutable)); - - ui->linkButton->menu()->actions().at(static_cast<int>(ShortcutType::Toolbar))->setIcon(selectedExecutable.isShownOnToolbar() ? removeIcon : addIcon); - ui->linkButton->menu()->actions().at(static_cast<int>(ShortcutType::Desktop))->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon); - ui->linkButton->menu()->actions().at(static_cast<int>(ShortcutType::StartMenu))->setIcon(linkMenuFile.exists() ? removeIcon : addIcon); -} - void MainWindow::on_showHiddenBox_toggled(bool checked) { m_OrganizerCore.downloadManager()->setShowHidden(checked); diff --git a/src/mainwindow.h b/src/mainwindow.h index 00f15a2b..eee269cf 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -403,18 +403,14 @@ private: MOBase::DelayedFileWriter m_ArchiveListWriter; + QAction* m_LinkToolbar; + QAction* m_LinkDesktop; + QAction* m_LinkStartMenu; + // icon set by the stylesheet, used to remember its original appearance // when painting the count QIcon m_originalNotificationIcon; - enum class ShortcutType { - Toolbar, - Desktop, - StartMenu - }; - - void addWindowsLink(ShortcutType const); - Executable const &getSelectedExecutable() const; Executable &getSelectedExecutable(); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 072cee2d..2bf1dba6 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -20,6 +20,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "util.h"
#include "windows_error.h"
#include "error_report.h"
+#include "executableslist.h"
+#include "instancemanager.h"
#include <utility.h>
#include <sstream>
@@ -307,10 +309,324 @@ struct COMReleaser }
};
+
template <class T>
using COMPtr = std::unique_ptr<T, COMReleaser>;
+class ShellLinkException {};
+
+// just a wrapper around IShellLink operations that throws ShellLinkException
+// on errors
+//
+class ShellLinkWrapper
+{
+public:
+ ShellLinkWrapper()
+ {
+ m_link = createShellLink();
+ m_file = createPersistFile();
+ }
+
+ void setPath(const QString& s)
+ {
+ if (s.isEmpty()) {
+ critical() << "path cannot be empty";
+ throw ShellLinkException();
+ }
+
+ const auto r = m_link->SetPath(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set target path '%1'").arg(s));
+ }
+
+ void setArguments(const QString& s)
+ {
+ const auto r = m_link->SetArguments(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set arguments '%1'").arg(s));
+ }
+
+ void setDescription(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetDescription(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set description '%1'").arg(s));
+ }
+
+ void setIcon(const QString& file, int i)
+ {
+ if (file.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetIconLocation(file.toStdWString().c_str(), i);
+ throwOnFail(r, QString("failed to set icon '%1' @ %2").arg(file).arg(i));
+ }
+
+ void setWorkingDirectory(const QString& s)
+ {
+ if (s.isEmpty()) {
+ return;
+ }
+
+ const auto r = m_link->SetWorkingDirectory(s.toStdWString().c_str());
+ throwOnFail(r, QString("failed to set working directory '%1'").arg(s));
+ }
+
+ void save(const QString& path)
+ {
+ const auto r = m_file->Save(path.toStdWString().c_str(), TRUE);
+ throwOnFail(r, QString("failed to save link '%1'").arg(path));
+ }
+
+private:
+ COMPtr<IShellLink> m_link;
+ COMPtr<IPersistFile> m_file;
+
+ QDebug critical()
+ {
+ return qCritical().noquote().nospace() << "system shortcut: ";
+ }
+
+ void throwOnFail(HRESULT r, const QString& s)
+ {
+ if (FAILED(r)) {
+ critical() << s << ", " << formatSystemMessageQ(r);
+ throw ShellLinkException();
+ }
+ }
+
+ COMPtr<IShellLink> createShellLink()
+ {
+ void* link = nullptr;
+
+ const auto r = CoCreateInstance(
+ CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLink, &link);
+
+ throwOnFail(r, "failed to create IShellLink instance");
+
+ if (!link) {
+ critical() << "creating IShellLink worked, but pointer is null";
+ throw ShellLinkException();
+ }
+
+ return COMPtr<IShellLink>(static_cast<IShellLink*>(link));
+ }
+
+ COMPtr<IPersistFile> createPersistFile()
+ {
+ void* file = nullptr;
+
+ const auto r = m_link->QueryInterface(IID_IPersistFile, &file);
+ throwOnFail(r, "failed to get IPersistFile interface");
+
+ if (!file) {
+ critical() << "querying IPersistFile worked, but pointer is null";
+ throw ShellLinkException();
+ }
+
+ return COMPtr<IPersistFile>(static_cast<IPersistFile*>(file));
+ }
+};
+
+
+Shortcut::Shortcut()
+ : m_iconIndex(0)
+{
+}
+
+Shortcut::Shortcut(const Executable& exe)
+ : Shortcut()
+{
+ m_name = exe.title();
+ m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
+
+ m_arguments = QString("\"moshortcut://%1:%2\"")
+ .arg(InstanceManager::instance().currentInstance())
+ .arg(exe.title());
+
+ m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
+
+ if (exe.usesOwnIcon()) {
+ m_icon = exe.binaryInfo().absoluteFilePath();
+ }
+
+ m_workingDirectory = qApp->applicationDirPath();
+}
+
+Shortcut& Shortcut::name(const QString& s)
+{
+ m_name = s;
+ return *this;
+}
+
+Shortcut& Shortcut::target(const QString& s)
+{
+ m_target = s;
+ return *this;
+}
+
+Shortcut& Shortcut::arguments(const QString& s)
+{
+ m_arguments = s;
+ return *this;
+}
+
+Shortcut& Shortcut::description(const QString& s)
+{
+ m_description = s;
+ return *this;
+}
+
+Shortcut& Shortcut::icon(const QString& s, int index)
+{
+ m_icon = s;
+ m_iconIndex = index;
+ return *this;
+}
+
+Shortcut& Shortcut::workingDirectory(const QString& s)
+{
+ m_workingDirectory = s;
+ return *this;
+}
+
+bool Shortcut::exists(Locations loc) const
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ return QFileInfo(path).exists();
+}
+
+bool Shortcut::toggle(Locations loc)
+{
+ if (exists(loc)) {
+ return remove(loc);
+ } else {
+ return add(loc);
+ }
+}
+
+bool Shortcut::add(Locations loc)
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ if (m_target.isEmpty()) {
+ qCritical() << "system shortcut: target is empty";
+ return false;
+ }
+
+ try
+ {
+ ShellLinkWrapper link;
+
+ link.setPath(m_target);
+ link.setArguments(m_arguments);
+ link.setDescription(m_description);
+ link.setIcon(m_icon, m_iconIndex);
+ link.setWorkingDirectory(m_workingDirectory);
+
+ link.save(path);
+
+ return true;
+ }
+ catch(ShellLinkException&)
+ {
+ }
+
+ return false;
+}
+
+bool Shortcut::remove(Locations loc)
+{
+ const auto path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ if (!QFile::exists(path)) {
+ qCritical().nospace().noquote()
+ << "system shortcut: can't remove '" << path << "', file not found";
+
+ return false;
+ }
+
+ if (!QFile::remove(path)) {
+ qCritical().nospace().noquote()
+ << "system shortcut: failed to remove '" << path << "'";
+
+ return false;
+ }
+
+ return true;
+}
+
+QString Shortcut::shortcutPath(Locations loc) const
+{
+ const auto dir = shortcutDirectory(loc);
+ if (dir.isEmpty()) {
+ return {};
+ }
+
+ const auto file = shortcutFilename();
+ if (file.isEmpty()) {
+ return {};
+ }
+
+ return dir + QDir::separator() + file;
+}
+
+QString Shortcut::shortcutDirectory(Locations loc) const
+{
+ QString dir;
+
+ try
+ {
+ switch (loc)
+ {
+ case Desktop:
+ dir = MOBase::getDesktopDirectory();
+ break;
+
+ case StartMenu:
+ dir = MOBase::getStartMenuDirectory();
+ break;
+
+ case None:
+ default:
+ qCritical() << "system shortcut: bad location " << loc;
+ return {};
+ }
+ }
+ catch(std::exception&)
+ {
+ return {};
+ }
+
+ return QDir::toNativeSeparators(dir);
+}
+
+QString Shortcut::shortcutFilename() const
+{
+ if (m_name.isEmpty()) {
+ qCritical() << "system shortcut: name is empty";
+ return {};
+ }
+
+ return m_name + ".lnk";
+}
+
+
+
class WMI
{
public:
@@ -674,7 +990,7 @@ std::optional<SecurityProduct> Environment::getWindowsFirewall() const if (FAILED(hr) || !rawPolicy) {
qCritical()
<< "CoCreateInstance for NetFwPolicy2 failed, "
- << formatSystemMessage(hr);
+ << formatSystemMessageQ(hr);
return {};
}
@@ -690,7 +1006,7 @@ std::optional<SecurityProduct> Environment::getWindowsFirewall() const {
qCritical()
<< "get_FirewallEnabled failed, "
- << formatSystemMessage(hr);
+ << formatSystemMessageQ(hr);
return {};
}
diff --git a/src/shared/util.h b/src/shared/util.h index 4df1d13c..a40fa201 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -29,6 +29,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <versioninfo.h>
+class Executable;
+
namespace MOShared {
/// Test if a file (or directory) by the specified name exists
@@ -52,6 +54,100 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); namespace env
{
+// an application shortcut that can be either on the desktop or the start menu
+//
+class Shortcut
+{
+public:
+ // location of a shortcut
+ //
+ enum Locations
+ {
+ None = 0,
+
+ // on the desktop
+ Desktop,
+
+ // in the start menu
+ StartMenu
+ };
+
+
+ // empty shortcut
+ //
+ Shortcut();
+
+ // shortcut from an executable
+ //
+ explicit Shortcut(const Executable& exe);
+
+ // sets the name of the shortcut, shown on icons and start menu entries
+ //
+ Shortcut& name(const QString& s);
+
+ // the program to start
+ //
+ Shortcut& target(const QString& s);
+
+ // arguments to pass
+ //
+ Shortcut& arguments(const QString& s);
+
+ // shows in the status bar of explorer, for example
+ //
+ Shortcut& description(const QString& s);
+
+ // path to a binary that contains the icon and its index
+ //
+ Shortcut& icon(const QString& s, int index=0);
+
+ // "start in" option for this shortcut
+ //
+ Shortcut& workingDirectory(const QString& s);
+
+
+ // returns whether this shortcut already exists at the given location; this
+ // does not check whether the shortcut parameters are different, it merely if
+ // the .lnk file exists
+ //
+ bool exists(Locations loc) const;
+
+ // calls remove() if exists(), or add()
+ //
+ bool toggle(Locations loc);
+
+ // adds the shortcut to the given location
+ //
+ bool add(Locations loc);
+
+ // removes the shortcut from the given location
+ //
+ bool remove(Locations loc);
+
+private:
+ QString m_name;
+ QString m_target;
+ QString m_arguments;
+ QString m_description;
+ QString m_icon;
+ int m_iconIndex;
+ QString m_workingDirectory;
+
+
+ // returns the path where the shortcut file should be saved
+ //
+ QString shortcutPath(Locations loc) const;
+
+ // returns the directory where the shortcut file should be saved
+ //
+ QString shortcutDirectory(Locations loc) const;
+
+ // returns the filename of the shortcut file that should be used when saving
+ //
+ QString shortcutFilename() const;
+};
+
+
// represents one module
//
class Module
|
