Compare commits

...
Author SHA1 Message Date
Tannin 0eb1662a0e - very effective optimization to findfirstfile-calls
- several configuration files are now only saved to disk if the content actually changed.
  This should also get rid of a problem where plugins.txt was re-written immediately after starting the game
  (causing a conflict with the game)
- reduced "noise" from hook.dll
- removed some debugging messages
2014-05-05 18:24:15 +02:00
Tannin 164ec25a75 - bugfix: endless loop in detection of mod order problems 2014-05-04 16:13:35 +02:00
Tannin 6fb36d6c02 - main window now has a small view displaying log messages
- mod list will now be highlighted when grouping is active is active
- download tooltip now supports bbcode markup in the description
- bbcode translator will now translate some named colors
- algorithm for detection of mod order problems is now more sophisticated
- exposed more functionality to python plugins
- updated to qt 4.8.6 dlls
- bugfix: plugin list wasn't
- bugfix: state changes in mod list wasn't always reported
- bugfix: loot client will now create necessary directory
- bugfix: NCC sometimes used wrong source path for extracting
- bugfix: removed noisy debug message
2014-05-04 14:50:01 +02:00
Tannin ea1f959ad5 - download tooltip now also includes the file description
- will now display an error message when the ini file can't be updated (in addition to what windows says)
2014-05-01 10:03:40 +02:00
Tannin 7cf3b3455b Added tag release v1.2.0 for changeset cc9f6dd8ee3f 2014-04-25 20:02:53 +02:00
25 changed files with 1312 additions and 1074 deletions
+24 -8
View File
@@ -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,10 +153,6 @@ 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["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"),
@@ -150,6 +160,11 @@ private:
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");
// 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 +172,11 @@ private:
}
}
private:
QRegExp m_TagNameExp;
TagMap m_TagMap;
std::map<QString, QString> m_ColorMap;
};
+1 -1
View File
@@ -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 {
+2 -1
View File
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "json.h"
#include "selectiondialog.h"
#include <utility.h>
#include <bbcode.h>
#include <QTimer>
#include <QFileInfo>
#include <QRegExp>
@@ -1159,7 +1160,7 @@ 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;
+3 -2
View File
@@ -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());
+79 -3
View File
@@ -29,7 +29,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 +51,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 +86,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);
@@ -125,6 +134,67 @@ char LogBuffer::msgTypeID(QtMsgType type)
}
}
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()) % 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;
}
} 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::log(QtMsgType type, const char *message)
{
QMutexLocker guard(&s_Mutex);
@@ -171,3 +241,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
View File
@@ -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;
};
-5
View File
@@ -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;
@@ -314,7 +310,6 @@ bool HaveWriteAccess(const std::wstring &path)
return writable;
}
int main(int argc, char *argv[])
{
MOApplication application(argc, argv);
+66 -48
View File
@@ -95,6 +95,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDesktopWidget>
#include <QtPlugin>
#include <QIdentityProxyModel>
#include <QClipboard>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/assign.hpp>
@@ -167,6 +168,16 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
ui->setupUi(this);
this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString());
ui->logList->setModel(LogBuffer::instance());
ui->logList->setColumnWidth(0, 100);
ui->logList->setAutoScroll(true);
ui->logList->scrollToBottom();
ui->logList->addAction(ui->actionCopy_Log_to_Clipboard);
int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value
ui->topLevelSplitter->setSizes(QList<int>() << splitterSize - 100 << 100);
connect(ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), ui->logList, SLOT(scrollToBottom()));
connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom()));
m_RefreshProgress = new QProgressBar(statusBar());
m_RefreshProgress->setTextVisible(true);
m_RefreshProgress->setRange(0, 100);
@@ -653,8 +664,9 @@ void MainWindow::saveArchiveList()
}
}
}
archiveFile.commit();
qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_CurrentProfile->getArchivesFileName())));
if (archiveFile.commitIfDifferent(m_ArchiveListHash)) {
qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_CurrentProfile->getArchivesFileName())));
}
} else {
qWarning("archive list not initialised");
}
@@ -670,10 +682,12 @@ void MainWindow::savePluginList()
m_PluginList.saveLoadOrder(*m_DirectoryStructure);
}
void MainWindow::modFilterActive(bool active)
void MainWindow::modFilterActive(bool filterActive)
{
if (active) {
if (filterActive) {
ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
} else if (ui->groupCombo->currentIndex() != 0) {
ui->modList->setStyleSheet("QTreeView { border: 2px ridge #337733; }");
} else {
ui->modList->setStyleSheet("");
}
@@ -1410,11 +1424,12 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
}
}
while (m_RefreshProgress->isVisible()) {
while (m_DirectoryUpdate) {
::Sleep(100);
QCoreApplication::processEvents();
}
// need to make sure all data is saved before we start the application
if (m_CurrentProfile != nullptr) {
m_CurrentProfile->writeModlistNow(true);
}
@@ -1428,28 +1443,6 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
}
}
/*
void MainWindow::spawnProgram(const QString &fileName, const QString &argumentsArg,
const QString &profileName, const QDir &currentDirectory)
{
QFileInfo binary;
QString arguments = argumentsArg;
QString steamAppID;
try {
const Executable &exe = m_ExecutablesList.find(fileName);
steamAppID = exe.m_SteamAppID;
if (arguments == "") {
arguments = exe.m_Arguments;
}
binary = exe.m_BinaryInfo;
} catch (const std::runtime_error&) {
qWarning("\"%s\" not set up as executable", fileName.toUtf8().constData());
binary = QFileInfo(fileName);
}
spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID);
}
*/
void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir &currentDirectory, bool closeAfterStart, const QString &steamAppID)
{
@@ -2211,7 +2204,10 @@ void MainWindow::storeSettings()
result = settings.status();
}
if (result == QSettings::NoError) {
shellRename(iniFile + ".new", iniFile, true, this);
if (!shellRename(iniFile + ".new", iniFile, true, this)) {
QMessageBox::critical(this, tr("Failed to write settings"),
tr("An error occured trying to write back MO settings: %1").arg(windowsErrorString(::GetLastError())));
}
} else {
QString reason = result == QSettings::AccessError ? tr("File is write protected")
: result == QSettings::FormatError ? tr("Invalid file format (probably a bug)")
@@ -2247,10 +2243,10 @@ void MainWindow::on_tabWidget_currentChanged(int index)
}
void MainWindow::installMod(const QString &fileName)
IModInterface *MainWindow::installMod(const QString &fileName)
{
if (m_CurrentProfile == NULL) {
return;
return NULL;
}
bool hasIniTweaks = false;
@@ -2275,12 +2271,14 @@ void MainWindow::installMod(const QString &fileName)
displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES);
}
testExtractBSA(modIndex);
return modInfo.data();
} else {
reportError(tr("mod \"%1\" not found").arg(modName));
}
} else if (m_InstallationManager.wasCancelled()) {
QMessageBox::information(this, tr("Installation cancelled"), tr("The mod was not installed completely."), QMessageBox::Ok);
}
return NULL;
}
QString MainWindow::resolvePath(const QString &fileName) const
@@ -2754,14 +2752,11 @@ void MainWindow::refresher_progress(int percent)
void MainWindow::directory_refreshed()
{
statusBar()->hide();
DirectoryEntry *newStructure = m_DirectoryRefresher.getDirectoryStructure();
if (newStructure != NULL) {
DirectoryEntry *oldStructure = m_DirectoryStructure;
m_DirectoryStructure = newStructure;
delete oldStructure;
refreshDataTree();
} else {
// TODO: don't know why this happens, this slot seems to get called twice with only one emit
@@ -2781,6 +2776,7 @@ void MainWindow::directory_refreshed()
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->clearCaches();
}
statusBar()->hide();
}
@@ -5242,10 +5238,10 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index)
connect(ui->modList, SIGNAL(expanded(QModelIndex)),newModel, SLOT(expanded(QModelIndex)));
connect(ui->modList, SIGNAL(collapsed(QModelIndex)), newModel, SLOT(collapsed(QModelIndex)));
connect(newModel, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex)));
} else {
m_ModListSortProxy->setSourceModel(&m_ModList);
}
modFilterActive(m_ModListSortProxy->isFilterActive());
}
void MainWindow::on_linkButton_pressed()
@@ -5339,6 +5335,8 @@ void MainWindow::on_bossButton_clicked()
m_CurrentProfile->writeModlistNow();
bool success = false;
try {
this->setEnabled(false);
ON_BLOCK_EXIT([&] () { this->setEnabled(true); });
@@ -5354,7 +5352,6 @@ void MainWindow::on_bossButton_clicked()
HANDLE stdOutWrite = INVALID_HANDLE_VALUE;
HANDLE stdOutRead = INVALID_HANDLE_VALUE;
createStdoutPipe(&stdOutRead, &stdOutWrite);
HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
parameters.join(" "),
m_CurrentProfile->getName(),
@@ -5380,30 +5377,39 @@ void MainWindow::on_bossButton_clicked()
if (remainder.length() > 0) {
processLOOTOut(remainder, reportURL, errorMessages, dialog);
}
DWORD exitCode = 0UL;
::GetExitCodeProcess(loot, &exitCode);
if (exitCode != 0UL) {
reportError(tr("loot failed. Exit code was: %1").arg(exitCode));
return;
} else {
success = true;
}
}
} catch (const std::exception &e) {
reportError(tr("failed to run boss: %1").arg(e.what()));
reportError(tr("failed to run loot: %1").arg(e.what()));
}
if (errorMessages.length() > 0) {
QMessageBox *warn = new QMessageBox(QMessageBox::Warning, tr("Errors occured"), errorMessages.c_str(), QMessageBox::Ok, this);
warn->setModal(false);
warn->show();
}
if (reportURL.length() > 0) {
m_IntegratedBrowser.setWindowTitle("LOOT Report");
QString report(reportURL.c_str());
if (QFile::exists(report)) {
m_IntegratedBrowser.openUrl(QUrl::fromLocalFile(report));
} else {
qWarning("report file missing");
if (success) {
if (reportURL.length() > 0) {
m_IntegratedBrowser.setWindowTitle("LOOT Report");
QString report(reportURL.c_str());
if (QFile::exists(report)) {
m_IntegratedBrowser.openUrl(QUrl::fromLocalFile(report));
} else {
qWarning("report file missing");
}
}
}
refreshESPList();
refreshESPList();
if (GameInfo::instance().getLoadOrderMechanism() == GameInfo::TYPE_FILETIME) {
QFile::remove(m_CurrentProfile->getLoadOrderFileName());
if (GameInfo::instance().getLoadOrderMechanism() == GameInfo::TYPE_FILETIME) {
QFile::remove(m_CurrentProfile->getLoadOrderFileName());
}
}
}
@@ -5503,3 +5509,15 @@ void MainWindow::on_restoreModsButton_clicked()
refreshModList(false);
}
}
void MainWindow::on_actionCopy_Log_to_Clipboard_triggered()
{
QStringList lines;
QAbstractItemModel *model = ui->logList->model();
for (int i = 0; i < model->rowCount(); ++i) {
lines.append(QString("%1 [%2] %3").arg(model->index(i, 0).data().toString())
.arg(model->index(i, 1).data(Qt::UserRole).toString())
.arg(model->index(i, 1).data().toString()));
}
QApplication::clipboard()->setText(lines.join("\n"));
}
+4 -1
View File
@@ -132,7 +132,7 @@ public:
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 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;
@@ -391,6 +391,8 @@ private:
std::vector<QTreeWidgetItem*> m_RemoveWidget;
uint m_ArchiveListHash;
private slots:
void showMessage(const QString &message);
@@ -594,6 +596,7 @@ private slots: // ui slots
void on_restoreButton_clicked();
void on_restoreModsButton_clicked();
void on_saveModsButton_clicked();
void on_actionCopy_Log_to_Clipboard_triggered();
};
#endif // MAINWINDOW_H
+975 -957
View File
File diff suppressed because it is too large Load Diff
+41 -20
View File
@@ -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;
}
@@ -578,10 +589,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 +615,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);
@@ -744,6 +761,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 +789,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;
+2
View File
@@ -239,6 +239,8 @@ private:
bool dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent);
ModStates state(unsigned int modIndex) const;
private slots:
private:
+7 -3
View File
@@ -28,8 +28,11 @@ 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_EnabledColumns.set(ModList::COL_FLAGS);
m_EnabledColumns.set(ModList::COL_NAME);
@@ -47,7 +50,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)
+18
View File
@@ -52,8 +52,24 @@ public:
**/
void disableAllVisible();
/**
* @brief tests if a filtere matches for a mod
* @param info mod information
* @param enabled true if the mod is currently active
* @return true if current active filters match for the specified mod
*/
bool filterMatchesMod(ModInfo::Ptr info, bool enabled) const;
/**
* @return true if a filter is currently active
*/
bool isFilterActive() const { return m_FilterActive; }
/**
* @brief tests if the specified index has child nodes
* @param parent the node to test
* @return true if there are child nodes
*/
virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const {
return rowCount(parent) > 0;
}
@@ -86,6 +102,8 @@ private:
std::bitset<ModList::COL_LASTCOLUMN + 1> m_EnabledColumns;
QString m_CurrentFilter;
bool m_FilterActive;
};
#endif // MODLISTSORTPROXY_H
+6 -3
View File
@@ -391,6 +391,8 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons
file->write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n"));
QStringList saveList;
bool invalidFileNames = false;
int writtenCount = 0;
for (size_t i = 0; i < m_ESPs.size(); ++i) {
@@ -401,6 +403,7 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons
invalidFileNames = true;
qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData());
} else {
saveList << m_ESPs[priority].m_Name;
file->write(textCodec->fromUnicode(m_ESPs[priority].m_Name));
}
file->write("\r\n");
@@ -413,9 +416,9 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons
"Please see mo_interface.log for a list of affected plugins and rename them."));
}
file.commit();
qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
if (file.commitIfDifferent(m_LastSaveHash[fileName])) {
qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
}
}
+2
View File
@@ -29,6 +29,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <boost/signals2.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>
#include <map>
#include "pdll.h"
#include <BOSS-API.h>
@@ -278,6 +279,7 @@ private:
private:
std::vector<ESPInfo> m_ESPs;
mutable std::map<QString, uint> m_LastSaveHash;
std::map<QString, int> m_ESPsByName;
std::vector<int> m_ESPsByPriority;
+3 -4
View File
@@ -147,7 +147,6 @@ void Profile::writeModlistNow(bool onlyOnTimer) const
m_SaveTimer->stop();
if (!m_Directory.exists()) return;
#pragma message("right now, this is doing unnecessary saves. Need a flag that says that mod priority, enabled-state or name of a mod has changed")
try {
QString fileName = getModlistFileName();
@@ -175,9 +174,9 @@ void Profile::writeModlistNow(bool onlyOnTimer) const
}
}
file.commit();
qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
if (file.commitIfDifferent(m_LastModlistHash)) {
qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
}
} catch (const std::exception &e) {
reportError(tr("failed to write mod list: %1").arg(e.what()));
return;
+1
View File
@@ -316,6 +316,7 @@ private:
QDir m_Directory;
mutable uint m_LastModlistHash;
std::vector<ModStatus> m_ModStatus;
std::vector<unsigned int> m_ModIndexByPriority;
unsigned int m_NumRegularMods;
+2 -2
View File
@@ -24,13 +24,13 @@
<file alias="previous">resources/go-previous_16.png</file>
<file alias="refresh">resources/view-refresh_16.png</file>
<file alias="update_available">resources/software-update-available.png</file>
<file>resources/emblem-important.png</file>
<file alias="important">resources/emblem-important.png</file>
<file>resources/check.png</file>
<file>mo_icon.ico</file>
<file alias="warning">resources/dialog-warning.png</file>
<file alias="emblem_backup">resources/symbol-backup.png</file>
<file alias="icon_tools">resources/applications-accessories.png</file>
<file alias="emblem_problem">resources/emblem-unreadable.png</file>
<file alias="problem">resources/emblem-unreadable.png</file>
<file>resources/internet-web-browser.png</file>
<file alias="update">resources/system-software-update.png</file>
<file alias="help">resources/help-browser_32.png</file>
+20
View File
@@ -46,3 +46,23 @@ void SafeWriteFile::commit() {
m_TempFile.setAutoRemove(false);
m_TempFile.close();
}
bool SafeWriteFile::commitIfDifferent(uint &inHash) {
uint newHash = hash();
if (newHash != inHash) {
commit();
inHash = newHash;
return true;
} else {
return false;
}
}
uint SafeWriteFile::hash()
{
qint64 pos = m_TempFile.pos();
m_TempFile.seek(0);
QByteArray data = m_TempFile.readAll();
m_TempFile.seek(pos);
return qHash(data);
}

Some files were not shown because too many files have changed in this diff Show More