mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af89733121 | ||
|
|
baf1240be7 | ||
|
|
6e4626852c | ||
|
|
e8f56d65d2 | ||
|
|
5e7c875b29 | ||
|
|
b2a7c7c223 | ||
|
|
7b4582c1b1 | ||
|
|
5d7b23acef | ||
|
|
3628ec930b | ||
|
|
e22aeca3ff | ||
|
|
cbd6b288eb | ||
|
|
484749e612 | ||
|
|
5b3cdaa83b | ||
|
|
6d3f88b330 | ||
|
|
0feb4b702d | ||
|
|
5a4b6e70fd | ||
|
|
18574c2ba8 | ||
|
|
1eb783aae3 | ||
|
|
93a19799b8 | ||
|
|
2a31eb40fb | ||
|
|
cf0a1bc2be | ||
|
|
48c8cca578 | ||
|
|
977b407525 | ||
|
|
f4b1aba61a |
@@ -18,5 +18,18 @@ staging_prepare/*
|
||||
staging_trans/*
|
||||
tools/python_zip/*
|
||||
Makefile
|
||||
html
|
||||
*.vcxproj
|
||||
*.pdb
|
||||
*.dll
|
||||
*.exp
|
||||
*.tlog
|
||||
*.user
|
||||
*.obj
|
||||
*.suo
|
||||
*.sln
|
||||
*.log
|
||||
*.filters
|
||||
*.lib
|
||||
syntax: regexp
|
||||
Makefile\.(Debug|Release)
|
||||
|
||||
+13
-6
@@ -153,17 +153,24 @@ private:
|
||||
"<a href=\"\\1\">\\1</a>");
|
||||
m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"),
|
||||
"<a href=\"\\1\">\\2</a>");
|
||||
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\\]"), " ");
|
||||
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), " ");
|
||||
m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"),
|
||||
"<a href=\"mailto:\\1\">\\2</a>");
|
||||
m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"),
|
||||
"<a href=\"http://www.youtube.com/v/\\1\">http://www.youtube.com/v/\\1</a>");
|
||||
|
||||
m_ColorMap = boost::assign::map_list_of("red", "FF0000")("green", "00FF00")("blue", "0000FF")
|
||||
("black", "000000")("gray", "7F7F7F")("white", "FFFFFF")
|
||||
("yellow", "FFFF00")("cyan", "00FFFF")("magenta", "FF00FF")
|
||||
("brown", "A52A2A")("orange", "FFCC00");
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("red", "FF0000"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("green", "00FF00"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("blue", "0000FF"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("black", "000000"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("gray", "7F7F7F"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("white", "FFFFFF"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("yellow", "FFFF00"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("cyan", "00FFFF"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("magenta", "FF00FF"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("brown", "A52A2A"));
|
||||
m_ColorMap.insert(std::make_pair<QString, QString>("orange", "FFCC00"));
|
||||
|
||||
// make all patterns non-greedy and case-insensitive
|
||||
for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) {
|
||||
|
||||
@@ -43,7 +43,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
BrowserDialog::BrowserDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::BrowserDialog)
|
||||
, m_AccessManager(new QNetworkAccessManager)
|
||||
, m_AccessManager(new QNetworkAccessManager(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -255,16 +255,16 @@ void BrowserDialog::on_searchEdit_returnPressed()
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserDialog::on_browserTabWidget_currentChanged(QWidget *current)
|
||||
void BrowserDialog::on_refreshBtn_clicked()
|
||||
{
|
||||
BrowserView *currentView = qobject_cast<BrowserView*>(current);
|
||||
getCurrentView()->reload();
|
||||
}
|
||||
|
||||
void BrowserDialog::on_browserTabWidget_currentChanged(int index)
|
||||
{
|
||||
BrowserView *currentView = qobject_cast<BrowserView*>(ui->browserTabWidget->widget(index));
|
||||
if (currentView != NULL) {
|
||||
ui->backBtn->setEnabled(currentView->history()->canGoBack());
|
||||
ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserDialog::on_refreshBtn_clicked()
|
||||
{
|
||||
getCurrentView()->reload();
|
||||
}
|
||||
|
||||
+2
-2
@@ -99,10 +99,10 @@ private slots:
|
||||
|
||||
void startSearch();
|
||||
|
||||
void on_browserTabWidget_currentChanged(QWidget *arg1);
|
||||
|
||||
void on_refreshBtn_clicked();
|
||||
|
||||
void on_browserTabWidget_currentChanged(int index);
|
||||
|
||||
private:
|
||||
|
||||
QString guessFileName(const QString &url);
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
|
||||
static const int CATEGORY_SPECIAL_CONFLICT = 10004;
|
||||
static const int CATEGORY_SPECIAL_NOTENDORSED = 10005;
|
||||
static const int CATEGORY_SPECIAL_MANAGED = 10006;
|
||||
static const int CATEGORY_SPECIAL_UNMANAGED = 10007;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
+62
-18
@@ -20,8 +20,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "directoryrefresher.h"
|
||||
#include "utility.h"
|
||||
#include "report.h"
|
||||
#include "modinfo.h"
|
||||
#include <gameinfo.h>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
|
||||
using namespace MOBase;
|
||||
@@ -46,12 +48,20 @@ DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods)
|
||||
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods
|
||||
, const std::set<QString> &managedArchives)
|
||||
{
|
||||
QMutexLocker locker(&m_RefreshLock);
|
||||
m_Mods = mods;
|
||||
}
|
||||
|
||||
m_Mods.clear();
|
||||
for (auto mod = mods.begin(); mod != mods.end(); ++mod) {
|
||||
QString name = std::get<0>(*mod);
|
||||
ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
|
||||
m_Mods.push_back(EntryInfo(name, std::get<1>(*mod), info->stealFiles(), info->archives(), std::get<2>(*mod)));
|
||||
}
|
||||
|
||||
m_EnabledArchives = managedArchives;
|
||||
}
|
||||
|
||||
void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
|
||||
{
|
||||
@@ -61,24 +71,59 @@ void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
|
||||
}
|
||||
|
||||
static wchar_t *dirs[] = { L"fomod" };
|
||||
for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
|
||||
structure->removeDir(dirs[i]);
|
||||
for (int i = 0; i < sizeof(dirs) / sizeof(wchar_t*); ++i) {
|
||||
structure->removeDir(std::wstring(dirs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory)
|
||||
void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives)
|
||||
{
|
||||
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
|
||||
|
||||
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
|
||||
QDir dir(directory);
|
||||
QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files);
|
||||
foreach (QFileInfo file, bsaFiles) {
|
||||
directoryStructure->addFromBSA(ToWString(modName), directoryW,
|
||||
ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
|
||||
foreach (const QString &archive, archives) {
|
||||
QFileInfo fileInfo(archive);
|
||||
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
|
||||
directoryStructure->addFromBSA(ToWString(modName), directoryW,
|
||||
ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles)
|
||||
{
|
||||
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
|
||||
|
||||
if (stealFiles.length() > 0) {
|
||||
// instead of adding all the files of the target directory, we just change the root of the specified
|
||||
// files to this mod
|
||||
FilesOrigin origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
|
||||
foreach (const QString &filename, stealFiles) {
|
||||
QFileInfo fileInfo(filename);
|
||||
FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
|
||||
if (file.get() != NULL) {
|
||||
if (file->getOrigin() == 0) {
|
||||
// replace data as the origin on this bsa
|
||||
file->removeOrigin(0);
|
||||
file->addOrigin(origin.getID(), file->getFileTime(), L"");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure
|
||||
, const QString &modName
|
||||
, int priority
|
||||
, const QString &directory
|
||||
, const QStringList &stealFiles
|
||||
, const QStringList &archives)
|
||||
{
|
||||
addModFilesToStructure(directoryStructure, modName, priority, directory, stealFiles);
|
||||
addModBSAToStructure(directoryStructure, modName, priority, directory, archives);
|
||||
}
|
||||
|
||||
void DirectoryRefresher::refresh()
|
||||
{
|
||||
QMutexLocker locker(&m_RefreshLock);
|
||||
@@ -87,23 +132,22 @@ void DirectoryRefresher::refresh()
|
||||
|
||||
m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0);
|
||||
|
||||
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
|
||||
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
|
||||
|
||||
// TODO what was the point of having the priority in this tuple? the list is already sorted by priority
|
||||
std::vector<std::tuple<QString, QString, int> >::const_iterator iter = m_Mods.begin();
|
||||
auto iter = m_Mods.begin();
|
||||
|
||||
//TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but inverted!
|
||||
for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
|
||||
QString modName = std::get<0>(*iter);
|
||||
try {
|
||||
addModToStructure(m_DirectoryStructure, modName, i, std::get<1>(*iter));
|
||||
addModToStructure(m_DirectoryStructure, iter->modName, i, iter->absolutePath, iter->stealFiles, iter->archives);
|
||||
} catch (const std::exception &e) {
|
||||
emit error(tr("failed to read bsa: %1").arg(e.what()));
|
||||
}
|
||||
emit progress((i * 100) / m_Mods.size() + 1);
|
||||
}
|
||||
|
||||
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
|
||||
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
|
||||
|
||||
emit progress(100);
|
||||
|
||||
cleanStructure(m_DirectoryStructure);
|
||||
|
||||
@@ -20,11 +20,13 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef DIRECTORYREFRESHER_H
|
||||
#define DIRECTORYREFRESHER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <vector>
|
||||
#include <QMutex>
|
||||
#include <tuple>
|
||||
#include <directoryentry.h>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QStringList>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,7 +62,7 @@ public:
|
||||
*
|
||||
* @param mods list of the mods to include
|
||||
**/
|
||||
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods);
|
||||
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods, const std::set<QString> &managedArchives);
|
||||
|
||||
/**
|
||||
* @brief sets up the directory where mods are stored
|
||||
@@ -77,12 +79,34 @@ public:
|
||||
|
||||
/**
|
||||
* @brief add files for a mod to the directory structure, including bsas
|
||||
* @param directoryStructure
|
||||
* @param modName
|
||||
* @param priorityBSA
|
||||
* @param priority
|
||||
* @param directory
|
||||
* @param priorityDir
|
||||
* @param stealFiles
|
||||
* @param archives
|
||||
*/
|
||||
static void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory);
|
||||
void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles, const QStringList &archives);
|
||||
|
||||
/**
|
||||
* @brief add only the bsas of a mod to the directory structure
|
||||
* @param directoryStructure
|
||||
* @param modName
|
||||
* @param priority
|
||||
* @param directory
|
||||
* @param archives
|
||||
*/
|
||||
void addModBSAToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives);
|
||||
|
||||
/**
|
||||
* @brief add only regular files ofr a mod to the directory structure
|
||||
* @param directoryStructure
|
||||
* @param modName
|
||||
* @param priority
|
||||
* @param directory
|
||||
* @param stealFiles
|
||||
*/
|
||||
void addModFilesToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -99,7 +123,22 @@ signals:
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::tuple<QString, QString, int> > m_Mods;
|
||||
struct EntryInfo {
|
||||
EntryInfo(const QString &modName, const QString &absolutePath,
|
||||
const QStringList &stealFiles, const QStringList &archives, int priority)
|
||||
: modName(modName), absolutePath(absolutePath), stealFiles(stealFiles)
|
||||
, archives(archives), priority(priority) {}
|
||||
QString modName;
|
||||
QString absolutePath;
|
||||
QStringList stealFiles;
|
||||
QStringList archives;
|
||||
int priority;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::vector<EntryInfo> m_Mods;
|
||||
std::set<QString> m_EnabledArchives;
|
||||
MOShared::DirectoryEntry *m_DirectoryStructure;
|
||||
QMutex m_RefreshLock;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
|
||||
<file name="icuin49.dll"/>
|
||||
<file name="icuuc49.dll"/>
|
||||
<file name="icudt49.dll"/>
|
||||
<file name="icuin52.dll"/>
|
||||
<file name="icuuc52.dll"/>
|
||||
<file name="icudt52.dll"/>
|
||||
<file name="Qt5Core.dll"/>
|
||||
<file name="Qt5Declarative.dll"/>
|
||||
<file name="Qt5Gui.dll"/>
|
||||
@@ -14,4 +14,4 @@
|
||||
<file name="Qt5Widgets.dll"/>
|
||||
<file name="Qt5Xml.dll"/>
|
||||
<file name="Qt5XmlPatterns.dll"/>
|
||||
</assembly>
|
||||
</assembly>
|
||||
+122
-84
@@ -26,8 +26,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "utility.h"
|
||||
#include "json.h"
|
||||
#include "selectiondialog.h"
|
||||
#include "bbcode.h"
|
||||
#include <utility.h>
|
||||
#include <bbcode.h>
|
||||
#include <QTimer>
|
||||
#include <QFileInfo>
|
||||
#include <QRegExp>
|
||||
@@ -148,7 +148,10 @@ void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile)
|
||||
metaFile.rename(newName.mid(0).append(".meta"));
|
||||
}
|
||||
}
|
||||
m_Output.setFileName(newName);
|
||||
if (!m_Output.isOpen()) {
|
||||
// can't set file name if it's open
|
||||
m_Output.setFileName(newName);
|
||||
}
|
||||
}
|
||||
|
||||
bool DownloadManager::DownloadInfo::isPausedState()
|
||||
@@ -253,67 +256,70 @@ void DownloadManager::setShowHidden(bool showHidden)
|
||||
|
||||
void DownloadManager::refreshList()
|
||||
{
|
||||
int downloadsBefore = m_ActiveDownloads.size();
|
||||
try {
|
||||
int downloadsBefore = m_ActiveDownloads.size();
|
||||
|
||||
// remove finished downloads
|
||||
for (QVector<DownloadInfo*>::iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end();) {
|
||||
if (((*Iter)->m_State == STATE_READY) || ((*Iter)->m_State == STATE_INSTALLED) || ((*Iter)->m_State == STATE_UNINSTALLED)) {
|
||||
delete *Iter;
|
||||
Iter = m_ActiveDownloads.erase(Iter);
|
||||
} else {
|
||||
++Iter;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList nameFilters(m_SupportedExtensions);
|
||||
foreach (const QString &extension, m_SupportedExtensions) {
|
||||
nameFilters.append("*." + extension);
|
||||
}
|
||||
|
||||
nameFilters.append(QString("*").append(UNFINISHED));
|
||||
QDir dir(QDir::fromNativeSeparators(m_OutputDirectory));
|
||||
|
||||
// find orphaned meta files and delete them (sounds cruel but it's better for everyone)
|
||||
QStringList orphans;
|
||||
QStringList metaFiles = dir.entryList(QStringList() << "*.meta");
|
||||
foreach (const QString &metaFile, metaFiles) {
|
||||
QString baseFile = metaFile.left(metaFile.length() - 5);
|
||||
if (!QFile::exists(dir.absoluteFilePath(baseFile))) {
|
||||
orphans.append(dir.absoluteFilePath(metaFile));
|
||||
}
|
||||
}
|
||||
if (orphans.size() > 0) {
|
||||
qDebug("%d orphaned meta files will be deleted", orphans.size());
|
||||
shellDelete(orphans, true);
|
||||
}
|
||||
|
||||
// add existing downloads to list
|
||||
foreach (QString file, dir.entryList(nameFilters, QDir::Files, QDir::Time)) {
|
||||
bool Exists = false;
|
||||
for (QVector<DownloadInfo*>::const_iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end() && !Exists; ++Iter) {
|
||||
if (QString::compare((*Iter)->m_FileName, file, Qt::CaseInsensitive) == 0) {
|
||||
Exists = true;
|
||||
} else if (QString::compare(QFileInfo((*Iter)->m_Output.fileName()).fileName(), file, Qt::CaseInsensitive) == 0) {
|
||||
Exists = true;
|
||||
// remove finished downloads
|
||||
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end();) {
|
||||
if (((*iter)->m_State == STATE_READY) || ((*iter)->m_State == STATE_INSTALLED) || ((*iter)->m_State == STATE_UNINSTALLED)) {
|
||||
delete *iter;
|
||||
iter = m_ActiveDownloads.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
if (Exists) {
|
||||
qDebug("%s exists", qPrintable(file));
|
||||
continue;
|
||||
|
||||
QStringList nameFilters(m_SupportedExtensions);
|
||||
foreach (const QString &extension, m_SupportedExtensions) {
|
||||
nameFilters.append("*." + extension);
|
||||
}
|
||||
|
||||
QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
|
||||
nameFilters.append(QString("*").append(UNFINISHED));
|
||||
QDir dir(QDir::fromNativeSeparators(m_OutputDirectory));
|
||||
|
||||
DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden);
|
||||
if (info != NULL) {
|
||||
m_ActiveDownloads.push_front(info);
|
||||
// find orphaned meta files and delete them (sounds cruel but it's better for everyone)
|
||||
QStringList orphans;
|
||||
QStringList metaFiles = dir.entryList(QStringList() << "*.meta");
|
||||
foreach (const QString &metaFile, metaFiles) {
|
||||
QString baseFile = metaFile.left(metaFile.length() - 5);
|
||||
if (!QFile::exists(dir.absoluteFilePath(baseFile))) {
|
||||
orphans.append(dir.absoluteFilePath(metaFile));
|
||||
}
|
||||
}
|
||||
if (orphans.size() > 0) {
|
||||
qDebug("%d orphaned meta files will be deleted", orphans.size());
|
||||
shellDelete(orphans, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ActiveDownloads.size() != downloadsBefore) {
|
||||
qDebug("downloads after refresh: %d", m_ActiveDownloads.size());
|
||||
// add existing downloads to list
|
||||
foreach (QString file, dir.entryList(nameFilters, QDir::Files, QDir::Time)) {
|
||||
bool Exists = false;
|
||||
for (QVector<DownloadInfo*>::const_iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end() && !Exists; ++Iter) {
|
||||
if (QString::compare((*Iter)->m_FileName, file, Qt::CaseInsensitive) == 0) {
|
||||
Exists = true;
|
||||
} else if (QString::compare(QFileInfo((*Iter)->m_Output.fileName()).fileName(), file, Qt::CaseInsensitive) == 0) {
|
||||
Exists = true;
|
||||
}
|
||||
}
|
||||
if (Exists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
|
||||
|
||||
DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden);
|
||||
if (info != NULL) {
|
||||
m_ActiveDownloads.push_front(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ActiveDownloads.size() != downloadsBefore) {
|
||||
qDebug("downloads after refresh: %d", m_ActiveDownloads.size());
|
||||
}
|
||||
emit update(-1);
|
||||
} catch (const std::bad_alloc&) {
|
||||
reportError(tr("Memory allocation error (in refreshing directory)."));
|
||||
}
|
||||
emit update(-1);
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +398,7 @@ void DownloadManager::removePending(int modID, int fileID)
|
||||
|
||||
void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume)
|
||||
{
|
||||
reply->setReadBufferSize(1024 * 1024); // don't read more than 1MB at once to avoid memory troubles
|
||||
newDownload->m_Reply = reply;
|
||||
setState(newDownload, STATE_DOWNLOADING);
|
||||
if (newDownload->m_Urls.count() == 0) {
|
||||
@@ -404,6 +411,7 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
|
||||
}
|
||||
|
||||
newDownload->m_StartTime.start();
|
||||
createMetaFile(newDownload);
|
||||
|
||||
if (!newDownload->m_Output.open(mode)) {
|
||||
reportError(tr("failed to download %1: could not open output file: %2")
|
||||
@@ -413,6 +421,7 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
|
||||
|
||||
connect(newDownload->m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)));
|
||||
connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
|
||||
connect(newDownload->m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
|
||||
connect(newDownload->m_Reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
|
||||
connect(newDownload->m_Reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
|
||||
|
||||
@@ -421,11 +430,15 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
|
||||
removePending(newDownload->m_FileInfo->modID, newDownload->m_FileInfo->fileID);
|
||||
|
||||
emit aboutToUpdate();
|
||||
|
||||
m_ActiveDownloads.append(newDownload);
|
||||
|
||||
emit update(-1);
|
||||
emit downloadAdded();
|
||||
|
||||
if (reply->isFinished()) {
|
||||
// it's possible the download has already finished before this function ran
|
||||
downloadFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +461,7 @@ void DownloadManager::addNXMDownload(const QString &url)
|
||||
|
||||
emit update(-1);
|
||||
emit downloadAdded();
|
||||
m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId()));
|
||||
m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId(), ""));
|
||||
}
|
||||
|
||||
|
||||
@@ -593,8 +606,11 @@ void DownloadManager::pauseDownload(int index)
|
||||
DownloadInfo *info = m_ActiveDownloads.at(index);
|
||||
|
||||
if (info->m_State == STATE_DOWNLOADING) {
|
||||
setState(info, STATE_PAUSING);
|
||||
qDebug("pausing %d - %s", index, info->m_FileName.toUtf8().constData());
|
||||
if (info->m_Reply->isRunning()) {
|
||||
setState(info, STATE_PAUSING);
|
||||
} else {
|
||||
setState(info, STATE_PAUSED);
|
||||
}
|
||||
} else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) {
|
||||
setState(info, STATE_READY);
|
||||
}
|
||||
@@ -619,6 +635,11 @@ void DownloadManager::resumeDownloadInt(int index)
|
||||
}
|
||||
DownloadInfo *info = m_ActiveDownloads[index];
|
||||
if (info->isPausedState()) {
|
||||
if ((info->m_Urls.size() == 0)
|
||||
|| ((info->m_Urls.size() == 1) && (info->m_Urls[0].size() == 0))) {
|
||||
emit showMessage(tr("No known download urls. Sorry, this download can't be resumed."));
|
||||
return;
|
||||
}
|
||||
if (info->m_State == STATE_ERROR) {
|
||||
info->m_CurrentUrl = (info->m_CurrentUrl + 1) % info->m_Urls.count();
|
||||
}
|
||||
@@ -919,10 +940,10 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana
|
||||
info->m_Reply->abort();
|
||||
} break;
|
||||
case STATE_FETCHINGMODINFO: {
|
||||
m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID));
|
||||
m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID, QString()));
|
||||
} break;
|
||||
case STATE_FETCHINGFILEINFO: {
|
||||
m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID));
|
||||
m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID, QString()));
|
||||
} break;
|
||||
case STATE_READY: {
|
||||
createMetaFile(info);
|
||||
@@ -955,32 +976,40 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
return;
|
||||
}
|
||||
int index = 0;
|
||||
DownloadInfo *info = findDownload(this->sender(), &index);
|
||||
if (info != NULL) {
|
||||
if (info->m_State == STATE_CANCELING) {
|
||||
setState(info, STATE_CANCELED);
|
||||
} else if (info->m_State == STATE_PAUSING) {
|
||||
setState(info, STATE_PAUSED);
|
||||
} else {
|
||||
if (bytesTotal > info->m_TotalSize) {
|
||||
info->m_TotalSize = bytesTotal;
|
||||
}
|
||||
int oldProgress = info->m_Progress;
|
||||
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
|
||||
TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal);
|
||||
if (oldProgress != info->m_Progress) {
|
||||
emit update(index);
|
||||
try {
|
||||
DownloadInfo *info = findDownload(this->sender(), &index);
|
||||
if (info != NULL) {
|
||||
if (info->m_State == STATE_CANCELING) {
|
||||
setState(info, STATE_CANCELED);
|
||||
} else if (info->m_State == STATE_PAUSING) {
|
||||
setState(info, STATE_PAUSED);
|
||||
} else {
|
||||
if (bytesTotal > info->m_TotalSize) {
|
||||
info->m_TotalSize = bytesTotal;
|
||||
}
|
||||
int oldProgress = info->m_Progress;
|
||||
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
|
||||
TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal);
|
||||
if (oldProgress != info->m_Progress) {
|
||||
emit update(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::bad_alloc&) {
|
||||
reportError(tr("Memory allocation error (in processing progress event)."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DownloadManager::downloadReadyRead()
|
||||
{
|
||||
DownloadInfo *info = findDownload(this->sender());
|
||||
if (info != NULL) {
|
||||
info->m_Output.write(info->m_Reply->readAll());
|
||||
try {
|
||||
DownloadInfo *info = findDownload(this->sender());
|
||||
if (info != NULL) {
|
||||
info->m_Output.write(info->m_Reply->readAll());
|
||||
}
|
||||
} catch (const std::bad_alloc&) {
|
||||
reportError(tr("Memory allocation error (in processing downloaded data)."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,7 +1196,7 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD
|
||||
info->fileID = fileID;
|
||||
|
||||
QObject *test = info;
|
||||
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test)));
|
||||
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test), QString()));
|
||||
}
|
||||
|
||||
|
||||
@@ -1267,7 +1296,6 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
|
||||
foreach (const QVariant &server, resultList) {
|
||||
URLs.append(server.toMap()["URI"].toString());
|
||||
}
|
||||
|
||||
addDownload(URLs, modID, fileID, info);
|
||||
}
|
||||
|
||||
@@ -1309,8 +1337,11 @@ void DownloadManager::downloadFinished()
|
||||
DownloadInfo *info = findDownload(this->sender(), &index);
|
||||
if (info != NULL) {
|
||||
QNetworkReply *reply = info->m_Reply;
|
||||
QByteArray data = info->m_Reply->readAll();
|
||||
info->m_Output.write(data);
|
||||
QByteArray data;
|
||||
if (reply->isOpen()) {
|
||||
data = reply->readAll();
|
||||
info->m_Output.write(data);
|
||||
}
|
||||
info->m_Output.close();
|
||||
TaskProgressManager::instance().forgetMe(info->m_TaskProgressId);
|
||||
|
||||
@@ -1356,7 +1387,6 @@ void DownloadManager::downloadFinished()
|
||||
createMetaFile(info);
|
||||
emit update(index);
|
||||
} else {
|
||||
|
||||
QString url = info->m_Urls[info->m_CurrentUrl];
|
||||
if (info->m_FileInfo->userData.contains("downloadMap")) {
|
||||
foreach (const QVariant &server, info->m_FileInfo->userData["downloadMap"].toList()) {
|
||||
@@ -1406,6 +1436,14 @@ void DownloadManager::downloadFinished()
|
||||
}
|
||||
|
||||
|
||||
void DownloadManager::downloadError(QNetworkReply::NetworkError error)
|
||||
{
|
||||
if (error != QNetworkReply::OperationCanceledError) {
|
||||
qWarning("Download error occured: %d", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DownloadManager::metaDataChanged()
|
||||
{
|
||||
int index = 0;
|
||||
@@ -1416,7 +1454,7 @@ void DownloadManager::metaDataChanged()
|
||||
if (!newName.isEmpty() && (newName != info->m_FileName)) {
|
||||
info->setName(getDownloadFileName(newName), true);
|
||||
refreshAlphabeticalTranslation();
|
||||
if (!info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) {
|
||||
if (!info->m_Output.isOpen() && !info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) {
|
||||
reportError(tr("failed to re-open %1").arg(info->m_FileName));
|
||||
setState(info, STATE_CANCELING);
|
||||
}
|
||||
|
||||
@@ -417,6 +417,7 @@ private slots:
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void downloadReadyRead();
|
||||
void downloadFinished();
|
||||
void downloadError(QNetworkReply::NetworkError error);
|
||||
void metaDataChanged();
|
||||
void directoryChanged(const QString &dirctory);
|
||||
|
||||
|
||||
+41
-29
@@ -21,6 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "report.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QFile>
|
||||
#include <QIcon>
|
||||
#include <QDateTime>
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -108,21 +109,6 @@ void LogBuffer::init(int messageCount, QtMsgType minMsgType, const QString &outp
|
||||
#endif
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
|
||||
void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
if (!s_Instance.isNull()) {
|
||||
s_Instance->logMessage(type, message);
|
||||
}
|
||||
fprintf(stdout, "(%s:%u) %s\n", context.file, context.line, qPrintable(message));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
char LogBuffer::msgTypeID(QtMsgType type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -134,6 +120,39 @@ char LogBuffer::msgTypeID(QtMsgType type)
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
|
||||
void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
if (!s_Instance.isNull()) {
|
||||
s_Instance->logMessage(type, message);
|
||||
}
|
||||
// fprintf(stdout, "(%s:%u) %s\n", context.file, context.line, qPrintable(message));
|
||||
if (type == QtDebugMsg) {
|
||||
fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message));
|
||||
} else {
|
||||
fprintf(stdout, "%s [%c] (%s:%u) %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type),
|
||||
context.file, context.line, qPrintable(message));
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void LogBuffer::log(QtMsgType type, const char *message)
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
if (!s_Instance.isNull()) {
|
||||
s_Instance->logMessage(type, message);
|
||||
}
|
||||
fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), message);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
QModelIndex LogBuffer::index(int row, int column, const QModelIndex&) const
|
||||
{
|
||||
return createIndex(row, column, row);
|
||||
@@ -162,13 +181,18 @@ QVariant LogBuffer::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
unsigned offset = m_NumMessages < m_Messages.size() ? 0
|
||||
: m_NumMessages - m_Messages.size();
|
||||
unsigned int msgIndex = (offset + index.row()) % m_Messages.size();
|
||||
unsigned int msgIndex = (offset + index.row() + 1) % m_Messages.size();
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
if (index.column() == 0) {
|
||||
return m_Messages.at(msgIndex).time;
|
||||
} else if (index.column() == 1) {
|
||||
return m_Messages.at(msgIndex).message;
|
||||
const QString &msg = m_Messages.at(msgIndex).message;
|
||||
if (msg.length() < 200) {
|
||||
return msg;
|
||||
} else {
|
||||
return msg.mid(0, 200) + "...";
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Qt::DecorationRole: {
|
||||
@@ -195,18 +219,6 @@ QVariant LogBuffer::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void LogBuffer::log(QtMsgType type, const char *message)
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
if (!s_Instance.isNull()) {
|
||||
s_Instance->logMessage(type, message);
|
||||
}
|
||||
fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), message);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void LogBuffer::writeNow()
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
|
||||
+5
-2
@@ -174,7 +174,9 @@ void cleanupDir()
|
||||
"QtXml4.dll",
|
||||
"QtWebKit4.dll",
|
||||
"qjpeg4.dll",
|
||||
"NCC/GamebryoBase.dll"
|
||||
"NCC/GamebryoBase.dll",
|
||||
"plugins/helloWorld.dll",
|
||||
"plugins/testnexus.py"
|
||||
};
|
||||
|
||||
static const int NUM_FILES = sizeof(fileNames) / sizeof(QString);
|
||||
@@ -310,6 +312,7 @@ bool HaveWriteAccess(const std::wstring &path)
|
||||
return writable;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MOApplication application(argc, argv);
|
||||
@@ -328,7 +331,7 @@ int main(int argc, char *argv[])
|
||||
, ToWString(QDir::currentPath()).c_str(), SW_SHOWNORMAL);
|
||||
return 1;
|
||||
}
|
||||
LogBuffer::init(200, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
|
||||
LogBuffer::init(100, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
|
||||
|
||||
qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath())));
|
||||
qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath())));
|
||||
|
||||
+568
-584
File diff suppressed because it is too large
Load Diff
+50
-40
@@ -64,11 +64,13 @@ class ModListSortProxy;
|
||||
class ModListGroupCategoriesProxy;
|
||||
|
||||
|
||||
class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose
|
||||
class MainWindow : public QMainWindow, public MOBase::IPluginDiagnose
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPluginDiagnose)
|
||||
|
||||
friend class OrganizerProxy;
|
||||
|
||||
private:
|
||||
|
||||
struct SignalCombinerAnd
|
||||
@@ -88,6 +90,7 @@ private:
|
||||
};
|
||||
|
||||
typedef boost::signals2::signal<bool (const QString&), SignalCombinerAnd> SignalAboutToRunApplication;
|
||||
typedef boost::signals2::signal<void (const QString&)> SignalModInstalled;
|
||||
|
||||
public:
|
||||
explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0);
|
||||
@@ -96,8 +99,6 @@ public:
|
||||
void readSettings();
|
||||
|
||||
bool addProfile();
|
||||
void refreshLists();
|
||||
void refreshESPList();
|
||||
void refreshBSAList();
|
||||
void refreshDataTree();
|
||||
void refreshSaveList();
|
||||
@@ -117,34 +118,6 @@ public:
|
||||
|
||||
void loadPlugins();
|
||||
|
||||
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<QString> &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 = QVariant()) const;
|
||||
virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync = true);
|
||||
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<bool(const QString &)> &filter) const;
|
||||
virtual QList<FileInfo> findFileInfos(const QString &path, const std::function<bool(const FileInfo&)> &filter) const;
|
||||
|
||||
virtual MOBase::IDownloadManager *downloadManager();
|
||||
virtual MOBase::IPluginList *pluginList();
|
||||
virtual MOBase::IModList *modList();
|
||||
virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "");
|
||||
virtual bool onAboutToRun(const std::function<bool(const QString&)> &func);
|
||||
virtual void refreshModList(bool saveChanges = true);
|
||||
|
||||
virtual std::vector<unsigned int> activeProblems() const;
|
||||
virtual QString shortDescription(unsigned int key) const;
|
||||
virtual QString fullDescription(unsigned int key) const;
|
||||
@@ -153,13 +126,20 @@ public:
|
||||
|
||||
void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info);
|
||||
|
||||
void saveArchiveList();
|
||||
bool saveArchiveList();
|
||||
|
||||
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 = "");
|
||||
|
||||
void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo);
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshLists();
|
||||
|
||||
void displayColumnSelection(const QPoint &pos);
|
||||
|
||||
void externalMessage(const QString &message);
|
||||
@@ -200,6 +180,9 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
void refreshESPList();
|
||||
void refreshModList(bool saveChanges = true);
|
||||
|
||||
void actionToToolButton(QAction *&sourceAction);
|
||||
bool verifyPlugin(MOBase::IPlugin *plugin);
|
||||
void registerPluginTool(MOBase::IPluginTool *tool);
|
||||
@@ -211,8 +194,6 @@ private:
|
||||
|
||||
void setExecutableIndex(int index);
|
||||
|
||||
bool nexusLogin();
|
||||
|
||||
bool testForSteam();
|
||||
void startSteam();
|
||||
|
||||
@@ -224,6 +205,13 @@ private:
|
||||
bool refreshProfiles(bool selectProfile = true);
|
||||
void refreshExecutablesList();
|
||||
void installMod();
|
||||
MOBase::IModInterface *installMod(const QString &fileName);
|
||||
MOBase::IModInterface *getMod(const QString &name);
|
||||
MOBase::IModInterface *createMod(MOBase::GuessedValue<QString> &name);
|
||||
bool removeMod(MOBase::IModInterface *mod);
|
||||
|
||||
QList<MOBase::IOrganizer::FileInfo> findFileInfos(const QString &path, const std::function<bool (const MOBase::IOrganizer::FileInfo &)> &filter) const;
|
||||
|
||||
bool modifyExecutablesDialog();
|
||||
void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab);
|
||||
void displayModInformation(int row, int tab = 0);
|
||||
@@ -267,7 +255,7 @@ private:
|
||||
|
||||
bool extractProgress(QProgressDialog &extractProgress, int percentage, std::string fileName);
|
||||
|
||||
bool checkForProblems();
|
||||
int checkForProblems();
|
||||
|
||||
int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments);
|
||||
QTreeWidgetItem *addFilterItem(QTreeWidgetItem *root, const QString &name, int categoryID);
|
||||
@@ -275,8 +263,6 @@ private:
|
||||
|
||||
void setCategoryListVisible(bool visible);
|
||||
|
||||
void updateProblemsButton();
|
||||
|
||||
SaveGameGamebryo *getSaveGame(const QString &name);
|
||||
SaveGameGamebryo *getSaveGame(QListWidgetItem *item);
|
||||
|
||||
@@ -298,6 +284,14 @@ private:
|
||||
bool createBackup(const QString &filePath, const QDateTime &time);
|
||||
QString queryRestore(const QString &filePath);
|
||||
|
||||
QMenu *modListContextMenu();
|
||||
|
||||
std::set<QString> enabledArchives();
|
||||
|
||||
void scheduleUpdateButton();
|
||||
|
||||
void updateModActiveState(int index, bool active);
|
||||
|
||||
private:
|
||||
|
||||
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
|
||||
@@ -367,6 +361,7 @@ private:
|
||||
bool m_ArchivesInit;
|
||||
QTimer m_CheckBSATimer;
|
||||
QTimer m_SaveMetaTimer;
|
||||
QTimer m_UpdateProblemsTimer;
|
||||
|
||||
QTime m_StartTime;
|
||||
SaveGameInfoWidget *m_CurrentSaveView;
|
||||
@@ -380,6 +375,7 @@ private:
|
||||
QFile m_PluginsCheck;
|
||||
|
||||
SignalAboutToRunApplication m_AboutToRun;
|
||||
SignalModInstalled m_ModInstalled;
|
||||
|
||||
QString m_CurrentLanguage;
|
||||
std::vector<QTranslator*> m_Translators;
|
||||
@@ -391,7 +387,9 @@ private:
|
||||
|
||||
std::vector<QTreeWidgetItem*> m_RemoveWidget;
|
||||
|
||||
uint m_ArchiveListHash;
|
||||
QByteArray m_ArchiveListHash;
|
||||
|
||||
bool m_DidUpdateMasterList;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -458,6 +456,8 @@ private slots:
|
||||
|
||||
void linkClicked(const QString &url);
|
||||
|
||||
bool nexusLogin();
|
||||
|
||||
void loginSuccessful(bool necessary);
|
||||
void loginSuccessfulUpdate(bool necessary);
|
||||
void loginFailed(const QString &message);
|
||||
@@ -484,11 +484,12 @@ private slots:
|
||||
void modlistChanged(int row);
|
||||
|
||||
void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID);
|
||||
// void nxmEndorsementToggled(int, QVariant, QVariant resultData, int);
|
||||
void nxmEndorsementToggled(int, QVariant, QVariant resultData, int);
|
||||
void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString);
|
||||
|
||||
void editCategories();
|
||||
void deselectFilters();
|
||||
|
||||
void displayModInformation(const QString &modName, int tab);
|
||||
void modOpenNext();
|
||||
@@ -515,6 +516,9 @@ private slots:
|
||||
void startExeAction();
|
||||
|
||||
void checkBSAList();
|
||||
|
||||
void updateProblemsButton();
|
||||
|
||||
void saveModMetas();
|
||||
|
||||
void updateStyle(const QString &style);
|
||||
@@ -558,6 +562,7 @@ private slots:
|
||||
void requestDownload(const QUrl &url, QNetworkReply *reply);
|
||||
|
||||
private slots: // ui slots
|
||||
void profileRefresh();
|
||||
// actions
|
||||
void on_actionAdd_Profile_triggered();
|
||||
void on_actionInstallMod_triggered();
|
||||
@@ -578,7 +583,6 @@ private slots: // ui slots
|
||||
void on_modList_customContextMenuRequested(const QPoint &pos);
|
||||
void on_modList_doubleClicked(const QModelIndex &index);
|
||||
void on_profileBox_currentIndexChanged(int index);
|
||||
void on_profileRefreshBtn_clicked();
|
||||
void on_savegameList_customContextMenuRequested(const QPoint &pos);
|
||||
void on_startButton_clicked();
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
@@ -597,6 +601,12 @@ private slots: // ui slots
|
||||
void on_restoreModsButton_clicked();
|
||||
void on_saveModsButton_clicked();
|
||||
void on_actionCopy_Log_to_Clipboard_triggered();
|
||||
void on_categoriesAndBtn_toggled(bool checked);
|
||||
void on_categoriesOrBtn_toggled(bool checked);
|
||||
void on_managedArchiveLabel_linkHovered(const QString &link);
|
||||
void on_manageArchivesBox_toggled(bool checked);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
+107
-33
@@ -75,6 +75,9 @@
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
@@ -85,6 +88,54 @@
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="clickBlankLabel">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Click blank area to deselect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="categoriesAndBtn">
|
||||
<property name="toolTip">
|
||||
<string>If checked, only mods that match all selected categories are displayed.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>And</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="categoriesOrBtn">
|
||||
<property name="toolTip">
|
||||
<string>If checked, all mods that match at least one of the selected categories are displayed.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Or</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -139,29 +190,6 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="profileRefreshBtn">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Refresh list</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Refresh list. This is usually not necessary unless you modified data outside the program.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/MO/gui/resources/view-refresh.png</normaloff>:/MO/gui/resources/view-refresh.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -175,6 +203,35 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="listOptionsBtn">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open list options...</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Refresh list. This is usually not necessary unless you modified data outside the program.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/MO/gui/settings</normaloff>:/MO/gui/settings</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreModsButton">
|
||||
<property name="toolTip">
|
||||
@@ -841,6 +898,33 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9" stretch="0,1">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="manageArchivesBox">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="managedArchiveLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>BSAs are bundles of game assets (textures, scripts, ...). By default, the engine loads these bundles in a separate step from loose files. MO can manage those archives to align their load order with that of loose files:</p><p>If archives are <span style=" font-weight:600;">managed</span>, their load order is specified by the priority of the corresponding mod (left pane), the same as the loose files. You can manually enable any BSA that has no corresponding plugin active.<br/></p><p>If archives are <span style=" font-weight:600;">not managed</span> their load order is specified by the priority of the corresponding plugin (right pane, plugins tab). You can then not manually enable BSAs where the plugin isn't active.</p><p>In either case you can not disable archives if there is a matching plugin, the game will load them no matter what.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Have MO manage archives (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">read more</span></a>)</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MOBase::SortableTreeWidget" name="bsaList">
|
||||
<property name="contextMenuPolicy">
|
||||
@@ -901,16 +985,6 @@ BSAs checked here are loaded in such a way that your installation order is obeye
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="bsaWarning">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Marked Archives (<img src=":/MO/gui/warning_16"/>) are still loaded on Skyrim but the <a href="http://forums.bethsoft.com/topic/1354395-update-bsas-and-you/"><span style=" text-decoration: underline; color:#0000ff;">regular file override</span></a> mechanism will apply: Loose files override BSAs, no matter the mod/plugin priority.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="dataTab">
|
||||
|
||||
@@ -81,6 +81,7 @@ void MessageDialog::resizeEvent(QResizeEvent *event)
|
||||
|
||||
void MessageDialog::showMessage(const QString &text, QWidget *reference, bool bringToFront)
|
||||
{
|
||||
qDebug("%s", qPrintable(text));
|
||||
if (reference != NULL) {
|
||||
if (bringToFront || (qApp->activeWindow() != NULL)) {
|
||||
MessageDialog *dialog = new MessageDialog(text, reference);
|
||||
|
||||
+233
-117
@@ -73,6 +73,16 @@ ModInfo::Ptr ModInfo::createFrom(const QDir &dir, DirectoryEntry **directoryStru
|
||||
}
|
||||
|
||||
|
||||
ModInfo::Ptr ModInfo::createFromPlugin(const QString &espName, const QStringList &bsaNames
|
||||
, DirectoryEntry ** directoryStructure)
|
||||
{
|
||||
QMutexLocker locker(&s_Mutex);
|
||||
ModInfo::Ptr result = ModInfo::Ptr(new ModInfoForeign(espName, bsaNames, directoryStructure));
|
||||
s_Collection.push_back(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void ModInfo::createFromOverwrite()
|
||||
{
|
||||
QMutexLocker locker(&s_Mutex);
|
||||
@@ -174,17 +184,37 @@ unsigned int ModInfo::findMod(const boost::function<bool (ModInfo::Ptr)> &filter
|
||||
}
|
||||
|
||||
|
||||
void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure)
|
||||
void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure, bool displayForeign)
|
||||
{
|
||||
QMutexLocker lock(&s_Mutex);
|
||||
s_Collection.clear();
|
||||
s_NextID = 0;
|
||||
// list all directories in the mod directory and make a mod out of each
|
||||
QDir mods(QDir::fromNativeSeparators(modDirectory));
|
||||
mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QDirIterator modIter(mods);
|
||||
while (modIter.hasNext()) {
|
||||
createFrom(QDir(modIter.next()), directoryStructure);
|
||||
|
||||
{ // list all directories in the mod directory and make a mod out of each
|
||||
QDir mods(QDir::fromNativeSeparators(modDirectory));
|
||||
mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QDirIterator modIter(mods);
|
||||
while (modIter.hasNext()) {
|
||||
createFrom(QDir(modIter.next()), directoryStructure);
|
||||
}
|
||||
}
|
||||
|
||||
{ // list plugins in the data directory and make a foreign-managed mod out of each
|
||||
std::vector<std::wstring> dlcPlugins = GameInfo::instance().getDLCPlugins();
|
||||
QDir dataDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data");
|
||||
foreach (const QFileInfo &file, dataDir.entryInfoList(QStringList() << "*.esp" << "*.esm")) {
|
||||
if ((file.baseName() != "Update") // hide update
|
||||
&& (file.baseName() != ToQString(GameInfo::instance().getGameName())) // hide the game esp
|
||||
&& (displayForeign // show non-dlc bundles only if the user wants them
|
||||
|| std::find(dlcPlugins.begin(), dlcPlugins.end(), ToWString(file.fileName())) != dlcPlugins.end())) {
|
||||
QStringList archives;
|
||||
foreach (const QString archiveName, dataDir.entryList(QStringList() << file.baseName() + "*.bsa")) {
|
||||
archives.append(dataDir.absoluteFilePath(archiveName));
|
||||
}
|
||||
|
||||
createFromPlugin(file.fileName(), archives, directoryStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createFromOverwrite();
|
||||
@@ -219,7 +249,7 @@ ModInfo::ModInfo()
|
||||
void ModInfo::checkChunkForUpdate(const std::vector<int> &modIDs, QObject *receiver)
|
||||
{
|
||||
if (modIDs.size() != 0) {
|
||||
NexusInterface::instance()->requestUpdates(modIDs, receiver, QVariant());
|
||||
NexusInterface::instance()->requestUpdates(modIDs, receiver, QVariant(), QString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +282,12 @@ void ModInfo::setVersion(const VersionInfo &version)
|
||||
m_Version = version;
|
||||
}
|
||||
|
||||
bool ModInfo::hasFlag(ModInfo::EFlag flag) const
|
||||
{
|
||||
std::vector<EFlag> flags = getFlags();
|
||||
return std::find(flags.begin(), flags.end(), flag) != flags.end();
|
||||
}
|
||||
|
||||
|
||||
bool ModInfo::categorySet(int categoryID) const
|
||||
{
|
||||
@@ -293,18 +329,136 @@ void ModInfo::testValid()
|
||||
}
|
||||
|
||||
|
||||
ModInfoWithConflictInfo::ModInfoWithConflictInfo(DirectoryEntry **directoryStructure)
|
||||
: m_DirectoryStructure(directoryStructure) {}
|
||||
|
||||
void ModInfoWithConflictInfo::clearCaches()
|
||||
{
|
||||
m_LastConflictCheck = QTime();
|
||||
}
|
||||
|
||||
std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
|
||||
{
|
||||
std::vector<ModInfo::EFlag> result;
|
||||
switch (isConflicted()) {
|
||||
case CONFLICT_MIXED: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_MIXED);
|
||||
} break;
|
||||
case CONFLICT_OVERWRITE: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE);
|
||||
} break;
|
||||
case CONFLICT_OVERWRITTEN: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN);
|
||||
} break;
|
||||
case CONFLICT_REDUNDANT: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT);
|
||||
} break;
|
||||
default: { /* NOP */ }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
|
||||
{
|
||||
// this is costy so cache the result
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
bool overwrite = false;
|
||||
bool overwritten = false;
|
||||
bool regular = false;
|
||||
|
||||
int dataID = 0;
|
||||
if ((*m_DirectoryStructure)->originExists(L"data")) {
|
||||
dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID();
|
||||
}
|
||||
|
||||
std::wstring name = ToWString(this->name());
|
||||
if ((*m_DirectoryStructure)->originExists(name)) {
|
||||
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
|
||||
std::vector<FileEntry::Ptr> files = origin.getFiles();
|
||||
for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) {
|
||||
const std::vector<int> &alternatives = (*iter)->getAlternatives();
|
||||
if (alternatives.size() == 0) {
|
||||
// no alternatives -> no conflict
|
||||
regular = true;
|
||||
} else {
|
||||
for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) {
|
||||
// don't treat files overwritten in data as "conflict"
|
||||
if (*altIter != dataID) {
|
||||
bool ignore = false;
|
||||
if ((*iter)->getOrigin(ignore) == origin.getID()) {
|
||||
overwrite = true;
|
||||
break;
|
||||
} else {
|
||||
overwritten = true;
|
||||
break;
|
||||
}
|
||||
} else if (alternatives.size() == 1) {
|
||||
// only alternative is data -> no conflict
|
||||
regular = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_LastConflictCheck = QTime::currentTime();
|
||||
|
||||
if (overwrite && overwritten) m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (overwrite) m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (overwritten) {
|
||||
if (!regular) {
|
||||
m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
} else {
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
}
|
||||
}
|
||||
else m_CurrentConflictState = CONFLICT_NONE;
|
||||
}
|
||||
|
||||
return m_CurrentConflictState;
|
||||
}
|
||||
|
||||
|
||||
bool ModInfoWithConflictInfo::isRedundant() const
|
||||
{
|
||||
std::wstring name = ToWString(this->name());
|
||||
if ((*m_DirectoryStructure)->originExists(name)) {
|
||||
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
|
||||
std::vector<FileEntry::Ptr> files = origin.getFiles();
|
||||
bool ignore = false;
|
||||
for (auto iter = files.begin(); iter != files.end(); ++iter) {
|
||||
if ((*iter)->getOrigin(ignore) == origin.getID()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure)
|
||||
: ModInfo(), m_Name(path.dirName()), m_Path(path.absolutePath()), m_MetaInfoChanged(false),
|
||||
m_EndorsedState(ENDORSED_UNKNOWN), m_DirectoryStructure(directoryStructure)
|
||||
: ModInfoWithConflictInfo(directoryStructure)
|
||||
, m_Name(path.dirName())
|
||||
, m_Path(path.absolutePath())
|
||||
, m_MetaInfoChanged(false)
|
||||
, m_EndorsedState(ENDORSED_UNKNOWN)
|
||||
{
|
||||
testValid();
|
||||
m_CreationTime = QFileInfo(path.absolutePath()).created();
|
||||
// read out the meta-file for information
|
||||
readMeta();
|
||||
|
||||
connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)), this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant)));
|
||||
connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)), this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant)));
|
||||
connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)), this, SLOT(nxmRequestFailed(int,int,QVariant,QString)));
|
||||
connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant))
|
||||
, this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant)));
|
||||
connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant))
|
||||
, this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant)));
|
||||
connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString))
|
||||
, this, SLOT(nxmRequestFailed(int,int,QVariant,QString)));
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +756,7 @@ void ModInfoRegular::setNeverEndorse()
|
||||
bool ModInfoRegular::remove()
|
||||
{
|
||||
m_MetaInfoChanged = false;
|
||||
return shellDelete(QStringList(absolutePath()));
|
||||
return shellDelete(QStringList(absolutePath()), true);
|
||||
}
|
||||
|
||||
void ModInfoRegular::endorse(bool doEndorse)
|
||||
@@ -612,11 +766,6 @@ void ModInfoRegular::endorse(bool doEndorse)
|
||||
}
|
||||
}
|
||||
|
||||
void ModInfoRegular::clearCaches()
|
||||
{
|
||||
m_LastConflictCheck = QTime();
|
||||
}
|
||||
|
||||
|
||||
QString ModInfoRegular::absolutePath() const
|
||||
{
|
||||
@@ -636,22 +785,7 @@ void ModInfoRegular::ignoreUpdate(bool ignore)
|
||||
|
||||
std::vector<ModInfo::EFlag> ModInfoRegular::getFlags() const
|
||||
{
|
||||
std::vector<ModInfo::EFlag> result;
|
||||
switch (isConflicted()) {
|
||||
case CONFLICT_MIXED: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_MIXED);
|
||||
} break;
|
||||
case CONFLICT_OVERWRITE: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE);
|
||||
} break;
|
||||
case CONFLICT_OVERWRITTEN: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN);
|
||||
} break;
|
||||
case CONFLICT_REDUNDANT: {
|
||||
result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT);
|
||||
} break;
|
||||
default: { /* NOP */ }
|
||||
}
|
||||
std::vector<ModInfo::EFlag> result = ModInfoWithConflictInfo::getFlags();
|
||||
if ((m_NexusID != -1) && (endorsedState() == ENDORSED_FALSE)) {
|
||||
result.push_back(ModInfo::FLAG_NOTENDORSED);
|
||||
}
|
||||
@@ -712,92 +846,22 @@ ModInfoRegular::EEndorsedState ModInfoRegular::endorsedState() const
|
||||
return m_EndorsedState;
|
||||
}
|
||||
|
||||
ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const
|
||||
{
|
||||
// this is costy so cache the result
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
bool overwrite = false;
|
||||
bool overwritten = false;
|
||||
bool regular = false;
|
||||
|
||||
int dataID = 0;
|
||||
if ((*m_DirectoryStructure)->originExists(L"data")) {
|
||||
dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID();
|
||||
}
|
||||
|
||||
std::wstring name = ToWString(m_Name);
|
||||
if ((*m_DirectoryStructure)->originExists(name)) {
|
||||
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
|
||||
std::vector<FileEntry::Ptr> files = origin.getFiles();
|
||||
for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) {
|
||||
const std::vector<int> &alternatives = (*iter)->getAlternatives();
|
||||
if (alternatives.size() == 0) {
|
||||
// no alternatives -> no conflict
|
||||
regular = true;
|
||||
} else {
|
||||
for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) {
|
||||
// don't treat files overwritten in data as "conflict"
|
||||
if (*altIter != dataID) {
|
||||
bool ignore = false;
|
||||
if ((*iter)->getOrigin(ignore) == origin.getID()) {
|
||||
overwrite = true;
|
||||
break;
|
||||
} else {
|
||||
overwritten = true;
|
||||
break;
|
||||
}
|
||||
} else if (alternatives.size() == 1) {
|
||||
// only alternative is data -> no conflict
|
||||
regular = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_LastConflictCheck = QTime::currentTime();
|
||||
|
||||
if (overwrite && overwritten) m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (overwrite) m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (overwritten) {
|
||||
if (!regular) {
|
||||
m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
} else {
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
}
|
||||
}
|
||||
else m_CurrentConflictState = CONFLICT_NONE;
|
||||
}
|
||||
|
||||
return m_CurrentConflictState;
|
||||
}
|
||||
|
||||
|
||||
bool ModInfoRegular::isRedundant() const
|
||||
{
|
||||
std::wstring name = ToWString(m_Name);
|
||||
if ((*m_DirectoryStructure)->originExists(name)) {
|
||||
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
|
||||
std::vector<FileEntry::Ptr> files = origin.getFiles();
|
||||
bool ignore = false;
|
||||
for (auto iter = files.begin(); iter != files.end(); ++iter) {
|
||||
if ((*iter)->getOrigin(ignore) == origin.getID()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QDateTime ModInfoRegular::getLastNexusQuery() const
|
||||
{
|
||||
return m_LastNexusQuery;
|
||||
}
|
||||
|
||||
|
||||
QStringList ModInfoRegular::archives() const
|
||||
{
|
||||
QStringList result;
|
||||
QDir dir(this->absolutePath());
|
||||
foreach (const QString &archive, dir.entryList(QStringList("*.bsa"))) {
|
||||
result.append(this->absolutePath() + "/" + archive);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<QString> ModInfoRegular::getIniTweaks() const
|
||||
{
|
||||
QString metaFileName = absolutePath().append("/meta.ini");
|
||||
@@ -874,9 +938,61 @@ int ModInfoOverwrite::getHighlight() const
|
||||
return (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER;
|
||||
}
|
||||
|
||||
|
||||
QString ModInfoOverwrite::getDescription() const
|
||||
{
|
||||
return tr("This pseudo mod contains files from the virtual data tree that got "
|
||||
"modified (i.e. by the construction kit)");
|
||||
}
|
||||
|
||||
QStringList ModInfoOverwrite::archives() const
|
||||
{
|
||||
QStringList result;
|
||||
QDir dir(this->absolutePath());
|
||||
foreach (const QString &archive, dir.entryList(QStringList("*.bsa"))) {
|
||||
result.append(this->absolutePath() + "/" + archive);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ModInfoForeign::name() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
QDateTime ModInfoForeign::creationTime() const
|
||||
{
|
||||
return m_CreationTime;
|
||||
}
|
||||
|
||||
QString ModInfoForeign::absolutePath() const
|
||||
{
|
||||
return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data";
|
||||
}
|
||||
|
||||
std::vector<ModInfo::EFlag> ModInfoForeign::getFlags() const
|
||||
{
|
||||
std::vector<ModInfo::EFlag> result = ModInfoWithConflictInfo::getFlags();
|
||||
result.push_back(FLAG_FOREIGN);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ModInfoForeign::getHighlight() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString ModInfoForeign::getDescription() const
|
||||
{
|
||||
return tr("This pseudo mod represents content managed outside MO. It isn't modified by MO.");
|
||||
}
|
||||
|
||||
ModInfoForeign::ModInfoForeign(const QString &referenceFile, const QStringList &archives,
|
||||
DirectoryEntry **directoryStructure)
|
||||
: ModInfoWithConflictInfo(directoryStructure)
|
||||
, m_ReferenceFile(referenceFile)
|
||||
, m_Archives(archives)
|
||||
{
|
||||
m_CreationTime = QFileInfo(referenceFile).created();
|
||||
m_Name = QFileInfo(m_ReferenceFile).baseName();
|
||||
}
|
||||
|
||||
+141
-34
@@ -59,6 +59,7 @@ public:
|
||||
FLAG_INVALID,
|
||||
FLAG_BACKUP,
|
||||
FLAG_OVERWRITE,
|
||||
FLAG_FOREIGN,
|
||||
FLAG_NOTENDORSED,
|
||||
FLAG_NOTES,
|
||||
FLAG_CONFLICT_OVERWRITE,
|
||||
@@ -86,7 +87,7 @@ public:
|
||||
/**
|
||||
* @brief read the mod directory and Mod ModInfo objects for all subdirectories
|
||||
**/
|
||||
static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure);
|
||||
static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure, bool displayForeign);
|
||||
|
||||
static void clear() { s_Collection.clear(); s_ModsByName.clear(); s_ModsByModID.clear(); }
|
||||
|
||||
@@ -160,6 +161,14 @@ public:
|
||||
*/
|
||||
static ModInfo::Ptr createFrom(const QDir &dir, MOShared::DirectoryEntry **directoryStructure);
|
||||
|
||||
/**
|
||||
* @brief create a new "foreign-managed" mod from a tuple of plugin and archives
|
||||
* @param espName name of the plugin
|
||||
* @param bsaNames names of archives
|
||||
* @return a new mod
|
||||
*/
|
||||
static ModInfo::Ptr createFromPlugin(const QString &espName, const QStringList &bsaNames, MOShared::DirectoryEntry **directoryStructure);
|
||||
|
||||
virtual bool isRegular() const { return false; }
|
||||
|
||||
virtual bool isEmpty() const { return false; }
|
||||
@@ -339,6 +348,11 @@ public:
|
||||
*/
|
||||
virtual int getFixedPriority() const = 0;
|
||||
|
||||
/**
|
||||
* @return true if the mod is always enabled
|
||||
*/
|
||||
virtual bool alwaysEnabled() const { return false; }
|
||||
|
||||
/**
|
||||
* @return true if the mod can be updated
|
||||
*/
|
||||
@@ -354,6 +368,13 @@ public:
|
||||
*/
|
||||
virtual std::vector<EFlag> getFlags() const = 0;
|
||||
|
||||
/**
|
||||
* @brief test if the specified flag is set for this mod
|
||||
* @param flag the flag to test
|
||||
* @return true if the flag is set, false otherwise
|
||||
*/
|
||||
bool hasFlag(EFlag flag) const;
|
||||
|
||||
/**
|
||||
* @return an indicator if and how this mod should be highlighted by the UI
|
||||
*/
|
||||
@@ -389,6 +410,16 @@ public:
|
||||
*/
|
||||
virtual QDateTime getLastNexusQuery() const = 0;
|
||||
|
||||
/**
|
||||
* @return a list of files that, if they exist in the data directory are treated as files in THIS mod
|
||||
*/
|
||||
virtual QStringList stealFiles() const { return QStringList(); }
|
||||
|
||||
/**
|
||||
* @return a list of archives belonging to this mod (as absolute file paths)
|
||||
*/
|
||||
virtual QStringList archives() const = 0;
|
||||
|
||||
/**
|
||||
* @brief test if the mod belongs to the specified category
|
||||
*
|
||||
@@ -481,6 +512,50 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class ModInfoWithConflictInfo : public ModInfo
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ModInfoWithConflictInfo(MOShared::DirectoryEntry **directoryStructure);
|
||||
|
||||
std::vector<ModInfo::EFlag> getFlags() const;
|
||||
|
||||
/**
|
||||
* @brief clear all caches held for this mod
|
||||
*/
|
||||
virtual void clearCaches();
|
||||
private:
|
||||
|
||||
enum EConflictType {
|
||||
CONFLICT_NONE,
|
||||
CONFLICT_OVERWRITE,
|
||||
CONFLICT_OVERWRITTEN,
|
||||
CONFLICT_MIXED,
|
||||
CONFLICT_REDUNDANT
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @return true if there is a conflict for files in this mod
|
||||
*/
|
||||
EConflictType isConflicted() const;
|
||||
|
||||
/**
|
||||
* @return true if this mod is completely replaced by others
|
||||
*/
|
||||
bool isRedundant() const;
|
||||
|
||||
private:
|
||||
|
||||
MOShared::DirectoryEntry **m_DirectoryStructure;
|
||||
|
||||
mutable EConflictType m_CurrentConflictState;
|
||||
mutable QTime m_LastConflictCheck;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Represents meta information about a single mod.
|
||||
@@ -489,7 +564,7 @@ private:
|
||||
* to manage the mod collection
|
||||
*
|
||||
**/
|
||||
class ModInfoRegular : public ModInfo
|
||||
class ModInfoRegular : public ModInfoWithConflictInfo
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
@@ -643,11 +718,6 @@ public:
|
||||
*/
|
||||
virtual void endorse(bool doEndorse);
|
||||
|
||||
/**
|
||||
* @brief clear all caches held for this mod
|
||||
*/
|
||||
virtual void clearCaches();
|
||||
|
||||
/**
|
||||
* @brief getter for the mod name
|
||||
*
|
||||
@@ -746,7 +816,9 @@ public:
|
||||
/**
|
||||
* @return last time nexus was queried for infos on this mod
|
||||
*/
|
||||
QDateTime getLastNexusQuery() const;
|
||||
virtual QDateTime getLastNexusQuery() const;
|
||||
|
||||
virtual QStringList archives() const;
|
||||
|
||||
/**
|
||||
* @brief stores meta information back to disk
|
||||
@@ -754,15 +826,6 @@ public:
|
||||
virtual void saveMeta();
|
||||
|
||||
void readMeta();
|
||||
private:
|
||||
|
||||
enum EConflictType {
|
||||
CONFLICT_NONE,
|
||||
CONFLICT_OVERWRITE,
|
||||
CONFLICT_OVERWRITTEN,
|
||||
CONFLICT_MIXED,
|
||||
CONFLICT_REDUNDANT
|
||||
};
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -770,18 +833,6 @@ private slots:
|
||||
void nxmEndorsementToggled(int, QVariant userData, QVariant resultData);
|
||||
void nxmRequestFailed(int modID, int fileID, QVariant userData, const QString &errorMessage);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @return true if there is a conflict for files in this mod
|
||||
*/
|
||||
EConflictType isConflicted() const;
|
||||
|
||||
/**
|
||||
* @return true if this mod is completely replaced by others
|
||||
*/
|
||||
bool isRedundant() const;
|
||||
|
||||
protected:
|
||||
|
||||
ModInfoRegular(const QDir &path, MOShared::DirectoryEntry **directoryStructure);
|
||||
@@ -807,11 +858,6 @@ private:
|
||||
|
||||
NexusBridge m_NexusBridge;
|
||||
|
||||
MOShared::DirectoryEntry **m_DirectoryStructure;
|
||||
|
||||
mutable EConflictType m_CurrentConflictState;
|
||||
mutable QTime m_LastConflictCheck;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -872,6 +918,7 @@ public:
|
||||
virtual void setNeverEndorse() {}
|
||||
virtual bool remove() { return false; }
|
||||
virtual void endorse(bool) {}
|
||||
virtual bool alwaysEnabled() const { return true; }
|
||||
virtual bool isEmpty() const;
|
||||
virtual QString name() const { return "Overwrite"; }
|
||||
virtual QString notes() const { return ""; }
|
||||
@@ -887,6 +934,7 @@ public:
|
||||
virtual QString getDescription() const;
|
||||
virtual QDateTime getLastNexusQuery() const { return QDateTime(); }
|
||||
virtual QString getNexusDescription() const { return QString(); }
|
||||
virtual QStringList archives() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -898,4 +946,63 @@ private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ModInfoForeign : public ModInfoWithConflictInfo
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
friend class ModInfo;
|
||||
|
||||
public:
|
||||
|
||||
virtual bool updateAvailable() const { return false; }
|
||||
virtual bool updateIgnored() const { return false; }
|
||||
virtual bool downgradeAvailable() const { return false; }
|
||||
virtual bool updateNXMInfo() { return false; }
|
||||
virtual void setCategory(int, bool) {}
|
||||
virtual bool setName(const QString&) { return false; }
|
||||
virtual void setNotes(const QString&) {}
|
||||
virtual void setNexusID(int) {}
|
||||
virtual void setNewestVersion(const MOBase::VersionInfo&) {}
|
||||
virtual void ignoreUpdate(bool) {}
|
||||
virtual void setNexusDescription(const QString&) {}
|
||||
virtual void addNexusCategory(int) {}
|
||||
virtual void setIsEndorsed(bool) {}
|
||||
virtual void setNeverEndorse() {}
|
||||
virtual bool remove() { return false; }
|
||||
virtual void endorse(bool) {}
|
||||
virtual bool isEmpty() const { return false; }
|
||||
virtual QString name() const;
|
||||
virtual QString notes() const { return ""; }
|
||||
virtual QDateTime creationTime() const;
|
||||
virtual QString absolutePath() const;
|
||||
virtual MOBase::VersionInfo getNewestVersion() const { return ""; }
|
||||
virtual QString getInstallationFile() const { return ""; }
|
||||
virtual int getNexusID() const { return -1; }
|
||||
virtual std::vector<QString> getIniTweaks() const { return std::vector<QString>(); }
|
||||
virtual std::vector<ModInfo::EFlag> getFlags() const;
|
||||
virtual int getHighlight() const;
|
||||
virtual QString getDescription() const;
|
||||
virtual QDateTime getLastNexusQuery() const { return QDateTime(); }
|
||||
virtual QString getNexusDescription() const { return QString(); }
|
||||
virtual int getFixedPriority() const { return INT_MIN; }
|
||||
virtual QStringList archives() const { return m_Archives; }
|
||||
virtual QStringList stealFiles() const { return m_Archives + QStringList(m_ReferenceFile); }
|
||||
virtual bool alwaysEnabled() const { return true; }
|
||||
|
||||
protected:
|
||||
|
||||
ModInfoForeign(const QString &referenceFile, const QStringList &archives, MOShared::DirectoryEntry **directoryStructure);
|
||||
|
||||
private:
|
||||
|
||||
QString m_Name;
|
||||
QString m_ReferenceFile;
|
||||
QStringList m_Archives;
|
||||
QDateTime m_CreationTime;
|
||||
int m_Priority;
|
||||
|
||||
};
|
||||
|
||||
#endif // MODINFO_H
|
||||
|
||||
+121
-107
@@ -60,7 +60,7 @@ bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS)
|
||||
}
|
||||
|
||||
|
||||
ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, QWidget *parent)
|
||||
ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, QWidget *parent)
|
||||
: TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo),
|
||||
m_ThumbnailMapper(this), m_RequestStarted(false),
|
||||
m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL),
|
||||
@@ -69,16 +69,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle(modInfo->name());
|
||||
this->setWindowModality(Qt::WindowModal);
|
||||
m_UTF8Codec = QTextCodec::codecForName("utf-8");
|
||||
|
||||
QListWidget *textFileList = findChild<QListWidget*>("textFileList");
|
||||
QListWidget *iniTweaksList = findChild<QListWidget*>("iniTweaksList");
|
||||
QListWidget *activeESPList = findChild<QListWidget*>("activeESPList");
|
||||
QListWidget *inactiveESPList = findChild<QListWidget*>("inactiveESPList");
|
||||
QTreeWidget *categoriesTree = findChild<QTreeWidget*>("categoriesTree");
|
||||
QHBoxLayout *thumbnailArea = findChild<QHBoxLayout*>("thumbnailArea");
|
||||
|
||||
m_FileTree = findChild<QTreeView*>("fileTree");
|
||||
|
||||
m_RootPath = modInfo->absolutePath();
|
||||
|
||||
@@ -86,37 +76,14 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo
|
||||
m_Settings = new QSettings(metaFileName, QSettings::IniFormat);
|
||||
|
||||
QLineEdit *modIDEdit = findChild<QLineEdit*>("modIDEdit");
|
||||
modIDEdit->setValidator(new QIntValidator(modIDEdit));
|
||||
modIDEdit->setText(QString("%1").arg(modInfo->getNexusID()));
|
||||
ui->modIDEdit->setValidator(new QIntValidator(modIDEdit));
|
||||
ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID()));
|
||||
|
||||
ui->notesEdit->setText(modInfo->notes());
|
||||
|
||||
m_FileSystemModel = new QFileSystemModel(this);
|
||||
m_FileSystemModel->setReadOnly(false);
|
||||
m_FileSystemModel->setRootPath(m_RootPath);
|
||||
m_FileTree->setModel(m_FileSystemModel);
|
||||
m_FileTree->setRootIndex(m_FileSystemModel->index(m_RootPath));
|
||||
m_FileTree->setColumnWidth(0, 300);
|
||||
|
||||
connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&)));
|
||||
connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&)));
|
||||
|
||||
m_DeleteAction = new QAction(tr("&Delete"), m_FileTree);
|
||||
m_RenameAction = new QAction(tr("&Rename"), m_FileTree);
|
||||
m_HideAction = new QAction(tr("&Hide"), m_FileTree);
|
||||
m_UnhideAction = new QAction(tr("&Unhide"), m_FileTree);
|
||||
m_OpenAction = new QAction(tr("&Open"), m_FileTree);
|
||||
m_NewFolderAction = new QAction(tr("&New Folder"), m_FileTree);
|
||||
QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered()));
|
||||
QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered()));
|
||||
QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered()));
|
||||
QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered()));
|
||||
QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered()));
|
||||
connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered()));
|
||||
connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool)));
|
||||
|
||||
// ui->descriptionView->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
// QObject::connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl)));
|
||||
connect(ui->descriptionView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl)));
|
||||
|
||||
if (directory->originExists(ToWString(modInfo->name()))) {
|
||||
@@ -126,32 +93,39 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo
|
||||
}
|
||||
}
|
||||
|
||||
addCategories(CategoryFactory::instance(), modInfo->getCategories(), categoriesTree->invisibleRootItem(), 0);
|
||||
refreshPrimaryCategoriesBox();
|
||||
if (unmanaged) {
|
||||
ui->tabWidget->setTabEnabled(TAB_INIFILES, false);
|
||||
ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false);
|
||||
ui->tabWidget->setTabEnabled(TAB_NEXUS, false);
|
||||
ui->tabWidget->setTabEnabled(TAB_FILETREE, false);
|
||||
ui->tabWidget->setTabEnabled(TAB_NOTES, false);
|
||||
} else {
|
||||
initFiletree(modInfo);
|
||||
initINITweaks();
|
||||
addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0);
|
||||
refreshPrimaryCategoriesBox();
|
||||
}
|
||||
refreshLists();
|
||||
|
||||
int numTweaks = m_Settings->beginReadArray("INI Tweaks");
|
||||
for (int i = 0; i < numTweaks; ++i) {
|
||||
m_Settings->setArrayIndex(i);
|
||||
QList<QListWidgetItem*> items = iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString);
|
||||
if (items.size() != 0) {
|
||||
items.at(0)->setCheckState(Qt::Checked);
|
||||
}
|
||||
}
|
||||
m_Settings->endArray();
|
||||
ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0);
|
||||
ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0);
|
||||
ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0));
|
||||
ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL);
|
||||
|
||||
QTabWidget *tabWidget = findChild<QTabWidget*>("tabWidget");
|
||||
tabWidget->setTabEnabled(TAB_TEXTFILES, textFileList->count() != 0);
|
||||
tabWidget->setTabEnabled(TAB_IMAGES, thumbnailArea->count() != 0);
|
||||
tabWidget->setTabEnabled(TAB_ESPS, (inactiveESPList->count() != 0) || (activeESPList->count() != 0));
|
||||
tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL);
|
||||
|
||||
if (tabWidget->currentIndex() == TAB_NEXUS) {
|
||||
if (ui->tabWidget->currentIndex() == TAB_NEXUS) {
|
||||
activateNexusTab();
|
||||
}
|
||||
|
||||
ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) ||
|
||||
(m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER));
|
||||
|
||||
// activate first enabled tab
|
||||
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||
if (ui->tabWidget->isTabEnabled(i)) {
|
||||
ui->tabWidget->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +139,45 @@ ModInfoDialog::~ModInfoDialog()
|
||||
}
|
||||
|
||||
|
||||
void ModInfoDialog::initINITweaks()
|
||||
{
|
||||
int numTweaks = m_Settings->beginReadArray("INI Tweaks");
|
||||
for (int i = 0; i < numTweaks; ++i) {
|
||||
m_Settings->setArrayIndex(i);
|
||||
QList<QListWidgetItem*> items = ui->iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString);
|
||||
if (items.size() != 0) {
|
||||
items.at(0)->setCheckState(Qt::Checked);
|
||||
}
|
||||
}
|
||||
m_Settings->endArray();
|
||||
}
|
||||
|
||||
void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo)
|
||||
{
|
||||
ui->fileTree = findChild<QTreeView*>("fileTree");
|
||||
|
||||
m_FileSystemModel = new QFileSystemModel(this);
|
||||
m_FileSystemModel->setReadOnly(false);
|
||||
m_FileSystemModel->setRootPath(m_RootPath);
|
||||
ui->fileTree->setModel(m_FileSystemModel);
|
||||
ui->fileTree->setRootIndex(m_FileSystemModel->index(m_RootPath));
|
||||
ui->fileTree->setColumnWidth(0, 300);
|
||||
|
||||
m_DeleteAction = new QAction(tr("&Delete"), ui->fileTree);
|
||||
m_RenameAction = new QAction(tr("&Rename"), ui->fileTree);
|
||||
m_HideAction = new QAction(tr("&Hide"), ui->fileTree);
|
||||
m_UnhideAction = new QAction(tr("&Unhide"), ui->fileTree);
|
||||
m_OpenAction = new QAction(tr("&Open"), ui->fileTree);
|
||||
m_NewFolderAction = new QAction(tr("&New Folder"), ui->fileTree);
|
||||
QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered()));
|
||||
QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered()));
|
||||
QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered()));
|
||||
QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered()));
|
||||
QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered()));
|
||||
connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered()));
|
||||
}
|
||||
|
||||
|
||||
int ModInfoDialog::tabIndex(const QString &tabId)
|
||||
{
|
||||
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||
@@ -271,51 +284,52 @@ void ModInfoDialog::refreshLists()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RootPath.length() > 0) {
|
||||
QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories);
|
||||
while (dirIterator.hasNext()) {
|
||||
QString fileName = dirIterator.next();
|
||||
|
||||
QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories);
|
||||
while (dirIterator.hasNext()) {
|
||||
QString fileName = dirIterator.next();
|
||||
|
||||
if (fileName.endsWith(".txt", Qt::CaseInsensitive)) {
|
||||
ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1));
|
||||
} else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) &&
|
||||
!fileName.endsWith("meta.ini")) {
|
||||
QString namePart = fileName.mid(m_RootPath.length() + 1);
|
||||
if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) {
|
||||
QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList);
|
||||
newItem->setData(Qt::UserRole, namePart);
|
||||
newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
|
||||
newItem->setCheckState(Qt::Unchecked);
|
||||
ui->iniTweaksList->addItem(newItem);
|
||||
} else {
|
||||
ui->iniFileList->addItem(namePart);
|
||||
}
|
||||
} else if (fileName.endsWith(".esp", Qt::CaseInsensitive) ||
|
||||
fileName.endsWith(".esm", Qt::CaseInsensitive)) {
|
||||
QString relativePath = fileName.mid(m_RootPath.length() + 1);
|
||||
if (relativePath.contains('/')) {
|
||||
QFileInfo fileInfo(fileName);
|
||||
QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName());
|
||||
newItem->setData(Qt::UserRole, relativePath);
|
||||
ui->inactiveESPList->addItem(newItem);
|
||||
} else {
|
||||
ui->activeESPList->addItem(relativePath);
|
||||
}
|
||||
} else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) ||
|
||||
(fileName.endsWith(".jpg", Qt::CaseInsensitive))) {
|
||||
QImage image = QImage(fileName);
|
||||
if (!image.isNull()) {
|
||||
if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) {
|
||||
image = image.scaledToWidth(128);
|
||||
if (fileName.endsWith(".txt", Qt::CaseInsensitive)) {
|
||||
ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1));
|
||||
} else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) &&
|
||||
!fileName.endsWith("meta.ini")) {
|
||||
QString namePart = fileName.mid(m_RootPath.length() + 1);
|
||||
if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) {
|
||||
QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList);
|
||||
newItem->setData(Qt::UserRole, namePart);
|
||||
newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
|
||||
newItem->setCheckState(Qt::Unchecked);
|
||||
ui->iniTweaksList->addItem(newItem);
|
||||
} else {
|
||||
image = image.scaledToHeight(96);
|
||||
ui->iniFileList->addItem(namePart);
|
||||
}
|
||||
} else if (fileName.endsWith(".esp", Qt::CaseInsensitive) ||
|
||||
fileName.endsWith(".esm", Qt::CaseInsensitive)) {
|
||||
QString relativePath = fileName.mid(m_RootPath.length() + 1);
|
||||
if (relativePath.contains('/')) {
|
||||
QFileInfo fileInfo(fileName);
|
||||
QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName());
|
||||
newItem->setData(Qt::UserRole, relativePath);
|
||||
ui->inactiveESPList->addItem(newItem);
|
||||
} else {
|
||||
ui->activeESPList->addItem(relativePath);
|
||||
}
|
||||
} else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) ||
|
||||
(fileName.endsWith(".jpg", Qt::CaseInsensitive))) {
|
||||
QImage image = QImage(fileName);
|
||||
if (!image.isNull()) {
|
||||
if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) {
|
||||
image = image.scaledToWidth(128);
|
||||
} else {
|
||||
image = image.scaledToHeight(96);
|
||||
}
|
||||
|
||||
QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), "");
|
||||
thumbnailButton->setIconSize(QSize(image.width(), image.height()));
|
||||
connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map()));
|
||||
m_ThumbnailMapper.setMapping(thumbnailButton, fileName);
|
||||
ui->thumbnailArea->addWidget(thumbnailButton);
|
||||
QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), "");
|
||||
thumbnailButton->setIconSize(QSize(image.width(), image.height()));
|
||||
connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map()));
|
||||
m_ThumbnailMapper.setMapping(thumbnailButton, fileName);
|
||||
ui->thumbnailArea->addWidget(thumbnailButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -460,7 +474,8 @@ void ModInfoDialog::openIniFile(const QString &fileName)
|
||||
QFile iniFile(fileName);
|
||||
iniFile.open(QIODevice::ReadOnly);
|
||||
QByteArray buffer = iniFile.readAll();
|
||||
QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec);
|
||||
|
||||
QTextCodec *codec = QTextCodec::codecForUtfText(buffer, QTextCodec::codecForName("utf-8"));
|
||||
QTextEdit *iniFileView = findChild<QTextEdit*>("iniFileView");
|
||||
iniFileView->setText(codec->toUnicode(buffer));
|
||||
iniFileView->setProperty("currentFile", fileName);
|
||||
@@ -929,7 +944,7 @@ void ModInfoDialog::renameTriggered()
|
||||
return;
|
||||
}
|
||||
|
||||
m_FileTree->edit(index);
|
||||
ui->fileTree->edit(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -999,18 +1014,18 @@ void ModInfoDialog::createDirectoryTriggered()
|
||||
return;
|
||||
}
|
||||
|
||||
m_FileTree->setCurrentIndex(newIndex);
|
||||
m_FileTree->edit(newIndex);
|
||||
ui->fileTree->setCurrentIndex(newIndex);
|
||||
ui->fileTree->edit(newIndex);
|
||||
}
|
||||
|
||||
|
||||
void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QItemSelectionModel *selectionModel = m_FileTree->selectionModel();
|
||||
QItemSelectionModel *selectionModel = ui->fileTree->selectionModel();
|
||||
m_FileSelection = selectionModel->selectedRows(0);
|
||||
|
||||
// m_FileSelection = m_FileTree->indexAt(pos);
|
||||
QMenu menu(m_FileTree);
|
||||
// m_FileSelection = ui->fileTree->indexAt(pos);
|
||||
QMenu menu(ui->fileTree);
|
||||
|
||||
menu.addAction(m_NewFolderAction);
|
||||
|
||||
@@ -1038,7 +1053,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos)
|
||||
m_FileSelection.clear();
|
||||
m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0));
|
||||
}
|
||||
menu.exec(m_FileTree->mapToGlobal(pos));
|
||||
menu.exec(ui->fileTree->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
||||
@@ -1164,18 +1179,17 @@ void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &po
|
||||
{
|
||||
m_ConflictsContextItem = ui->overwriteTree->itemAt(pos.x(), pos.y());
|
||||
|
||||
QMenu menu;
|
||||
if (m_ConflictsContextItem != NULL) {
|
||||
// offer to hide/unhide file, but not for files from archives
|
||||
// if (!m_ConflictsContextItem->data(0, Qt::UserRole + 1).toBool()) {
|
||||
if (!m_ConflictsContextItem->data(0, Qt::UserRole + 2).toBool()) {
|
||||
if (!m_ConflictsContextItem->data(1, Qt::UserRole + 2).toBool()) {
|
||||
QMenu menu;
|
||||
if (m_ConflictsContextItem->text(0).endsWith(ModInfo::s_HiddenExt)) {
|
||||
menu.addAction(tr("Un-Hide"), this, SLOT(unhideConflictFile()));
|
||||
} else {
|
||||
menu.addAction(tr("Hide"), this, SLOT(hideConflictFile()));
|
||||
}
|
||||
menu.exec(ui->overwriteTree->mapToGlobal(pos));
|
||||
}
|
||||
menu.exec(ui->overwriteTree->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -65,6 +65,7 @@ public:
|
||||
TAB_CONFLICTS,
|
||||
TAB_CATEGORIES,
|
||||
TAB_NEXUS,
|
||||
TAB_NOTES,
|
||||
TAB_FILETREE
|
||||
};
|
||||
|
||||
@@ -76,7 +77,7 @@ public:
|
||||
* @param modInfo info structure about the mod to display
|
||||
* @param parent parend widget
|
||||
**/
|
||||
explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, QWidget *parent = 0);
|
||||
explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, QWidget *parent = 0);
|
||||
~ModInfoDialog();
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,9 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
void initFiletree(ModInfo::Ptr modInfo);
|
||||
void initINITweaks();
|
||||
|
||||
void refreshLists();
|
||||
|
||||
void addCategories(const CategoryFactory &factory, const std::set<int> &enabledCategories, QTreeWidgetItem *root, int rootLevel);
|
||||
@@ -225,7 +229,6 @@ private:
|
||||
|
||||
const MOShared::DirectoryEntry *m_Directory;
|
||||
MOShared::FilesOrigin *m_Origin;
|
||||
QTextCodec *m_UTF8Codec;
|
||||
|
||||
std::map<int, int> m_RealTabPos;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user