mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0feb4b702d | ||
|
|
5a4b6e70fd | ||
|
|
18574c2ba8 | ||
|
|
1eb783aae3 | ||
|
|
93a19799b8 | ||
|
|
2a31eb40fb | ||
|
|
cf0a1bc2be | ||
|
|
48c8cca578 | ||
|
|
977b407525 | ||
|
|
f4b1aba61a | ||
|
|
0eb1662a0e | ||
|
|
164ec25a75 | ||
|
|
6fb36d6c02 | ||
|
|
ea1f959ad5 | ||
|
|
7cf3b3455b |
+33
-10
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <QRegExp>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <boost/assign.hpp>
|
||||
|
||||
|
||||
namespace BBCode {
|
||||
@@ -43,7 +44,6 @@ public:
|
||||
// extract the tag name
|
||||
m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset);
|
||||
QString tagName = m_TagNameExp.cap(0).toLower();
|
||||
//qDebug("tag name %s", tagName.toUtf8().constData());
|
||||
TagMap::iterator tagIter = m_TagMap.find(tagName);
|
||||
if (tagIter != m_TagMap.end()) {
|
||||
// recognized tag
|
||||
@@ -60,7 +60,21 @@ public:
|
||||
length = closeTagPos + closeTag.length();
|
||||
QString temp = input.mid(0, length);
|
||||
if (tagIter->second.first.indexIn(temp) == 0) {
|
||||
return temp.replace(tagIter->second.first, tagIter->second.second);
|
||||
if (tagIter->second.second.isEmpty()) {
|
||||
if (tagName == "color") {
|
||||
QString color = tagIter->second.first.cap(1);
|
||||
QString content = tagIter->second.first.cap(2);
|
||||
auto colIter = m_ColorMap.find(color.toLower());
|
||||
if (colIter != m_ColorMap.end()) {
|
||||
color = colIter->second;
|
||||
}
|
||||
return temp.replace(tagIter->second.first, QString("<font style=\"color: #%1;\">%2</font>").arg(color, content));
|
||||
} else {
|
||||
qWarning("don't know how to deal with tag %s", qPrintable(tagName));
|
||||
}
|
||||
} else {
|
||||
return temp.replace(tagIter->second.first, tagIter->second.second);
|
||||
}
|
||||
} else {
|
||||
// expression doesn't match. either the input string is invalid
|
||||
// or the expression is
|
||||
@@ -96,7 +110,7 @@ private:
|
||||
m_TagMap["size="] = std::make_pair(QRegExp("\\[size=([^\\]]*)\\](.*)\\[/size\\]"),
|
||||
"<font size=\"\\1\">\\2</font>");
|
||||
m_TagMap["color="] = std::make_pair(QRegExp("\\[color=([^\\]]*)\\](.*)\\[/color\\]"),
|
||||
"<font style=\"color: #\\1;\">\\2</font>");
|
||||
"");
|
||||
m_TagMap["font="] = std::make_pair(QRegExp("\\[font=([^\\]]*)\\](.*)\\[/font\\]"),
|
||||
"<font face=\\1>\\2</font>");
|
||||
m_TagMap["center"] = std::make_pair(QRegExp("\\[center\\](.*)\\[/center\\]"),
|
||||
@@ -139,17 +153,25 @@ 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\\]"),
|
||||
"<img src=\"\\1\"/>");
|
||||
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"),
|
||||
"<img src=\"\\2\" align=\"\\1\" />");*/
|
||||
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.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) {
|
||||
iter->second.first.setCaseSensitivity(Qt::CaseInsensitive);
|
||||
@@ -157,10 +179,11 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
QRegExp m_TagNameExp;
|
||||
TagMap m_TagMap;
|
||||
std::map<QString, QString> m_ColorMap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+60
-15
@@ -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,10 +48,19 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,21 +72,56 @@ 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::addModToStructure(DirectoryEntry *directoryStructure
|
||||
, const QString &modName
|
||||
, int priority
|
||||
, const QString &directory
|
||||
, const QStringList &stealFiles
|
||||
, const QStringList &archives)
|
||||
{
|
||||
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
|
||||
std::wstring modNameW = ToWString(modName);
|
||||
|
||||
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
|
||||
QDir dir(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
|
||||
directoryStructure->createOrigin(modNameW, 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(directoryStructure->getOriginByName(modNameW).getID(),
|
||||
file->getFileTime(), L"");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
directoryStructure->addFromOrigin(modNameW, 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);
|
||||
if (m_EnabledArchives.find(file.fileName()) != m_EnabledArchives.end()) {
|
||||
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(modNameW, directoryW,
|
||||
ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,23 +133,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
|
||||
@@ -82,7 +84,7 @@ public:
|
||||
* @param directory
|
||||
* @param priorityDir
|
||||
*/
|
||||
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);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -99,7 +101,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;
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const
|
||||
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
|
||||
} else {
|
||||
const MOBase::ModRepositoryFileInfo *info = m_Manager->getFileInfo(index.row());
|
||||
return QString("%1 (ID %2) %3").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString());
|
||||
return QString("%1 (ID %2) %3<br><span>%4</span>").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString()).arg(info->description);
|
||||
}
|
||||
return text;
|
||||
} else {
|
||||
|
||||
+120
-82
@@ -26,6 +26,7 @@ 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 <QTimer>
|
||||
#include <QFileInfo>
|
||||
@@ -147,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()
|
||||
@@ -252,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -343,6 +350,9 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const ModRepositoryFileI
|
||||
bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName,
|
||||
int modID, int fileID, const ModRepositoryFileInfo *fileInfo)
|
||||
{
|
||||
if (!reply->isRunning()) {
|
||||
qDebug("this is not a running download! %d", reply->isFinished());
|
||||
}
|
||||
// download invoked from an already open network reply (i.e. download link in the browser)
|
||||
DownloadInfo *newDownload = DownloadInfo::createNew(fileInfo, URLs);
|
||||
|
||||
@@ -403,6 +413,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")
|
||||
@@ -412,6 +423,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()));
|
||||
|
||||
@@ -420,11 +432,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +463,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(), ""));
|
||||
}
|
||||
|
||||
|
||||
@@ -592,8 +608,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);
|
||||
}
|
||||
@@ -618,6 +637,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();
|
||||
}
|
||||
@@ -918,10 +942,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);
|
||||
@@ -954,32 +978,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)."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1159,14 +1191,14 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD
|
||||
info->fileName = result["uri"].toString();
|
||||
info->fileCategory = result["category_id"].toInt();
|
||||
info->fileTime = matchDate(result["date"].toString());
|
||||
info->description = result["description"].toString();
|
||||
info->description = BBCode::convertToHTML(result["description"].toString());
|
||||
|
||||
info->repository = "Nexus";
|
||||
info->modID = modID;
|
||||
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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -1266,7 +1298,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);
|
||||
}
|
||||
|
||||
@@ -1355,7 +1386,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()) {
|
||||
@@ -1405,6 +1435,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;
|
||||
@@ -1415,7 +1453,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);
|
||||
|
||||
|
||||
@@ -522,9 +522,10 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
|
||||
return false;
|
||||
}
|
||||
|
||||
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory.mid(0).append("\\").append(modName));
|
||||
QString targetDirectoryNative = m_ModsDirectory.mid(0).append("\\").append(modName);
|
||||
QString targetDirectory = QDir::fromNativeSeparators(targetDirectoryNative);
|
||||
|
||||
qDebug("installing to \"%s\"", targetDirectory.toUtf8().constData());
|
||||
qDebug("installing to \"%s\"", targetDirectoryNative.toUtf8().constData());
|
||||
|
||||
m_InstallationProgress.setWindowTitle(tr("Extracting files"));
|
||||
m_InstallationProgress.setLabelText(QString());
|
||||
|
||||
+106
-18
@@ -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>
|
||||
|
||||
@@ -29,7 +30,7 @@ QMutex LogBuffer::s_Mutex;
|
||||
|
||||
|
||||
LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
|
||||
: QObject(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
|
||||
: QAbstractItemModel(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
|
||||
m_MinMsgType(minMsgType), m_NumMessages(0)
|
||||
{
|
||||
m_Messages.resize(messageCount);
|
||||
@@ -51,7 +52,16 @@ LogBuffer::~LogBuffer()
|
||||
void LogBuffer::logMessage(QtMsgType type, const QString &message)
|
||||
{
|
||||
if (type >= m_MinMsgType) {
|
||||
m_Messages.at(m_NumMessages % m_Messages.size()) = message;
|
||||
Message msg = { type, QTime::currentTime(), message };
|
||||
if (m_NumMessages < m_Messages.size()) {
|
||||
beginInsertRows(QModelIndex(), m_NumMessages, m_NumMessages + 1);
|
||||
}
|
||||
m_Messages.at(m_NumMessages % m_Messages.size()) = msg;
|
||||
if (m_NumMessages < m_Messages.size()) {
|
||||
endInsertRows();
|
||||
} else {
|
||||
emit dataChanged(createIndex(0, 0), createIndex(m_Messages.size(), 0));
|
||||
}
|
||||
++m_NumMessages;
|
||||
if (type >= QtCriticalMsg) {
|
||||
write();
|
||||
@@ -77,7 +87,7 @@ void LogBuffer::write() const
|
||||
unsigned int i = (m_NumMessages > m_Messages.size()) ? m_NumMessages - m_Messages.size()
|
||||
: 0U;
|
||||
for (; i < m_NumMessages; ++i) {
|
||||
file.write(m_Messages.at(i % m_Messages.size()).toUtf8());
|
||||
file.write(m_Messages.at(i % m_Messages.size()).toString().toUtf8());
|
||||
file.write("\r\n");
|
||||
}
|
||||
::SetLastError(lastError);
|
||||
@@ -99,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) {
|
||||
@@ -125,6 +120,26 @@ 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);
|
||||
@@ -137,6 +152,73 @@ void LogBuffer::log(QtMsgType type, const char *message)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
QModelIndex LogBuffer::index(int row, int column, const QModelIndex&) const
|
||||
{
|
||||
return createIndex(row, column, row);
|
||||
}
|
||||
|
||||
QModelIndex LogBuffer::parent(const QModelIndex&) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int LogBuffer::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
else
|
||||
return std::min(m_NumMessages, m_Messages.size());
|
||||
}
|
||||
|
||||
int LogBuffer::columnCount(const QModelIndex&) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
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() + 1) % m_Messages.size();
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
if (index.column() == 0) {
|
||||
return m_Messages.at(msgIndex).time;
|
||||
} else if (index.column() == 1) {
|
||||
const QString &msg = m_Messages.at(msgIndex).message;
|
||||
if (msg.length() < 200) {
|
||||
return msg;
|
||||
} else {
|
||||
return msg.mid(0, 200) + "...";
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Qt::DecorationRole: {
|
||||
if (index.column() == 1) {
|
||||
switch (m_Messages.at(msgIndex).type) {
|
||||
case QtDebugMsg: return QIcon(":/MO/gui/information");
|
||||
case QtWarningMsg: return QIcon(":/MO/gui/warning");
|
||||
case QtCriticalMsg: return QIcon(":/MO/gui/important");
|
||||
case QtFatalMsg: return QIcon(":/MO/gui/problem");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Qt::UserRole: {
|
||||
if (index.column() == 1) {
|
||||
switch (m_Messages.at(msgIndex).type) {
|
||||
case QtDebugMsg: return "D";
|
||||
case QtWarningMsg: return "W";
|
||||
case QtCriticalMsg: return "C";
|
||||
case QtFatalMsg: return "F";
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void LogBuffer::writeNow()
|
||||
{
|
||||
QMutexLocker guard(&s_Mutex);
|
||||
@@ -171,3 +253,9 @@ void log(const char *format, ...)
|
||||
va_end(argList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString LogBuffer::Message::toString() const
|
||||
{
|
||||
return QString("%1 [%2] %3").arg(time.toString()).arg(msgTypeID(type)).arg(message);
|
||||
}
|
||||
|
||||
+23
-2
@@ -23,10 +23,12 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QScopedPointer>
|
||||
#include <QStringListModel>
|
||||
#include <QTime>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class LogBuffer : public QObject
|
||||
class LogBuffer : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -42,12 +44,22 @@ public:
|
||||
static void writeNow();
|
||||
static void cleanQuit();
|
||||
|
||||
static LogBuffer *instance() { return s_Instance.data(); }
|
||||
|
||||
public:
|
||||
|
||||
virtual ~LogBuffer();
|
||||
|
||||
void logMessage(QtMsgType type, const QString &message);
|
||||
|
||||
// QAbstractItemModel interface
|
||||
public:
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
@@ -62,6 +74,15 @@ private:
|
||||
|
||||
static char msgTypeID(QtMsgType type);
|
||||
|
||||
private:
|
||||
|
||||
struct Message {
|
||||
QtMsgType type;
|
||||
QTime time;
|
||||
QString message;
|
||||
QString toString() const;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
static QScopedPointer<LogBuffer> s_Instance;
|
||||
@@ -71,7 +92,7 @@ private:
|
||||
bool m_ShutDown;
|
||||
QtMsgType m_MinMsgType;
|
||||
unsigned int m_NumMessages;
|
||||
std::vector<QString> m_Messages;
|
||||
std::vector<Message> m_Messages;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+1
-5
@@ -195,7 +195,6 @@ bool isNxmLink(const QString &link)
|
||||
return link.left(6).toLower() == "nxm://";
|
||||
}
|
||||
|
||||
|
||||
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs)
|
||||
{
|
||||
typedef BOOL (WINAPI *FuncMiniDumpWriteDump)(HANDLE process, DWORD pid, HANDLE file, MINIDUMP_TYPE dumpType,
|
||||
@@ -257,14 +256,11 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void registerMetaTypes()
|
||||
{
|
||||
registerExecutable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool HaveWriteAccess(const std::wstring &path)
|
||||
{
|
||||
bool writable = false;
|
||||
@@ -333,7 +329,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())));
|
||||
|
||||
+539
-566
File diff suppressed because it is too large
Load Diff
+38
-34
@@ -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);
|
||||
@@ -97,7 +100,6 @@ public:
|
||||
|
||||
bool addProfile();
|
||||
void refreshLists();
|
||||
void refreshESPList();
|
||||
void refreshBSAList();
|
||||
void refreshDataTree();
|
||||
void refreshSaveList();
|
||||
@@ -117,34 +119,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 void 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;
|
||||
@@ -158,6 +132,9 @@ public:
|
||||
void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite);
|
||||
std::string readFromPipe(HANDLE stdOutRead);
|
||||
void processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog);
|
||||
|
||||
HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "");
|
||||
|
||||
public slots:
|
||||
|
||||
void displayColumnSelection(const QPoint &pos);
|
||||
@@ -200,6 +177,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 +191,6 @@ private:
|
||||
|
||||
void setExecutableIndex(int index);
|
||||
|
||||
bool nexusLogin();
|
||||
|
||||
bool testForSteam();
|
||||
void startSteam();
|
||||
|
||||
@@ -224,6 +202,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 +252,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);
|
||||
@@ -298,6 +283,10 @@ private:
|
||||
bool createBackup(const QString &filePath, const QDateTime &time);
|
||||
QString queryRestore(const QString &filePath);
|
||||
|
||||
QMenu *modListContextMenu();
|
||||
|
||||
std::set<QString> enabledArchives();
|
||||
|
||||
private:
|
||||
|
||||
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
|
||||
@@ -380,6 +369,7 @@ private:
|
||||
QFile m_PluginsCheck;
|
||||
|
||||
SignalAboutToRunApplication m_AboutToRun;
|
||||
SignalModInstalled m_ModInstalled;
|
||||
|
||||
QString m_CurrentLanguage;
|
||||
std::vector<QTranslator*> m_Translators;
|
||||
@@ -391,6 +381,10 @@ private:
|
||||
|
||||
std::vector<QTreeWidgetItem*> m_RemoveWidget;
|
||||
|
||||
uint m_ArchiveListHash;
|
||||
|
||||
bool m_DidUpdateMasterList;
|
||||
|
||||
private slots:
|
||||
|
||||
void showMessage(const QString &message);
|
||||
@@ -456,6 +450,8 @@ private slots:
|
||||
|
||||
void linkClicked(const QString &url);
|
||||
|
||||
bool nexusLogin();
|
||||
|
||||
void loginSuccessful(bool necessary);
|
||||
void loginSuccessfulUpdate(bool necessary);
|
||||
void loginFailed(const QString &message);
|
||||
@@ -487,6 +483,7 @@ private slots:
|
||||
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();
|
||||
@@ -554,6 +551,7 @@ private slots:
|
||||
void delayedRemove();
|
||||
|
||||
void requestDownload(const QUrl &url, QNetworkReply *reply);
|
||||
void profileRefresh();
|
||||
|
||||
private slots: // ui slots
|
||||
// actions
|
||||
@@ -576,7 +574,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);
|
||||
@@ -594,6 +591,13 @@ private slots: // ui slots
|
||||
void on_restoreButton_clicked();
|
||||
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
|
||||
|
||||
+1046
-954
File diff suppressed because it is too large
Load Diff
+232
-116
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+53
-24
@@ -354,10 +354,10 @@ bool ModList::renameMod(int index, const QString &newName)
|
||||
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
|
||||
QString oldName = modInfo->name();
|
||||
modInfo->setName(nameFixed);
|
||||
// this just broke all profiles! The recipient of modRenamed has to do some magic
|
||||
// to can't write the currently active profile back
|
||||
emit modRenamed(oldName, nameFixed);
|
||||
if (modInfo->setName(nameFixed)) {
|
||||
// this just disabled the mod in all profiles. The recipient of modRenamed must fix that
|
||||
emit modRenamed(oldName, nameFixed);
|
||||
}
|
||||
|
||||
// invalidate the currently displayed state of this list
|
||||
notifyChange(-1);
|
||||
@@ -375,20 +375,23 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
|
||||
int modID = index.row();
|
||||
|
||||
ModInfo::Ptr info = ModInfo::getByIndex(modID);
|
||||
IModList::ModStates oldState = state(modID);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
bool enabled = value.toInt() == Qt::Checked;
|
||||
if (m_Profile->modEnabled(modID) != enabled) {
|
||||
m_Profile->setModEnabled(modID, enabled);
|
||||
m_Modified = true;
|
||||
|
||||
emit modlist_changed(index, role);
|
||||
}
|
||||
return true;
|
||||
result = true;
|
||||
} else if (role == Qt::EditRole) {
|
||||
bool res = false;
|
||||
switch (index.column()) {
|
||||
case COL_NAME: {
|
||||
res = renameMod(modID, value.toString());
|
||||
result = renameMod(modID, value.toString());
|
||||
} break;
|
||||
case COL_PRIORITY: {
|
||||
bool ok = false;
|
||||
@@ -397,47 +400,55 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
m_Profile->setModPriority(modID, newPriority);
|
||||
|
||||
emit modlist_changed(index, role);
|
||||
res = true;
|
||||
result = true;
|
||||
} else {
|
||||
res = false;
|
||||
result = false;
|
||||
}
|
||||
} break;
|
||||
case COL_MODID: {
|
||||
ModInfo::Ptr info = ModInfo::getByIndex(modID);
|
||||
bool ok = false;
|
||||
int newID = value.toInt(&ok);
|
||||
if (ok) {
|
||||
info->setNexusID(newID);
|
||||
emit modlist_changed(index, role);
|
||||
res = true;
|
||||
result = true;
|
||||
} else {
|
||||
res = false;
|
||||
result = false;
|
||||
}
|
||||
} break;
|
||||
case COL_VERSION: {
|
||||
ModInfo::Ptr info = ModInfo::getByIndex(modID);
|
||||
VersionInfo::VersionScheme scheme = info->getVersion().scheme();
|
||||
VersionInfo version(value.toString(), scheme);
|
||||
if (version.isValid()) {
|
||||
info->setVersion(version);
|
||||
res = true;
|
||||
result = true;
|
||||
} else {
|
||||
res = false;
|
||||
result = false;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
qWarning("edit on column \"%s\" not supported",
|
||||
getColumnName(index.column()).toUtf8().constData());
|
||||
res = false;
|
||||
result = false;
|
||||
} break;
|
||||
}
|
||||
if (res) {
|
||||
if (result) {
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
IModList::ModStates newState = state(modID);
|
||||
if (oldState != newState) {
|
||||
try {
|
||||
m_ModStateChanged(info->name(), newState);
|
||||
} catch (const std::exception &e) {
|
||||
qCritical("failed to invoke state changed notification: %s", e.what());
|
||||
} catch (...) {
|
||||
qCritical("failed to invoke state changed notification: unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -536,7 +547,9 @@ void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
|
||||
|
||||
for (std::vector<int>::const_iterator iter = sourceIndices.begin();
|
||||
iter != sourceIndices.end(); ++iter) {
|
||||
int oldPriority = m_Profile->getModPriority(*iter);
|
||||
m_Profile->setModPriority(*iter, newPriority);
|
||||
m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority);
|
||||
}
|
||||
|
||||
emit layoutChanged();
|
||||
@@ -578,10 +591,9 @@ void ModList::modInfoChanged(ModInfo::Ptr info)
|
||||
}
|
||||
}
|
||||
|
||||
IModList::ModStates ModList::state(const QString &name) const
|
||||
IModList::ModStates ModList::state(unsigned int modIndex) const
|
||||
{
|
||||
ModStates result;
|
||||
unsigned int modIndex = ModInfo::getIndex(name);
|
||||
IModList::ModStates result;
|
||||
if (modIndex != UINT_MAX) {
|
||||
result |= IModList::STATE_EXISTS;
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
|
||||
@@ -605,6 +617,13 @@ IModList::ModStates ModList::state(const QString &name) const
|
||||
return result;
|
||||
}
|
||||
|
||||
IModList::ModStates ModList::state(const QString &name) const
|
||||
{
|
||||
unsigned int modIndex = ModInfo::getIndex(name);
|
||||
|
||||
return state(modIndex);
|
||||
}
|
||||
|
||||
int ModList::priority(const QString &name) const
|
||||
{
|
||||
unsigned int modIndex = ModInfo::getIndex(name);
|
||||
@@ -638,6 +657,12 @@ bool ModList::onModStateChanged(const std::function<void (const QString &, IModL
|
||||
return conn.connected();
|
||||
}
|
||||
|
||||
bool ModList::onModMoved(const std::function<void (const QString &, int, int)> &func)
|
||||
{
|
||||
auto conn = m_ModMoved.connect(func);
|
||||
return conn.connected();
|
||||
}
|
||||
|
||||
bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent)
|
||||
{
|
||||
QStringList source;
|
||||
@@ -744,6 +769,8 @@ void ModList::removeRowForce(int row)
|
||||
}
|
||||
if (m_Profile == NULL) return;
|
||||
|
||||
m_Profile->setModEnabled(row, false);
|
||||
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(row);
|
||||
|
||||
bool wasEnabled = m_Profile->modEnabled(row);
|
||||
@@ -770,6 +797,8 @@ void ModList::removeRow(int row, const QModelIndex&)
|
||||
}
|
||||
if (m_Profile == NULL) return;
|
||||
|
||||
m_Profile->setModEnabled(row, false);
|
||||
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(row);
|
||||
if (!modInfo->isRegular()) return;
|
||||
|
||||
|
||||
+8
-5
@@ -62,6 +62,7 @@ public:
|
||||
};
|
||||
|
||||
typedef boost::signals2::signal<void (const QString &, ModStates)> SignalModStateChanged;
|
||||
typedef boost::signals2::signal<void (const QString &, int, int)> SignalModMoved;
|
||||
|
||||
public:
|
||||
|
||||
@@ -113,6 +114,9 @@ public:
|
||||
/// \copydoc MOBase::IModList::onModStateChanged
|
||||
virtual bool onModStateChanged(const std::function<void (const QString &, ModStates)> &func);
|
||||
|
||||
/// \copydoc MOBase::IModList::onModMoved
|
||||
virtual bool onModMoved(const std::function<void (const QString &, int, int)> &func);
|
||||
|
||||
public: // implementation of virtual functions of QAbstractItemModel
|
||||
|
||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
@@ -239,6 +243,8 @@ private:
|
||||
|
||||
bool dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent);
|
||||
|
||||
ModStates state(unsigned int modIndex) const;
|
||||
|
||||
private slots:
|
||||
|
||||
private:
|
||||
@@ -255,7 +261,7 @@ private:
|
||||
|
||||
struct TModInfoChange {
|
||||
QString name;
|
||||
QFlags<IModList::ModStates> state;
|
||||
QFlags<IModList::ModState> state;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -274,11 +280,8 @@ private:
|
||||
TModInfoChange m_ChangeInfo;
|
||||
|
||||
SignalModStateChanged m_ModStateChanged;
|
||||
SignalModMoved m_ModMoved;
|
||||
|
||||
|
||||
// QAbstractItemModel interface
|
||||
|
||||
// IModList interface
|
||||
};
|
||||
|
||||
#endif // MODLIST_H
|
||||
|
||||
+67
-13
@@ -28,8 +28,12 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent)
|
||||
: QSortFilterProxyModel(parent), m_Profile(profile),
|
||||
m_CategoryFilter(), m_CurrentFilter()
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_Profile(profile)
|
||||
, m_CategoryFilter()
|
||||
, m_CurrentFilter()
|
||||
, m_FilterActive(false)
|
||||
, m_FilterMode(FILTER_AND)
|
||||
{
|
||||
m_EnabledColumns.set(ModList::COL_FLAGS);
|
||||
m_EnabledColumns.set(ModList::COL_NAME);
|
||||
@@ -47,7 +51,8 @@ void ModListSortProxy::setProfile(Profile *profile)
|
||||
|
||||
void ModListSortProxy::updateFilterActive()
|
||||
{
|
||||
emit filterActive((m_CategoryFilter.size() > 0) || !m_CurrentFilter.isEmpty());
|
||||
m_FilterActive = (m_CategoryFilter.size() > 0) || !m_CurrentFilter.isEmpty();
|
||||
emit filterActive(m_FilterActive);
|
||||
}
|
||||
|
||||
void ModListSortProxy::setCategoryFilter(const std::vector<int> &categories)
|
||||
@@ -220,20 +225,15 @@ bool ModListSortProxy::hasConflictFlag(const std::vector<ModInfo::EFlag> &flags)
|
||||
}
|
||||
|
||||
|
||||
bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
|
||||
bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const
|
||||
{
|
||||
if (!m_CurrentFilter.isEmpty() &&
|
||||
!info->name().contains(m_CurrentFilter, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) {
|
||||
switch (*iter) {
|
||||
case CategoryFactory::CATEGORY_SPECIAL_CHECKED: {
|
||||
if (!enabled) return false;
|
||||
if (!enabled && !info->alwaysEnabled()) return false;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_UNCHECKED: {
|
||||
if (enabled) return false;
|
||||
if (enabled || info->alwaysEnabled()) return false;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: {
|
||||
if (!info->updateAvailable() && !info->downgradeAvailable()) return false;
|
||||
@@ -246,17 +246,71 @@ bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: {
|
||||
ModInfo::EEndorsedState state = info->endorsedState();
|
||||
return (state == ModInfo::ENDORSED_FALSE) || (state == ModInfo::ENDORSED_NEVER);
|
||||
if (state != ModInfo::ENDORSED_FALSE) return false;
|
||||
} break;
|
||||
default: {
|
||||
if (!info->categorySet(*iter)) return false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const
|
||||
{
|
||||
for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) {
|
||||
switch (*iter) {
|
||||
case CategoryFactory::CATEGORY_SPECIAL_CHECKED: {
|
||||
if (enabled || info->alwaysEnabled()) return true;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_UNCHECKED: {
|
||||
if (!enabled && !info->alwaysEnabled()) return true;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: {
|
||||
if (info->updateAvailable() || info->downgradeAvailable()) return true;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY: {
|
||||
if (info->getCategories().size() == 0) return true;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_CONFLICT: {
|
||||
if (hasConflictFlag(info->getFlags())) return true;
|
||||
} break;
|
||||
case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: {
|
||||
ModInfo::EEndorsedState state = info->endorsedState();
|
||||
if ((state == ModInfo::ENDORSED_FALSE) && (state != ModInfo::ENDORSED_NEVER)) return true;
|
||||
} break;
|
||||
default: {
|
||||
if (info->categorySet(*iter)) return true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
|
||||
{
|
||||
if (!m_CurrentFilter.isEmpty() &&
|
||||
!info->name().contains(m_CurrentFilter, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_FilterMode == FILTER_AND) {
|
||||
return filterMatchesModAnd(info, enabled);
|
||||
} else {
|
||||
return (m_CategoryFilter.size() == 0) || filterMatchesModOr(info, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void ModListSortProxy::setFilterMode(ModListSortProxy::FilterMode mode)
|
||||
{
|
||||
if (m_FilterMode != mode) {
|
||||
m_FilterMode = mode;
|
||||
this->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user