summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2013-06-29 18:04:10 +0200
committerTannin <devnull@localhost>2013-06-29 18:04:10 +0200
commitfe37b48dffd8edcd6e30f4e304d3e27a731ac8ae (patch)
treee996c5d329d9abf77bcd12af4d05238fd6ebca1f /src
parentf8c683f700a3fff30540ff343df2bfba15080e86 (diff)
- caching of downloadwidgets to fix performance problem, currently broken and disabled
- added new state for downloads "uninstalled" for mods that were at one point installed and then removed - user-configured server preference is now used - updated tutorial to account for removal of integrated browser - reverted to qt 4 - using performance optimised findfirstfile on win vista and up - bugfix: it was possible to disable all columns of the mod list - bugfix: hook.dll doesn't load on win xp
Diffstat (limited to 'src')
-rw-r--r--src/dlls.manifest.debug.qt515
-rw-r--r--src/dlls.manifest.qt517
-rw-r--r--src/downloadlistwidget.cpp62
-rw-r--r--src/downloadlistwidget.h177
-rw-r--r--src/downloadlistwidgetcompact.cpp55
-rw-r--r--src/downloadlistwidgetcompact.h183
-rw-r--r--src/downloadmanager.cpp95
-rw-r--r--src/downloadmanager.h32
-rw-r--r--src/installationmanager.cpp24
-rw-r--r--src/main.cpp5
-rw-r--r--src/mainwindow.cpp21
-rw-r--r--src/mainwindow.h1
-rw-r--r--src/modlist.cpp2
-rw-r--r--src/modlist.h6
-rw-r--r--src/organizer.pro2
-rw-r--r--src/selfupdater.cpp5
-rw-r--r--src/serverinfo.cpp1
-rw-r--r--src/serverinfo.h18
-rw-r--r--src/settings.cpp17
-rw-r--r--src/settings.h5
-rw-r--r--src/shared/directoryentry.cpp23
-rw-r--r--src/tutorials/tutorial_firststeps_main.js14
22 files changed, 532 insertions, 248 deletions
diff --git a/src/dlls.manifest.debug.qt5 b/src/dlls.manifest.debug.qt5
new file mode 100644
index 00000000..369539d1
--- /dev/null
+++ b/src/dlls.manifest.debug.qt5
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
+ <file name="icuin49.dll"/>
+ <file name="icuuc49.dll"/>
+ <file name="icudt49.dll"/>
+ <file name="Qt5Cored.dll"/>
+ <file name="Qt5Declaratived.dll"/>
+ <file name="Qt5Guid.dll"/>
+ <file name="Qt5Networkd.dll"/>
+ <file name="Qt5Scriptd.dll"/>
+ <file name="Qt5Sqld.dll"/>
+ <file name="Qt5Widgetsd.dll"/>
+ <file name="Qt5XmlPatternsd.dll"/>
+</assembly>
diff --git a/src/dlls.manifest.qt5 b/src/dlls.manifest.qt5
new file mode 100644
index 00000000..ed82e878
--- /dev/null
+++ b/src/dlls.manifest.qt5
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
+ <file name="icuin49.dll"/>
+ <file name="icuuc49.dll"/>
+ <file name="icudt49.dll"/>
+ <file name="Qt5Core.dll"/>
+ <file name="Qt5Declarative.dll"/>
+ <file name="Qt5Gui.dll"/>
+ <file name="Qt5Network.dll"/>
+ <file name="Qt5Script.dll"/>
+ <file name="Qt5Sql.dll"/>
+ <file name="Qt5Svg.dll"/>
+ <file name="Qt5Widgets.dll"/>
+ <file name="Qt5Xml.dll"/>
+ <file name="Qt5XmlPatterns.dll"/>
+</assembly>
diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp
index 656b224d..743fc159 100644
--- a/src/downloadlistwidget.cpp
+++ b/src/downloadlistwidget.cpp
@@ -30,7 +30,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
DownloadListWidget::DownloadListWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::DownloadListWidget)
{
- ui->setupUi(this);
+ ui->setupUi(this);
}
@@ -48,6 +48,9 @@ DownloadListWidgetDelegate::DownloadListWidgetDelegate(DownloadManager *manager,
m_InstallLabel = m_ItemWidget->findChild<QLabel*>("installLabel");
m_InstallLabel->setVisible(false);
+
+ connect(manager, SIGNAL(stateChanged(int,DownloadManager::DownloadState)), this, SLOT(stateChanged(int,DownloadManager::DownloadState)));
+ connect(manager, SIGNAL(downloadRemoved(int)), this, SLOT(resetCache(int)));
}
@@ -57,10 +60,35 @@ DownloadListWidgetDelegate::~DownloadListWidgetDelegate()
}
+void DownloadListWidgetDelegate::stateChanged(int row,DownloadManager::DownloadState)
+{
+ m_Cache.remove(row);
+}
+
+
+void DownloadListWidgetDelegate::resetCache(int)
+{
+ m_Cache.clear();
+}
+
+
+void DownloadListWidgetDelegate::drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const
+{
+ QRect rect = option.rect;
+ rect.setLeft(0);
+ rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2));
+ painter->drawPixmap(rect, cache);
+}
+
+
void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
try {
- if (index.column() != 2) return;
+ auto iter = m_Cache.find(index.row());
+ if (iter != m_Cache.end()) {
+ drawCache(painter, option, *iter);
+ return;
+ }
m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height()));
@@ -103,9 +131,14 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Installed - Double Click to re-install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkGray);
+ } else if (state == DownloadManager::STATE_UNINSTALLED) {
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+ m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0));
+#else
+ m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0, QApplication::UnicodeUTF8));
+#endif
+ labelPalette.setColor(QPalette::WindowText, Qt::lightGray);
} else {
- // the tr-macro doesn't work here, maybe because the translation is actually associated with DownloadListWidget instead
- // of DownloadListWidgetDelegate?
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Done - Double Click to install", 0));
#else
@@ -123,10 +156,23 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
m_Progress->setValue(m_Manager->getProgress(downloadIndex));
}
- painter->save();
- painter->translate(QPoint(0, option.rect.topLeft().y()));
- m_ItemWidget->render(painter);
- painter->restore();
+#pragma message("caching disabled because changes in the list (including resorting) doesn't work correctly")
+// if (state >= DownloadManager::STATE_READY) {
+ if (false) {
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+ QPixmap cache = m_ItemWidget->grab();
+#else
+ QPixmap cache = QPixmap::grabWidget(m_ItemWidget);
+#endif
+ m_Cache[index.row()] = cache;
+ drawCache(painter, option, cache);
+ } else {
+ painter->save();
+ painter->translate(QPoint(0, option.rect.topLeft().y()));
+
+ m_ItemWidget->render(painter);
+ painter->restore();
+ }
} catch (const std::exception &e) {
qCritical("failed to paint download list: %s", e.what());
}
diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h
index 6f9fa24b..5379356f 100644
--- a/src/downloadlistwidget.h
+++ b/src/downloadlistwidget.h
@@ -17,86 +17,97 @@ 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 DOWNLOADLISTWIDGET_H
-#define DOWNLOADLISTWIDGET_H
-
-#include <QWidget>
-#include <QItemDelegate>
-#include <QLabel>
-#include <QProgressBar>
-#include <QTreeView>
-
-namespace Ui {
- class DownloadListWidget;
-}
-
-class DownloadListWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit DownloadListWidget(QWidget *parent = 0);
- ~DownloadListWidget();
-
-
-private:
- Ui::DownloadListWidget *ui;
-};
-
-class DownloadManager;
-
-class DownloadListWidgetDelegate : public QItemDelegate
-{
-
- Q_OBJECT
-
-public:
-
- DownloadListWidgetDelegate(DownloadManager *manager, QTreeView *view, QObject *parent = 0);
- ~DownloadListWidgetDelegate();
-
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
- virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
-
-signals:
-
- void installDownload(int index);
- void queryInfo(int index);
- void removeDownload(int index, bool deleteFile);
- void cancelDownload(int index);
- void pauseDownload(int index);
- void resumeDownload(int index);
-
-protected:
-
- bool editorEvent(QEvent *event, QAbstractItemModel *model,
- const QStyleOptionViewItem &option, const QModelIndex &index);
-
-private slots:
-
- void issueInstall();
- void issueDelete();
- void issueRemoveFromView();
- void issueCancel();
- void issuePause();
- void issueResume();
- void issueDeleteAll();
- void issueDeleteCompleted();
- void issueRemoveFromViewAll();
- void issueRemoveFromViewCompleted();
- void issueQueryInfo();
-
-private:
-
- DownloadListWidget *m_ItemWidget;
- DownloadManager *m_Manager;
-
- QLabel *m_NameLabel;
- QProgressBar *m_Progress;
- QLabel *m_InstallLabel;
- int m_ContextRow;
-
- QTreeView *m_View;
-};
-
-#endif // DOWNLOADLISTWIDGET_H
+#ifndef DOWNLOADLISTWIDGET_H
+#define DOWNLOADLISTWIDGET_H
+
+#include <QWidget>
+#include <QItemDelegate>
+#include <QLabel>
+#include <QProgressBar>
+#include <QTreeView>
+
+#include "downloadmanager.h"
+
+namespace Ui {
+ class DownloadListWidget;
+}
+
+class DownloadListWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DownloadListWidget(QWidget *parent = 0);
+ ~DownloadListWidget();
+
+
+private:
+ Ui::DownloadListWidget *ui;
+};
+
+class DownloadManager;
+
+class DownloadListWidgetDelegate : public QItemDelegate
+{
+
+ Q_OBJECT
+
+public:
+
+ DownloadListWidgetDelegate(DownloadManager *manager, QTreeView *view, QObject *parent = 0);
+ ~DownloadListWidgetDelegate();
+
+ virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+signals:
+
+ void installDownload(int index);
+ void queryInfo(int index);
+ void removeDownload(int index, bool deleteFile);
+ void cancelDownload(int index);
+ void pauseDownload(int index);
+ void resumeDownload(int index);
+
+protected:
+
+ bool editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option, const QModelIndex &index);
+
+private:
+
+ void drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const;
+
+private slots:
+
+ void issueInstall();
+ void issueDelete();
+ void issueRemoveFromView();
+ void issueCancel();
+ void issuePause();
+ void issueResume();
+ void issueDeleteAll();
+ void issueDeleteCompleted();
+ void issueRemoveFromViewAll();
+ void issueRemoveFromViewCompleted();
+ void issueQueryInfo();
+
+ void stateChanged(int row, DownloadManager::DownloadState);
+ void resetCache(int);
+
+private:
+
+ DownloadListWidget *m_ItemWidget;
+ DownloadManager *m_Manager;
+
+ QLabel *m_NameLabel;
+ QProgressBar *m_Progress;
+ QLabel *m_InstallLabel;
+ int m_ContextRow;
+
+ QTreeView *m_View;
+
+ mutable QMap<int, QPixmap> m_Cache;
+};
+
+#endif // DOWNLOADLISTWIDGET_H
diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp
index 68ed6a00..39e8010b 100644
--- a/src/downloadlistwidgetcompact.cpp
+++ b/src/downloadlistwidgetcompact.cpp
@@ -48,6 +48,9 @@ DownloadListWidgetCompactDelegate::DownloadListWidgetCompactDelegate(DownloadMan
m_DoneLabel = m_ItemWidget->findChild<QLabel*>("doneLabel");
m_DoneLabel->setVisible(false);
+
+ connect(manager, SIGNAL(stateChanged(int,DownloadManager::DownloadState)), this, SLOT(stateChanged(int,DownloadManager::DownloadState)));
+ connect(manager, SIGNAL(downloadRemoved(int)), this, SLOT(resetCache(int)));
}
@@ -57,11 +60,37 @@ DownloadListWidgetCompactDelegate::~DownloadListWidgetCompactDelegate()
}
+void DownloadListWidgetCompactDelegate::stateChanged(int row,DownloadManager::DownloadState)
+{
+ m_Cache.remove(row);
+}
+
+
+void DownloadListWidgetCompactDelegate::resetCache(int)
+{
+ m_Cache.clear();
+}
+
+
+void DownloadListWidgetCompactDelegate::drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const
+{
+ QRect rect = option.rect;
+ rect.setLeft(0);
+ rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2));
+ painter->drawPixmap(rect, cache);
+}
+
+
void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
#pragma message("This is quite costy - room for optimization?")
- if (index.column() != 2) return;
try {
+ auto iter = m_Cache.find(index.row());
+ if (iter != m_Cache.end()) {
+ drawCache(painter, option, *iter);
+ return;
+ }
+
m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height()));
if (index.row() % 2 == 1) {
m_ItemWidget->setBackgroundRole(QPalette::AlternateBase);
@@ -93,6 +122,9 @@ void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOpt
if (state == DownloadManager::STATE_INSTALLED) {
m_DoneLabel->setText(tr("Installed"));
m_DoneLabel->setForegroundRole(QPalette::Mid);
+ } else if (state == DownloadManager::STATE_UNINSTALLED) {
+ m_DoneLabel->setText(tr("Uninstalled"));
+ m_DoneLabel->setForegroundRole(QPalette::Dark);
} else {
m_DoneLabel->setText(tr("Done"));
m_DoneLabel->setForegroundRole(QPalette::WindowText);
@@ -106,10 +138,23 @@ void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOpt
m_Progress->setValue(m_Manager->getProgress(downloadIndex));
}
- painter->save();
- painter->translate(QPoint(0, option.rect.topLeft().y()));
- m_ItemWidget->render(painter);
- painter->restore();
+#pragma message("caching disabled because changes in the list (including resorting) doesn't work correctly")
+ if (false) {
+// if (state >= DownloadManager::STATE_READY) {
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+ QPixmap cache = m_ItemWidget->grab();
+#else
+ QPixmap cache = QPixmap::grabWidget(m_ItemWidget);
+#endif
+ m_Cache[index.row()] = cache;
+ drawCache(painter, option, cache);
+ } else {
+ painter->save();
+ painter->translate(QPoint(0, option.rect.topLeft().y()));
+
+ m_ItemWidget->render(painter);
+ painter->restore();
+ }
} catch (const std::exception &e) {
qCritical("failed to paint download list item %d: %s", index.row(), e.what());
}
diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h
index 65b110b6..3ac36e8d 100644
--- a/src/downloadlistwidgetcompact.h
+++ b/src/downloadlistwidgetcompact.h
@@ -17,90 +17,99 @@ 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 DOWNLOADLISTWIDGETCOMPACT_H
-#define DOWNLOADLISTWIDGETCOMPACT_H
-
-#include <QWidget>
-#include <QItemDelegate>
-#include <QLabel>
-#include <QProgressBar>
-#include <QTreeView>
-
-
-namespace Ui {
-class DownloadListWidgetCompact;
-}
-
-class DownloadListWidgetCompact : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit DownloadListWidgetCompact(QWidget *parent = 0);
- ~DownloadListWidgetCompact();
-
-private:
- Ui::DownloadListWidgetCompact *ui;
- int m_ContextRow;
-};
-
-class DownloadManager;
-
-class DownloadListWidgetCompactDelegate : public QItemDelegate
-{
-
- Q_OBJECT
-
-public:
-
- DownloadListWidgetCompactDelegate(DownloadManager *manager, QTreeView *view, QObject *parent = 0);
- ~DownloadListWidgetCompactDelegate();
-
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
- virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
-
-signals:
-
- void installDownload(int index);
- void queryInfo(int index);
- void removeDownload(int index, bool deleteFile);
- void cancelDownload(int index);
- void pauseDownload(int index);
- void resumeDownload(int index);
-
-protected:
-
- bool editorEvent(QEvent *event, QAbstractItemModel *model,
- const QStyleOptionViewItem &option, const QModelIndex &index);
-
-private slots:
-
- void issueInstall();
- void issueDelete();
- void issueRemoveFromView();
- void issueCancel();
- void issuePause();
- void issueResume();
- void issueDeleteAll();
- void issueDeleteCompleted();
- void issueRemoveFromViewAll();
- void issueRemoveFromViewCompleted();
- void issueQueryInfo();
-
-private:
-
- DownloadListWidgetCompact *m_ItemWidget;
- DownloadManager *m_Manager;
-
- QLabel *m_NameLabel;
- QProgressBar *m_Progress;
- QLabel *m_DoneLabel;
-
- QModelIndex m_ContextIndex;
-
- QTreeView *m_View;
-
-};
-
-#endif // DOWNLOADLISTWIDGETCOMPACT_H
-
+#ifndef DOWNLOADLISTWIDGETCOMPACT_H
+#define DOWNLOADLISTWIDGETCOMPACT_H
+
+#include <QWidget>
+#include <QItemDelegate>
+#include <QLabel>
+#include <QProgressBar>
+#include <QTreeView>
+#include "downloadmanager.h"
+
+
+namespace Ui {
+class DownloadListWidgetCompact;
+}
+
+class DownloadListWidgetCompact : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DownloadListWidgetCompact(QWidget *parent = 0);
+ ~DownloadListWidgetCompact();
+
+private:
+ Ui::DownloadListWidgetCompact *ui;
+ int m_ContextRow;
+};
+
+class DownloadManager;
+
+class DownloadListWidgetCompactDelegate : public QItemDelegate
+{
+
+ Q_OBJECT
+
+public:
+
+ DownloadListWidgetCompactDelegate(DownloadManager *manager, QTreeView *view, QObject *parent = 0);
+ ~DownloadListWidgetCompactDelegate();
+
+ virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+signals:
+
+ void installDownload(int index);
+ void queryInfo(int index);
+ void removeDownload(int index, bool deleteFile);
+ void cancelDownload(int index);
+ void pauseDownload(int index);
+ void resumeDownload(int index);
+
+protected:
+
+ bool editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option, const QModelIndex &index);
+
+private:
+
+ void drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const;
+
+private slots:
+
+ void issueInstall();
+ void issueDelete();
+ void issueRemoveFromView();
+ void issueCancel();
+ void issuePause();
+ void issueResume();
+ void issueDeleteAll();
+ void issueDeleteCompleted();
+ void issueRemoveFromViewAll();
+ void issueRemoveFromViewCompleted();
+ void issueQueryInfo();
+
+ void stateChanged(int row, DownloadManager::DownloadState);
+ void resetCache(int);
+private:
+
+ DownloadListWidgetCompact *m_ItemWidget;
+ DownloadManager *m_Manager;
+
+ QLabel *m_NameLabel;
+ QProgressBar *m_Progress;
+ QLabel *m_DoneLabel;
+
+ QModelIndex m_ContextIndex;
+
+ QTreeView *m_View;
+
+ mutable QMap<int, QPixmap> m_Cache;
+
+};
+
+#endif // DOWNLOADLISTWIDGETCOMPACT_H
+
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index 662034c2..3a2d26eb 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -30,6 +30,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QRegExp>
#include <QDirIterator>
#include <QInputDialog>
+#include <boost/bind.hpp>
#include <regex>
#include <QMessageBox>
@@ -84,6 +85,8 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con
if (metaFile.value("paused", false).toBool()) {
info->m_State = STATE_PAUSED;
+ } else if (metaFile.value("uninstalled", false).toBool()) {
+ info->m_State = STATE_UNINSTALLED;
} else if (metaFile.value("installed", false).toBool()) {
info->m_State = STATE_INSTALLED;
} else {
@@ -177,6 +180,13 @@ void DownloadManager::setOutputDirectory(const QString &outputDirectory)
refreshList();
}
+
+void DownloadManager::setPreferredServers(const std::map<QString, int> &preferredServers)
+{
+ m_PreferredServers = preferredServers;
+}
+
+
void DownloadManager::setSupportedExtensions(const QStringList &extensions)
{
m_SupportedExtensions = extensions;
@@ -606,11 +616,26 @@ void DownloadManager::markInstalled(int index)
DownloadInfo *info = m_ActiveDownloads.at(index);
QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat);
metaFile.setValue("installed", true);
+ metaFile.setValue("uninstalled", false);
setState(m_ActiveDownloads.at(index), STATE_INSTALLED);
}
+void DownloadManager::markUninstalled(int index)
+{
+ if ((index < 0) || (index >= m_ActiveDownloads.size())) {
+ throw MyException(tr("invalid index"));
+ }
+
+ DownloadInfo *info = m_ActiveDownloads.at(index);
+ QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat);
+ metaFile.setValue("uninstalled", true);
+
+ setState(m_ActiveDownloads.at(index), STATE_UNINSTALLED);
+}
+
+
QString DownloadManager::getDownloadFileName(const QString &baseName) const
{
QString fullPath = m_OutputDirectory + "/" + baseName;
@@ -643,6 +668,13 @@ QString DownloadManager::getFileNameFromNetworkReply(QNetworkReply *reply)
void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadManager::DownloadState state)
{
+ int row = 0;
+ for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
+ if (m_ActiveDownloads[i] == info) {
+ row = i;
+ break;
+ }
+ }
info->m_State = state;
switch (state) {
case STATE_PAUSED:
@@ -660,15 +692,11 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana
} break;
case STATE_READY: {
createMetaFile(info);
- for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
- if (m_ActiveDownloads[i] == info) {
- emit downloadComplete(i);
- return;
- }
- }
+ emit downloadComplete(row);
} break;
default: /* NOP */ break;
}
+ emit stateChanged(row, state);
}
@@ -736,6 +764,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info)
metaFile.setValue("newestVersion", info->m_NexusInfo.m_NewestVersion);
metaFile.setValue("category", info->m_NexusInfo.m_Category);
metaFile.setValue("installed", info->m_State == DownloadManager::STATE_INSTALLED);
+ metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED);
metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) ||
(info->m_State == DownloadManager::STATE_ERROR));
@@ -759,7 +788,6 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
QVariantMap result = resultData.toMap();
-// DownloadInfo *info = static_cast<DownloadInfo*>(userData.value<void*>());
DownloadInfo *info = downloadInfoByID(userData.toInt());
if (info == NULL) return;
@@ -869,30 +897,45 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant, QVar
// sort function to sort by best download server
-bool DownloadManager::ServerByPreference(const QVariant &LHS, const QVariant &RHS)
+bool DownloadManager::ServerByPreference(const std::map<QString, int> &preferredServers, const QVariant &LHS, const QVariant &RHS)
{
+ int LHSVal = 0;
+ int RHSVal = 0;
+
QVariantMap LHSMap = LHS.toMap();
QVariantMap RHSMap = RHS.toMap();
+
int LHSUsers = LHSMap["ConnectedUsers"].toInt();
int RHSUsers = RHSMap["ConnectedUsers"].toInt();
- bool LHSPremium = LHSMap["IsPremium"].toBool();
- bool RHSPremium = RHSMap["IsPremium"].toBool();
-
// 0 users is probably a sign that the server is offline. Since there is currently no
// mechanism to try a different server, we avoid those without users
- if ((LHSUsers == 0) && (RHSUsers != 0)) return false;
- if ((LHSUsers != 0) && (RHSUsers == 0)) return true;
+ if (LHSUsers == 0) {
+ LHSVal -= 500;
+ } else {
+ LHSVal -= LHSUsers;
+ }
+ if (RHSUsers == 0) {
+ RHSVal -= 500;
+ } else {
+ RHSVal -= RHSUsers;
+ }
+ // user preference. This is a bit silly because the more servers on the preferred list the higher the boost
+ auto LHSPreference = preferredServers.find(LHSMap["Name"].toString());
+ auto RHSPreference = preferredServers.find(RHSMap["Name"].toString());
- if (LHSPremium && !RHSPremium) {
- return true;
- } else if (!LHSPremium && RHSPremium) {
- return false;
+ if (LHSPreference != preferredServers.end()) {
+ LHSVal += 100 + LHSPreference->second * 20;
+ }
+ if (RHSPreference != preferredServers.end()) {
+ RHSVal += 100 + RHSPreference->second * 20;
}
- // TODO implement country preference
+ // premium isn't valued high because premium servers already get a massive boost for having few users online
+ if (LHSMap["IsPremium"].toBool()) LHSVal += 5;
+ if (RHSMap["IsPremium"].toBool()) RHSVal += 5;
- return LHSUsers < RHSUsers;
+ return RHSVal < LHSVal;
}
int DownloadManager::startDownloadURLs(const QStringList &urls)
@@ -913,6 +956,16 @@ QString DownloadManager::downloadPath(int id)
return getFilePath(id);
}
+int DownloadManager::indexByName(const QString &fileName) const
+{
+ for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
+ if (m_ActiveDownloads[i]->m_FileName == fileName) {
+ return i;
+ }
+ }
+ return -1;
+}
+
void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
{
std::set<int>::iterator idIter = m_RequestIDs.find(requestID);
@@ -928,14 +981,14 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
emit showMessage(tr("No download server available. Please try again later."));
return;
}
- qSort(resultList.begin(), resultList.end(), ServerByPreference);
+
+ std::sort(resultList.begin(), resultList.end(), boost::bind(&DownloadManager::ServerByPreference, m_PreferredServers, _1, _2));
QStringList URLs;
foreach (const QVariant &server, resultList) {
URLs.append(server.toMap()["URI"].toString());
}
- qDebug("urls: %s", qPrintable(URLs.join(";")));
addDownload(URLs, modID, fileID, info);
}
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
index 07219f08..623dda7a 100644
--- a/src/downloadmanager.h
+++ b/src/downloadmanager.h
@@ -70,7 +70,8 @@ public:
STATE_FETCHINGMODINFO,
STATE_FETCHINGFILEINFO,
STATE_READY,
- STATE_INSTALLED
+ STATE_INSTALLED,
+ STATE_UNINSTALLED
};
private:
@@ -150,6 +151,11 @@ public:
QString getOutputDirectory() const { return m_OutputDirectory; }
/**
+ * @brief setPreferredServers set the list of preferred servers
+ */
+ void setPreferredServers(const std::map<QString, int> &preferredServers);
+
+ /**
* @brief set the list of supported extensions
* @param extensions list of supported extensions
*/
@@ -251,6 +257,13 @@ public:
void markInstalled(int index);
/**
+ * @brief mark a download as uninstalled
+ *
+ * @param index index of the file to mark uninstalled
+ */
+ void markUninstalled(int index);
+
+ /**
* @brief refreshes the list of downloads
*/
void refreshList();
@@ -261,13 +274,20 @@ public:
* @param RHS
* @return
*/
- static bool ServerByPreference(const QVariant &LHS, const QVariant &RHS);
+ static bool ServerByPreference(const std::map<QString, int> &preferredServers, const QVariant &LHS, const QVariant &RHS);
virtual int startDownloadURLs(const QStringList &urls);
virtual int startDownloadNexusFile(int modID, int fileID);
virtual QString downloadPath(int id);
+ /**
+ * @brief retrieve a download index from the filename
+ * @param fileName file to look up
+ * @return index of that download or -1 if it wasn't found
+ */
+ int indexByName(const QString &fileName) const;
+
signals:
void aboutToUpdate();
@@ -286,6 +306,13 @@ signals:
**/
void showMessage(const QString &message);
+ /**
+ * @brief emitted whenever the state of a download changes
+ * @param row the row that changed
+ * @param state the new state
+ */
+ void stateChanged(int row, DownloadManager::DownloadState state);
+
public slots:
/**
@@ -371,6 +398,7 @@ private:
QVector<DownloadInfo*> m_ActiveDownloads;
QString m_OutputDirectory;
+ std::map<QString, int> m_PreferredServers;
QStringList m_SupportedExtensions;
std::set<int> m_RequestIDs;
QVector<int> m_AlphabeticalTranslation;
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index 38173d8b..a6d86f36 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -484,30 +484,6 @@ bool InstallationManager::testOverwrite(GuessedValue<QString> &modName) const
return true;
}
-/*
-bool InstallationManager::fixModName(QString &name)
-{
- QString temp = name.simplified();
- while (temp.endsWith('.')) temp.chop(1);
-
- temp.replace(QRegExp("[<>:\"/\\|?*]"), "");
- static QString invalidNames[] = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
- "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
- for (int i = 0; i < sizeof(invalidNames) / sizeof(QString); ++i) {
- if (temp == invalidNames[i]) {
- temp = "";
- break;
- }
- }
-
- if (temp.length() > 1) {
- name = temp;
- return true;
- } else {
- return false;
- }
-}
-*/
bool InstallationManager::ensureValidModName(GuessedValue<QString> &name) const
{
diff --git a/src/main.cpp b/src/main.cpp
index f93e9dec..a1129b8c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -281,9 +281,12 @@ void registerMetaTypes()
int main(int argc, char *argv[])
{
MOApplication application(argc, argv);
+
+ qApp->addLibraryPath(application.applicationDirPath() + "/dlls");
+
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
- LogBuffer::init(20, QtDebugMsg, application.applicationDirPath().append("/logs/mo_interface.log"));
+ LogBuffer::init(20, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
qDebug("Working directory: %s", qPrintable(QDir::currentPath()));
qDebug("MO at: %s", qPrintable(application.applicationDirPath()));
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 9132751b..8027e13a 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -179,6 +179,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
//ui->modList->setAcceptDrops(true);
ui->modList->header()->installEventFilter(&m_ModList);
ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray());
+ ui->modList->header()->setSectionHidden(0, false); // prevent the name-column from being hidden
ui->modList->installEventFilter(&m_ModList);
// restoreState also seems to restores the resize mode from previous session,
@@ -222,6 +223,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
ui->linkButton->setMenu(linkMenu);
m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory());
+ m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers());
NexusInterface::instance()->setCacheDirectory(m_Settings.getCacheDirectory());
NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion());
@@ -239,6 +241,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString)));
connect(&m_ModList, SIGNAL(showMessage(QString)), this, SLOT(showMessage(QString)));
connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), this, SLOT(modRenamed(QString,QString)));
+ connect(&m_ModList, SIGNAL(modUninstalled(QString)), this, SLOT(modRemoved(QString)));
connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), this, SLOT(modlistChanged(QModelIndex, int)));
connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked()));
connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), this, SLOT(displayColumnSelection(QPoint)));
@@ -2553,6 +2556,17 @@ void MainWindow::removeMod_clicked()
}
+void MainWindow::modRemoved(const QString &fileName)
+{
+ if (!fileName.isEmpty() && !QFileInfo(fileName).isAbsolute()) {
+ int index = m_DownloadManager.indexByName(fileName);
+ if (index >= 0) {
+ m_DownloadManager.markUninstalled(index);
+ }
+ }
+}
+
+
void MainWindow::reinstallMod_clicked()
{
ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow);
@@ -3417,6 +3431,7 @@ void MainWindow::on_actionSettings_triggered()
m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory());
}
}
+ m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers());
if (m_Settings.getModDirectory() != oldModDirectory) {
refreshModList();
@@ -3551,9 +3566,9 @@ void MainWindow::installDownload(int index)
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) {
MessageDialog::showMessage(tr("Installation successful"), this);
-
refreshModList();
+
QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName));
if (posList.count() == 1) {
ui->modList->scrollTo(posList.at(0));
@@ -4165,7 +4180,7 @@ void MainWindow::displayColumnSelection(const QPoint &pos)
// display a list of all headers as checkboxes
QAbstractItemModel *model = ui->modList->header()->model();
- for (int i = 0; i < model->columnCount(); ++i) {
+ for (int i = 1; i < model->columnCount(); ++i) {
QString columnName = model->headerData(i, Qt::Horizontal).toString();
QCheckBox *checkBox = new QCheckBox(&menu);
checkBox->setText(columnName);
@@ -4177,7 +4192,7 @@ void MainWindow::displayColumnSelection(const QPoint &pos)
menu.exec(pos);
// view/hide columns depending on check-state
- int i = 0;
+ int i = 1;
foreach (const QAction *action, menu.actions()) {
const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction*>(action);
if (widgetAction != NULL) {
diff --git a/src/mainwindow.h b/src/mainwindow.h
index d718c537..61157f13 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -393,6 +393,7 @@ private slots:
void modOpenPrev();
void modRenamed(const QString &oldName, const QString &newName);
+ void modRemoved(const QString &fileName);
void hideSaveGameInfo();
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 21aca0bf..7a8d9e0b 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -642,6 +642,8 @@ void ModList::removeRowForce(int row)
if (wasEnabled) {
emit removeOrigin(modInfo->name());
}
+
+ emit modUninstalled(modInfo->getInstallationFile());
}
diff --git a/src/modlist.h b/src/modlist.h
index aeff799a..4f1bf85e 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -160,6 +160,12 @@ signals:
void modRenamed(const QString &oldName, const QString &newName);
/**
+ * @brief emitted after a mod has been uninstalled
+ * @param fileName filename of the mod being uninstalled
+ */
+ void modUninstalled(const QString &fileName);
+
+ /**
* @brief emitted whenever a row in the list has changed
*
* @param index the index of the changed field
diff --git a/src/organizer.pro b/src/organizer.pro
index 957a9266..7ff6b4e9 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -268,14 +268,12 @@ OTHER_FILES += \
tutorials/firststeps.qml \
tutorials/tutorials.js \
tutorials/tutorial_firststeps_main.js \
- tutorials/tutorial_firststeps_settings.js \
tutorials/tutorials_settingsdialog.qml \
tutorials/tutorials_mainwindow.qml \
tutorials/Highlight.qml \
tutorials/TutorialDescription.qml \
tutorials/TutorialOverlay.qml \
tutorials/tutorials_nexusdialog.qml \
- tutorials/tutorial_firststeps_browser.js \
tutorials/tutorials_modinfodialog.qml \
tutorials/tutorial_firststeps_modinfo.js \
tutorials/tutorial_conflictresolution_main.js \
diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp
index 78c7dea7..421fbeab 100644
--- a/src/selfupdater.cpp
+++ b/src/selfupdater.cpp
@@ -35,6 +35,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QProcess>
#include <QApplication>
#include <util.h>
+#include <boost/bind.hpp>
using namespace MOBase;
@@ -449,7 +450,9 @@ void SelfUpdater::nxmDownloadURLsAvailable(int, int, QVariant userData, QVariant
m_UpdateRequestID = -1;
QVariantList serverList = resultData.toList();
if (serverList.count() != 0) {
- qSort(serverList.begin(), serverList.end(), DownloadManager::ServerByPreference);
+ std::map<QString, int> dummy;
+ qSort(serverList.begin(), serverList.end(), boost::bind(&DownloadManager::ServerByPreference, dummy, _1, _2));
+
QVariantMap dlServer = serverList.first().toMap();
diff --git a/src/serverinfo.cpp b/src/serverinfo.cpp
new file mode 100644
index 00000000..e96b69d2
--- /dev/null
+++ b/src/serverinfo.cpp
@@ -0,0 +1 @@
+#include "serverinfo.h"
diff --git a/src/serverinfo.h b/src/serverinfo.h
new file mode 100644
index 00000000..8e5e935a
--- /dev/null
+++ b/src/serverinfo.h
@@ -0,0 +1,18 @@
+#ifndef SERVERINFO_H
+#define SERVERINFO_H
+
+#include <QString>
+#include <QDate>
+#include <QMetaType>
+
+struct ServerInfo
+{
+ QString name;
+ bool premium;
+ QDate lastSeen;
+ bool preferred;
+};
+
+Q_DECLARE_METATYPE(ServerInfo)
+
+#endif // SERVERINFO_H
diff --git a/src/settings.cpp b/src/settings.cpp
index 991f1f59..2bfdc0a6 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -153,6 +153,23 @@ QString Settings::getDownloadDirectory() const
return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString());
}
+std::map<QString, int> Settings::getPreferredServers()
+{
+ std::map<QString, int> result;
+ m_Settings.beginGroup("Servers");
+
+ foreach (const QString &serverKey, m_Settings.childKeys()) {
+ QVariantMap data = m_Settings.value(serverKey).toMap();
+ int preference = data["preferred"].toInt();
+ if (preference > 0) {
+ result[serverKey] = preference;
+ }
+ }
+ m_Settings.endGroup();
+
+ return result;
+}
+
QString Settings::getCacheDirectory() const
{
return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString());
diff --git a/src/settings.h b/src/settings.h
index 56a4a2a4..2d66fd59 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -96,6 +96,11 @@ public:
QString getDownloadDirectory() const;
/**
+ * retrieve a sorted list of preferred servers
+ */
+ std::map<QString, int> getPreferredServers();
+
+ /**
* retrieve the directory where mods are stored (with native separators)
**/
QString getModDirectory() const;
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 9958b934..5f0e0b5a 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -421,12 +421,33 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di
}
+static bool SupportOptimizedFind()
+{
+ OSVERSIONINFO versionInfo;
+ versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&versionInfo);
+
+ // large fetch and basic info for FindFirstFileEx is supported on win server 2008 r2, win 7 and newer
+ return (versionInfo.dwMajorVersion > 6) ||
+ ((versionInfo.dwMajorVersion == 6) && (versionInfo.dwMinorVersion >= 1));
+}
+
+
void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
{
WIN32_FIND_DATAW findData;
_snwprintf(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, L"\\*");
- HANDLE searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
+
+ HANDLE searchHandle = NULL;
+
+ if (SupportOptimizedFind()) {
+ searchHandle = ::FindFirstFileExW(buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+ } else {
+ searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
+ }
+
if (searchHandle != INVALID_HANDLE_VALUE) {
BOOL result = true;
while (result) {
diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js
index 9785dc42..150fde84 100644
--- a/src/tutorials/tutorial_firststeps_main.js
+++ b/src/tutorials/tutorial_firststeps_main.js
@@ -43,9 +43,10 @@ function getTutorialSteps()
},
function() {
- tutorial.text = qsTr("There are a few ways to get mods into ModOrganizer. The easiest is to use the embedded nexus browser. Click \"Nexus\"")
+ tutorial.text = qsTr("There are a few ways to get mods into ModOrganizer. You can use your regular browser to send download from nexusmods to MO."
+ + "Click on \"Nexus\" to open the appropriate nexusmods page. This will also register ModOrganizer as the downloader "
+ + "for \"nxm links\" for the game MO is managing. \"nxm links\" are the green buttons on Nexus saying \"Download with Manager\".")
highlightAction("actionNexus", true)
- manager.activateTutorial("NexusDialog", "tutorial_firststeps_browser.js")
tutorialControl.waitForAction("actionNexus")
},
@@ -63,14 +64,7 @@ function getTutorialSteps()
},
function() {
- tutorial.text = qsTr("I promised you multiple ways of getting mods into MO. Please open the settings dialog now.")
- highlightAction("actionSettings", true)
- manager.activateTutorial("SettingsDialog", "tutorial_firststeps_settings.js")
- tutorialControl.waitForAction("actionSettings")
- },
-
- function() {
- tutorial.text = qsTr("So far I only showed you how to get mods from Nexus.\n"
+ tutorial.text = qsTr("This is how to get mods from Nexus.\n"
+ "You can also install mods from disk using the \"Install Mod\" button.")
highlightAction("actionInstallMod", false)
waitForClick()