From 84c66727bff954fd0820fc9f33833848496e9531 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 14 Jul 2014 21:22:44 +0200 Subject: - when highlighting a mod the overwritten and overwriting mods are now highlighted in the list - when starting an external application MO now wraps the process in a job and waits on that instead. This way MO is not unlocked early when skyrim is started through skse - mod info dialog no longer offers the esp tab for foreign mods because that caused confusion - updated translation files - download directory and mod directory are now created if necessary - bugfix: staging script created unnecessary copies of translation files - bugfix: potential invalid array access when trying to determine best mod order - bugfix: deleter file wasn't removed after esp hiding was disabled - bugfix: potential access to to un-initialized login reply - bugfix: changed the initialization order to allow more ui controls to be localized --- src/viewmarkingscrollbar.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/viewmarkingscrollbar.cpp (limited to 'src/viewmarkingscrollbar.cpp') diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp new file mode 100644 index 00000000..eca37562 --- /dev/null +++ b/src/viewmarkingscrollbar.cpp @@ -0,0 +1,39 @@ +#include "viewmarkingscrollbar.h" +#include +#include +#include + +ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *parent, int role) + : QScrollBar(parent) + , m_Model(model) + , m_Role(role) +{ + // not implemented for horizontal sliders + Q_ASSERT(this->orientation() == Qt::Vertical); +} + +void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) +{ + QScrollBar::paintEvent(event); + + QStyleOptionSlider styleOption; + initStyleOption(&styleOption); + + QPainter painter(this); + + QRect handleRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarSlider, this); + QRect innerRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarGroove, this); + + painter.translate(innerRect.topLeft() + QPoint(0, 3)); + qreal scale = static_cast(innerRect.height() - 3) / static_cast(m_Model->rowCount()); + + for (int i = 0; i < m_Model->rowCount(); ++i) { + QVariant data = m_Model->data(m_Model->index(i, 0), m_Role); + if (data.isValid()) { + QColor col = data.value(); + painter.setPen(col); + painter.setBrush(col); + painter.drawRect(QRect(2, i * scale - 2, handleRect.width() - 5, 3)); + } + } +} -- cgit v1.3.1 From 470b6ed0bf20525988d719d23725f7c9d19a9f36 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 27 Jul 2014 18:34:32 +0200 Subject: - browser dialog now has a hidden url-field (for testing only) - loot_cli is no longer part of this project. I will probably create a fork of loot that allows command-line usage instead - loot integration now works with such a modified loot version - integrated loot will now also integrate incompatibility messages in the MO UI - overwrite-markers are now updated as the list order is changed - fnis checker will now always allow the user to ignore fnis errors - plugin interface now has a function to wait for handles returned from startApplication (which can be job or process handles) - bugfix: non-mo mods sharing the name with regular mods now have a different internal name - bugfix: using hotkeys the vanilla game-plugins could be moved --- src/browserdialog.cpp | 30 ++++++++- src/browserdialog.h | 4 ++ src/browserdialog.ui | 29 ++++++++- src/browserview.cpp | 3 +- src/genericicondelegate.cpp | 39 ++++++++++++ src/genericicondelegate.h | 36 +++++++++++ src/mainwindow.cpp | 139 +++++++++++++++++++++++++++++++++++------ src/mainwindow.h | 4 +- src/modinfo.cpp | 103 +++++++++++++++--------------- src/modinfo.h | 13 ++-- src/modlist.cpp | 4 +- src/modlistsortproxy.cpp | 16 ++--- src/modlistsortproxy.h | 3 + src/modlistview.cpp | 1 - src/organizer.pro | 4 +- src/organizerproxy.cpp | 5 ++ src/organizerproxy.h | 1 + src/pluginflagicondelegate.cpp | 39 ------------ src/pluginflagicondelegate.h | 36 ----------- src/pluginlist.cpp | 7 +++ src/viewmarkingscrollbar.cpp | 3 + 21 files changed, 348 insertions(+), 171 deletions(-) create mode 100644 src/genericicondelegate.cpp create mode 100644 src/genericicondelegate.h delete mode 100644 src/pluginflagicondelegate.cpp delete mode 100644 src/pluginflagicondelegate.h (limited to 'src/viewmarkingscrollbar.cpp') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 4897ea4f..521459d0 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -37,6 +37,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include @@ -57,9 +58,12 @@ BrowserDialog::BrowserDialog(QWidget *parent) m_Tabs = this->findChild("browserTabWidget"); + installEventFilter(this); + connect(m_Tabs, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int))); -} + ui->urlEdit->setVisible(false); +} BrowserDialog::~BrowserDialog() { @@ -108,13 +112,14 @@ BrowserView *BrowserDialog::getCurrentView() } -void BrowserDialog::urlChanged(const QUrl&) +void BrowserDialog::urlChanged(const QUrl &url) { BrowserView *currentView = getCurrentView(); if (currentView != NULL) { ui->backBtn->setEnabled(currentView->history()->canGoBack()); ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); } + ui->urlEdit->setText(url.toString()); } @@ -268,3 +273,24 @@ void BrowserDialog::on_browserTabWidget_currentChanged(int index) ui->fwdBtn->setEnabled(currentView->history()->canGoForward()); } } + +void BrowserDialog::on_urlEdit_returnPressed() +{ + QWebView *currentView = getCurrentView(); + if (currentView != NULL) { + currentView->setUrl(QUrl(ui->urlEdit->text())); + } +} + +bool BrowserDialog::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = reinterpret_cast(event); + if ((keyEvent->modifiers() & Qt::ControlModifier) + && (keyEvent->key() == Qt::Key_U)) { + ui->urlEdit->setVisible(!ui->urlEdit->isVisible()); + return true; + } + } + return QDialog::eventFilter(object, event); +} diff --git a/src/browserdialog.h b/src/browserdialog.h index c6e0aab6..10b44fac 100644 --- a/src/browserdialog.h +++ b/src/browserdialog.h @@ -63,6 +63,7 @@ public: **/ void openUrl(const QUrl &url); + virtual bool eventFilter(QObject *object, QEvent *event); signals: /** @@ -103,6 +104,8 @@ private slots: void on_browserTabWidget_currentChanged(int index); + void on_urlEdit_returnPressed(); + private: QString guessFileName(const QString &url); @@ -119,6 +122,7 @@ private: QTabWidget *m_Tabs; + }; #endif // BROWSERDIALOG_H diff --git a/src/browserdialog.ui b/src/browserdialog.ui index 7d154fbb..c682fc5d 100644 --- a/src/browserdialog.ui +++ b/src/browserdialog.ui @@ -17,7 +17,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -180,7 +189,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -237,6 +255,13 @@ + + + + true + + + diff --git a/src/browserview.cpp b/src/browserview.cpp index bd43a18e..aeb16520 100644 --- a/src/browserview.cpp +++ b/src/browserview.cpp @@ -28,6 +28,7 @@ along with Mod Organizer. If not, see . #include #include "utility.h" + BrowserView::BrowserView(QWidget *parent) : QWebView(parent) { @@ -36,7 +37,6 @@ BrowserView::BrowserView(QWidget *parent) page()->settings()->setMaximumPagesInCache(10); } - QWebView *BrowserView::createWindow(QWebPage::WebWindowType) { BrowserView *newView = new BrowserView(parentWidget()); @@ -44,7 +44,6 @@ QWebView *BrowserView::createWindow(QWebPage::WebWindowType) return newView; } - bool BrowserView::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::ShortcutOverride) { diff --git a/src/genericicondelegate.cpp b/src/genericicondelegate.cpp new file mode 100644 index 00000000..c3471735 --- /dev/null +++ b/src/genericicondelegate.cpp @@ -0,0 +1,39 @@ +#include "genericicondelegate.h" +#include "pluginlist.h" +#include + + +GenericIconDelegate::GenericIconDelegate(QObject *parent, int role, int logicalIndex, int compactSize) + : IconDelegate(parent) + , m_Role(role) + , m_LogicalIndex(logicalIndex) + , m_CompactSize(compactSize) + , m_Compact(false) +{ +} + +void GenericIconDelegate::columnResized(int logicalIndex, int, int newSize) +{ + if (logicalIndex == m_LogicalIndex) { + m_Compact = newSize < m_CompactSize; + } +} + +QList GenericIconDelegate::getIcons(const QModelIndex &index) const +{ + QList result; + if (index.isValid()) { + foreach (const QVariant &var, index.data(m_Role).toList()) { + QIcon icon = var.value(); + if (!m_Compact || !icon.isNull()) { + result.append(icon); + } + } + } + return result; +} + +size_t GenericIconDelegate::getNumIcons(const QModelIndex &index) const +{ + return index.data(m_Role).toList().count(); +} diff --git a/src/genericicondelegate.h b/src/genericicondelegate.h new file mode 100644 index 00000000..8bc75e27 --- /dev/null +++ b/src/genericicondelegate.h @@ -0,0 +1,36 @@ +#ifndef GENERICICONDELEGATE_H +#define GENERICICONDELEGATE_H + +#include "icondelegate.h" + +/** + * @brief an icon delegate that takes the list of icons from a user-defines data role + */ +class GenericIconDelegate : public IconDelegate +{ +Q_OBJECT +public: + /** + * @brief constructor + * @param parent parent object + * @param role role of the itemmodel from which the icon list can be queried (as a QVariantList) + * @param logicalIndex logical index within the model. This is part of a "hack". Normally "empty" icons will be allocated the same + * space as a regular icon. This way the model can use empty icons as spacers and thus align same icons horizontally. + * Now, if you set the logical Index to a valid column and connect the columnResized slot to the sectionResized signal + * of the view, the delegate will turn off this behaviour if the column is smaller than "compactSize" + * @param compactSize see explanation of logicalIndex + */ + GenericIconDelegate(QObject *parent = NULL, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150); +public slots: + void columnResized(int logicalIndex, int oldSize, int newSize); +private: + virtual QList getIcons(const QModelIndex &index) const; + virtual size_t getNumIcons(const QModelIndex &index) const; +private: + int m_Role; + int m_LogicalIndex; + int m_CompactSize; + bool m_Compact; +}; + +#endif // GENERICICONDELEGATE_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a3887a23..4aea645f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -49,7 +49,7 @@ along with Mod Organizer. If not, see . #include "questionboxmemory.h" #include "tutorialmanager.h" #include "modflagicondelegate.h" -#include "pluginflagicondelegate.h" +#include "genericicondelegate.h" #include "credentialsdialog.h" #include "selectiondialog.h" #include "csvbuilder.h" @@ -728,6 +728,7 @@ void MainWindow::savePluginList() void MainWindow::modFilterActive(bool filterActive) { if (filterActive) { + m_ModList.setOverwriteMarkers(std::set(), std::set()); ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }"); } else if (ui->groupCombo->currentIndex() != 0) { ui->modList->setStyleSheet("QTreeView { border: 2px ridge #337733; }"); @@ -784,7 +785,6 @@ bool MainWindow::saveCurrentLists() return true; } - bool MainWindow::addProfile() { QComboBox *profileBox = findChild("profileBox"); @@ -807,7 +807,6 @@ bool MainWindow::addProfile() } } - void MainWindow::hookUpWindowTutorials() { QDirIterator dirIter(QApplication::applicationDirPath() + "/tutorials", QStringList("*.js"), QDir::Files); @@ -829,7 +828,6 @@ void MainWindow::hookUpWindowTutorials() } } - void MainWindow::showEvent(QShowEvent *event) { refreshFilters(); @@ -2773,6 +2771,19 @@ void MainWindow::modorder_changed() m_CurrentProfile->writeModlist(); saveArchiveList(); m_DirectoryStructure->getFileRegister()->sortOrigins(); + + { // refresh selection + QModelIndex current = ui->modList->currentIndex(); + if (current.isValid()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); + modInfo->doConflictCheck(); + m_ModList.setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten()); + if (m_ModListSortProxy != NULL) { + m_ModListSortProxy->invalidate(); + } + ui->modList->verticalScrollBar()->repaint(); + } + } } void MainWindow::procError(QProcess::ProcessError error) @@ -2960,6 +2971,7 @@ void MainWindow::refreshFilters() { QItemSelection currentSelection = ui->modList->selectionModel()->selection(); + QVariant currentIndexName = ui->modList->currentIndex().data(); ui->modList->setCurrentIndex(QModelIndex()); QStringList selectedItems; @@ -2998,8 +3010,11 @@ void MainWindow::refreshFilters() matches.at(0)->setSelected(true); } } - ui->modList->selectionModel()->select(currentSelection, QItemSelectionModel::Select); + QModelIndexList matchList = ui->modList->model()->match(ui->modList->model()->index(0, 0), Qt::DisplayRole, currentIndexName); + if (matchList.size() > 0) { + ui->modList->setCurrentIndex(matchList.at(0)); + } } @@ -3069,9 +3084,14 @@ void MainWindow::modlistChanged(const QModelIndex&, int) void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex&) { - ModInfo::Ptr selectedMod = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); - m_ModList.setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten()); - if (m_ModListSortProxy != NULL) { + if (current.isValid()) { + ModInfo::Ptr selectedMod = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); + m_ModList.setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten()); + } else { + m_ModList.setOverwriteMarkers(std::set(), std::set()); + } + if ((m_ModListSortProxy != NULL) + && !m_ModListSortProxy->beingInvalidated()) { m_ModListSortProxy->invalidate(); } ui->modList->verticalScrollBar()->repaint(); @@ -5277,11 +5297,12 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportU boost::split(lines, lootOut, boost::is_any_of("\r\n")); std::tr1::regex exRequires("\"([^\"]*)\" requires \"([^\"]*)\", but it is missing\\."); + std::tr1::regex exIncompatible("\"([^\"]*)\" is incompatible with \"([^\"]*)\", but both are present\\."); foreach (const std::string &line, lines) { if (line.length() > 0) { size_t progidx = line.find("[progress]"); - size_t reportidx = line.find("[report]"); + size_t reportidx = line.find("[Report]"); size_t erroridx = line.find("[error]"); if (progidx != std::string::npos) { dialog.setLabelText(line.substr(progidx + 11).c_str()); @@ -5296,8 +5317,12 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportU std::string modName(match[1].first, match[1].second); std::string dependency(match[2].first, match[2].second); m_PluginList.addInformation(modName.c_str(), tr("depends on missing \"%1\"").arg(dependency.c_str())); + } else if (std::tr1::regex_match(line, match, exIncompatible)) { + std::string modName(match[1].first, match[1].second); + std::string dependency(match[2].first, match[2].second); + m_PluginList.addInformation(modName.c_str(), tr("incompatible with \"%1\"").arg(dependency.c_str())); } else { - qDebug("%s", line.c_str()); + qDebug("[loot] %s", line.c_str()); } } } @@ -5350,6 +5375,65 @@ HANDLE MainWindow::startApplication(const QString &executable, const QStringList return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID); } + +bool MainWindow::waitForProcessOrJob(HANDLE handle, LPDWORD exitCode) +{ + LockedDialog *dialog = new LockedDialog(this); + dialog->show(); + setEnabled(false); + ON_BLOCK_EXIT([&] () { dialog->hide(); dialog->deleteLater(); this->setEnabled(true); }); + + DWORD retLen; + JOBOBJECT_BASIC_PROCESS_ID_LIST info; + + bool isJobHandle = true; + + ULONG lastProcessID = ULONG_MAX; + HANDLE processHandle = handle; + + DWORD res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); + while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { + if (isJobHandle) { + if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { + if (info.NumberOfProcessIdsInList == 0) { + // fake signaled state + res = WAIT_OBJECT_0; + break; + } else { + // this is indeed a job handle. Figure out one of the process handles as well. + if (lastProcessID != info.ProcessIdList[0]) { + lastProcessID = info.ProcessIdList[0]; + if (processHandle != handle) { + ::CloseHandle(processHandle); + } + processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); + } + } + } else { + // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there + // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. + // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without + // the right to break out. + if (::GetLastError() != ERROR_MORE_DATA) { + isJobHandle = false; + } + } + } + + // keep processing events so the app doesn't appear dead + QCoreApplication::processEvents(); + + res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE); + } + + if (exitCode != NULL) { + ::GetExitCodeProcess(processHandle, exitCode); + } + ::CloseHandle(processHandle); + + return res == WAIT_OBJECT_0; +} + void MainWindow::on_bossButton_clicked() { std::string reportURL; @@ -5368,18 +5452,21 @@ void MainWindow::on_bossButton_clicked() dialog.show(); QStringList parameters; - parameters << "--game" << ToQString(GameInfo::instance().getGameShortName()) + parameters << "--unattended" + << "--stdout" + << "--noreport" + << "--game" << ToQString(GameInfo::instance().getGameShortName()) << "--gamePath" << QString("\"%1\"").arg(ToQString(GameInfo::instance().getGameDirectory())); - if (!m_DidUpdateMasterList) { - parameters << "--updateMasterlist"; + if (m_DidUpdateMasterList) { + parameters << "--skipUpdateMasterlist"; + } else { m_DidUpdateMasterList = true; } - HANDLE stdOutWrite = INVALID_HANDLE_VALUE; HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); - HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), + HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/LOOT.exe"), parameters.join(" "), m_CurrentProfile->getName(), m_Settings.logLevel(), @@ -5395,6 +5482,8 @@ void MainWindow::on_bossButton_clicked() DWORD retLen; JOBOBJECT_BASIC_PROCESS_ID_LIST info; bool isJobHandle = true; + ULONG lastProcessID; + HANDLE processHandle = loot; if (loot != INVALID_HANDLE_VALUE) { DWORD res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); @@ -5404,6 +5493,14 @@ void MainWindow::on_bossButton_clicked() if (info.NumberOfProcessIdsInList == 0) { qDebug("no more processes in job"); break; + } else { + if (lastProcessID != info.ProcessIdList[0]) { + lastProcessID = info.ProcessIdList[0]; + if (processHandle != loot) { + ::CloseHandle(processHandle); + } + processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lastProcessID); + } } } else { // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there @@ -5437,7 +5534,8 @@ void MainWindow::on_bossButton_clicked() processLOOTOut(remainder, reportURL, errorMessages, dialog); } DWORD exitCode = 0UL; - ::GetExitCodeProcess(loot, &exitCode); + ::GetExitCodeProcess(processHandle, &exitCode); + ::CloseHandle(processHandle); if (exitCode != 0UL) { reportError(tr("loot failed. Exit code was: %1").arg(exitCode)); return; @@ -5460,11 +5558,12 @@ void MainWindow::on_bossButton_clicked() 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"); + QStringList temp = report.split("?"); + QUrl url = QUrl::fromLocalFile(temp.at(0)); + if (temp.size() > 1) { + url.setEncodedQuery(temp.at(1).toUtf8()); } + m_IntegratedBrowser.openUrl(url); } refreshESPList(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 62503062..124dde5f 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -134,9 +134,11 @@ public: HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); + bool waitForProcessOrJob(HANDLE processHandle, LPDWORD exitCode = NULL); + void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); - void waitForProcessOrJob(HANDLE processHandle); + public slots: void refreshLists(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index cb20567e..60df0137 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -359,67 +359,72 @@ std::vector ModInfoWithConflictInfo::getFlags() const } -ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const +void ModInfoWithConflictInfo::doConflictCheck() const { - // this is costy so cache the result - QTime now = QTime::currentTime(); - if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) { - m_OverwriteList.clear(); - m_OverwrittenList.clear(); - bool regular = false; + m_OverwriteList.clear(); + m_OverwrittenList.clear(); + bool regular = false; - int dataID = 0; - if ((*m_DirectoryStructure)->originExists(L"data")) { - dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); - } + 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 files = origin.getFiles(); - // for all files in this origin - for (auto iter = files.begin(); iter != files.end(); ++iter) { - const std::vector &alternatives = (*iter)->getAlternatives(); - if ((alternatives.size() == 0) - || (alternatives[0] == dataID)) { - // no alternatives -> no conflict - regular = true; - } else { - if ((*iter)->getOrigin() != origin.getID()) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID((*iter)->getOrigin()); + std::wstring name = ToWString(this->name()); + if ((*m_DirectoryStructure)->originExists(name)) { + FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + std::vector files = origin.getFiles(); + // for all files in this origin + for (auto iter = files.begin(); iter != files.end(); ++iter) { + const std::vector &alternatives = (*iter)->getAlternatives(); + if ((alternatives.size() == 0) + || (alternatives[0] == dataID)) { + // no alternatives -> no conflict + regular = true; + } else { + if ((*iter)->getOrigin() != origin.getID()) { + FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID((*iter)->getOrigin()); + unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); + m_OverwrittenList.insert(altIndex); + } + // for all non-providing alternative origins + for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { + if ((*altIter != dataID) && (*altIter != origin.getID())) { + FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(*altIter); unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); - m_OverwrittenList.insert(altIndex); - } - // for all non-providing alternative origins - for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { - if ((*altIter != dataID) && (*altIter != origin.getID())) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(*altIter); - unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); - if (origin.getPriority() > altOrigin.getPriority()) { - m_OverwriteList.insert(altIndex); - } else { - m_OverwrittenList.insert(altIndex); - } + if (origin.getPriority() > altOrigin.getPriority()) { + m_OverwriteList.insert(altIndex); + } else { + m_OverwrittenList.insert(altIndex); } } } } } + } - m_LastConflictCheck = QTime::currentTime(); + m_LastConflictCheck = QTime::currentTime(); - if (!m_OverwriteList.empty() && !m_OverwrittenList.empty()) - m_CurrentConflictState = CONFLICT_MIXED; - else if (!m_OverwriteList.empty()) - m_CurrentConflictState = CONFLICT_OVERWRITE; - else if (!m_OverwrittenList.empty()) { - if (!regular) { - m_CurrentConflictState = CONFLICT_REDUNDANT; - } else { - m_CurrentConflictState = CONFLICT_OVERWRITTEN; - } + if (!m_OverwriteList.empty() && !m_OverwrittenList.empty()) + m_CurrentConflictState = CONFLICT_MIXED; + else if (!m_OverwriteList.empty()) + m_CurrentConflictState = CONFLICT_OVERWRITE; + else if (!m_OverwrittenList.empty()) { + if (!regular) { + m_CurrentConflictState = CONFLICT_REDUNDANT; + } else { + m_CurrentConflictState = CONFLICT_OVERWRITTEN; } - else m_CurrentConflictState = CONFLICT_NONE; + } + else m_CurrentConflictState = CONFLICT_NONE; +} + +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)) { + doConflictCheck(); } return m_CurrentConflictState; diff --git a/src/modinfo.h b/src/modinfo.h index e2121828..1097e5ba 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -498,17 +498,20 @@ public: virtual void saveMeta() {} /** - * @brief retrieve list of mods (as mod index) that are overwritten by this one. Updates may be delayed - * @return + * @return retrieve list of mods (as mod index) that are overwritten by this one. Updates may be delayed */ virtual std::set getModOverwrite() { return std::set(); } /** - * @brief retrieve list of mods (as mod index) that overwrite this one. Updates may be delayed - * @return + * @return list of mods (as mod index) that overwrite this one. Updates may be delayed */ virtual std::set getModOverwritten() { return std::set(); } + /** + * @brief update conflict information + */ + virtual void doConflictCheck() const {} + signals: /** @@ -567,6 +570,8 @@ public: virtual std::set getModOverwritten() { return m_OverwrittenList; } + virtual void doConflictCheck() const; + private: enum EConflictType { diff --git a/src/modlist.cpp b/src/modlist.cpp index ca173b18..681d880c 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -224,7 +224,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QString(); } } else { - //return tr("None"); return QVariant(); } } @@ -887,6 +886,8 @@ void ModList::removeRow(int row, const QModelIndex&) void ModList::notifyChange(int rowStart, int rowEnd) { if (rowStart < 0) { + m_Overwrite.clear(); + m_Overwritten.clear(); beginResetModel(); endResetModel(); } else { @@ -1021,6 +1022,7 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) notifyChange(idx.row()); } } + emit modorder_changed(); return true; } else if (keyEvent->key() == Qt::Key_Delete) { QItemSelectionModel *selectionModel = itemView->selectionModel(); diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index d790e277..ecd1755d 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -34,6 +34,7 @@ ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) , m_CurrentFilter() , m_FilterActive(false) , m_FilterMode(FILTER_AND) + , m_BeingInvalidated(false) { m_EnabledColumns.set(ModList::COL_FLAGS); m_EnabledColumns.set(ModList::COL_NAME); @@ -43,7 +44,6 @@ ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) // but I don't know why. This should be necessary } - void ModListSortProxy::setProfile(Profile *profile) { m_Profile = profile; @@ -72,7 +72,6 @@ Qt::ItemFlags ModListSortProxy::flags(const QModelIndex &modelIndex) const return flags; } - void ModListSortProxy::displayColumnSelection(const QPoint &pos) { QMenu menu; @@ -103,7 +102,6 @@ void ModListSortProxy::displayColumnSelection(const QPoint &pos) emit layoutChanged(); } - void ModListSortProxy::enableAllVisible() { if (m_Profile == NULL) return; @@ -115,7 +113,6 @@ void ModListSortProxy::enableAllVisible() invalidate(); } - void ModListSortProxy::disableAllVisible() { if (m_Profile == NULL) return; @@ -127,7 +124,6 @@ void ModListSortProxy::disableAllVisible() invalidate(); } - bool ModListSortProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { @@ -208,15 +204,16 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, return lt; } - void ModListSortProxy::updateFilter(const QString &filter) { m_CurrentFilter = filter; updateFilterActive(); + // workaround because qt throws a fit if, as a result of this invalidation, another invalidate is called + m_BeingInvalidated = true; invalidateFilter(); + m_BeingInvalidated = false; } - bool ModListSortProxy::hasConflictFlag(const std::vector &flags) const { foreach (ModInfo::EFlag flag, flags) { @@ -231,7 +228,6 @@ bool ModListSortProxy::hasConflictFlag(const std::vector &flags) return false; } - bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const { for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { @@ -306,8 +302,6 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const return false; } - - bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const { if (!m_CurrentFilter.isEmpty() && @@ -330,7 +324,6 @@ void ModListSortProxy::setFilterMode(ModListSortProxy::FilterMode mode) } } - bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const { if (m_Profile == NULL) { @@ -362,7 +355,6 @@ bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) cons } } - bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index fc7edcd5..cb474200 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -83,6 +83,7 @@ public: return rowCount(parent) > 0; } + bool beingInvalidated() const { return m_BeingInvalidated; } public slots: @@ -116,6 +117,8 @@ private: bool m_FilterActive; FilterMode m_FilterMode; + bool m_BeingInvalidated; + }; #endif // MODLISTSORTPROXY_H diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 2789afe1..50a13a8c 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -38,7 +38,6 @@ ModListView::ModListView(QWidget *parent) : QTreeView(parent) , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) { -// setStyle(new ModListViewStyle(style(), indentation())); setVerticalScrollBar(m_Scrollbar); } diff --git a/src/organizer.pro b/src/organizer.pro index 19d3a3a0..994da396 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -88,7 +88,7 @@ SOURCES += \ json.cpp \ safewritefile.cpp \ modflagicondelegate.cpp \ - pluginflagicondelegate.cpp \ + genericicondelegate.cpp \ organizerproxy.cpp \ viewmarkingscrollbar.cpp @@ -168,7 +168,7 @@ HEADERS += \ safewritefile.h\ pdll.h \ modflagicondelegate.h \ - pluginflagicondelegate.h \ + genericicondelegate.h \ organizerproxy.h \ viewmarkingscrollbar.h diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index c48bc765..8adfda7e 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -104,6 +104,11 @@ HANDLE OrganizerProxy::startApplication(const QString &executable, const QString return m_Proxied->startApplication(executable, args, cwd, profile); } +bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const +{ + return m_Proxied->waitForProcessOrJob(handle, exitCode); +} + bool OrganizerProxy::onAboutToRun(const std::function &func) { auto conn = m_Proxied->m_AboutToRun.connect(func); diff --git a/src/organizerproxy.h b/src/organizerproxy.h index 017908e5..7e49c99c 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -35,6 +35,7 @@ public: 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 waitForApplication(HANDLE handle, LPDWORD exitCode = NULL) const; virtual void refreshModList(bool saveChanges); virtual bool onAboutToRun(const std::function &func); diff --git a/src/pluginflagicondelegate.cpp b/src/pluginflagicondelegate.cpp deleted file mode 100644 index a4f66c04..00000000 --- a/src/pluginflagicondelegate.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "pluginflagicondelegate.h" -#include "pluginlist.h" -#include - - -GenericIconDelegate::GenericIconDelegate(QObject *parent, int role, int logicalIndex, int compactSize) - : IconDelegate(parent) - , m_Role(role) - , m_LogicalIndex(logicalIndex) - , m_CompactSize(compactSize) - , m_Compact(false) -{ -} - -void GenericIconDelegate::columnResized(int logicalIndex, int, int newSize) -{ - if (logicalIndex == m_LogicalIndex) { - m_Compact = newSize < m_CompactSize; - } -} - -QList GenericIconDelegate::getIcons(const QModelIndex &index) const -{ - QList result; - if (index.isValid()) { - foreach (const QVariant &var, index.data(m_Role).toList()) { - QIcon icon = var.value(); - if (!m_Compact || !icon.isNull()) { - result.append(icon); - } - } - } - return result; -} - -size_t GenericIconDelegate::getNumIcons(const QModelIndex &index) const -{ - return index.data(m_Role).toList().count(); -} diff --git a/src/pluginflagicondelegate.h b/src/pluginflagicondelegate.h deleted file mode 100644 index 3c05db72..00000000 --- a/src/pluginflagicondelegate.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef PLUGINFLAGICONDELEGATE_H -#define PLUGINFLAGICONDELEGATE_H - -#include "icondelegate.h" - -/** - * @brief an icon delegate that takes the list of icons from a user-defines data role - */ -class GenericIconDelegate : public IconDelegate -{ -Q_OBJECT -public: - /** - * @brief constructor - * @param parent parent object - * @param role role of the itemmodel from which the icon list can be queried (as a QVariantList) - * @param logicalIndex logical index within the model. This is part of a "hack". Normally "empty" icons will be allocated the same - * space as a regular icon. This way the model can use empty icons as spacers and thus align same icons horizontally. - * Now, if you set the logical Index to a valid column and connect the columnResized slot to the sectionResized signal - * of the view, the delegate will turn off this behaviour if the column is smaller than "compactSize" - * @param compactSize see explanation of logicalIndex - */ - GenericIconDelegate(QObject *parent = NULL, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150); -public slots: - void columnResized(int logicalIndex, int oldSize, int newSize); -private: - virtual QList getIcons(const QModelIndex &index) const; - virtual size_t getNumIcons(const QModelIndex &index) const; -private: - int m_Role; - int m_LogicalIndex; - int m_CompactSize; - bool m_Compact; -}; - -#endif // PLUGINFLAGICONDELEGATE_H diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 5159871d..3532397d 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -1089,6 +1089,13 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) diff = 1; } QModelIndexList rows = selectionModel->selectedRows(); + // remove elements that aren't supposed to be movable + QMutableListIterator iter(rows); + while (iter.hasNext()) { + if ((iter.next().flags() & Qt::ItemIsDragEnabled) == 0) { + iter.remove(); + } + } if (keyEvent->key() == Qt::Key_Down) { for (int i = 0; i < rows.size() / 2; ++i) { rows.swap(i, rows.size() - i - 1); diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp index eca37562..f1b1ba34 100644 --- a/src/viewmarkingscrollbar.cpp +++ b/src/viewmarkingscrollbar.cpp @@ -14,6 +14,9 @@ ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *p void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) { + if (m_Model == NULL) { + return; + } QScrollBar::paintEvent(event); QStyleOptionSlider styleOption; -- cgit v1.3.1