From f6ef5477e718b14af99bd22436f66dee0b9d22cd Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 17 Jul 2014 22:02:15 +0200 Subject: normalized eol style (all files should now have windows line endings) --- src/nexusinterface.h | 644 +++++++++++++++++++++++++-------------------------- 1 file changed, 322 insertions(+), 322 deletions(-) (limited to 'src/nexusinterface.h') diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 5a46eebf..db61b0bf 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -1,322 +1,322 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef NEXUSINTERFACE_H -#define NEXUSINTERFACE_H - - -#include "nxmaccessmanager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class NexusInterface; - - -/** - * @brief convenience class to make nxm requests easier - * usually, all objects that started a nxm request will be signaled if one finished. - * Therefore, the objects need to store the id of the requests they started and then filter - * the result. - * NexusBridge does this automatically. Users connect to the signals of NexusBridge they intend - * to handle and only receive the signals the caused - **/ -class NexusBridge : public MOBase::IModRepositoryBridge -{ - - Q_OBJECT - -public: - - NexusBridge(const QString &subModule = ""); - - /** - * @brief request description for a mod - * - * @param modID id of the mod caller is interested in - * @param userData user data to be returned with the result - * @param url the url to request from - **/ - virtual void requestDescription(int modID, QVariant userData); - - /** - * @brief request a list of the files belonging to a mod - * - * @param modID id of the mod caller is interested in - * @param userData user data to be returned with the result - **/ - virtual void requestFiles(int modID, QVariant userData); - - /** - * @brief request info about a single file of a mod - * - * @param modID id of the mod caller is interested in - * @param fileID id of the file the caller is interested in - * @param userData user data to be returned with the result - **/ - virtual void requestFileInfo(int modID, int fileID, QVariant userData); - - /** - * @brief request the download url of a file - * - * @param modID id of the mod caller is interested in - * @param fileID id of the file the caller is interested in - * @param userData user data to be returned with the result - **/ - virtual void requestDownloadURL(int modID, int fileID, QVariant userData); - - /** - * @brief requestToggleEndorsement - * @param modID id of the mod caller is interested in - * @param userData user data to be returned with the result - */ - virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData); - -public slots: - - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); - -private: - - NexusInterface *m_Interface; - QString m_Url; - QString m_SubModule; - std::set m_RequestIDs; - -}; - - -/** - * @brief Makes asynchronous requests to the nexus API - * - * This class can be used to make asynchronous requests to the Nexus API. - * Currently, responses are sent to all receivers that have sent a request of the relevant type, so the - * recipient has to filter the response by the id returned when making the request - **/ -class NexusInterface : public QObject -{ - Q_OBJECT - -public: - - static NexusInterface *instance(); - - /** - * @return the access manager object used to connect to nexus - **/ - NXMAccessManager *getAccessManager(); - - /** - * @brief request description for a mod - * - * @param modID id of the mod caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - **/ - int requestDescription(int modID, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()), - int nexusGameId = -1); - - /** - * @brief request nexus descriptions for multiple mods at once - * @param modIDs a list of ids of mods the caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - */ - int requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); - - /** - * @brief request a list of the files belonging to a mod - * - * @param modID id of the mod caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - **/ - int requestFiles(int modID, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); - - /** - * @brief request info about a single file of a mod - * - * @param modID id of the mod caller is interested in - * @param fileID id of the file the caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - **/ - int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); - - /** - * @brief request the download url of a file - * - * @param modID id of the mod caller is interested in - * @param fileID id of the file the caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - **/ - int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); - - /** - * @brief toggle endorsement state of the mod - * @param modID id of the mod - * @param endorse true if the mod should be endorsed, false for un-endorse - * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) - * @param userData user data to be returned with the result - * @param url the url to request from - * @return int an id to identify the request - */ - int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, - const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); - - /** - * @param directory the directory to store cache files - **/ - void setCacheDirectory(const QString &directory); - - /** - * MO has to send a "Nexus Client Vx.y.z" as part of the user agent to be allowed to use the API - * @param nmmVersion the version of nmm to impersonate - **/ - void setNMMVersion(const QString &nmmVersion); - - /** - * @brief called when the log-in completes. This was, requests waiting for the log-in can be run - */ - void loginCompleted(); - -public: - - /** - * @brief guess the mod id from a filename as delivered by Nexus - * @param fileName name of the file - * @return the guessed mod id - * @note this currently doesn't fit well with the remaining interface but this is the best place for the function - */ - static void interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query); - -signals: - - void requestNXMDownload(const QString &url); - - void needLogin(); - - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); - -private slots: - - void requestFinished(); - void requestError(QNetworkReply::NetworkError error); - void requestTimeout(); - - void downloadRequestedNXM(const QString &url); - - void fakeFiles(); - -private: - - struct NXMRequestInfo { - int m_ModID; - std::vector m_ModIDList; - int m_FileID; - QNetworkReply *m_Reply; - enum Type { - TYPE_DESCRIPTION, - TYPE_FILES, - TYPE_FILEINFO, - TYPE_DOWNLOADURL, - TYPE_TOGGLEENDORSEMENT, - TYPE_GETUPDATES - } m_Type; - QVariant m_UserData; - QTimer *m_Timeout; - QString m_URL; - QString m_SubModule; - int m_NexusGameID; - bool m_Reroute; - int m_ID; - int m_Endorse; - - NXMRequestInfo(int modID, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); - NXMRequestInfo(std::vector modIDList, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); - NXMRequestInfo(int modID, int fileID, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); - - private: - static QAtomicInt s_NextID; - }; - - static const int MAX_ACTIVE_DOWNLOADS = 2; - -private: - - NexusInterface(); - void nextRequest(); - void requestFinished(std::list::iterator iter); - bool requiresLogin(const NXMRequestInfo &info); - static void cleanup(); - -private: - -// static NexusInterface *s_Instance; - - QNetworkDiskCache *m_DiskCache; - - NXMAccessManager *m_AccessManager; - - std::list m_ActiveRequest; - QQueue m_RequestQueue; - - MOBase::VersionInfo m_MOVersion; - QString m_NMMVersion; - -}; - -#endif // NEXUSINTERFACE_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef NEXUSINTERFACE_H +#define NEXUSINTERFACE_H + + +#include "nxmaccessmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class NexusInterface; + + +/** + * @brief convenience class to make nxm requests easier + * usually, all objects that started a nxm request will be signaled if one finished. + * Therefore, the objects need to store the id of the requests they started and then filter + * the result. + * NexusBridge does this automatically. Users connect to the signals of NexusBridge they intend + * to handle and only receive the signals the caused + **/ +class NexusBridge : public MOBase::IModRepositoryBridge +{ + + Q_OBJECT + +public: + + NexusBridge(const QString &subModule = ""); + + /** + * @brief request description for a mod + * + * @param modID id of the mod caller is interested in + * @param userData user data to be returned with the result + * @param url the url to request from + **/ + virtual void requestDescription(int modID, QVariant userData); + + /** + * @brief request a list of the files belonging to a mod + * + * @param modID id of the mod caller is interested in + * @param userData user data to be returned with the result + **/ + virtual void requestFiles(int modID, QVariant userData); + + /** + * @brief request info about a single file of a mod + * + * @param modID id of the mod caller is interested in + * @param fileID id of the file the caller is interested in + * @param userData user data to be returned with the result + **/ + virtual void requestFileInfo(int modID, int fileID, QVariant userData); + + /** + * @brief request the download url of a file + * + * @param modID id of the mod caller is interested in + * @param fileID id of the file the caller is interested in + * @param userData user data to be returned with the result + **/ + virtual void requestDownloadURL(int modID, int fileID, QVariant userData); + + /** + * @brief requestToggleEndorsement + * @param modID id of the mod caller is interested in + * @param userData user data to be returned with the result + */ + virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData); + +public slots: + + void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); + +private: + + NexusInterface *m_Interface; + QString m_Url; + QString m_SubModule; + std::set m_RequestIDs; + +}; + + +/** + * @brief Makes asynchronous requests to the nexus API + * + * This class can be used to make asynchronous requests to the Nexus API. + * Currently, responses are sent to all receivers that have sent a request of the relevant type, so the + * recipient has to filter the response by the id returned when making the request + **/ +class NexusInterface : public QObject +{ + Q_OBJECT + +public: + + static NexusInterface *instance(); + + /** + * @return the access manager object used to connect to nexus + **/ + NXMAccessManager *getAccessManager(); + + /** + * @brief request description for a mod + * + * @param modID id of the mod caller is interested in + * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + **/ + int requestDescription(int modID, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()), + int nexusGameId = -1); + + /** + * @brief request nexus descriptions for multiple mods at once + * @param modIDs a list of ids of mods the caller is interested in + * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + */ + int requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); + + /** + * @brief request a list of the files belonging to a mod + * + * @param modID id of the mod caller is interested in + * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + **/ + int requestFiles(int modID, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); + + /** + * @brief request info about a single file of a mod + * + * @param modID id of the mod caller is interested in + * @param fileID id of the file the caller is interested in + * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + **/ + int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); + + /** + * @brief request the download url of a file + * + * @param modID id of the mod caller is interested in + * @param fileID id of the file the caller is interested in + * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + **/ + int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); + + /** + * @brief toggle endorsement state of the mod + * @param modID id of the mod + * @param endorse true if the mod should be endorsed, false for un-endorse + * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) + * @param userData user data to be returned with the result + * @param url the url to request from + * @return int an id to identify the request + */ + int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, + const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())); + + /** + * @param directory the directory to store cache files + **/ + void setCacheDirectory(const QString &directory); + + /** + * MO has to send a "Nexus Client Vx.y.z" as part of the user agent to be allowed to use the API + * @param nmmVersion the version of nmm to impersonate + **/ + void setNMMVersion(const QString &nmmVersion); + + /** + * @brief called when the log-in completes. This was, requests waiting for the log-in can be run + */ + void loginCompleted(); + +public: + + /** + * @brief guess the mod id from a filename as delivered by Nexus + * @param fileName name of the file + * @return the guessed mod id + * @note this currently doesn't fit well with the remaining interface but this is the best place for the function + */ + static void interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query); + +signals: + + void requestNXMDownload(const QString &url); + + void needLogin(); + + void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); + void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); + +private slots: + + void requestFinished(); + void requestError(QNetworkReply::NetworkError error); + void requestTimeout(); + + void downloadRequestedNXM(const QString &url); + + void fakeFiles(); + +private: + + struct NXMRequestInfo { + int m_ModID; + std::vector m_ModIDList; + int m_FileID; + QNetworkReply *m_Reply; + enum Type { + TYPE_DESCRIPTION, + TYPE_FILES, + TYPE_FILEINFO, + TYPE_DOWNLOADURL, + TYPE_TOGGLEENDORSEMENT, + TYPE_GETUPDATES + } m_Type; + QVariant m_UserData; + QTimer *m_Timeout; + QString m_URL; + QString m_SubModule; + int m_NexusGameID; + bool m_Reroute; + int m_ID; + int m_Endorse; + + NXMRequestInfo(int modID, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); + NXMRequestInfo(std::vector modIDList, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); + NXMRequestInfo(int modID, int fileID, Type type, QVariant userData, const QString &subModule, const QString &url, int nexusGameId); + + private: + static QAtomicInt s_NextID; + }; + + static const int MAX_ACTIVE_DOWNLOADS = 2; + +private: + + NexusInterface(); + void nextRequest(); + void requestFinished(std::list::iterator iter); + bool requiresLogin(const NXMRequestInfo &info); + static void cleanup(); + +private: + +// static NexusInterface *s_Instance; + + QNetworkDiskCache *m_DiskCache; + + NXMAccessManager *m_AccessManager; + + std::list m_ActiveRequest; + QQueue m_RequestQueue; + + MOBase::VersionInfo m_MOVersion; + QString m_NMMVersion; + +}; + +#endif // NEXUSINTERFACE_H -- cgit v1.3.1 From 06701856f8eaed0f6bf802308c07952f0f0dd92e Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 18 Aug 2014 22:57:00 +0200 Subject: - login package now also uses a "proper" user-agent --- src/nexusinterface.cpp | 12 ++++++------ src/nexusinterface.h | 2 -- src/nxmaccessmanager.cpp | 12 ++++++++++-- src/nxmaccessmanager.h | 7 ++++++- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/nexusinterface.h') diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index c7e2df9e..d844cd39 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -146,17 +146,16 @@ NexusInterface::NexusInterface() : m_NMMVersion() { atexit(&cleanup); - m_AccessManager = new NXMAccessManager(this); - - m_DiskCache = new QNetworkDiskCache(this); - connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); - m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, version.dwFileVersionMS & 0xFFFF, version.dwFileVersionLS >> 16); + + m_AccessManager = new NXMAccessManager(this, m_MOVersion.displayString()); + + m_DiskCache = new QNetworkDiskCache(this); + connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); } @@ -188,6 +187,7 @@ void NexusInterface::setCacheDirectory(const QString &directory) void NexusInterface::setNMMVersion(const QString &nmmVersion) { m_NMMVersion = nmmVersion; + m_AccessManager->setNMMVersion(nmmVersion); } void NexusInterface::loginCompleted() diff --git a/src/nexusinterface.h b/src/nexusinterface.h index db61b0bf..cc54daa9 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -305,8 +305,6 @@ private: private: -// static NexusInterface *s_Instance; - QNetworkDiskCache *m_DiskCache; NXMAccessManager *m_AccessManager; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index a238d1c8..18b47707 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -41,10 +41,11 @@ using namespace MOBase; using namespace MOShared; -NXMAccessManager::NXMAccessManager(QObject *parent) +NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) : QNetworkAccessManager(parent) , m_LoginReply(NULL) , m_ProgressDialog() + , m_MOVersion(moVersion) , m_LoginAttempted(false) { setCookieJar(new PersistentCookieJar( @@ -59,6 +60,10 @@ NXMAccessManager::~NXMAccessManager() } } +void NXMAccessManager::setNMMVersion(const QString &nmmVersion) +{ + m_NMMVersion = nmmVersion; +} QNetworkReply *NXMAccessManager::createRequest( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, @@ -122,7 +127,7 @@ void NXMAccessManager::login(const QString &username, const QString &password) void NXMAccessManager::pageLogin() { - QString requestString = (ToQString(GameInfo::instance().getNexusPage()) + "/sessions/?Login&uri=%1") + QString requestString = (ToQString(GameInfo::instance().getNexusPage()) + "/Sessions/?Login&uri=%1") .arg(QString(QUrl::toPercentEncoding(ToQString(GameInfo::instance().getNexusPage())))); QNetworkRequest request(requestString); @@ -141,6 +146,9 @@ void NXMAccessManager::pageLogin() postDataQuery = postData.encodedQuery(); #endif + QString userAgent = QString("Mod Organizer v%1 (compatible to Nexus Client v%2)").arg(m_MOVersion).arg(m_NMMVersion); + request.setRawHeader("User-Agent", userAgent.toUtf8()); + m_ProgressDialog.setLabelText(tr("Logging into Nexus")); QList buttons = m_ProgressDialog.findChildren(); buttons.at(0)->setEnabled(false); diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 525394c9..9d692bba 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -35,10 +35,12 @@ class NXMAccessManager : public QNetworkAccessManager Q_OBJECT public: - explicit NXMAccessManager(QObject *parent); + explicit NXMAccessManager(QObject *parent, const QString &moVersion); ~NXMAccessManager(); + void setNMMVersion(const QString &nmmVersion); + bool loggedIn() const; bool loginAttempted() const { return m_LoginAttempted; } @@ -93,6 +95,9 @@ private: QNetworkReply *m_LoginReply; QProgressDialog m_ProgressDialog; + QString m_MOVersion; + QString m_NMMVersion; + QString m_Username; QString m_Password; -- cgit v1.3.1 From 482f13a50b921e61d34d09f72a7fb4216efe742b Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 8 Sep 2014 20:37:23 +0200 Subject: - re-enabled building of loot_cli and started developing against the new api - extended set of default categories - more tolerand bbcode parser - added a few colors for the bbcode parser - more fixes to qt5 compatibility - started work on ability to unloading (and thus re-loading) of plugins - names of plugins are no longer localizable (because those names are also used to store settings) - added settings to disable individual diagnosis settings - path of dependencies is now configured in a .pri file instead of environment variablees - bugfix: if the modid-input is canceled, the id was saved as -1 and wasn't re-requested from the user - bugfix: moving files with the SHFileOperation-Api didn't update the vfs correctly (still not perfect but better) - bugfix: attempt to remove the deleter-file seems to have caused error messages for some users - bugfix: fixed a couple of cases that might have caused the tutorial to hang --- .hgignore | 3 + src/ModOrganizer.pro | 6 +- src/bbcode.cpp | 93 +++++++++++++++++++++---------- src/categories.cpp | 26 +++++++-- src/downloadmanager.cpp | 2 +- src/editexecutablesdialog.cpp | 2 +- src/main.cpp | 12 +++- src/mainwindow.cpp | 77 ++++++++++++++++++------- src/mainwindow.h | 8 +++ src/modinfo.cpp | 4 +- src/modlist.cpp | 11 ++++ src/modlist.h | 6 ++ src/nexusinterface.cpp | 23 ++++---- src/nexusinterface.h | 8 ++- src/organizer.pro | 50 +++++++++-------- src/pluginlist.cpp | 5 +- src/pluginlist.h | 12 ++-- src/settings.cpp | 1 - src/shared/directoryentry.cpp | 4 -- src/shared/directoryentry.h | 2 + src/shared/shared.pro | 53 ++++++++++-------- src/tutorials/TutorialOverlay.qml | 6 +- src/tutorials/tutorial_firststeps_main.js | 35 ++++++++---- 23 files changed, 305 insertions(+), 144 deletions(-) (limited to 'src/nexusinterface.h') diff --git a/.hgignore b/.hgignore index d1092a1d..acba45c6 100644 --- a/.hgignore +++ b/.hgignore @@ -32,5 +32,8 @@ html *.filters *.lib source/organizer/resources/contents/icons +source/plugins/build-* +*/GeneratedFiles/* syntax: regexp Makefile\.(Debug|Release) +source/.*/debug/.* diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index 9907d086..9b2d998b 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -1,6 +1,5 @@ TEMPLATE = subdirs - SUBDIRS = bsatk \ shared \ uibase \ @@ -13,9 +12,10 @@ SUBDIRS = bsatk \ nxmhandler \ BossDummy \ pythonRunner \ + loot_cli \ esptk -plugins.depends = pythonRunner +plugins.depends = pythonRunner uibase hookdll.depends = shared organizer.depends = shared uibase plugins @@ -30,7 +30,7 @@ DLLSPATH = $${DESTDIR}\\dlls otherlibs.path = $$DLLSPATH otherlibs.files += $${STATICDATAPATH}\\7z.dll \ - $$(BOOSTPATH)\\stage\\lib\\boost_python-vc*-mt-1*.dll + $${BOOSTPATH}\\stage\\lib\\boost_python-vc*-mt-1*.dll qtlibs.path = $$DLLSPATH diff --git a/src/bbcode.cpp b/src/bbcode.cpp index 18f2beb1..92eb427f 100644 --- a/src/bbcode.cpp +++ b/src/bbcode.cpp @@ -39,7 +39,7 @@ public: return s_Instance; } - QString convertTag(const QString &input, int &length) + QString convertTag(QString input, int &length) { // extract the tag name m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset); @@ -50,14 +50,30 @@ public: if (tagName.endsWith('=')) { tagName.chop(1); } - QString closeTag = tagName == "*" ? "
" - : QString("[/%1]").arg(tagName); - int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive); - //qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos); + int closeTagPos = 0; + int closeTagLength = 0; + if (tagName == "*") { + // ends at the next bullet point + closeTagPos = input.indexOf(QRegExp("(\\[\\*\\]|)", Qt::CaseInsensitive), 3); + // leave closeTagLength at 0 because we don't want to "eat" the next bullet point + } else if (tagName == "line") { + // ends immediately after the tag + closeTagPos = 6; + // leave closeTagLength at 0 because there is no close tag to skip over + } else { + QString closeTag = QString("[/%1]").arg(tagName); + closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive); + if (closeTagPos == -1) { + // workaround to improve compatibility: add fake closing tag + input.append(closeTag); + closeTagPos = input.size() - closeTag.size(); + } + closeTagLength = closeTag.size(); + } if (closeTagPos > -1) { - length = closeTagPos + closeTag.length(); + length = closeTagPos + closeTagLength; QString temp = input.mid(0, length); if (tagIter->second.first.indexIn(temp) == 0) { if (tagIter->second.second.isEmpty()) { @@ -73,6 +89,9 @@ public: qWarning("don't know how to deal with tag %s", qPrintable(tagName)); } } else { + if (tagName == "*") { + temp.remove(QRegExp("(\\[/\\*\\])?(
)?$")); + } return temp.replace(tagIter->second.first, tagIter->second.second); } } else { @@ -123,6 +142,8 @@ private: "
\\1
"); m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"), "

\\1

"); + m_TagMap["line"] = std::make_pair(QRegExp("\\[line\\]"), + "
"); // lists m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"), @@ -135,8 +156,6 @@ private: "
    \\1
"); m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"), "
  • \\1
  • "); - m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)
    "), - "
  • \\1
  • "); // tables m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"), @@ -153,13 +172,26 @@ private: "\\1"); m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"), "\\2"); - m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), " "); - m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), " "); + m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), + "\\1"); + m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), + "\\2"); m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"), "\\2"); m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"), "http://www.youtube.com/v/\\1"); + + // make all patterns non-greedy and case-insensitive + for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) { + iter->second.first.setCaseSensitivity(Qt::CaseInsensitive); + iter->second.first.setMinimal(true); + } + + // this tag is in fact greedy + m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)"), + "
  • \\1
  • "); + m_ColorMap.insert(std::make_pair("red", "FF0000")); m_ColorMap.insert(std::make_pair("green", "00FF00")); m_ColorMap.insert(std::make_pair("blue", "0000FF")); @@ -170,13 +202,13 @@ private: m_ColorMap.insert(std::make_pair("cyan", "00FFFF")); m_ColorMap.insert(std::make_pair("magenta", "FF00FF")); m_ColorMap.insert(std::make_pair("brown", "A52A2A")); - m_ColorMap.insert(std::make_pair("orange", "FFCC00")); - - // make all patterns non-greedy and case-insensitive - for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) { - iter->second.first.setCaseSensitivity(Qt::CaseInsensitive); - iter->second.first.setMinimal(true); - } + m_ColorMap.insert(std::make_pair("orange", "FFA500")); + m_ColorMap.insert(std::make_pair("gold", "FFD700")); + m_ColorMap.insert(std::make_pair("deepskyblue", "00BFFF")); + m_ColorMap.insert(std::make_pair("salmon", "FA8072")); + m_ColorMap.insert(std::make_pair("dodgerblue", "1E90FF")); + m_ColorMap.insert(std::make_pair("greenyellow", "ADFF2F")); + m_ColorMap.insert(std::make_pair("peru", "CD853F")); } private: @@ -209,18 +241,23 @@ QString convertToHTML(const QString &inputParam) // append everything between the previous tag-block and the current one result.append(input.midRef(lastBlock, pos - lastBlock)); - // convert the tag and content if necessary - int length = -1; - QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length); - if (length != 0) { - QString temp = convertToHTML(replacement); - result.append(temp); - // length contains the number of characters in the original tag - pos += length; + if ((pos < (input.size() - 1)) && (input.at(pos + 1) == '/')) { + // skip invalid end tag + int tagEnd = input.indexOf(']', pos) + 1; + pos = tagEnd; } else { - // nothing replaced - result.append('['); - ++pos; + // convert the tag and content if necessary + int length = -1; + QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length); + if (length != 0) { + result.append(convertToHTML(replacement)); + // length contains the number of characters in the original tag + pos += length; + } else { + // nothing replaced + result.append('['); + ++pos; + } } lastBlock = pos; } diff --git a/src/categories.cpp b/src/categories.cpp index 4c851338..28b1f4a2 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -104,7 +104,11 @@ void CategoryFactory::reset() { m_Categories.clear(); m_IDMap.clear(); - addCategory(0, "None", MakeVector(2, 28, 87), 0); + // 28 = + // 43 = Savegames (makes no sense to install them through MO) + // 45 = Videos and trailers + // 87 = Miscelanous + addCategory(0, "None", MakeVector(4, 28, 43, 45, 87), 0); } @@ -189,24 +193,38 @@ void CategoryFactory::loadDefaultCategories() // mods appear in the combo box addCategory(1, "Animations", MakeVector(1, 51), 0); addCategory(2, "Armour", MakeVector(1, 54), 0); - addCategory(3, "Audio", MakeVector(1, 61), 0); + addCategory(3, "Sound & Music", MakeVector(1, 61), 0); addCategory(5, "Clothing", MakeVector(1, 60), 0); addCategory(6, "Collectables", MakeVector(1, 92), 0); - addCategory(7, "Creatures", MakeVector(2, 83, 65), 0); + addCategory(28, "Companions", MakeVector(2, 66, 96), 0); + addCategory(7, "Creatures & Mounts", MakeVector(2, 83, 65), 0); addCategory(8, "Factions", MakeVector(1, 25), 0); addCategory(9, "Gameplay", MakeVector(1, 24), 0); addCategory(10, "Hair", MakeVector(1, 26), 0); addCategory(11, "Items", MakeVector(2, 27, 85), 0); - addCategory(19, "Weapons", MakeVector(2, 36, 55), 11); + addCategory(32, "Mercantile", MakeVector(1, 69), 0); + addCategory(19, "Weapons", MakeVector(1, 55), 11); + addCategory(36, "Weapon & Armour Sets", MakeVector(1, 39), 11); addCategory(12, "Locations", MakeVector(7, 22, 30, 70, 88, 89, 90, 91), 0); + addCategory(31, "Landscape Changes", MakeVector(1, 58), 0); addCategory(4, "Cities", MakeVector(1, 53), 12); + addCategory(29, "Environment", MakeVector(1, 74), 0); + addCategory(30, "Immersion", MakeVector(1, 78), 0); + addCategory(25, "Castles & Mansions", MakeVector(1, 68), 23); addCategory(20, "Magic", MakeVector(3, 75, 93, 94), 0); addCategory(21, "Models & Textures", MakeVector(1, 29), 0); + addCategory(33, "Modders resources", MakeVector(1, 82), 0); addCategory(13, "NPCs", MakeVector(1, 33), 0); addCategory(14, "Patches", MakeVector(2, 79, 84), 0); + addCategory(24, "Bugfixes", MakeVector(1, 95), 0); + addCategory(35, "Utilities", MakeVector(1, 39), 0); + addCategory(26, "Cheats", MakeVector(1, 40), 0); + addCategory(23, "Player Homes", MakeVector(1, 67), 0); addCategory(15, "Quests", MakeVector(1, 35), 0); addCategory(16, "Races & Classes", MakeVector(1, 34), 0); + addCategory(27, "Combat", MakeVector(1, 77), 0); addCategory(22, "Skills", MakeVector(1, 73), 0); + addCategory(34, "Stealth", MakeVector(1, 76), 0); addCategory(17, "UI", MakeVector(1, 42), 0); addCategory(18, "Visuals", MakeVector(1, 62), 0); } diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 856ca79c..bc31adf4 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -685,7 +685,7 @@ void DownloadManager::queryInfo(int index) return; } - if (info->m_FileInfo->modID == 0UL) { + if (info->m_FileInfo->modID <= 0) { QString fileName = getFileName(index); QString ignore; NexusInterface::interpretNexusFileName(fileName, ignore, info->m_FileInfo->modID, true); diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 02abf30e..0e3aa55b 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -132,7 +132,7 @@ void EditExecutablesDialog::on_browseButton_clicked() if (::FindExecutableW(binaryNameW.c_str(), NULL, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(binaryNameW.c_str(), &binaryType)) { - qDebug("failed to determine binary type: %lu", ::GetLastError()); + qDebug("failed to determine binary type of \"%ls\": %lu", binaryNameW.c_str(), ::GetLastError()); } else if (binaryType == SCS_32BIT_BINARY) { binaryPath = ToQString(buffer); } diff --git a/src/main.cpp b/src/main.cpp index a21f41e4..3198208a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -311,9 +311,19 @@ int main(int argc, char *argv[]) } QString dataPath = instanceID.isEmpty() ? application.applicationDirPath() - : QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation)) + "/" + instanceID; + : QDir::fromNativeSeparators( +#if QT_VERSION >= 0x050000 + QStandardPaths::writableLocation(QStandardPaths::DataLocation) +#else + QDesktopServices::storageLocation(QDesktopServices::DataLocation) +#endif + ) + "/" + instanceID; application.setProperty("dataPath", dataPath); +#if QT_VERSION >= 0x050000 + qDebug("ssl support: %d", QSslSocket::supportsSsl()); +#endif + qDebug("data path: %s", qPrintable(dataPath)); if (!QDir(dataPath).exists()) { if (!QDir().mkpath(dataPath)) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0d9fad5d..984d8cfd 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -97,9 +97,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include -#include -#include #include #include #include @@ -114,8 +111,13 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include #include +#include +#include +#include +#endif #include #ifdef TEST_MODELS @@ -313,8 +315,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_Updater, SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(&m_Updater, SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); -// connect(ExitProxy::instance(), SIGNAL(exit()), this, SLOT(close())); - connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); @@ -367,6 +367,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget MainWindow::~MainWindow() { + m_AboutToRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); m_RefresherThread.exit(); m_RefresherThread.wait(); m_IntegratedBrowser.close(); @@ -888,6 +890,8 @@ void MainWindow::closeEvent(QCloseEvent* event) storeSettings(); +// unloadPlugins(); + // profile has to be cleaned up before the modinfo-buffer is cleared delete m_CurrentProfile; m_CurrentProfile = NULL; @@ -895,6 +899,7 @@ void MainWindow::closeEvent(QCloseEvent* event) ModInfo::clear(); LogBuffer::cleanQuit(); m_ModList.setProfile(NULL); + NexusInterface::instance()->cleanup(); } @@ -1175,7 +1180,9 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) IPluginDiagnose *diagnose = qobject_cast(plugin); if (diagnose != NULL) { m_DiagnosisPlugins.push_back(diagnose); - diagnose->onInvalidated([&] () { this->scheduleUpdateButton(); }); + m_DiagnosisConnections.push_back( + diagnose->onInvalidated([&] () { this->scheduleUpdateButton(); }) + ); } } { // mod page plugin @@ -1204,6 +1211,7 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) IPluginPreview *preview = qobject_cast(plugin); if (verifyPlugin(preview)) { m_PreviewGenerator.registerPlugin(preview); + return true; } } { // proxy plugins @@ -1242,13 +1250,41 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) return false; } - -void MainWindow::loadPlugins() +void MainWindow::unloadPlugins() { + // disconnect all slots before unloading plugins so plugins don't have to take care of that + m_AboutToRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); + m_ModList.disconnectSlots(); + m_PluginList.disconnectSlots(); + m_DiagnosisPlugins.clear(); + foreach (const boost::signals2::connection &connection, m_DiagnosisConnections) { + connection.disconnect(); + } + m_DiagnosisConnections.clear(); + m_Settings.clearPlugins(); + if (ui->actionTool->menu() != NULL) { + ui->actionTool->menu()->clear(); + } + + foreach (QPluginLoader *loader, m_PluginLoaders) { + qDebug("unloading %s", qPrintable(loader->fileName())); + if (!loader->unload()) { + qDebug("failed to unload %s: %s", qPrintable(loader->fileName()), qPrintable(loader->errorString())); + } + delete loader; + } + m_PluginLoaders.clear(); +} + +void MainWindow::loadPlugins() +{ + unloadPlugins(); + foreach (QObject *plugin, QPluginLoader::staticInstances()) { registerPlugin(plugin, ""); } @@ -1287,14 +1323,15 @@ void MainWindow::loadPlugins() loadCheck.flush(); QString pluginName = iter.filePath(); if (QLibrary::isLibrary(pluginName)) { - QPluginLoader pluginLoader(pluginName); - if (pluginLoader.instance() == NULL) { + QPluginLoader *pluginLoader = new QPluginLoader(pluginName, this); + if (pluginLoader->instance() == NULL) { m_UnloadedPlugins.push_back(pluginName); qCritical("failed to load plugin %s: %s", - qPrintable(pluginName), qPrintable(pluginLoader.errorString())); + qPrintable(pluginName), qPrintable(pluginLoader->errorString())); } else { - if (registerPlugin(pluginLoader.instance(), pluginName)) { + if (registerPlugin(pluginLoader->instance(), pluginName)) { qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); + m_PluginLoaders.push_back(pluginLoader); } else { m_UnloadedPlugins.push_back(pluginName); qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); @@ -2214,6 +2251,8 @@ void MainWindow::storeSettings() void MainWindow::on_btnRefreshData_clicked() { if (!m_DirectoryUpdate) { + // save the mod list so changes don't get lost + m_CurrentProfile->writeModlistNow(true); refreshDirectoryStructure(); } else { qDebug("directory update"); @@ -4315,13 +4354,10 @@ void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QStrin void MainWindow::installTranslator(const QString &name) { -/* if (m_CurrentLanguage == "en_US") { - return; - }*/ QTranslator *translator = new QTranslator(this); QString fileName = name + "_" + m_CurrentLanguage; if (!translator->load(fileName, qApp->applicationDirPath() + "/translations")) { - if (m_CurrentLanguage != "en-US") { + if ((m_CurrentLanguage != "en-US") && (m_CurrentLanguage != "en_US")) { qWarning("localization file %s not found", qPrintable(fileName)); } // we don't actually expect localization files for english } @@ -4485,7 +4521,7 @@ int MainWindow::getBinaryExecuteInfo(const QFileInfo &targetInfo, if (::FindExecutableW(targetPathW.c_str(), NULL, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(targetPathW.c_str(), &binaryType)) { - qDebug("failed to determine binary type: %lu", ::GetLastError()); + qDebug("failed to determine binary type of \"%ls\": %lu", targetPathW.c_str(), ::GetLastError()); } else if (binaryType == SCS_32BIT_BINARY) { binaryPath = ToQString(buffer); } @@ -5088,9 +5124,6 @@ void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) void MainWindow::on_actionProblems_triggered() { -// QString problemDescription; -// checkForProblems(problemDescription); -// QMessageBox::information(this, tr("Problems"), problemDescription); ProblemsDialog problems(m_DiagnosisPlugins, this); if (problems.hasProblems()) { problems.exec(); @@ -5594,7 +5627,11 @@ void MainWindow::on_bossButton_clicked() QStringList temp = report.split("?"); QUrl url = QUrl::fromLocalFile(temp.at(0)); if (temp.size() > 1) { +#if QT_VERSION >= 0x050000 + url.setQuery(temp.at(1).toUtf8()); +#else url.setEncodedQuery(temp.at(1).toUtf8()); +#endif } m_IntegratedBrowser.openUrl(url); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 22152f1c..8bd663ac 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include "executableslist.h" #include "modlist.h" #include "pluginlist.h" @@ -53,7 +54,9 @@ along with Mod Organizer. If not, see . #include "browserdialog.h" #include #include +#ifndef Q_MOC_RUN #include +#endif namespace Ui { class MainWindow; @@ -139,6 +142,8 @@ public: void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); + QString getOriginDisplayName(int originID); + void unloadPlugins(); public slots: void refreshLists(); @@ -191,6 +196,7 @@ private: void registerPluginTool(MOBase::IPluginTool *tool); void registerModPage(MOBase::IPluginModPage *modPage); bool registerPlugin(QObject *pluginObj, const QString &fileName); + bool unregisterPlugin(QObject *pluginObj, const QString &fileName); void updateToolBar(); void activateSelectedProfile(); @@ -373,8 +379,10 @@ private: MOBase::IGameInfo *m_GameInfo; std::vector m_DiagnosisPlugins; + std::vector m_DiagnosisConnections; std::vector m_ModPages; std::vector m_UnloadedPlugins; + std::vector m_PluginLoaders; QFile m_PluginsCheck; diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 9558e03b..4ab6d142 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -569,9 +569,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); metaFile.setValue("version", m_Version.canonicalString()); - if (m_NexusID != -1) { - metaFile.setValue("modid", m_NexusID); - } + metaFile.setValue("modid", m_NexusID); metaFile.setValue("notes", m_Notes); metaFile.setValue("nexusDescription", m_NexusDescription); metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); diff --git a/src/modlist.cpp b/src/modlist.cpp index 681d880c..eedf1ec6 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -64,6 +64,12 @@ ModList::ModList(QObject *parent) m_ContentIcons[ModInfo::CONTENT_TEXTURE] = std::make_tuple(QIcon(":/MO/gui/content/texture"), ":/MO/gui/content/texture", tr("Textures")); } +ModList::~ModList() +{ + m_ModStateChanged.disconnect_all_slots(); + m_ModMoved.disconnect_all_slots(); +} + void ModList::setProfile(Profile *profile) { m_Profile = profile; @@ -653,6 +659,11 @@ void ModList::modInfoChanged(ModInfo::Ptr info) } } +void ModList::disconnectSlots() { + m_ModMoved.disconnect_all_slots(); + m_ModStateChanged.disconnect_all_slots(); +} + IModList::ModStates ModList::state(unsigned int modIndex) const { IModList::ModStates result; diff --git a/src/modlist.h b/src/modlist.h index 6116a913..632689c6 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -32,7 +32,9 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include +#endif #include #include #include @@ -73,6 +75,8 @@ public: **/ ModList(QObject *parent = NULL); + ~ModList(); + /** * @brief set the profile used for status information * @@ -103,6 +107,8 @@ public: void modInfoAboutToChange(ModInfo::Ptr info); void modInfoChanged(ModInfo::Ptr info); + void disconnectSlots(); + public: /// \copydoc MOBase::IModList::displayName diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index d844cd39..30221f4b 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -145,30 +145,25 @@ QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); NexusInterface::NexusInterface() : m_NMMVersion() { - atexit(&cleanup); - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, version.dwFileVersionMS & 0xFFFF, version.dwFileVersionLS >> 16); m_AccessManager = new NXMAccessManager(this, m_MOVersion.displayString()); - m_DiskCache = new QNetworkDiskCache(this); connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); } - -void NexusInterface::cleanup() -{ -} - - NXMAccessManager *NexusInterface::getAccessManager() { return m_AccessManager; } +NexusInterface::~NexusInterface() +{ + cleanup(); +} NexusInterface *NexusInterface::instance() { @@ -176,14 +171,12 @@ NexusInterface *NexusInterface::instance() return &s_Instance; } - void NexusInterface::setCacheDirectory(const QString &directory) { m_DiskCache->setCacheDirectory(directory); m_AccessManager->setCache(m_DiskCache); } - void NexusInterface::setNMMVersion(const QString &nmmVersion) { m_NMMVersion = nmmVersion; @@ -378,6 +371,14 @@ bool NexusInterface::requiresLogin(const NXMRequestInfo &info) || (info.m_Type == NXMRequestInfo::TYPE_DOWNLOADURL); } +void NexusInterface::cleanup() +{ +// delete m_AccessManager; +// delete m_DiskCache; + m_AccessManager = nullptr; + m_DiskCache = nullptr; +} + void NexusInterface::nextRequest() { if ((m_ActiveRequest.size() >= MAX_ACTIVE_DOWNLOADS) diff --git a/src/nexusinterface.h b/src/nexusinterface.h index cc54daa9..af2f8c75 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -130,6 +130,8 @@ class NexusInterface : public QObject public: + ~NexusInterface(); + static NexusInterface *instance(); /** @@ -137,6 +139,11 @@ public: **/ NXMAccessManager *getAccessManager(); + /** + * @brief cleanup this interface. this is destructive, afterwards it can't be used again + */ + void cleanup(); + /** * @brief request description for a mod * @@ -301,7 +308,6 @@ private: void nextRequest(); void requestFinished(std::list::iterator iter); bool requiresLogin(const NXMRequestInfo &info); - static void cleanup(); private: diff --git a/src/organizer.pro b/src/organizer.pro index bffd1e16..b24586e6 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -9,11 +9,15 @@ TARGET = ModOrganizer TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4) { - QT += core gui widgets network xml sql xmlpatterns qml quick script webkit + QT += core gui widgets network xml sql xmlpatterns qml declarative script webkit webkitwidgets } else { QT += core gui network xml declarative script sql xmlpatterns webkit } +!include(../LocalPaths.pri) { + message("paths to required libraries need to be set up in LocalPaths.pri") +} + SOURCES += \ transfersavesdialog.cpp \ syncoverwritedialog.cpp \ @@ -248,9 +252,9 @@ LIBS += -lDbgHelp #DEFINES += TEST_MODELS -INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$(BOOSTPATH)" +INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$${BOOSTPATH}" -LIBS += -L"$(BOOSTPATH)/stage/lib" +LIBS += -L"$${BOOSTPATH}/stage/lib" CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../shared/debug @@ -258,7 +262,8 @@ CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../uibase/debug LIBS += -L$$OUT_PWD/../boss_modified/debug LIBS += -lDbgHelp - PRE_TARGETDEPS += $$OUT_PWD/../shared/debug/mo_shared.lib \ + PRE_TARGETDEPS += \ + $$OUT_PWD/../shared/debug/mo_shared.lib \ $$OUT_PWD/../bsatk/debug/bsatk.lib } else { LIBS += -L$$OUT_PWD/../shared/release @@ -266,20 +271,19 @@ CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../uibase/release LIBS += -L$$OUT_PWD/../boss_modified/release QMAKE_CXXFLAGS += /Zi# /GL -# QMAKE_CXXFLAGS -= -O2 QMAKE_LFLAGS += /DEBUG# /LTCG /OPT:REF /OPT:ICF - PRE_TARGETDEPS += $$OUT_PWD/../shared/release/mo_shared.lib \ + PRE_TARGETDEPS += \ + $$OUT_PWD/../shared/release/mo_shared.lib \ $$OUT_PWD/../bsatk/release/bsatk.lib } #QMAKE_CXXFLAGS_WARN_ON -= -W3 #QMAKE_CXXFLAGS_WARN_ON += -W4 -QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189 +QMAKE_CXXFLAGS += -wd4100 -wd4127 -wd4512 -wd4189 CONFIG += embed_manifest_exe # QMAKE_CXXFLAGS += /analyze - # QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'highestAvailable\' uiAccess=\'false\'\" TRANSLATIONS = organizer_de.ts \ @@ -313,9 +317,9 @@ TRANSLATIONS = organizer_de.ts \ LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk -lshlwapi -LIBS += -L"$(ZLIBPATH)/build" -lzlibstatic +LIBS += -L"$${ZLIBPATH}/build" -lzlibstatic -DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX +DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS NOMINMAX DEFINES += BOOST_DISABLE_ASSERTS NDEBUG #DEFINES += QMLJSDEBUGGER @@ -324,40 +328,40 @@ HGID = $$system(hg id -i) DEFINES += HGID=\\\"$${HGID}\\\" CONFIG(debug, debug|release) { - OUTDIR = $$OUT_PWD/debug + SRCDIR = $$OUT_PWD/debug DSTDIR = $$PWD/../../outputd } else { - OUTDIR = $$OUT_PWD/release + SRCDIR = $$OUT_PWD/release DSTDIR = $$PWD/../../output } -SRCDIR = $$PWD +BASEDIR = $$PWD +BASEDIR ~= s,/,$$QMAKE_DIR_SEP,g SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g -OUTDIR ~= s,/,$$QMAKE_DIR_SEP,g DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g -QMAKE_POST_LINK += xcopy /y /I $$quote($$OUTDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /I $$quote($$OUTDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) CONFIG(debug, debug|release) { greaterThan(QT_MAJOR_VERSION, 4) { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.debug.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.debug.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR\\dlls\\dlls.manifest.debug.qt5) $$quote($$DSTDIR\\dlls\\dlls.manifest) $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug.qt5 $$escape_expand(\\n) } else { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.debug) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.debug) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug $$quote($$DSTDIR)\\dlls\\dlls.manifest $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug $$escape_expand(\\n) } } else { greaterThan(QT_MAJOR_VERSION, 4) { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR\\dlls\\dlls.manifest.qt5) $$quote($$DSTDIR\\dlls\\dlls.manifest) $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.qt5 $$escape_expand(\\n) } else { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) } } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index f37f9e3a..43246b55 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -33,6 +33,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -95,6 +96,8 @@ PluginList::PluginList(QObject *parent) PluginList::~PluginList() { + m_Refreshed.disconnect_all_slots(); + m_PluginMoved.disconnect_all_slots(); } @@ -484,7 +487,7 @@ void PluginList::saveTo(const QString &pluginFileName if (deleterFile.commitIfDifferent(m_LastSaveHash[deleterFileName])) { qDebug("%s saved", qPrintable(QDir::toNativeSeparators(deleterFileName))); } - } else { + } else if (QFile::exists(deleterFileName)) { shellDelete(QStringList() << deleterFileName); } diff --git a/src/pluginlist.h b/src/pluginlist.h index 67d1b640..8c06fefd 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -26,15 +26,16 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include #include +#endif #include #include #include "pdll.h" #include - template class ChangeBracket { public: @@ -212,6 +213,11 @@ public: void refreshLoadOrder(); + void disconnectSlots() { + m_PluginMoved.disconnect_all_slots(); + m_Refreshed.disconnect_all_slots(); + } + public: virtual PluginState state(const QString &name) const; @@ -333,13 +339,11 @@ private: mutable QTimer m_SaveTimer; SignalRefreshed m_Refreshed; + SignalPluginMoved m_PluginMoved; QTemporaryFile m_TempFile; - SignalPluginMoved m_PluginMoved; }; - - #endif // PLUGINLIST_H diff --git a/src/settings.cpp b/src/settings.cpp index 6bf13ee0..65ea3a27 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -132,7 +132,6 @@ void Settings::registerPlugin(IPlugin *plugin) } } - QString Settings::obfuscate(const QString &password) const { QByteArray temp = password.toUtf8(); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index e0914870..5d785822 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -936,10 +936,6 @@ void FileRegister::removeOriginMulti(std::set indices, int ori } } - if (removedFiles.size() > 0) { - log("%d files actually removed", removedFiles.size()); - } - // optimization: this is only called when disabling an origin and in this case we don't have // to remove the file from the origin diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 7836d719..096f373e 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -29,8 +29,10 @@ along with Mod Organizer. If not, see . #define WIN32_MEAN_AND_LEAN #include #include +#ifndef Q_MOC_RUN #include #include +#endif #include "util.h" diff --git a/src/shared/shared.pro b/src/shared/shared.pro index 84a9c274..69f6f2e6 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -11,33 +11,11 @@ TARGET = mo_shared TEMPLATE = lib CONFIG += staticlib -INCLUDEPATH += ../bsatk "$(BOOSTPATH)" - -# only for custom leak detection -DEFINES += TRACE_LEAKS -LIBS += -lDbgHelp - - -CONFIG(debug, debug|release) { - LIBS += -L$$OUT_PWD/../bsatk/debug - LIBS += -lDbgHelp - QMAKE_CXXFLAGS_DEBUG -= -Zi - QMAKE_CXXFLAGS += -Z7 - PRE_TARGETDEPS += $$OUT_PWD/../bsatk/debug/bsatk.lib -} else { - LIBS += -L$$OUT_PWD/../bsatk/release - PRE_TARGETDEPS += $$OUT_PWD/../bsatk/release/bsatk.lib +!include(../LocalPaths.pri) { + message("paths to required libraries need to be set up in LocalPaths.pri") } -LIBS += -lbsatk - -DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS - -DEFINES += BOOST_DISABLE_ASSERTS NDEBUG - -# QMAKE_CXXFLAGS += /analyze - SOURCES += \ inject.cpp \ windows_error.cpp \ @@ -66,3 +44,30 @@ HEADERS += \ appconfig.h \ appconfig.inc \ leaktrace.h + + +# only for custom leak detection +DEFINES += TRACE_LEAKS +LIBS += -lDbgHelp + + +CONFIG(debug, debug|release) { + LIBS += -L$$OUT_PWD/../bsatk/debug + LIBS += -lDbgHelp + QMAKE_CXXFLAGS_DEBUG -= -Zi + QMAKE_CXXFLAGS += -Z7 + PRE_TARGETDEPS += $$OUT_PWD/../bsatk/debug/bsatk.lib +} else { + LIBS += -L$$OUT_PWD/../bsatk/release + PRE_TARGETDEPS += $$OUT_PWD/../bsatk/release/bsatk.lib +} + +LIBS += -lbsatk + +DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS + +DEFINES += BOOST_DISABLE_ASSERTS NDEBUG + +# QMAKE_CXXFLAGS += /analyze + +INCLUDEPATH += ../bsatk "$${BOOSTPATH}" diff --git a/src/tutorials/TutorialOverlay.qml b/src/tutorials/TutorialOverlay.qml index 34959d7d..f2aad027 100644 --- a/src/tutorials/TutorialOverlay.qml +++ b/src/tutorials/TutorialOverlay.qml @@ -1,4 +1,3 @@ -// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 import "tutorials.js" as Logic @@ -21,10 +20,11 @@ Rectangle { function enableBackground(enabled) { disabledBackground.visible = enabled } - +/* signal nextStep - onNextStep: { + onNextStep: {*/ + function nextStep() { if (step == 0) { Logic.init() } diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js index 4f9c1a74..5ee4e790 100644 --- a/src/tutorials/tutorial_firststeps_main.js +++ b/src/tutorials/tutorial_firststeps_main.js @@ -15,19 +15,24 @@ function getTutorialSteps() function() { tutorial.text = qsTr("The highlighted button provides hints on solving problems MO recognized automatically.") - if (!tutorialControl.waitForAction("actionProblems")) { - highlightAction("actionProblems", false) - waitForClick() - } else { + if (tutorialControl.waitForAction("actionProblems")) { tutorial.text += qsTr("\nThere IS a problem now but you may want to hold off on fixing it until after completing the tutorial.") highlightAction("actionProblems", true) + } else { + highlightAction("actionProblems", false) + waitForClick() } }, function() { + console.log("next") tutorial.text = qsTr("This button provides multiple sources of information and further tutorials.") - highlightItem("actionHelp", true) - tutorialControl.waitForButton("actionHelp") + if (tutorialControl.waitForButton("actionHelp")) { + highlightItem("actionHelp", true) + } else { + console.error("help button broken") + waitForClick() + } }, function() { @@ -47,12 +52,16 @@ function getTutorialSteps() function() { tutorial.text = qsTr("Before we start installing mods, let's have a quick look at the settings.") manager.activateTutorial("SettingsDialog", "tutorial_firststeps_settings.js") - highlightAction("actionSettings", true) - tutorialControl.waitForAction("actionSettings") + if (tutorialControl.waitForAction("actionSettings")) { + highlightAction("actionSettings", true) + } else { + console.error("settings action broken") + waitForClick() + } }, function() { - tutorial.text = qsTr("Now it's time to install a few mods!" + tutorial.text = qsTr("Now it's time to install a few mods!" + "Please go along with this because we need a few mods installed to demonstrate other features") waitForClick() }, @@ -61,8 +70,12 @@ function getTutorialSteps() tutorial.text = qsTr("There are a few ways to get mods into ModOrganizer. " + "If you associated MO with NXM links in the settings you can now use your regular browser to send downloads from Nexus to MO. " + "Click on \"Nexus\" to open nexus, find a mod and click the green download buttons on Nexus saying \"Download with Manager\".") - highlightAction("actionNexus", true) - tutorialControl.waitForAction("actionNexus") + if (tutorialControl.waitForAction("actionNexus")) { + highlightAction("actionNexus", true) + } else { + console.error("browser action broken") + waitForClick() + } }, function() { -- cgit v1.3.1 From 78f628e0af2f2df562c40ac1424b432b6a969055 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 28 Nov 2014 11:06:28 +0100 Subject: cleanup und bugfixes after refactoring --- src/browserdialog.cpp | 4 +- src/browserdialog.h | 1 - src/downloadmanager.cpp | 2 +- src/executableslist.cpp | 16 ++- src/installationmanager.cpp | 3 +- src/iuserinterface.h | 14 +- src/main.cpp | 159 ++++++--------------- src/mainwindow.cpp | 337 +++++++++++--------------------------------- src/mainwindow.h | 42 +++--- src/modinfo.cpp | 1 + src/nexusinterface.cpp | 20 ++- src/nexusinterface.h | 4 + src/nxmaccessmanager.cpp | 4 +- src/organizer.pro | 5 +- src/organizercore.cpp | 239 ++++++++++++++++++++++++++----- src/organizercore.h | 91 ++++++------ src/organizerproxy.cpp | 2 +- src/plugincontainer.cpp | 20 ++- src/plugincontainer.h | 35 ++--- src/pluginlist.cpp | 4 +- src/pluginlist.h | 2 +- src/profile.cpp | 8 +- src/selfupdater.cpp | 26 ++-- src/selfupdater.h | 1 + src/settings.cpp | 27 ++-- src/settings.h | 2 +- src/shared/appconfig.inc | 1 + src/shared/gameinfo.cpp | 20 +-- src/shared/gameinfo.h | 2 - src/shared/skyriminfo.cpp | 2 +- 30 files changed, 526 insertions(+), 568 deletions(-) (limited to 'src/nexusinterface.h') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 933b4bc0..82cd8d49 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -24,11 +24,11 @@ along with Mod Organizer. If not, see . #include "report.h" #include "persistentcookiejar.h" -#include #include "json.h" #include #include +#include "settings.h" #include #include #include @@ -49,7 +49,7 @@ BrowserDialog::BrowserDialog(QWidget *parent) ui->setupUi(this); m_AccessManager->setCookieJar(new PersistentCookieJar( - QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/cookies.dat", this)); + QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/cookies.dat"))); Qt::WindowFlags flags = windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint; Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; diff --git a/src/browserdialog.h b/src/browserdialog.h index 10b44fac..680f5c03 100644 --- a/src/browserdialog.h +++ b/src/browserdialog.h @@ -23,7 +23,6 @@ along with Mod Organizer. If not, see . #include "browserview.h" #include "tutorialcontrol.h" #include -#include #include #include #include diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index b803d170..d785a939 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -980,7 +980,7 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) int index = 0; try { DownloadInfo *info = findDownload(this->sender(), &index); - if (info != NULL) { + if (info != nullptr) { if (info->m_State == STATE_CANCELING) { setState(info, STATE_CANCELED); } else if (info->m_State == STATE_PAUSING) { diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 11158c5b..c46cea10 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -72,18 +72,20 @@ ExecutablesList::~ExecutablesList() void ExecutablesList::init(IPluginGame *game) { + Q_ASSERT(game != nullptr); m_Executables.clear(); for (const ExecutableInfo &info : game->executables()) { - addExecutableInternal(info.title(), - info.binary().absoluteFilePath(), - info.arguments().join(" "), - info.workingDirectory().absolutePath(), - info.closeMO(), - info.steamAppID()); + if (info.isValid()) { + addExecutableInternal(info.title(), + info.binary().absoluteFilePath(), + info.arguments().join(" "), + info.workingDirectory().absolutePath(), + info.closeMO(), + info.steamAppID()); + } } } - void ExecutablesList::getExecutables(std::vector::iterator &begin, std::vector::iterator &end) { begin = m_Executables.begin(); diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 0fb1b78d..25fe06a4 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -68,7 +68,8 @@ template T resolveFunction(QLibrary &lib, const char *name) InstallationManager::InstallationManager() - : m_InstallationProgress(nullptr), m_SupportedExtensions(boost::assign::list_of("zip")("rar")("7z")("fomod")("001")) + : m_InstallationProgress(nullptr) + , m_SupportedExtensions({ "zip", "rar", "7z", "fomod", "001" }) { QLibrary archiveLib("dlls\\archive.dll"); if (!archiveLib.load()) { diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 76d4c75a..5cb61a25 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -11,11 +11,7 @@ class IUserInterface { public: - void storeSettings(QSettings &settings); - - virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "") = 0; - - virtual bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL) = 0; + virtual void storeSettings(QSettings &settings) = 0; virtual void registerPluginTool(MOBase::IPluginTool *tool) = 0; virtual void registerModPage(MOBase::IPluginModPage *modPage) = 0; @@ -24,9 +20,9 @@ public: virtual void disconnectPlugins() = 0; - virtual bool close() = 0; + virtual bool closeWindow() = 0; - virtual void setEnabled(bool enabled) = 0; + virtual void setWindowEnabled(bool enabled) = 0; virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; @@ -34,6 +30,10 @@ public: virtual bool saveArchiveList() = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual bool unlockClicked() = 0; + }; #endif // IUSERINTERFACE_H diff --git a/src/main.cpp b/src/main.cpp index 0e112873..3f0360c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,14 +82,13 @@ using namespace MOBase; using namespace MOShared; -// set up required folders (for a first install or after an update or to fix a broken installation) bool bootstrap() { GameInfo &gameInfo = GameInfo::instance(); // remove the temporary backup directory in case we're restarting after an update QString moDirectory = QDir::fromNativeSeparators(ToQString(gameInfo.getOrganizerDirectory())); - QString backupDirectory = moDirectory.mid(0).append("/update_backup"); + QString backupDirectory = moDirectory + "/update_backup"; if (QDir(backupDirectory).exists()) { shellDelete(QStringList(backupDirectory)); } @@ -97,47 +96,6 @@ bool bootstrap() // cycle logfile removeOldFiles(ToQString(GameInfo::instance().getLogDir()), "ModOrganizer*.log", 5, QDir::Name); - // create organizer directories - QString dirNames[] = { - QDir::fromNativeSeparators(ToQString(gameInfo.getProfilesDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getModsDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getDownloadDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getOverwriteDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getLogDir())), - QDir::fromNativeSeparators(ToQString(gameInfo.getTutorialDir())) - }; - static const int NUM_DIRECTORIES = sizeof(dirNames) / sizeof(QString); - - // optimistic run: try to simply create the directories: - for (int i = 0; i < NUM_DIRECTORIES; ++i) { - if (!QDir(dirNames[i]).exists()) { - QDir().mkdir(dirNames[i]); - } - } - - // verify all directories exist and are writable, - // otherwise invoke the helper to create them and make them writable - for (int i = 0; i < NUM_DIRECTORIES; ++i) { - QFileInfo fileInfo(dirNames[i]); - if (!fileInfo.exists() || !fileInfo.isWritable()) { - if (QMessageBox::question(NULL, QObject::tr("Permissions required"), - QObject::tr("The current user account doesn't have the required access rights to run " - "Mod Organizer. The neccessary changes can be made automatically (the MO directory " - "will be made writable for the current user account). You will be asked to run " - "\"helper.exe\" with administrative rights."), - QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { - if (!Helper::init(GameInfo::instance().getOrganizerDirectory())) { - return false; - } - } else { - return false; - } - // no matter which directory didn't exist/wasn't writable, the helper - // should have created them all so we can break the loop - break; - } - } - // verify the hook-dll exists QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()); @@ -154,29 +112,6 @@ bool bootstrap() return true; } - -void cleanupDir() -{ - // files from previous versions of MO that are no longer - // required (in that location) - QString fileNames[] = { - "NCC/GamebryoBase.dll", - "plugins/helloWorld.dll", - "plugins/testnexus.py" - }; - - static const int NUM_FILES = sizeof(fileNames) / sizeof(QString); - - qDebug("cleaning up unused files"); - - for (int i = 0; i < NUM_FILES; ++i) { - if (QFile::remove(QApplication::applicationDirPath().append("/").append(fileNames[i]))) { - qDebug("%s removed in cleanup", - QApplication::applicationDirPath().append("/").append(fileNames[i]).toUtf8().constData()); - } - } -} - bool isNxmLink(const QString &link) { return link.left(6).toLower() == "nxm://"; @@ -299,6 +234,27 @@ bool HaveWriteAccess(const std::wstring &path) } +QString determineProfile(QStringList arguments, const QSettings &settings) +{ + QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); + { // see if there is a profile on the command line + int profileIndex = arguments.indexOf("-p", 1); + if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { + qDebug("profile overwritten on command line"); + selectedProfileName = arguments.at(profileIndex + 1); + } + arguments.removeAt(profileIndex); + arguments.removeAt(profileIndex); + } + if (selectedProfileName.isEmpty()) { + qDebug("no configured profile"); + } else { + qDebug("configured profile: %s", qPrintable(selectedProfileName)); + } + + return selectedProfileName; +} + int main(int argc, char *argv[]) { MOApplication application(argc, argv); @@ -414,7 +370,7 @@ int main(int argc, char *argv[]) reportError(QObject::tr("No game identified in \"%1\". The directory is required to contain " "the game binary and its launcher.").arg(gamePath)); } - SelectionDialog selection(QObject::tr("Please select the game to manage"), NULL); + SelectionDialog selection(QObject::tr("Please select the game to manage"), nullptr); { // add options QString skyrimPath = ToQString(SkyrimInfo::getRegPathStatic()); @@ -462,6 +418,11 @@ int main(int argc, char *argv[]) settings.setValue("gamePath", gamePath.toUtf8().constData()); } + if (pluginContainer.managedGame(ToQString(GameInfo::instance().getGameName())) == nullptr) { + reportError(QObject::tr("Plugin to handle %1 not installed").arg(ToQString(GameInfo::instance().getGameName()))); + return 1; + } + if (!settings.contains("game_edition")) { std::vector editions = GameInfo::instance().getSteamVariants(); if (editions.size() > 1) { @@ -486,7 +447,25 @@ int main(int argc, char *argv[]) return -1; } - cleanupDir(); + QString selectedProfileName = determineProfile(arguments, settings); + organizer.setCurrentProfile(selectedProfileName); + + // if we have a command line parameter, it is either a nxm link or + // a binary to start + if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { + QString exeName = arguments.at(1); + qDebug("starting %s from command line", qPrintable(exeName)); + arguments.removeFirst(); // remove application name (ModOrganizer.exe) + arguments.removeFirst(); // remove binary name + // pass the remaining parameters to the binary + try { + organizer.startApplication(exeName, arguments, QString(), QString()); + return 0; + } catch (const std::exception &e) { + reportError(QObject::tr("failed to start application: %1").arg(e.what())); + return 1; + } + } qDebug("initializing tutorials"); TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/")); @@ -506,52 +485,8 @@ int main(int argc, char *argv[]) mainWindow.readSettings(); - QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray()); - - { // see if there is a profile on the command line - int profileIndex = arguments.indexOf("-p", 1); - if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { - qDebug("profile overwritten on command line"); - selectedProfileName = arguments.at(profileIndex + 1); - } - arguments.removeAt(profileIndex); - arguments.removeAt(profileIndex); - } - if (selectedProfileName.isEmpty()) { - qDebug("no configured profile"); - } else { - qDebug("configured profile: %s", qPrintable(selectedProfileName)); - } - - // if we have a command line parameter, it is either a nxm link or - // a binary to start - if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) { - QString exeName = arguments.at(1); - qDebug("starting %s from command line", qPrintable(exeName)); - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - // pass the remaining parameters to the binary - try { - mainWindow.startApplication(exeName, arguments, QString(), selectedProfileName); - } catch (const std::exception &e) { - reportError(QObject::tr("failed to start application: %1").arg(e.what())); - } - - return 0; - } - mainWindow.createFirstProfile(); - if (selectedProfileName.length() != 0) { - if (!mainWindow.setCurrentProfile(selectedProfileName)) { - mainWindow.setCurrentProfile(1); - qWarning("failed to set profile: %s", - selectedProfileName.toUtf8().constData()); - } - } else { - mainWindow.setCurrentProfile(1); - } - qDebug("displaying main window"); mainWindow.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4762358e..d4a7f4fc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -16,7 +16,6 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef Q_MOC_RUN #include "mainwindow.h" #include "ui_mainwindow.h" @@ -68,7 +67,6 @@ along with Mod Organizer. If not, see . #include #include #include -#endif // Q_MOC_RUN #include #include #include @@ -176,6 +174,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize m_RefreshProgress->setTextVisible(true); m_RefreshProgress->setRange(0, 100); m_RefreshProgress->setValue(0); + m_RefreshProgress->setVisible(false); statusBar()->addWidget(m_RefreshProgress, 1000); statusBar()->clearMessage(); @@ -255,7 +254,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString))); connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); - connect(m_OrganizerCore.pluginList(), SIGNAL(saveTimer()), this, SLOT(savePluginList())); connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved())); @@ -265,6 +263,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(m_OrganizerCore.directoryRefresher(), SIGNAL(progress(int)), this, SLOT(refresher_progress(int))); connect(m_OrganizerCore.directoryRefresher(), SIGNAL(error(QString)), this, SLOT(showError(QString))); + connect(m_OrganizerCore.downloadManager(), SIGNAL(downloadAdded()), ui->downloadView, SLOT(scrollToBottom())); + connect(&m_SavesWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(refreshSavesIfOpen())); connect(&m_OrganizerCore.settings(), SIGNAL(languageChanged(QString)), this, SLOT(languageChange(QString))); @@ -274,9 +274,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(m_OrganizerCore.updater(), SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(m_OrganizerCore.updater(), SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); + connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); - connect(NexusInterface::instance(), SIGNAL(needLogin()), this, SLOT(nexusLogin())); + connect(NexusInterface::instance(), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin())); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); @@ -285,7 +285,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize connect(&m_OrganizerCore, &OrganizerCore::modInstalled, this, &MainWindow::modInstalled); - connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), this, SLOT(requestDownload(QUrl,QNetworkReply*))); + connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), &m_OrganizerCore, SLOT(requestDownload(QUrl,QNetworkReply*))); connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString))); @@ -318,6 +318,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, Organize installTranslator(QFileInfo(fileName).baseName()); } + ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->getName()); + refreshExecutablesList(); updateToolBar(); } @@ -999,18 +1001,12 @@ void MainWindow::setExecutableIndex(int index) void MainWindow::activateSelectedProfile() { - QString profileName = ui->profileBox->currentText(); - qDebug("activate profile \"%s\"", qPrintable(profileName)); - QString profileDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())) - .append("/").append(profileName); - m_OrganizerCore.setCurrentProfile(new Profile(QDir(profileDir))); + m_OrganizerCore.setCurrentProfile(ui->profileBox->currentText()); m_ModListSortProxy->setProfile(m_OrganizerCore.currentProfile()); - connect(m_OrganizerCore.currentProfile(), SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); - refreshSaveList(); - refreshModList(); + m_OrganizerCore.refreshModList(); } void MainWindow::on_profileBox_currentIndexChanged(int index) @@ -1330,7 +1326,7 @@ static bool BySortValue(const std::pair &LHS, const st void MainWindow::updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) { - + m_DefaultArchives = defaultArchives; ui->bsaList->clear(); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) ui->bsaList->header()->setSectionResizeMode(QHeaderView::ResizeToContents); @@ -1413,11 +1409,11 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString subItem->setExpanded(true); } - m_OrganizerCore.checkBSAList(); + checkBSAList(); } -void MainWindow::checkBSAList(const QStringList &defaultArchives) +void MainWindow::checkBSAList() { ui->bsaList->blockSignals(true); @@ -1433,7 +1429,7 @@ void MainWindow::checkBSAList(const QStringList &defaultArchives) item->setToolTip(0, QString()); if (item->checkState(0) == Qt::Unchecked) { - if (defaultArchives.contains(filename)) { + if (m_DefaultArchives.contains(filename)) { item->setIcon(0, QIcon(":/MO/gui/warning")); item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); modWarning = true; @@ -1557,8 +1553,34 @@ void MainWindow::storeSettings(QSettings &settings) settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); settings.setValue("manage_bsas", ui->manageArchivesBox->isChecked()); + + settings.setValue("selected_executable", ui->executablesListBox->currentIndex()); } +void MainWindow::lock() +{ + m_LockDialog = new LockedDialog(qApp->activeWindow()); + m_LockDialog->show(); + setEnabled(false); +} + +void MainWindow::unlock() +{ + if (m_LockDialog != nullptr) { + m_LockDialog->hide(); + m_LockDialog->deleteLater(); + } + setEnabled(true); +} + +bool MainWindow::unlockClicked() +{ + if (m_LockDialog != nullptr) { + return m_LockDialog->unlockClicked(); + } else { + return false; + } +} void MainWindow::on_btnRefreshData_clicked() { @@ -1581,18 +1603,20 @@ void MainWindow::on_tabWidget_currentChanged(int index) } -void MainWindow::installMod() +void MainWindow::installMod(QString fileName) { try { - QStringList extensions = m_OrganizerCore.installationManager()->getSupportedExtensions(); - for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { - *iter = "*." + *iter; - } + if (fileName.isEmpty()) { + QStringList extensions = m_OrganizerCore.installationManager()->getSupportedExtensions(); + for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { + *iter = "*." + *iter; + } - QString fileName = FileDialogMemory::getOpenFileName("installMod", this, tr("Choose Mod"), QString(), - tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + fileName = FileDialogMemory::getOpenFileName("installMod", this, tr("Choose Mod"), QString(), + tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + } - if (fileName.length() == 0) { + if (fileName.isEmpty()) { return; } else { m_OrganizerCore.installMod(fileName); @@ -1692,7 +1716,7 @@ bool MainWindow::modifyExecutablesDialog() { bool result = false; try { - EditExecutablesDialog dialog(m_OrganizerCore.executablesList()); + EditExecutablesDialog dialog(*m_OrganizerCore.executablesList()); if (dialog.exec() == QDialog::Accepted) { m_OrganizerCore.setExecutablesDialog(dialog.getExecutablesList()); result = true; @@ -1805,33 +1829,6 @@ void MainWindow::setESPListSorting(int index) } } - -bool MainWindow::setCurrentProfile(int index) -{ - QComboBox *profilesBox = findChild("profileBox"); - if (index >= profilesBox->count()) { - return false; - } else { - profilesBox->setCurrentIndex(index); - return true; - } -} - -bool MainWindow::setCurrentProfile(const QString &name) -{ - QComboBox *profilesBox = findChild("profileBox"); - for (int i = 0; i < profilesBox->count(); ++i) { - if (QString::compare(profilesBox->itemText(i), name, Qt::CaseInsensitive) == 0) { - profilesBox->setCurrentIndex(i); - return true; - } - } - // profile not valid - profilesBox->setCurrentIndex(1); - return false; -} - - void MainWindow::refresher_progress(int percent) { if (percent == 100) { @@ -1864,7 +1861,7 @@ void MainWindow::modorder_changed() } m_OrganizerCore.refreshBSAList(); m_OrganizerCore.currentProfile()->writeModlist(); - m_OrganizerCore.saveArchiveList(); + saveArchiveList(); m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); { // refresh selection @@ -1881,11 +1878,11 @@ void MainWindow::modorder_changed() } } -void MainWindow::modInstalled() +void MainWindow::modInstalled(const QString &modName) { QModelIndexList posList = - m_OrganizerCore.modList().match(m_OrganizerCore.modList().index(0, 0), - Qt::DisplayRole, static_cast(modName)); + m_OrganizerCore.modList()->match(m_OrganizerCore.modList()->index(0, 0), + Qt::DisplayRole, static_cast(modName)); if (posList.count() == 1) { ui->modList->scrollTo(posList.at(0)); } @@ -2150,7 +2147,7 @@ void MainWindow::restoreBackup_clicked() if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); } - refreshModList(); + m_OrganizerCore.refreshModList(); } } } @@ -2261,7 +2258,7 @@ void MainWindow::resumeDownload(int downloadIndex) QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { //m_PostLoginTasks.push_back(boost::bind(&MainWindow::resumeDownload, _1, downloadIndex)); - m_OrganizerCore.doAfterLogin(std::bind(&MainWindow::resumeDownload, this, downloadIndex)); + m_OrganizerCore.doAfterLogin([&] () { this->resumeDownload(downloadIndex); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this); @@ -2277,7 +2274,7 @@ void MainWindow::endorseMod(ModInfo::Ptr mod) } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_PostLoginTasks.push_back(boost::bind(&MainWindow::endorseMod, _1, mod)); + m_OrganizerCore.doAfterLogin(boost::bind(&MainWindow::endorseMod, this, mod)); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2304,7 +2301,7 @@ void MainWindow::unendorse_clicked() ModInfo::getByIndex(m_ContextRow)->endorse(false); } else { if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_PostLoginTasks.push_back(boost::mem_fn(&MainWindow::unendorse_clicked)); + m_OrganizerCore.doAfterLogin([&] () { this->unendorse_clicked(); }); NexusInterface::instance()->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); @@ -2312,14 +2309,14 @@ void MainWindow::unendorse_clicked() } } -void MainWindow::loginFailed(const QString &message) +void MainWindow::loginFailed(const QString&) { statusBar()->hide(); } void MainWindow::windowTutorialFinished(const QString &windowName) { - m_OrganizerCore.settings().directInterface().setValue(QString("CompletedWindowTutorials/") + windowName, this); + m_OrganizerCore.settings().directInterface().setValue(QString("CompletedWindowTutorials/") + windowName, true); } void MainWindow::overwriteClosed(int) @@ -2356,7 +2353,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, modInfo->saveMeta(); ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), this); connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); - connect(&dialog, SIGNAL(downloadRequest(QString)), this, SLOT(downloadRequestedNXM(QString))); + connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext()), this, SLOT(modOpenNext()), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenPrev()), this, SLOT(modOpenPrev()), Qt::QueuedConnection); @@ -2393,6 +2390,16 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } } +bool MainWindow::closeWindow() +{ + return close(); +} + +void MainWindow::setWindowEnabled(bool enabled) +{ + setEnabled(enabled); +} + void MainWindow::modOpenNext() { @@ -2495,7 +2502,7 @@ void MainWindow::syncOverwrite() if (syncDialog.exec() == QDialog::Accepted) { syncDialog.apply(QDir::fromNativeSeparators(m_OrganizerCore.settings().getModDirectory())); modInfo->testValid(); - refreshDirectoryStructure(); + m_OrganizerCore.refreshDirectoryStructure(); } } @@ -2530,7 +2537,7 @@ void MainWindow::createModFromOverwrite() shellMove(QStringList(QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), QStringList(QDir::toNativeSeparators(newMod->absolutePath())), this); - refreshModList(); + m_OrganizerCore.refreshModList(); } void MainWindow::cancelModListEditor() @@ -2742,7 +2749,7 @@ void MainWindow::savePrimaryCategory() bool MainWindow::saveArchiveList() { - if (m_ArchivesInit) { + if (m_OrganizerCore.isArchivesInit()) { SafeWriteFile archiveFile(m_OrganizerCore.currentProfile()->getArchivesFileName()); for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); @@ -2776,7 +2783,7 @@ void MainWindow::checkModsForUpdates() } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { - m_OrganizerCore.doAfterLogin(boost::mem_fn(&MainWindow::checkModsForUpdates)); + m_OrganizerCore.doAfterLogin([this] () {this->checkModsForUpdates();}); NexusInterface::instance()->getAccessManager()->login(username, password); } else { // otherwise there will be no endorsement info m_ModsToUpdate = ModInfo::checkAllForUpdate(this); @@ -3297,12 +3304,6 @@ void MainWindow::linkMenu() } } -void MainWindow::downloadSpeed(const QString &serverName, int bytesPerSecond) -{ - m_OrganizerCore.settings().setDownloadSpeed(serverName, bytesPerSecond); -} - - void MainWindow::on_actionSettings_triggered() { QString oldModDirectory(m_OrganizerCore.settings().getModDirectory()); @@ -3400,64 +3401,6 @@ void MainWindow::languageChange(const QString &newLanguage) } -void MainWindow::installDownload(int index) -{ - try { - QString fileName = m_OrganizerCore.downloadManager()->getFilePath(index); - int modID = m_OrganizerCore.downloadManager()->getModID(index); - GuessedValue modName; - - // see if there already are mods with the specified mod id - if (modID != 0) { - std::vector modInfo = ModInfo::getByModID(modID); - for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { - std::vector flags = (*iter)->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { - modName.update((*iter)->name(), GUESS_PRESET); - (*iter)->saveMeta(); - } - } - } - - m_OrganizerCore.currentProfile()->writeModlistNow(); - - bool hasIniTweaks = false; - m_OrganizerCore.installationManager()->setModsDirectory(m_OrganizerCore.settings().getModDirectory()); - if (m_OrganizerCore.installationManager()->install(fileName, modName, hasIniTweaks)) { - MessageDialog::showMessage(tr("Installation successful"), this); - refreshModList(); - - QModelIndexList posList = m_OrganizerCore.modList()->match(m_OrganizerCore.modList()->index(0, 0), Qt::DisplayRole, static_cast(modName)); - if (posList.count() == 1) { - ui->modList->scrollTo(posList.at(0)); - } - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - - if (hasIniTweaks && - (QMessageBox::question(this, tr("Configure Mod"), - tr("This mod contains ini tweaks. Do you want to configure them now?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); - } - - m_ModInstalled(modName); - } else { - reportError(tr("mod \"%1\" not found").arg(modName)); - } - m_OrganizerCore.downloadManager()->markInstalled(index); - - emit modInstalled(); - } else if (m_OrganizerCore.installationManager()->wasCancelled()) { - QMessageBox::information(this, tr("Installation cancelled"), tr("The mod was not installed completely."), QMessageBox::Ok); - } - } catch (const std::exception &e) { - reportError(e.what()); - } -} - - void MainWindow::writeDataToFile(QFile &file, const QString &directory, const DirectoryEntry &directoryEntry) { { // list files @@ -3704,7 +3647,7 @@ void MainWindow::openDataFile() QString arguments; switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) { case 1: { - spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->getName(), targetInfo.absolutePath(), ""); + m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->getName(), targetInfo.absolutePath(), ""); } break; case 2: { ::ShellExecuteW(NULL, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), NULL, NULL, SW_SHOWNORMAL); @@ -3832,7 +3775,7 @@ void MainWindow::updateDownloadListDelegate() ui->downloadView->sortByColumn(1, Qt::AscendingOrder); ui->downloadView->header()->resizeSections(QHeaderView::Fixed); - connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), this, SLOT(installDownload(int))); + connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), m_OrganizerCore.downloadManager(), SLOT(removeDownload(int, bool))); connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), m_OrganizerCore.downloadManager(), SLOT(restoreDownload(int))); @@ -4312,7 +4255,7 @@ std::string MainWindow::readFromPipe(HANDLE stdOutRead) return result; } -void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog) +void MainWindow::processLOOTOut(const std::string &lootOut, std::string &errorMessages, QProgressDialog &dialog) { std::vector lines; boost::split(lines, lootOut, boost::is_any_of("\r\n")); @@ -4347,122 +4290,6 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportU } } - -HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) -{ - QFileInfo binary; - QString arguments = args.join(" "); - QString currentDirectory = cwd; - QString profileName = profile; - if (profile.length() == 0) { - if (m_OrganizerCore.currentProfile() != NULL) { - profileName = m_OrganizerCore.currentProfile()->getName(); - } else { - throw MyException(tr("No profile set")); - } - } - QString steamAppID; - if (executable.contains('\\') || executable.contains('/')) { - // file path - - binary = QFileInfo(executable); - if (binary.isRelative()) { - // relative path, should be relative to game directory - binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable); - } - - std::vector::iterator current, end; - m_OrganizerCore.executablesList()->getExecutables(current, end); - for (; current != end; ++current) { - if (current->m_BinaryInfo == binary) { - steamAppID = current->m_SteamAppID; - currentDirectory = current->m_WorkingDirectory; - } - } - - if (cwd.length() == 0) { - currentDirectory = binary.absolutePath(); - } - } else { - // only a file name, search executables list - try { - const Executable &exe = m_OrganizerCore.executablesList()->find(executable); - steamAppID = exe.m_SteamAppID; - if (arguments == "") { - arguments = exe.m_Arguments; - } - binary = exe.m_BinaryInfo; - if (cwd.length() == 0) { - currentDirectory = exe.m_WorkingDirectory; - } - } catch (const std::runtime_error&) { - qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); - binary = QFileInfo(executable); - } - } - - return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); -} - - -bool MainWindow::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) -{ - LockedDialog *dialog = new LockedDialog(this); - dialog->show(); - setEnabled(false); - ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); this->setEnabled(true); }); - - DWORD retLen; - JOBOBJECT_BASIC_PROCESS_ID_LIST info; - - bool isJobHandle = true; - - ULONG lastProcessID = ULONG_MAX; - HANDLE processHandle = handle; - - DWORD res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { - if (isJobHandle) { - if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { - if (info.NumberOfProcessIdsInList == 0) { - // fake signaled state - res = WAIT_OBJECT_0; - break; - } else { - // this is indeed a job handle. Figure out one of the process handles as well. - if (lastProcessID != info.ProcessIdList[0]) { - lastProcessID = info.ProcessIdList[0]; - if (processHandle != handle) { - ::CloseHandle(processHandle); - } - processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); - } - } - } else { - // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there - // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. - // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without - // the right to break out. - if (::GetLastError() != ERROR_MORE_DATA) { - isJobHandle = false; - } - } - } - - // keep processing events so the app doesn't appear dead - QCoreApplication::processEvents(); - - res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); - } - - if (exitCode != NULL) { - ::GetExitCodeProcess(processHandle, exitCode); - } - ::CloseHandle(processHandle); - - return res == WAIT_OBJECT_0; -} - void MainWindow::on_bossButton_clicked() { std::string reportURL; @@ -4556,14 +4383,14 @@ void MainWindow::on_bossButton_clicked() // keep processing events so the app doesn't appear dead QCoreApplication::processEvents(); std::string lootOut = readFromPipe(stdOutRead); - processLOOTOut(lootOut, reportURL, errorMessages, dialog); + processLOOTOut(lootOut, errorMessages, dialog); res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); } std::string remainder = readFromPipe(stdOutRead).c_str(); if (remainder.length() > 0) { - processLOOTOut(remainder, reportURL, errorMessages, dialog); + processLOOTOut(remainder, errorMessages, dialog); } DWORD exitCode = 0UL; ::GetExitCodeProcess(processHandle, &exitCode); @@ -4720,7 +4547,7 @@ void MainWindow::on_restoreModsButton_clicked() QMessageBox::critical(this, tr("Restore failed"), tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError()))); } - refreshModList(false); + m_OrganizerCore.refreshModList(false); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index c8cb8152..27d75ba8 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -61,6 +61,7 @@ namespace Ui { class MainWindow; } +class LockedDialog; class QToolButton; class ModListSortProxy; class ModListGroupCategoriesProxy; @@ -79,9 +80,13 @@ public: QWidget *parent = 0); ~MainWindow(); - void storeSettings(QSettings &settings); + void storeSettings(QSettings &settings) override; void readSettings(); + virtual void lock() override; + virtual void unlock() override; + virtual bool unlockClicked() override; + bool addProfile(); void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives); void refreshDataTree(); @@ -92,9 +97,6 @@ public: void setModListSorting(int index); void setESPListSorting(int index); - bool setCurrentProfile(int index); - bool setCurrentProfile(const QString &name); - void createFirstProfile(); bool saveArchiveList(); @@ -106,11 +108,7 @@ public: void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite); std::string readFromPipe(HANDLE stdOutRead); - void processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog); - - HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); - - bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL); + void processLOOTOut(const std::string &lootOut, std::string &errorMessages, QProgressDialog &dialog); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); @@ -120,11 +118,11 @@ public: virtual void disconnectPlugins(); - virtual bool close(); - virtual void setEnabled(bool enabled); - void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab); + virtual bool closeWindow(); + virtual void setWindowEnabled(bool enabled); + public slots: void displayColumnSelection(const QPoint &pos); @@ -161,8 +159,6 @@ protected: private: - void refreshModList(bool saveChanges = true); - void actionToToolButton(QAction *&sourceAction); void updateToolBar(); @@ -172,13 +168,10 @@ private: void startSteam(); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); - void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly); - void refreshDirectoryStructure(); bool refreshProfiles(bool selectProfile = true); void refreshExecutablesList(); - void installMod(); + void installMod(QString fileName = ""); QList findFileInfos(const QString &path, const std::function &filter) const; @@ -275,6 +268,8 @@ private: QProgressBar *m_RefreshProgress; bool m_Refreshing; + QStringList m_DefaultArchives; + QAbstractItemModel *m_ModListGroupingProxy; ModListSortProxy *m_ModListSortProxy; @@ -322,6 +317,8 @@ private: bool m_DidUpdateMasterList; + LockedDialog *m_LockDialog { nullptr }; + private slots: void showMessage(const QString &message); @@ -363,7 +360,6 @@ private slots: void linkMenu(); void languageChange(const QString &newLanguage); - void modStatusChanged(unsigned int index); void saveSelectionChanged(QListWidgetItem *newItem); void windowTutorialFinished(const QString &windowName); @@ -385,7 +381,6 @@ private slots: void linkClicked(const QString &url); - void installDownload(int index); void updateAvailable(); void motdReceived(const QString &motd); @@ -402,7 +397,7 @@ private slots: void modDetailsUpdated(bool success); void modlistChanged(int row); - void modInstalled(); + void modInstalled(const QString &modName); void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); @@ -436,7 +431,7 @@ private slots: void startExeAction(); - void checkBSAList(const QStringList &defaultArchives); + void checkBSAList(); void updateProblemsButton(); @@ -464,8 +459,6 @@ private slots: */ void allowListResize(); - void downloadSpeed(const QString &serverName, int bytesPerSecond); - void toolBar_customContextMenuRequested(const QPoint &point); void removeFromToolbar(); void overwriteClosed(int); @@ -479,7 +472,6 @@ private slots: void about(); void delayedRemove(); - void requestDownload(const QUrl &url, QNetworkReply *reply); void modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); void modListSortIndicatorChanged(int column, Qt::SortOrder order); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 189e67b2..d773823a 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -476,6 +476,7 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc , m_Path(path.absolutePath()) , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) + , m_NexusBridge() { testValid(); m_CreationTime = QFileInfo(path.absolutePath()).created(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index b4006097..d68fe8fe 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -34,35 +34,34 @@ using namespace MOShared; NexusBridge::NexusBridge(const QString &subModule) : m_Interface(NexusInterface::instance()) - , m_Url(MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl())) + , m_Url() // lazy initialized , m_SubModule(subModule) { } - void NexusBridge::requestDescription(int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule, url())); } void NexusBridge::requestFiles(int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule, url())); } void NexusBridge::requestFileInfo(int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule, url())); } void NexusBridge::requestDownloadURL(int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule, url())); } void NexusBridge::requestToggleEndorsement(int modID, bool endorse, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, m_Url)); + m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule, url())); } void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID) @@ -138,6 +137,13 @@ void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int } } +QString NexusBridge::url() { + if (m_Url.isEmpty()) { + m_Url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()); + } + return m_Url; +} + QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); diff --git a/src/nexusinterface.h b/src/nexusinterface.h index af2f8c75..28accd3d 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -107,6 +107,10 @@ public slots: void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); +private: + + QString url(); + private: NexusInterface *m_Interface; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 18b47707..18568dad 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -22,6 +22,7 @@ along with Mod Organizer. If not, see . #include "report.h" #include "utility.h" #include "selfupdater.h" +#include "settings.h" #include "persistentcookiejar.h" #include #include @@ -48,8 +49,9 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) , m_MOVersion(moVersion) , m_LoginAttempted(false) { + setCookieJar(new PersistentCookieJar( - QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/nexus_cookies.dat", this)); + QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat"))); } NXMAccessManager::~NXMAccessManager() diff --git a/src/organizer.pro b/src/organizer.pro index 98dbcd4a..a16e396d 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -284,7 +284,8 @@ CONFIG(debug, debug|release) { #QMAKE_CXXFLAGS_WARN_ON -= -W3 #QMAKE_CXXFLAGS_WARN_ON += -W4 -QMAKE_CXXFLAGS += /wd4100 -wd4127 -wd4512 -wd4189 +QMAKE_CXXFLAGS -= -w34100 -w34189 +QMAKE_CXXFLAGS += -wd4100 -wd4127 -wd4512 -wd4189 CONFIG += embed_manifest_exe @@ -346,7 +347,7 @@ SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 68989c8c..751cc010 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -141,7 +141,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) , m_UserInterface(nullptr) , m_PluginContainer(nullptr) , m_CurrentProfile(nullptr) - , m_Settings() + , m_Settings(initSettings) , m_Updater(NexusInterface::instance()) , m_AboutToRun() , m_FinishedRun() @@ -170,12 +170,11 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); + connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); - // make directory refresher run in a separate thread m_RefresherThread.start(); m_DirectoryRefresher.moveToThread(&m_RefresherThread); @@ -214,7 +213,9 @@ void OrganizerCore::storeSettings() if (m_UserInterface != nullptr) { m_UserInterface->storeSettings(settings); } - settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); + if (m_CurrentProfile != nullptr) { + settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData()); + } settings.setValue("ask_for_nexuspw", m_AskForNexusPW); settings.remove("customExecutables"); @@ -238,9 +239,6 @@ void OrganizerCore::storeSettings() } settings.endArray(); - QComboBox *executableBox = findChild("executablesListBox"); - settings.setValue("selected_executable", executableBox->currentIndex()); - FileDialogMemory::save(settings); settings.sync(); @@ -287,6 +285,10 @@ void OrganizerCore::updateExecutablesList(QSettings &settings) } settings.endArray(); + + + // TODO this has nothing to do with executables list move to an appropriate function! + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); } void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *widget) @@ -295,16 +297,17 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *wid m_UserInterface = userInterface; - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex,int)), widget, SLOT(modorder_changed())); - connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); - connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), widget, SLOT(modRenamed(QString,QString))); - connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); - connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); - connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); - connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, SLOT(fileMoved(QString, QString, QString))); - connect(&m_DownloadManager, SIGNAL(downloadAdded()), widget, SLOT(scrollToBottom())); - connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + if (widget != nullptr) { + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex,int)), widget, SLOT(modorder_changed())); + connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), widget, SLOT(modRenamed(QString,QString))); + connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); + connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); + connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); + connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, SLOT(fileMoved(QString, QString, QString))); + connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + } m_InstallationManager.setParentWidget(widget); m_Updater.setUserInterface(widget); @@ -423,15 +426,29 @@ void OrganizerCore::removeOrigin(const QString &name) refreshLists(); } +void OrganizerCore::downloadSpeed(const QString &serverName, int bytesPerSecond) +{ + m_Settings.setDownloadSpeed(serverName, bytesPerSecond); +} + InstallationManager *OrganizerCore::installationManager() { return &m_InstallationManager; } -void OrganizerCore::setCurrentProfile(Profile *profile) { +void OrganizerCore::setCurrentProfile(const QString &profileName) +{ + QString profileDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()) + "/" + profileName; + + Profile *newProfile = new Profile(QDir(profileDir)); + delete m_CurrentProfile; - m_CurrentProfile = profile; - m_ModList.setProfile(profile); + m_CurrentProfile = newProfile; + m_ModList.setProfile(newProfile); + + connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); + + refreshDirectoryStructure(); } MOBase::IGameInfo &OrganizerCore::gameInfo() const @@ -512,7 +529,7 @@ bool OrganizerCore::removeMod(MOBase::IModInterface *mod) } } -void OrganizerCore::modDataChanged(MOBase::IModInterface *mod) +void OrganizerCore::modDataChanged(MOBase::IModInterface*) { refreshModList(false); } @@ -579,6 +596,63 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName) return nullptr; } +void OrganizerCore::installDownload(int downloadIndex) +{ + try { + QString fileName = m_DownloadManager.getFilePath(downloadIndex); + int modID = m_DownloadManager.getModID(downloadIndex); + GuessedValue modName; + + // see if there already are mods with the specified mod id + if (modID != 0) { + std::vector modInfo = ModInfo::getByModID(modID); + for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { + std::vector flags = (*iter)->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { + modName.update((*iter)->name(), GUESS_PRESET); + (*iter)->saveMeta(); + } + } + } + + m_CurrentProfile->writeModlistNow(); + + bool hasIniTweaks = false; + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { + MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); + refreshModList(); + + int modIndex = ModInfo::getIndex(modName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + + if (hasIniTweaks + && (m_UserInterface != nullptr) + && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), + tr("This mod contains ini tweaks. Do you want to configure them now?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { + m_UserInterface->displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); + } + + m_ModInstalled(modName); + } else { + reportError(tr("mod \"%1\" not found").arg(modName)); + } + m_DownloadManager.markInstalled(downloadIndex); + + emit modInstalled(modName); + } else if (m_InstallationManager.wasCancelled()) { + QMessageBox::information(qApp->activeWindow(), + tr("Installation cancelled"), + tr("The mod was not installed completely."), + QMessageBox::Ok); + } + } catch (const std::exception &e) { + reportError(e.what()); + } +} + QString OrganizerCore::resolvePath(const QString &fileName) const { if (m_DirectoryStructure == nullptr) { @@ -687,10 +761,10 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->getName(), currentDirectory, steamAppID); if (processHandle != INVALID_HANDLE_VALUE) { if (closeAfterStart && (m_UserInterface != nullptr)) { - m_UserInterface->close(); + m_UserInterface->closeWindow(); } else { if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(false); + m_UserInterface->setWindowEnabled(false); } // re-enable the locked dialog because what'd be the point otherwise? dialog->setEnabled(true); @@ -738,7 +812,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument ::CloseHandle(processHandle); if (m_UserInterface != nullptr) { - m_UserInterface->setEnabled(true); + m_UserInterface->setWindowEnabled(true); } refreshDirectoryStructure(); // need to remove our stored load order because it may be outdated if a foreign tool changed the @@ -807,16 +881,118 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & HANDLE OrganizerCore::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile) { - if (m_UserInterface != nullptr) { - return m_UserInterface->startApplication(executable, args, cwd, profile); + QFileInfo binary; + QString arguments = args.join(" "); + QString currentDirectory = cwd; + QString profileName = profile; + if (profile.length() == 0) { + if (m_CurrentProfile != nullptr) { + profileName = m_CurrentProfile->getName(); + } else { + throw MyException(tr("No profile set")); + } + } + QString steamAppID; + if (executable.contains('\\') || executable.contains('/')) { + // file path + + binary = QFileInfo(executable); + if (binary.isRelative()) { + // relative path, should be relative to game directory + binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable); + } + + std::vector::iterator current, end; + m_ExecutablesList.getExecutables(current, end); + for (; current != end; ++current) { + if (current->m_BinaryInfo == binary) { + steamAppID = current->m_SteamAppID; + currentDirectory = current->m_WorkingDirectory; + } + } + + if (cwd.length() == 0) { + currentDirectory = binary.absolutePath(); + } + } else { + // only a file name, search executables list + try { + const Executable &exe = m_ExecutablesList.find(executable); + steamAppID = exe.m_SteamAppID; + if (arguments == "") { + arguments = exe.m_Arguments; + } + binary = exe.m_BinaryInfo; + if (cwd.length() == 0) { + currentDirectory = exe.m_WorkingDirectory; + } + } catch (const std::runtime_error&) { + qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); + binary = QFileInfo(executable); + } } + + return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } -bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) const +bool OrganizerCore::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) { if (m_UserInterface != nullptr) { - return m_UserInterface->waitForProcessOrJob(handle, exitCode); + m_UserInterface->lock(); + ON_BLOCK_EXIT([&] () { m_UserInterface->unlock(); }); + } + + DWORD retLen; + JOBOBJECT_BASIC_PROCESS_ID_LIST info; + + bool isJobHandle = true; + + ULONG lastProcessID = ULONG_MAX; + HANDLE processHandle = handle; + + DWORD res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); + while ((res != WAIT_FAILED) + && (res != WAIT_OBJECT_0) + && ((m_UserInterface == nullptr) || !m_UserInterface->unlockClicked())) { + if (isJobHandle) { + if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { + if (info.NumberOfProcessIdsInList == 0) { + // fake signaled state + res = WAIT_OBJECT_0; + break; + } else { + // this is indeed a job handle. Figure out one of the process handles as well. + if (lastProcessID != info.ProcessIdList[0]) { + lastProcessID = info.ProcessIdList[0]; + if (processHandle != handle) { + ::CloseHandle(processHandle); + } + processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); + } + } + } else { + // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there + // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. + // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without + // the right to break out. + if (::GetLastError() != ERROR_MORE_DATA) { + isJobHandle = false; + } + } + } + + // keep processing events so the app doesn't appear dead + QCoreApplication::processEvents(); + + res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); } + + if (exitCode != NULL) { + ::GetExitCodeProcess(processHandle, exitCode); + } + ::CloseHandle(processHandle); + + return res == WAIT_OBJECT_0; } bool OrganizerCore::onAboutToRun(const std::function &func) @@ -905,7 +1081,7 @@ void OrganizerCore::refreshBSAList() } if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(); + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); } m_ArchivesInit = true; @@ -922,7 +1098,6 @@ void OrganizerCore::refreshLists() void OrganizerCore::updateModActiveState(int index, bool active) { ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - QDir dir(modInfo->absolutePath()); foreach (const QString &esm, dir.entryList(QStringList("*.esm"), QDir::Files)) { m_PluginList.enableESP(esm, active); @@ -977,7 +1152,7 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::P void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) { if (m_PluginContainer != nullptr) { - for (IPluginModPage *modPage : m_PluginContainer->plugins()) { + for (IPluginModPage *modPage : m_PluginContainer->plugins()) { ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo(); if (modPage->handlesDownload(url, reply->url(), *fileInfo)) { fileInfo->repository = modPage->name(); @@ -1167,7 +1342,7 @@ void OrganizerCore::loginFailedUpdate(const QString &message) std::vector OrganizerCore::activeProblems() const { std::vector problems; - if (enabledCount() > 255) { + if (m_PluginList.enabledCount() > 255) { problems.push_back(PROBLEM_TOOMANYPLUGINS); } return problems; diff --git a/src/organizercore.h b/src/organizercore.h index b362a002..17b1d2dc 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -81,7 +81,7 @@ public: void setExecutablesDialog(const ExecutablesList &executablesList) { m_ExecutablesList = executablesList; } Profile *currentProfile() { return m_CurrentProfile; } - void setCurrentProfile(Profile *profile); + void setCurrentProfile(const QString &profileName); void setExecutablesList(const ExecutablesList &executablesList); @@ -95,7 +95,6 @@ public: bool isArchivesInit() const { return m_ArchivesInit; } bool saveCurrentLists(); - void savePluginList(); void prepareStart(); @@ -105,50 +104,45 @@ public: void refreshDirectoryStructure(); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); - void requestDownload(const QUrl &url, QNetworkReply *reply); - - void doAfterLogin(std::function &function) { m_PostLoginTasks.append(function); } + void doAfterLogin(std::function function) { m_PostLoginTasks.append(function); } void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir ¤tDirectory = QDir(), bool closeAfterStart = true, const QString &steamAppID = ""); + HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); - void modStatusChanged(unsigned int index); - - void loginSuccessful(bool necessary); void loginSuccessfulUpdate(bool necessary); - void loginFailed(const QString &message); void loginFailedUpdate(const QString &message); public: - virtual MOBase::IGameInfo &gameInfo() const; - virtual MOBase::IModRepositoryBridge *createNexusBridge() const; - virtual QString profileName() const; - virtual QString profilePath() const; - virtual QString downloadsPath() const; - virtual MOBase::VersionInfo appVersion() const; - virtual MOBase::IModInterface *getMod(const QString &name); - virtual MOBase::IModInterface *createMod(MOBase::GuessedValue &name); - virtual bool removeMod(MOBase::IModInterface *mod); - virtual void modDataChanged(MOBase::IModInterface *mod); - virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const; - virtual void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); - virtual QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def) const; - virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); - virtual QString pluginDataPath() const; - virtual MOBase::IModInterface *installMod(const QString &fileName); - virtual QString resolvePath(const QString &fileName) const; - virtual QStringList listDirectories(const QString &directoryName) const; - virtual QStringList findFiles(const QString &path, const std::function &filter) const; - virtual QStringList getFileOrigins(const QString &fileName) const; - virtual QList findFileInfos(const QString &path, const std::function &filter) const; - virtual DownloadManager *downloadManager(); - virtual PluginList *pluginList(); - virtual ModList *modList(); - virtual HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); - virtual bool waitForApplication(HANDLE handle, LPDWORD exitCode) const; - virtual bool onModInstalled(const std::function &func); - virtual bool onAboutToRun(const std::function &func); - virtual bool onFinishedRun(const std::function &func); - virtual void refreshModList(bool saveChanges = true); + MOBase::IGameInfo &gameInfo() const; + MOBase::IModRepositoryBridge *createNexusBridge() const; + QString profileName() const; + QString profilePath() const; + QString downloadsPath() const; + MOBase::VersionInfo appVersion() const; + MOBase::IModInterface *getMod(const QString &name); + MOBase::IModInterface *createMod(MOBase::GuessedValue &name); + bool removeMod(MOBase::IModInterface *mod); + void modDataChanged(MOBase::IModInterface *mod); + QVariant pluginSetting(const QString &pluginName, const QString &key) const; + void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); + QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def) const; + void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); + QString pluginDataPath() const; + MOBase::IModInterface *installMod(const QString &fileName); + QString resolvePath(const QString &fileName) const; + QStringList listDirectories(const QString &directoryName) const; + QStringList findFiles(const QString &path, const std::function &filter) const; + QStringList getFileOrigins(const QString &fileName) const; + QList findFileInfos(const QString &path, const std::function &filter) const; + DownloadManager *downloadManager(); + PluginList *pluginList(); + ModList *modList(); + HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); + bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = nullptr); + bool onModInstalled(const std::function &func); + bool onAboutToRun(const std::function &func); + bool onFinishedRun(const std::function &func); + void refreshModList(bool saveChanges = true); public: // IPluginDiagnose interface @@ -163,15 +157,25 @@ public slots: void profileRefresh(); void externalMessage(const QString &message); + void savePluginList(); + void refreshLists(); + void installDownload(int downloadIndex); + + void modStatusChanged(unsigned int index); + void requestDownload(const QUrl &url, QNetworkReply *reply); + void downloadRequestedNXM(const QString &url); + + bool nexusLogin(); + signals: /** * @brief emitted after a mod has been installed * @node this is currently only used for tutorials */ - void modInstalled(); + void modInstalled(const QString &modName); private: @@ -179,17 +183,16 @@ private: bool queryLogin(QString &username, QString &password); - bool nexusLogin(); - - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); void updateModActiveState(int index, bool active); private slots: void directory_refreshed(); - void downloadRequestedNXM(const QString &url); void downloadRequested(QNetworkReply *reply, int modID, const QString &fileName); void removeOrigin(const QString &name); + void downloadSpeed(const QString &serverName, int bytesPerSecond); + void loginSuccessful(bool necessary); + void loginFailed(const QString &message); private: diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 07a006f5..61470a1a 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -95,7 +95,7 @@ HANDLE OrganizerProxy::startApplication(const QString &executable, const QString bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const { - return m_Proxied->waitForApplication(handle, exitCode); + return m_Proxied->waitForProcessOrJob(handle, exitCode); } bool OrganizerProxy::onAboutToRun(const std::function &func) diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index bd96828e..92073c12 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -1,11 +1,9 @@ #include "plugincontainer.h" #include "organizerproxy.h" #include "report.h" -#include #include #include #include -#include #include #include #include @@ -24,6 +22,7 @@ namespace bf = boost::fusion; PluginContainer::PluginContainer(OrganizerCore *organizer) : m_Organizer(organizer) + , m_UserInterface(nullptr) { } @@ -33,6 +32,17 @@ void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *w for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { proxy->setParentWidget(widget); } + + if (userInterface != nullptr) { + for (IPluginModPage *modPage : bf::at_key(m_Plugins)) { + userInterface->registerModPage(modPage); + } + + for (IPluginTool *tool : bf::at_key(m_Plugins)) { + userInterface->registerPluginTool(tool); + } + } + m_UserInterface = userInterface; } @@ -69,7 +79,7 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) { { // generic treatment for all plugins IPlugin *pluginObj = qobject_cast(plugin); - if (pluginObj == NULL) { + if (pluginObj == nullptr) { qDebug("not an IPlugin"); return false; } @@ -90,7 +100,6 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) IPluginModPage *modPage = qobject_cast(plugin); if (verifyPlugin(modPage)) { bf::at_key(m_Plugins).push_back(modPage); - registerModPage(modPage); return true; } } @@ -106,7 +115,6 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) IPluginTool *tool = qobject_cast(plugin); if (verifyPlugin(tool)) { bf::at_key(m_Plugins).push_back(tool); - registerPluginTool(tool); return true; } } @@ -240,7 +248,7 @@ void PluginContainer::loadPlugins() loadCheck.open(QIODevice::WriteOnly); - QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath()); + QString pluginPath = qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()); qDebug("looking for plugins in %s", QDir::toNativeSeparators(pluginPath).toUtf8().constData()); QDirIterator iter(pluginPath, QDir::Files | QDir::NoDotAndDotDot); diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 213b4154..d0a101ad 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -15,6 +15,7 @@ #include #ifndef Q_MOC_RUN #include +#include #endif // Q_MOC_RUN #include @@ -25,6 +26,21 @@ class PluginContainer : public QObject, public MOBase::IPluginDiagnose Q_OBJECT Q_INTERFACES(MOBase::IPluginDiagnose) +private: + + typedef boost::fusion::map< + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair>, + boost::fusion::pair> + > PluginMap; + + static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; + public: PluginContainer(OrganizerCore *organizer); @@ -37,7 +53,7 @@ public: MOBase::IPluginGame *managedGame(const QString &name) const; template - std::vector plugins() const { + std::vector plugins() { return boost::fusion::at_key(m_Plugins); } @@ -60,26 +76,10 @@ signals: private: bool verifyPlugin(MOBase::IPlugin *plugin); - void registerPluginTool(MOBase::IPluginTool *tool); - void registerModPage(MOBase::IPluginModPage *modPage); void registerGame(MOBase::IPluginGame *game); bool registerPlugin(QObject *pluginObj, const QString &fileName); bool unregisterPlugin(QObject *pluginObj, const QString &fileName); -private: - - typedef boost::fusion::map< - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair>, - boost::fusion::pair> - > PluginMap; - - static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; private: @@ -99,4 +99,5 @@ private: QFile m_PluginsCheck; }; + #endif // PLUGINCONTAINER_H diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index b2d02cd2..2b1fcca1 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -166,7 +166,7 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD originName = modInfo->name(); } - m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), originName, ToQString(current->getFullPath()), hasIni)); + m_ESPs.push_back(ESPInfo(filename, forceEnabled, originName, ToQString(current->getFullPath()), hasIni)); } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); } @@ -1168,7 +1168,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) } -PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, +PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni) : m_Name(name), m_FullPath(fullPath), m_Enabled(enabled), m_ForceEnabled(enabled), diff --git a/src/pluginlist.h b/src/pluginlist.h index 729c6f8b..933a38ee 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -272,7 +272,7 @@ private: struct ESPInfo { - ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath, bool hasIni); + ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni); QString m_Name; QString m_FullPath; bool m_Enabled; diff --git a/src/profile.cpp b/src/profile.cpp index 6e9d8f0f..37ed94cc 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -59,7 +59,8 @@ Profile::Profile(const QString &name, bool useDefaultSettings) : m_SaveTimer(NULL) { initTimer(); - QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())); + QString profilesDir = qApp->property("datapath").toString() + "/" + ToQString(AppConfig::profilesPath()); +qDebug("pd %s", qPrintable(profilesDir)); QDir profileBase(profilesDir); QString fixedName = name; @@ -78,6 +79,7 @@ Profile::Profile(const QString &name, bool useDefaultSettings) touchFile("modlist.txt"); touchFile("archives.txt"); +qDebug("fp %s", qPrintable(fullPath)); GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings); } catch (...) { // clean up in case of an error @@ -88,8 +90,8 @@ Profile::Profile(const QString &name, bool useDefaultSettings) } -Profile::Profile(const QDir& directory) - : m_Directory(directory), m_SaveTimer(NULL) +Profile::Profile(const QDir &directory) + : m_Directory(directory), m_SaveTimer(nullptr) { initTimer(); if (!QFile::exists(m_Directory.filePath("modlist.txt"))) { diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 803b2cfa..0f75e392 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -56,8 +56,12 @@ template T resolveFunction(QLibrary &lib, const char *name) SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) - : m_Parent(nullptr), m_Interface(nexusInterface), m_UpdateRequestID(-1), - m_Reply(NULL), m_Progress(nullptr), m_Attempts(3) + : m_Parent(nullptr) + , m_Interface(nexusInterface) + , m_UpdateRequestID(-1) + , m_Reply(nullptr) + , m_Progress(nullptr) + , m_Attempts(3) { m_Progress.setMaximum(100); @@ -90,7 +94,7 @@ SelfUpdater::~SelfUpdater() void SelfUpdater::setUserInterface(QWidget *widget) { - m_Progress.setParent(widget); + m_Progress.setVisible(false); m_Parent = widget; } @@ -130,6 +134,16 @@ void SelfUpdater::startUpdate() } +void SelfUpdater::showProgress() +{ + m_Progress.setModal(true); + m_Progress.setParent(m_Parent, Qt::Dialog); + m_Progress.show(); + m_Progress.setValue(0); + m_Progress.setWindowTitle(tr("Update")); + m_Progress.setLabelText(tr("Download in progress")); +} + void SelfUpdater::download(const QString &downloadLink, const QString &fileName) { QNetworkAccessManager *accessManager = m_Interface->getAccessManager(); @@ -139,11 +153,7 @@ void SelfUpdater::download(const QString &downloadLink, const QString &fileName) m_Reply = accessManager->get(request); m_UpdateFile.setFileName(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()).append("\\").append(fileName))); m_UpdateFile.open(QIODevice::WriteOnly); - m_Progress.setModal(true); - m_Progress.show(); - m_Progress.setValue(0); - m_Progress.setWindowTitle(tr("Update")); - m_Progress.setLabelText(tr("Download in progress")); + showProgress(); connect(m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); connect(m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); diff --git a/src/selfupdater.h b/src/selfupdater.h index 3490b58a..c376234a 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -119,6 +119,7 @@ private: void updateProgressFile(LPCWSTR fileName); void report7ZipError(LPCWSTR errorMessage); QString retrieveNews(const QString &description); + void showProgress(); private slots: diff --git a/src/settings.cpp b/src/settings.cpp index 274e5979..418c5661 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -57,13 +57,13 @@ private: static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01, 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 }; -Settings *Settings::s_Instance = NULL; +Settings *Settings::s_Instance = nullptr; -Settings::Settings() - : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat) +Settings::Settings(const QSettings &config) + : m_Settings(config.fileName(), config.format()) { - if (s_Instance != NULL) { + if (s_Instance != nullptr) { throw std::runtime_error("second instance of \"Settings\" created"); } else { s_Instance = this; @@ -73,13 +73,13 @@ Settings::Settings() Settings::~Settings() { - s_Instance = NULL; + s_Instance = nullptr; } Settings &Settings::instance() { - if (s_Instance == NULL) { + if (s_Instance == nullptr) { throw std::runtime_error("no instance of \"Settings\""); } return *s_Instance; @@ -110,9 +110,9 @@ void Settings::registerAsNXMHandler(bool force) std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); std::wstring mode = force ? L"forcereg" : L"reg"; std::wstring parameters = mode + L" " + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\""; - HINSTANCE res = ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), parameters.c_str(), NULL, SW_SHOWNORMAL); + HINSTANCE res = ::ShellExecuteW(nullptr, L"open", nxmPath.c_str(), parameters.c_str(), nullptr, SW_SHOWNORMAL); if ((int)res <= 32) { - QMessageBox::critical(NULL, tr("Failed"), + QMessageBox::critical(nullptr, tr("Failed"), tr("Sorry, failed to start the helper application")); } } @@ -181,10 +181,9 @@ QString Settings::getSteamAppID() const QString Settings::getDownloadDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", qApp->applicationDirPath() + "/downloads").toString()); } - void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) { m_Settings.beginGroup("Servers"); @@ -221,12 +220,14 @@ std::map Settings::getPreferredServers() QString Settings::getCacheDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", + qApp->applicationDirPath() + "/webcache").toString()); } QString Settings::getModDirectory() const { - return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString()); + return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", + qApp->applicationDirPath() + "/mods").toString()); } QString Settings::getNMMVersion() const @@ -647,7 +648,7 @@ void Settings::query(QWidget *parent) { // advanced settings if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) && - (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " + (QMessageBox::question(nullptr, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " "Mods not present (or named differently) in the new location will be disabled in all profiles. " "There is no way to undo this unless you backed up your profiles manually. Proceed?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { diff --git a/src/settings.h b/src/settings.h index 5d398f49..cba8392e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -43,7 +43,7 @@ public: /** * @brief constructor **/ - Settings(); + Settings(const QSettings &config); virtual ~Settings(); diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc index 080d3655..5d9d9f8e 100644 --- a/src/shared/appconfig.inc +++ b/src/shared/appconfig.inc @@ -1,5 +1,6 @@ APPPARAM(std::wstring, translationPrefix, L"organizer") APPPARAM(std::wstring, pluginPath, L"plugins") +APPPARAM(std::wstring, profilesPath, L"profiles") APPPARAM(std::wstring, stylesheetsPath, L"stylesheets") APPPARAM(std::wstring, profileTweakIni, L"profile_tweaks.ini") APPPARAM(std::wstring, logFile, L"ModOrganizer.log") diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 5439efff..1a815701 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -27,12 +27,12 @@ along with Mod Organizer. If not, see . #include "skyriminfo.h" #include "util.h" +#include +#include #include #include #include -#include -#include namespace MOShared { @@ -49,7 +49,7 @@ GameInfo::GameInfo(const std::wstring &moDirectory, const std::wstring &moDataDi void GameInfo::cleanup() { delete GameInfo::s_Instance; - GameInfo::s_Instance = NULL; + GameInfo::s_Instance = nullptr; } @@ -126,7 +126,7 @@ bool GameInfo::init(const std::wstring &moDirectory, const std::wstring &moDataD GameInfo &GameInfo::instance() { - assert(s_Instance != NULL); + assert(s_Instance != nullptr && "gameinfo not yet initialized"); return *s_Instance; } @@ -151,18 +151,6 @@ std::wstring GameInfo::getIniFilename() const } -std::wstring GameInfo::getDownloadDir() const -{ - return m_OrganizerDirectory + L"\\downloads"; -} - - -std::wstring GameInfo::getCacheDir() const -{ - return m_OrganizerDirectory + L"\\webcache"; -} - - std::wstring GameInfo::getOverwriteDir() const { return m_OrganizerDirectory + L"\\overwrite"; diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index c11679f3..a9da0b97 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -109,8 +109,6 @@ public: virtual std::wstring getProfilesDir() const; virtual std::wstring getIniFilename() const; - virtual std::wstring getDownloadDir() const; - virtual std::wstring getCacheDir() const; virtual std::wstring getOverwriteDir() const; virtual std::wstring getLogDir() const; virtual std::wstring getLootDir() const; diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 1203e3ed..7f70c097 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -259,7 +259,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults) } if (!::CopyFileW(source.c_str(), target.c_str(), true)) { if (::GetLastError() != ERROR_FILE_EXISTS) { - throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false)); + throw windows_error(std::string("failed to copy ini file: ") + ToString(source, false) + " to " + ToString(target, false)); } } } -- cgit v1.3.1