summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2014-07-27 18:34:32 +0200
committerTannin <devnull@localhost>2014-07-27 18:34:32 +0200
commit715623e7905e5f64f5b331f4523642aab1aedd34 (patch)
tree38822f96aee0b584e13d19af9c5875a2950f3e55 /src
parent9879aae5757e0c2ca930b38c6b7a4aeb4715d2d9 (diff)
- 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
Diffstat (limited to 'src')
-rw-r--r--src/browserdialog.cpp30
-rw-r--r--src/browserdialog.h4
-rw-r--r--src/browserdialog.ui29
-rw-r--r--src/browserview.cpp3
-rw-r--r--src/genericicondelegate.cpp (renamed from src/pluginflagicondelegate.cpp)2
-rw-r--r--src/genericicondelegate.h (renamed from src/pluginflagicondelegate.h)6
-rw-r--r--src/mainwindow.cpp118
-rw-r--r--src/mainwindow.h2
-rw-r--r--src/modlist.cpp4
-rw-r--r--src/modlistsortproxy.cpp16
-rw-r--r--src/modlistsortproxy.h3
-rw-r--r--src/organizerproxy.cpp5
-rw-r--r--src/organizerproxy.h1
-rw-r--r--src/pluginlist.cpp7
14 files changed, 190 insertions, 40 deletions
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 <http://www.gnu.org/licenses/>.
#include <QDir>
#include <QWebFrame>
#include <QDesktopWidget>
+#include <QKeyEvent>
@@ -57,9 +58,12 @@ BrowserDialog::BrowserDialog(QWidget *parent)
m_Tabs = this->findChild<QTabWidget*>("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<QKeyEvent*>(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 @@
<property name="spacing">
<number>0</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -180,7 +189,16 @@
<property name="spacing">
<number>6</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -238,6 +256,13 @@
</widget>
</item>
<item>
+ <widget class="QLineEdit" name="urlEdit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
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 <http://www.gnu.org/licenses/>.
#include <Shlwapi.h>
#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/pluginflagicondelegate.cpp b/src/genericicondelegate.cpp
index a4f66c04..c3471735 100644
--- a/src/pluginflagicondelegate.cpp
+++ b/src/genericicondelegate.cpp
@@ -1,4 +1,4 @@
-#include "pluginflagicondelegate.h"
+#include "genericicondelegate.h"
#include "pluginlist.h"
#include <QList>
diff --git a/src/pluginflagicondelegate.h b/src/genericicondelegate.h
index 3c05db72..8bc75e27 100644
--- a/src/pluginflagicondelegate.h
+++ b/src/genericicondelegate.h
@@ -1,5 +1,5 @@
-#ifndef PLUGINFLAGICONDELEGATE_H
-#define PLUGINFLAGICONDELEGATE_H
+#ifndef GENERICICONDELEGATE_H
+#define GENERICICONDELEGATE_H
#include "icondelegate.h"
@@ -33,4 +33,4 @@ private:
bool m_Compact;
};
-#endif // PLUGINFLAGICONDELEGATE_H
+#endif // GENERICICONDELEGATE_H
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index adce2929..6fb53f0b 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -49,7 +49,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "questionboxmemory.h"
#include "tutorialmanager.h"
#include "modflagicondelegate.h"
-#include "pluginflagicondelegate.h"
+#include "genericicondelegate.h"
#include "credentialsdialog.h"
#include "selectiondialog.h"
#include "csvbuilder.h"
@@ -720,6 +720,7 @@ void MainWindow::savePluginList()
void MainWindow::modFilterActive(bool filterActive)
{
if (filterActive) {
+ m_ModList.setOverwriteMarkers(std::set<unsigned int>(), std::set<unsigned int>());
ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
} else if (ui->groupCombo->currentIndex() != 0) {
ui->modList->setStyleSheet("QTreeView { border: 2px ridge #337733; }");
@@ -776,7 +777,6 @@ bool MainWindow::saveCurrentLists()
return true;
}
-
bool MainWindow::addProfile()
{
QComboBox *profileBox = findChild<QComboBox*>("profileBox");
@@ -799,7 +799,6 @@ bool MainWindow::addProfile()
}
}
-
void MainWindow::hookUpWindowTutorials()
{
QDirIterator dirIter(QApplication::applicationDirPath() + "/tutorials", QStringList("*.js"), QDir::Files);
@@ -821,7 +820,6 @@ void MainWindow::hookUpWindowTutorials()
}
}
-
void MainWindow::showEvent(QShowEvent *event)
{
refreshFilters();
@@ -2741,6 +2739,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)
@@ -2928,6 +2939,7 @@ void MainWindow::refreshFilters()
{
QItemSelection currentSelection = ui->modList->selectionModel()->selection();
+ QVariant currentIndexName = ui->modList->currentIndex().data();
ui->modList->setCurrentIndex(QModelIndex());
QStringList selectedItems;
@@ -2966,8 +2978,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));
+ }
}
@@ -5231,11 +5246,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());
@@ -5250,8 +5266,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());
}
}
}
@@ -5304,6 +5324,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;
@@ -5322,18 +5401,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(),
@@ -5361,7 +5443,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;
@@ -5382,11 +5465,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 ab5e0cd5..e43feb89 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -134,6 +134,8 @@ 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);
public slots:
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 00bde4b9..fd8a2a4f 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -223,7 +223,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return QString();
}
} else {
- //return tr("None");
return QVariant();
}
}
@@ -870,6 +869,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 {
@@ -1003,6 +1004,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<ModInfo::EFlag> &flags) const
{
foreach (ModInfo::EFlag flag, flags) {
@@ -231,7 +228,6 @@ bool ModListSortProxy::hasConflictFlag(const std::vector<ModInfo::EFlag> &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/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<bool (const QString &)> &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<bool(const QString&)> &func);
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index dc81766f..52d95f24 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -1074,6 +1074,13 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event)
diff = 1;
}
QModelIndexList rows = selectionModel->selectedRows();
+ // remove elements that aren't supposed to be movable
+ QMutableListIterator<QModelIndex> 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);