diff options
| author | Tannin <devnull@localhost> | 2013-03-13 19:35:20 +0100 |
|---|---|---|
| committer | Tannin <devnull@localhost> | 2013-03-13 19:35:20 +0100 |
| commit | b3d0fcb2083143dc21c751adafd2523c3555e5d2 (patch) | |
| tree | 6adeeedc26848af1d5dd96be17b4bca97ede085d /src | |
| parent | 93e073c32445eefba5595123b28acf87d9f409e8 (diff) | |
- some more safety checks in the ini-limit removal code
- some code cleanup and minor bug fixes based on results from static code analysis
- added naemfilter for the esp list
- bsa changes are now stored automatically but delayed by up to 0.5 seconds (for performance reasons)
- bugfix: buffer overrun when certain functions are called with empty file names
- bugfix: reroute for the ini-limit fix was placed in the data segment
- bugfix: mod list got mixed up when the mod directory was changed externally
- bugfix: plugins.txt reroute on skyrim didn't work on winXP
- bugfix: MO could become unresponsive if the tutorial script couldn't be interpreted
Diffstat (limited to 'src')
| -rw-r--r-- | src/categoriesdialog.cpp | 3 | ||||
| -rw-r--r-- | src/loadmechanism.cpp | 3 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 56 | ||||
| -rw-r--r-- | src/mainwindow.h | 7 | ||||
| -rw-r--r-- | src/mainwindow.ui | 21 | ||||
| -rw-r--r-- | src/modinfo.cpp | 3 | ||||
| -rw-r--r-- | src/modlist.cpp | 2 | ||||
| -rw-r--r-- | src/organizer.pro | 7 | ||||
| -rw-r--r-- | src/pluginlist.cpp | 2 | ||||
| -rw-r--r-- | src/pluginlistsortproxy.cpp | 262 | ||||
| -rw-r--r-- | src/pluginlistsortproxy.h | 101 | ||||
| -rw-r--r-- | src/profile.cpp | 21 | ||||
| -rw-r--r-- | src/profile.h | 584 | ||||
| -rw-r--r-- | src/profilesdialog.cpp | 2 | ||||
| -rw-r--r-- | src/shared/skyriminfo.cpp | 12 | ||||
| -rw-r--r-- | src/shared/skyriminfo.h | 4 | ||||
| -rw-r--r-- | src/shared/util.cpp | 7 | ||||
| -rw-r--r-- | src/shared/util.h | 3 | ||||
| -rw-r--r-- | src/version.rc | 4 |
19 files changed, 593 insertions, 511 deletions
diff --git a/src/categoriesdialog.cpp b/src/categoriesdialog.cpp index 21e1a5aa..c7f03740 100644 --- a/src/categoriesdialog.cpp +++ b/src/categoriesdialog.cpp @@ -86,7 +86,8 @@ public: { QLineEdit *edit = qobject_cast<QLineEdit*>(editor); int pos = 0; - if (m_Validator->validate(edit->text(), pos) == QValidator::Acceptable) { + QString editText = edit->text(); + if (m_Validator->validate(editText, pos) == QValidator::Acceptable) { QItemDelegate::setModelData(editor, model, index); } } diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 8eed460f..426b3763 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -169,7 +169,8 @@ void LoadMechanism::deactivateProxyDLL() } } - removeHintFile(QDir(gameDirectory)); + QDir dir(gameDirectory); + removeHintFile(dir); } catch (const std::exception &e) { QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what()); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4e3fa296..2d00580e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -202,6 +202,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_ModList, SIGNAL(showMessage(QString)), this, SLOT(showMessage(QString))); connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), this, SLOT(modRenamed(QString,QString))); connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString))); + connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString))); connect(&m_ModList, SIGNAL(modlist_changed(int)), m_ModListSortProxy, SLOT(invalidate())); connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked())); @@ -221,6 +222,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); + m_CheckBSATimer.setSingleShot(true); + connect(&m_CheckBSATimer, SIGNAL(timeout()), this, SLOT(checkBSAList())); + m_DirectoryRefresher.moveToThread(&m_RefresherThread); m_RefresherThread.start(); @@ -478,6 +482,27 @@ void MainWindow::createHelpWidget() } +void MainWindow::saveArchiveList() +{ + if (m_ArchivesInit) { + QFile archiveFile(m_CurrentProfile->getArchivesFileName()); + if (archiveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = ui->bsaList->topLevelItem(i); + if ((item != NULL) && (item->checkState(0) == Qt::Checked)) { + archiveFile.write(item->text(0).toUtf8().append("\r\n")); + } + } + } else { + reportError(tr("failed to save archives order, do you have write access " + "to \"%1\"?").arg(m_CurrentProfile->getArchivesFileName())); + } + archiveFile.close(); + } else { + qWarning("archive list not initialised"); + } +} + bool MainWindow::saveCurrentLists() { if (m_DirectoryUpdate) { @@ -503,23 +528,6 @@ bool MainWindow::saveCurrentLists() reportError(tr("failed to save load order: %1").arg(e.what())); } - if (m_ArchivesInit) { - QFile archiveFile(m_CurrentProfile->getArchivesFileName()); - if (archiveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { - QTreeWidgetItem *item = ui->bsaList->topLevelItem(i); - if ((item != NULL) && (item->checkState(0) == Qt::Checked)) { - archiveFile.write(item->text(0).toUtf8().append("\r\n")); - } - } - } else { - reportError(tr("failed to save archives order, do you have write access " - "to \"%1\"?").arg(m_CurrentProfile->getArchivesFileName())); - } - archiveFile.close(); - } else { - qWarning("archive list not initialised"); - } return true; } @@ -1059,6 +1067,9 @@ void MainWindow::startExeAction() void MainWindow::refreshModList() { + // don't lose changes! + m_CurrentProfile->writeModlistNow(true); + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure); m_CurrentProfile->refreshModStatus(); @@ -1102,7 +1113,7 @@ void MainWindow::setExecutableIndex(int index) void MainWindow::activateSelectedProfile() { QString profileName = ui->profileBox->currentText(); - qDebug() << "activate profile " << profileName; + qDebug("activate profile \"%s\"", qPrintable(profileName)); QString profileDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())) .append("/").append(profileName); delete m_CurrentProfile; @@ -1650,12 +1661,6 @@ void MainWindow::on_tabWidget_currentChanged(int index) } -static QString guessModName(const QString &fileName) -{ - return QFileInfo(fileName).baseName(); -} - - void MainWindow::installMod(const QString &fileName) { bool hasIniTweaks = false; @@ -3831,7 +3836,8 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos) void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) { - checkBSAList(); + saveArchiveList(); + m_CheckBSATimer.start(500); } void MainWindow::on_actionProblems_triggered() diff --git a/src/mainwindow.h b/src/mainwindow.h index 26e4c9dc..075c6247 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -103,6 +103,7 @@ public: void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info); + void saveArchiveList(); public slots: void displayColumnSelection(const QPoint &pos); @@ -197,8 +198,6 @@ private: bool extractProgress(QProgressDialog &extractProgress, int percentage, std::string fileName); - void checkBSAList(); - bool checkForProblems(QString &problemDescription); int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments); @@ -277,6 +276,7 @@ private: QStringList m_ActiveArchives; bool m_DirectoryUpdate; bool m_ArchivesInit; + QTimer m_CheckBSATimer; QTime m_StartTime; SaveGameInfoWidget *m_CurrentSaveView; @@ -394,6 +394,8 @@ private slots: void startExeAction(); + void checkBSAList(); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); @@ -425,6 +427,7 @@ private slots: // ui slots void on_espList_customContextMenuRequested(const QPoint &pos); void on_displayCategoriesBtn_toggled(bool checked); + }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 3493b710..71f0e3c6 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -596,9 +596,18 @@ p, li { white-space: pre-wrap; } <string notr="true">ESPs</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
<number>6</number>
</property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -720,6 +729,16 @@ p, li { white-space: pre-wrap; } </attribute>
</widget>
</item>
+ <item>
+ <widget class="MOBase::LineEditClear" name="espFilterEdit">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="placeholderText">
+ <string>Namefilter</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="bsaTab">
diff --git a/src/modinfo.cpp b/src/modinfo.cpp index b8ef834f..80f27ccf 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -731,6 +731,9 @@ ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const overwritten = true; break; } + } else if (alternatives.size() == 1) { + // only alternative is data -> no conflict + regular = true; } } } diff --git a/src/modlist.cpp b/src/modlist.cpp index e70e3c31..1f318e0d 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -145,7 +145,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { if ((m_Profile->getModPriority(modIndex) == 0) && (role == Qt::DisplayRole)) { return tr("min"); - } else if ((m_Profile->getModPriority(modIndex) == m_Profile->numRegularMods() - 1) && + } else if ((m_Profile->getModPriority(modIndex) == static_cast<int>(m_Profile->numRegularMods()) - 1) && (role == Qt::DisplayRole)) { return tr("max"); } else { diff --git a/src/organizer.pro b/src/organizer.pro index 723c1e04..86e9c616 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -199,6 +199,11 @@ CONFIG(debug, debug|release) { QMAKE_LFLAGS += /DEBUG
}
+QMAKE_CXXFLAGS_WARN_ON -= -W3
+QMAKE_CXXFLAGS_WARN_ON += -W4
+QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189
+
+
CONFIG += embed_manifest_exe
# QMAKE_CXXFLAGS += /analyze
@@ -231,7 +236,7 @@ TRANSLATIONS = organizer_de.ts \ PRE_TARGETDEPS += compiler_TSQM_make_all
} else:message(No translation files in project)
-LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk
+LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk -lshlwapi
DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 486d6f42..25dc69bd 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -602,7 +602,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const case COL_PRIORITY: { if (m_ESPs[index].m_Priority == 0) { return tr("min"); - } else if (m_ESPs[index].m_Priority == m_ESPs.size() - 1) { + } else if (m_ESPs[index].m_Priority == static_cast<int>(m_ESPs.size()) - 1) { return tr("max"); } else { return QString::number(m_ESPs[index].m_Priority); diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp index 788073ab..5bff24f5 100644 --- a/src/pluginlistsortproxy.cpp +++ b/src/pluginlistsortproxy.cpp @@ -17,127 +17,141 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#include "pluginlistsortproxy.h"
-#include "pluginlist.h"
-#include <QMenu>
-#include <QCheckBox>
-#include <QWidgetAction>
-
-
-PluginListSortProxy::PluginListSortProxy(QObject *parent)
- : QSortFilterProxyModel(parent),
- m_SortIndex(0), m_SortOrder(Qt::AscendingOrder)
-{
- m_EnabledColumns.set(PluginList::COL_NAME);
- m_EnabledColumns.set(PluginList::COL_PRIORITY);
- m_EnabledColumns.set(PluginList::COL_MODINDEX);
- this->setDynamicSortFilter(true);
-}
-
-
-unsigned int PluginListSortProxy::getEnabledColumns() const
-{
- unsigned int result = 0;
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- if (m_EnabledColumns.test(i)) {
- result |= 1 << i;
- }
- }
- return result;
-}
-
-
-void PluginListSortProxy::setEnabledColumns(unsigned int columns)
-{
- emit layoutAboutToBeChanged();
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- m_EnabledColumns.set(i, (columns & (1 << i)) != 0);
- }
- emit layoutChanged();
-}
-
-
-Qt::ItemFlags PluginListSortProxy::flags(const QModelIndex &modelIndex) const
-{
- Qt::ItemFlags flags = sourceModel()->flags(mapToSource(modelIndex));
- if (sortColumn() == PluginList::COL_NAME) {
- flags &= ~Qt::ItemIsDragEnabled;
- }
- return flags;
-}
-
-
-void PluginListSortProxy::displayColumnSelection(const QPoint &pos)
-{
- QMenu menu;
-
- for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) {
- QCheckBox *checkBox = new QCheckBox(&menu);
- checkBox->setText(PluginList::getColumnName(i));
- checkBox->setChecked(m_EnabledColumns.test(i) ? Qt::Checked : Qt::Unchecked);
- QWidgetAction *checkableAction = new QWidgetAction(&menu);
- checkableAction->setDefaultWidget(checkBox);
- menu.addAction(checkableAction);
- }
- menu.exec(pos);
- int i = 0;
-
- emit layoutAboutToBeChanged();
- m_EnabledColumns.reset();
- foreach (const QAction *action, menu.actions()) {
- const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction*>(action);
- if (widgetAction != NULL) {
- const QCheckBox *checkBox = qobject_cast<const QCheckBox*>(widgetAction->defaultWidget());
- if (checkBox != NULL) {
- m_EnabledColumns.set(i, checkBox->checkState() == Qt::Checked);
- }
- }
- ++i;
- }
- emit layoutChanged();
-}
-
-
-bool PluginListSortProxy::lessThan(const QModelIndex &left,
- const QModelIndex &right) const
-{
- PluginList *plugins = qobject_cast<PluginList*>(sourceModel());
- switch (left.column()) {
- case PluginList::COL_NAME: {
- return QString::compare(plugins->getName(left.row()), plugins->getName(right.row()), Qt::CaseInsensitive) < 0;
- } break;
- case PluginList::COL_PRIORITY:
- case PluginList::COL_MODINDEX: {
- return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
- } break;
- default: {
- static bool first = true;
- if (first) {
- qCritical("invalid sort column %d", left.column());
- first = false;
- }
- return plugins->getPriority(left.row()) < plugins->getPriority(right.row());
- } break;
- }
-}
-
-
-bool PluginListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action,
- int row, int column, const QModelIndex &parent)
-{
- if ((row == -1) && (column == -1)) {
- return this->sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
- }
- // in the regular model, when dropping between rows, the row-value passed to
- // the sourceModel is inconsistent between ascending and descending ordering.
- // This should fix that
- if (sortOrder() == Qt::DescendingOrder) {
- --row;
- }
-
- QModelIndex proxyIndex = index(row, column, parent);
- QModelIndex sourceIndex = mapToSource(proxyIndex);
- return this->sourceModel()->dropMimeData(data, action, sourceIndex.row(), sourceIndex.column(),
- sourceIndex.parent());
-}
-
+#include "pluginlistsortproxy.h" +#include "pluginlist.h" +#include <QMenu> +#include <QCheckBox> +#include <QWidgetAction> + + +PluginListSortProxy::PluginListSortProxy(QObject *parent) + : QSortFilterProxyModel(parent), + m_SortIndex(0), m_SortOrder(Qt::AscendingOrder) +{ + m_EnabledColumns.set(PluginList::COL_NAME); + m_EnabledColumns.set(PluginList::COL_PRIORITY); + m_EnabledColumns.set(PluginList::COL_MODINDEX); + this->setDynamicSortFilter(true); +} + + +unsigned int PluginListSortProxy::getEnabledColumns() const +{ + unsigned int result = 0; + for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) { + if (m_EnabledColumns.test(i)) { + result |= 1 << i; + } + } + return result; +} + + +void PluginListSortProxy::setEnabledColumns(unsigned int columns) +{ + emit layoutAboutToBeChanged(); + for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) { + m_EnabledColumns.set(i, (columns & (1 << i)) != 0); + } + emit layoutChanged(); +} + + +Qt::ItemFlags PluginListSortProxy::flags(const QModelIndex &modelIndex) const +{ + Qt::ItemFlags flags = sourceModel()->flags(mapToSource(modelIndex)); + if (sortColumn() == PluginList::COL_NAME) { + flags &= ~Qt::ItemIsDragEnabled; + } + return flags; +} + + +void PluginListSortProxy::displayColumnSelection(const QPoint &pos) +{ + QMenu menu; + + for (int i = 0; i <= PluginList::COL_LASTCOLUMN; ++i) { + QCheckBox *checkBox = new QCheckBox(&menu); + checkBox->setText(PluginList::getColumnName(i)); + checkBox->setChecked(m_EnabledColumns.test(i) ? Qt::Checked : Qt::Unchecked); + QWidgetAction *checkableAction = new QWidgetAction(&menu); + checkableAction->setDefaultWidget(checkBox); + menu.addAction(checkableAction); + } + menu.exec(pos); + int i = 0; + + emit layoutAboutToBeChanged(); + m_EnabledColumns.reset(); + foreach (const QAction *action, menu.actions()) { + const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction*>(action); + if (widgetAction != NULL) { + const QCheckBox *checkBox = qobject_cast<const QCheckBox*>(widgetAction->defaultWidget()); + if (checkBox != NULL) { + m_EnabledColumns.set(i, checkBox->checkState() == Qt::Checked); + } + } + ++i; + } + emit layoutChanged(); +} + + +void PluginListSortProxy::updateFilter(const QString &filter) +{ + m_CurrentFilter = filter; + invalidateFilter(); +} + + +bool PluginListSortProxy::filterAcceptsRow(int row, const QModelIndex&) const +{ + return m_CurrentFilter.isEmpty() || + sourceModel()->data(sourceModel()->index(row, 0)).toString().contains(m_CurrentFilter, Qt::CaseInsensitive); +} + + +bool PluginListSortProxy::lessThan(const QModelIndex &left, + const QModelIndex &right) const +{ + PluginList *plugins = qobject_cast<PluginList*>(sourceModel()); + switch (left.column()) { + case PluginList::COL_NAME: { + return QString::compare(plugins->getName(left.row()), plugins->getName(right.row()), Qt::CaseInsensitive) < 0; + } break; + case PluginList::COL_PRIORITY: + case PluginList::COL_MODINDEX: { + return plugins->getPriority(left.row()) < plugins->getPriority(right.row()); + } break; + default: { + static bool first = true; + if (first) { + qCritical("invalid sort column %d", left.column()); + first = false; + } + return plugins->getPriority(left.row()) < plugins->getPriority(right.row()); + } break; + } +} + + +bool PluginListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if ((row == -1) && (column == -1)) { + return this->sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent)); + } + // in the regular model, when dropping between rows, the row-value passed to + // the sourceModel is inconsistent between ascending and descending ordering. + // This should fix that + if (sortOrder() == Qt::DescendingOrder) { + --row; + } + + QModelIndex proxyIndex = index(row, column, parent); + QModelIndex sourceIndex = mapToSource(proxyIndex); + return this->sourceModel()->dropMimeData(data, action, sourceIndex.row(), sourceIndex.column(), + sourceIndex.parent()); +} + diff --git a/src/pluginlistsortproxy.h b/src/pluginlistsortproxy.h index 7397ff7c..d5c71858 100644 --- a/src/pluginlistsortproxy.h +++ b/src/pluginlistsortproxy.h @@ -17,52 +17,55 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PLUGINLISTSORTPROXY_H
-#define PLUGINLISTSORTPROXY_H
-
-
-#include <bitset>
-#include <QSortFilterProxyModel>
-#include "pluginlist.h"
-
-
-class PluginListSortProxy : public QSortFilterProxyModel
-{
- Q_OBJECT
-public:
-
- enum ESorting {
- SORT_ASCENDING,
- SORT_DESCENDING
- };
-
-public:
-
- explicit PluginListSortProxy(QObject *parent = 0);
-
- unsigned int getEnabledColumns() const;
-
- void setEnabledColumns(unsigned int columns);
-
- virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
-
- virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
-
-public slots:
-
- void displayColumnSelection(const QPoint &pos);
-
-protected:
-
- virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
-
-private:
-
- int m_SortIndex;
- Qt::SortOrder m_SortOrder;
-
- std::bitset<PluginList::COL_LASTCOLUMN + 1> m_EnabledColumns;
-
-};
-
-#endif // PLUGINLISTSORTPROXY_H
+#ifndef PLUGINLISTSORTPROXY_H +#define PLUGINLISTSORTPROXY_H + + +#include <bitset> +#include <QSortFilterProxyModel> +#include "pluginlist.h" + + +class PluginListSortProxy : public QSortFilterProxyModel +{ + Q_OBJECT +public: + + enum ESorting { + SORT_ASCENDING, + SORT_DESCENDING + }; + +public: + + explicit PluginListSortProxy(QObject *parent = 0); + + unsigned int getEnabledColumns() const; + + void setEnabledColumns(unsigned int columns); + + virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const; + + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + +public slots: + + void displayColumnSelection(const QPoint &pos); + void updateFilter(const QString &filter); + +protected: + + virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const; + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + +private: + + int m_SortIndex; + Qt::SortOrder m_SortOrder; + + std::bitset<PluginList::COL_LASTCOLUMN + 1> m_EnabledColumns; + QString m_CurrentFilter; + +}; + +#endif // PLUGINLISTSORTPROXY_H diff --git a/src/profile.cpp b/src/profile.cpp index 002c93c0..67fd1e13 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -130,8 +130,10 @@ void Profile::cancelWriteModlist() const } -void Profile::writeModlistNow() const +void Profile::writeModlistNow(bool onlyOnTimer) const { + if (onlyOnTimer && !m_SaveTimer->isActive()) return; + m_SaveTimer->stop(); #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") @@ -194,11 +196,6 @@ void Profile::createTweakedIniFile() void Profile::refreshModStatus() { - // don't lose changes! - if (m_SaveTimer->isActive()) { - writeModlistNow(); - } - QFile file(getModlistFileName()); if (!file.exists()) { throw MyException(QObject::tr("failed to find \"%1\"").arg(getModlistFileName())); @@ -571,11 +568,15 @@ void Profile::deactivateInvalidation() const } } + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) || !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) { - throw windows_error("failed to modify ini file"); + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); } } } @@ -598,11 +599,15 @@ void Profile::activateInvalidation(const QString& dataDirectory) const archives.insert(0, invalidationBSA); } + // just to be safe... + ::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL); + if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(), ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) || !::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) || !::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) { - throw windows_error("failed to modify ini file"); + QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName)); + throw windows_error(errorMessage.toUtf8().constData()); } QString bsaFile = dataDirectory + "/" + invalidationBSA; diff --git a/src/profile.h b/src/profile.h index 0c25b8f1..32647591 100644 --- a/src/profile.h +++ b/src/profile.h @@ -17,295 +17,295 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROFILE_H
-#define PROFILE_H
-
-
-#include "modinfo.h"
-
-#include <QString>
-#include <QDir>
-#include <QMetaType>
-#include <QSettings>
-#include <vector>
-#include <tuple>
-
-
-/**
- * @brief represents a profile
- **/
-class Profile : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- /**
- * @brief default constructor
- * @todo This constructor initialised nothing, the resulting object is not usable
- **/
- Profile();
-
- /**
- * @brief constructor
- *
- * This constructor is used to create a new profile so it is to be assumed a profile
- * by this name does not yet exist
- * @param name name of the new profile
- * @param filter save game filter. Defaults to <no filter>.
- **/
- Profile(const QString &name, bool useDefaultSettings);
- /**
- * @brief constructor
- *
- * This constructor is used to open an existing profile though it will also try to repair
- * the profile if important files are missing (including the directory itself) so technically,
- * invoking this should always produce a working profile
- * @param directory directory to read the profile from
- **/
- Profile(const QDir &directory);
-
- Profile(const Profile &reference);
-
- ~Profile();
-
- /**
- * @return true if this profile (still) exists on disc
- */
- bool exists() const;
-
- /**
- * @brief copy constructor
- *
- * @param name of the new profile
- * @param reference profile to copy from
- **/
- static Profile createFrom(const QString &name, const Profile &reference);
-
- /**
- * @brief write out the modlist.txt
- **/
- void writeModlist() const;
-
- /**
- * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is
- * saved while it's being modified
- */
- void cancelWriteModlist() const;
-
- /**
- * @brief test if this profile uses archive invalidation
- *
- * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile
- * @return true if archive invalidation is active
- * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist
- **/
- bool invalidationActive(bool *supported) const;
-
- /**
- * @brief deactivate archive invalidation if it was active
- **/
- void deactivateInvalidation() const;
-
- /**
- * @brief activate archive invalidation
- *
- * @param dataDirectory data directory of the game
- * @todo passing the data directory as a parameter is useless, the function should
- * be able to query it from GameInfo
- **/
- void activateInvalidation(const QString &dataDirectory) const;
-
- /**
- * @return true if this profile uses local save games
- */
- bool localSavesEnabled() const;
-
- /**
- * @brief enables or disables the use of local save games for this profile
- * disabling this does not delete exising local saves but they will not be visible
- * in the game
- * @param enable if true, local saves are enabled, otherewise they are disabled
- */
- bool enableLocalSaves(bool enable);
-
- /**
- * @return name of the profile (this is identical to its directory name)
- **/
- QString getName() const { return m_Directory.dirName(); }
-
- /**
- * @return the path of the plugins file in this profile
- * @todo is this required? can the functionality using this function be moved to the Profile-class?
- **/
- QString getPluginsFileName() const;
-
- /**
- * @return the path of the loadorder file in this profile
- **/
- QString getLoadOrderFileName() const;
-
- /**
- * @return the path of the file containing locked mod indices
- */
- QString getLockedOrderFileName() const;
-
- /**
- * @return path of the archives file in this profile
- */
- QString getArchivesFileName() const;
-
- /**
- * @return the path of the delete file in this profile
- * @note the deleter file lists plugins that should be hidden from the game and other tools
- **/
- QString getDeleterFileName() const;
-
- /**
- * @return the path of the ini file in this profile
- * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini)
- * the concept of this function is somewhat broken
- **/
- QString getIniFileName() const;
-
- /**
- * @return path to this profile
- **/
- QString getPath() const;
-
- /**
- * @brief create the ini file to be used by the game
- *
- * the tweaked ini file constructed by this file is a merger
- * of the game-ini of this profile with ini tweaks applied */
- void createTweakedIniFile();
-
- /**
- * @brief re-read the modlist.txt and update the mod status from it
- **/
- void refreshModStatus();
-
- /**
- * @brief retrieve a list of mods that are enabled in this profile
- *
- * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path
- **/
- std::vector<std::tuple<QString, QString, int> > getActiveMods();
-
- /**
- * retrieve the number of mods for which this object has status information.
- * This is usually the same as ModInfo::getNumMods() except between
- * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus()
- *
- * @return number of mods for which the profile has status information
- **/
- unsigned int numMods() const { return m_ModStatus.size(); }
-
- /**
- * @return the number of mods that can be enabled and where the priority can be modified
- */
- unsigned int numRegularMods() const { return m_NumRegularMods; }
-
- /**
- * @brief retrieve the mod index based on the priority
- *
- * @param priority priority to look up
- * @return the index of the mod
- * @throw std::exception an exception is thrown if there is no mod with the specified priority
- **/
- unsigned int modIndexByPriority(unsigned int priority) const;
-
- /**
- * @brief enable or disable a mod
- *
- * @param index index of the mod to enable/disable
- * @param enabled true if the mod is to be enabled, false if it is to be disabled
- **/
- void setModEnabled(unsigned int index, bool enabled);
-
- /**
- * change the priority of a mod. Of course this also changes the priority of other mods.
- * The priority of the mods in the range ]old, new priority] are shifted so that no gaps
- * are possible.
- *
- * @param index index of the mod to change
- * @param newPriority the new priority value
- *
- * @todo what happens if the new priority is outside the range?
- **/
- void setModPriority(unsigned int index, int &newPriority);
-
- /**
- * @brief determine if a mod is enabled
- *
- * @param index index of the mod to look up
- * @return true if the mod is enabled, false otherwise
- **/
- bool modEnabled(unsigned int index) const;
-
- /**
- * @brief query the priority of a mod
- *
- * @param index index of the mod to look up
- * @return priority of the specified mod
- **/
- int getModPriority(unsigned int index) const;
-
- void dumpModStatus() const;
-
-signals:
-
- /**
- * @brief emitted whenever the status (enabled/disabled) of a mod changed
- *
- * @param index index of the mod that changed
- **/
- void modStatusChanged(unsigned int index);
-
-public slots:
-
- void writeModlistNow() const;
-
-private:
-
- class ModStatus {
- friend class Profile;
- public:
- ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {}
- private:
- bool m_Overwrite;
- bool m_Enabled;
- int m_Priority;
- };
-
-private:
- Profile& operator=(const Profile &reference); // not implemented
-
- void initTimer();
-
- void updateIndices();
-
- QString getModlistFileName() const;
- void copyFilesTo(QString &target) const;
-
- std::vector<std::wstring> splitDZString(const wchar_t *buffer) const;
- void mergeTweak(const QString &tweakName, const QString &tweakedIni) const;
- void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const;
-
-private:
-
- QDir m_Directory;
-
- std::vector<ModStatus> m_ModStatus;
- std::vector<unsigned int> m_ModIndexByPriority;
- unsigned int m_NumRegularMods;
-
- QTimer *m_SaveTimer;
-
-};
-
-Q_DECLARE_METATYPE(Profile)
-
-
-#endif // PROFILE_H
+#ifndef PROFILE_H +#define PROFILE_H + + +#include "modinfo.h" + +#include <QString> +#include <QDir> +#include <QMetaType> +#include <QSettings> +#include <vector> +#include <tuple> + + +/** + * @brief represents a profile + **/ +class Profile : public QObject +{ + + Q_OBJECT + +public: + + /** + * @brief default constructor + * @todo This constructor initialised nothing, the resulting object is not usable + **/ + Profile(); + + /** + * @brief constructor + * + * This constructor is used to create a new profile so it is to be assumed a profile + * by this name does not yet exist + * @param name name of the new profile + * @param filter save game filter. Defaults to <no filter>. + **/ + Profile(const QString &name, bool useDefaultSettings); + /** + * @brief constructor + * + * This constructor is used to open an existing profile though it will also try to repair + * the profile if important files are missing (including the directory itself) so technically, + * invoking this should always produce a working profile + * @param directory directory to read the profile from + **/ + Profile(const QDir &directory); + + Profile(const Profile &reference); + + ~Profile(); + + /** + * @return true if this profile (still) exists on disc + */ + bool exists() const; + + /** + * @brief copy constructor + * + * @param name of the new profile + * @param reference profile to copy from + **/ + static Profile createFrom(const QString &name, const Profile &reference); + + /** + * @brief write out the modlist.txt + **/ + void writeModlist() const; + + /** + * cancel any potential request to write modlist.txt. This is used to ensure no invalid modlist is + * saved while it's being modified + */ + void cancelWriteModlist() const; + + /** + * @brief test if this profile uses archive invalidation + * + * @param supported if this is not null, the parameter will be set to false if invalidation is not supported in this profile + * @return true if archive invalidation is active + * @note currently, invalidation is not supported if the relevant entry in the ini file does not exist + **/ + bool invalidationActive(bool *supported) const; + + /** + * @brief deactivate archive invalidation if it was active + **/ + void deactivateInvalidation() const; + + /** + * @brief activate archive invalidation + * + * @param dataDirectory data directory of the game + * @todo passing the data directory as a parameter is useless, the function should + * be able to query it from GameInfo + **/ + void activateInvalidation(const QString &dataDirectory) const; + + /** + * @return true if this profile uses local save games + */ + bool localSavesEnabled() const; + + /** + * @brief enables or disables the use of local save games for this profile + * disabling this does not delete exising local saves but they will not be visible + * in the game + * @param enable if true, local saves are enabled, otherewise they are disabled + */ + bool enableLocalSaves(bool enable); + + /** + * @return name of the profile (this is identical to its directory name) + **/ + QString getName() const { return m_Directory.dirName(); } + + /** + * @return the path of the plugins file in this profile + * @todo is this required? can the functionality using this function be moved to the Profile-class? + **/ + QString getPluginsFileName() const; + + /** + * @return the path of the loadorder file in this profile + **/ + QString getLoadOrderFileName() const; + + /** + * @return the path of the file containing locked mod indices + */ + QString getLockedOrderFileName() const; + + /** + * @return path of the archives file in this profile + */ + QString getArchivesFileName() const; + + /** + * @return the path of the delete file in this profile + * @note the deleter file lists plugins that should be hidden from the game and other tools + **/ + QString getDeleterFileName() const; + + /** + * @return the path of the ini file in this profile + * @todo since the game can contain multiple ini files (i.e. skyrim.ini skyrimprefs.ini) + * the concept of this function is somewhat broken + **/ + QString getIniFileName() const; + + /** + * @return path to this profile + **/ + QString getPath() const; + + /** + * @brief create the ini file to be used by the game + * + * the tweaked ini file constructed by this file is a merger + * of the game-ini of this profile with ini tweaks applied */ + void createTweakedIniFile(); + + /** + * @brief re-read the modlist.txt and update the mod status from it + **/ + void refreshModStatus(); + + /** + * @brief retrieve a list of mods that are enabled in this profile + * + * @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path + **/ + std::vector<std::tuple<QString, QString, int> > getActiveMods(); + + /** + * retrieve the number of mods for which this object has status information. + * This is usually the same as ModInfo::getNumMods() except between + * calls to ModInfo::updateFromDisc() and the Profile::refreshModStatus() + * + * @return number of mods for which the profile has status information + **/ + unsigned int numMods() const { return m_ModStatus.size(); } + + /** + * @return the number of mods that can be enabled and where the priority can be modified + */ + unsigned int numRegularMods() const { return m_NumRegularMods; } + + /** + * @brief retrieve the mod index based on the priority + * + * @param priority priority to look up + * @return the index of the mod + * @throw std::exception an exception is thrown if there is no mod with the specified priority + **/ + unsigned int modIndexByPriority(unsigned int priority) const; + + /** + * @brief enable or disable a mod + * + * @param index index of the mod to enable/disable + * @param enabled true if the mod is to be enabled, false if it is to be disabled + **/ + void setModEnabled(unsigned int index, bool enabled); + + /** + * change the priority of a mod. Of course this also changes the priority of other mods. + * The priority of the mods in the range ]old, new priority] are shifted so that no gaps + * are possible. + * + * @param index index of the mod to change + * @param newPriority the new priority value + * + * @todo what happens if the new priority is outside the range? + **/ + void setModPriority(unsigned int index, int &newPriority); + + /** + * @brief determine if a mod is enabled + * + * @param index index of the mod to look up + * @return true if the mod is enabled, false otherwise + **/ + bool modEnabled(unsigned int index) const; + + /** + * @brief query the priority of a mod + * + * @param index index of the mod to look up + * @return priority of the specified mod + **/ + int getModPriority(unsigned int index) const; + + void dumpModStatus() const; + +signals: + + /** + * @brief emitted whenever the status (enabled/disabled) of a mod changed + * + * @param index index of the mod that changed + **/ + void modStatusChanged(unsigned int index); + +public slots: + + void writeModlistNow(bool onlyOnTimer = false) const; + +private: + + class ModStatus { + friend class Profile; + public: + ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {} + private: + bool m_Overwrite; + bool m_Enabled; + int m_Priority; + }; + +private: + Profile& operator=(const Profile &reference); // not implemented + + void initTimer(); + + void updateIndices(); + + QString getModlistFileName() const; + void copyFilesTo(QString &target) const; + + std::vector<std::wstring> splitDZString(const wchar_t *buffer) const; + void mergeTweak(const QString &tweakName, const QString &tweakedIni) const; + void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const; + +private: + + QDir m_Directory; + + std::vector<ModStatus> m_ModStatus; + std::vector<unsigned int> m_ModIndexByPriority; + unsigned int m_NumRegularMods; + + QTimer *m_SaveTimer; + +}; + +Q_DECLARE_METATYPE(Profile) + + +#endif // PROFILE_H diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index cbf50dfc..ae3d6660 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -261,7 +261,7 @@ void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current void ProfilesDialog::on_localSavesBox_stateChanged(int state) { - Profile ¤tProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>(); + Profile currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>(); if (currentProfile.enableLocalSaves(state == Qt::Checked)) { ui->transferButton->setEnabled(state == Qt::Checked); } else { diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index 6302cef3..26395e67 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "error_report.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
+#include <Shlwapi.h>
#include <boost/assign.hpp>
namespace MOShared {
@@ -36,6 +37,11 @@ SkyrimInfo::SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gam : GameInfo(omoDirectory, gameDirectory)
{
identifyMyGamesDirectory(L"skyrim");
+
+ wchar_t appDataPath[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath))) {
+ m_AppData = appDataPath;
+ }
}
bool SkyrimInfo::identifyGame(const std::wstring &searchPath)
@@ -258,8 +264,12 @@ bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPa }
}
+if (_wcsicmp(fileName, L"plugins.txt") == 0) {
+ log("plugins.txt expected in \"%ls\", got \"%ls\"", m_AppData.c_str(), fullPath);
+}
+
if ((_wcsicmp(fileName, L"plugins.txt") == 0) &&
- (wcsstr(fullPath, L"AppData") != NULL)){
+ (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != NULL))) {
return true;
}
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index b12a5d58..024d0395 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -97,6 +97,10 @@ private: static bool identifyGame(const std::wstring &searchPath);
+private:
+
+ std::wstring m_AppData;
+
};
} // namespace MOShared
diff --git a/src/shared/util.cpp b/src/shared/util.cpp index a6378a74..e61ae8a6 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -79,6 +79,13 @@ std::string &ToLower(std::string &text) return text;
}
+std::string ToLower(const std::string &text)
+{
+ std::string temp = text;
+
+ std::transform(temp.begin(), temp.end(), temp.begin(), tolower);
+ return temp;
+}
std::wstring &ToLower(std::wstring &text)
{
diff --git a/src/shared/util.h b/src/shared/util.h index 9c59f6db..80983cb0 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -39,8 +39,9 @@ std::string ToString(const std::wstring &source, bool utf8); std::wstring ToWString(const std::string &source, bool utf8);
std::string &ToLower(std::string &text);
-std::wstring &ToLower(std::wstring &text);
+std::string ToLower(const std::string &text);
+std::wstring &ToLower(std::wstring &text);
std::wstring ToLower(const std::wstring &text);
VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName);
diff --git a/src/version.rc b/src/version.rc index 23f7b6e3..9f083ed4 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h"
-#define VER_FILEVERSION 0,12,8,0
-#define VER_FILEVERSION_STR 0,12,8,0
+#define VER_FILEVERSION 0,12,9,0
+#define VER_FILEVERSION_STR 0,12,9,0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
|
