summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--MultiMC.cpp101
-rw-r--r--MultiMC.h35
-rw-r--r--config.h.in6
-rw-r--r--gui/MainWindow.cpp61
-rw-r--r--gui/MainWindow.h2
-rw-r--r--gui/widgets/ModListView.cpp20
-rw-r--r--logic/ModList.cpp2
-rw-r--r--logic/lists/InstanceList.cpp22
-rw-r--r--logic/updater/DownloadUpdateTask.cpp9
-rw-r--r--logic/updater/NotificationChecker.cpp120
-rw-r--r--logic/updater/NotificationChecker.h54
-rw-r--r--tests/TestUtil.h2
13 files changed, 382 insertions, 56 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c026de4d..7ac13fa0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -144,6 +144,8 @@ SET(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
# Updater enabled?
SET(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.")
+# Notification URL
+SET(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# Build a version string to display in the configure logs.
SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
@@ -337,6 +339,8 @@ logic/updater/UpdateChecker.h
logic/updater/UpdateChecker.cpp
logic/updater/DownloadUpdateTask.h
logic/updater/DownloadUpdateTask.cpp
+logic/updater/NotificationChecker.h
+logic/updater/NotificationChecker.cpp
# News System
logic/news/NewsChecker.h
diff --git a/MultiMC.cpp b/MultiMC.cpp
index 4e06f558..b105fd66 100644
--- a/MultiMC.cpp
+++ b/MultiMC.cpp
@@ -26,6 +26,7 @@
#include "logic/JavaUtils.h"
#include "logic/updater/UpdateChecker.h"
+#include "logic/updater/NotificationChecker.h"
#include "pathutils.h"
#include "cmdutils.h"
@@ -47,7 +48,7 @@
using namespace Util::Commandline;
-MultiMC::MultiMC(int &argc, char **argv, const QString &root)
+MultiMC::MultiMC(int &argc, char **argv, const QString &data_dir_override)
: QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD,
VERSION_CHANNEL, VERSION_BUILD_TYPE}
{
@@ -60,10 +61,6 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root)
// Don't quit on hiding the last window
this->setQuitOnLastWindowClosed(false);
- // Print app header
- std::cout << "MultiMC 5" << std::endl;
- std::cout << "(c) 2013 MultiMC Contributors" << std::endl << std::endl;
-
// Commandline parsing
QHash<QString, QVariant> args;
{
@@ -82,16 +79,6 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root)
parser.addShortOpt("dir", 'd');
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of "
"the binary location (use '.' for current)");
- // --update
- parser.addOption("update");
- parser.addShortOpt("update", 'u');
- parser.addDocumentation("update", "replaces the given file with the running executable",
- "<path>");
- // --quietupdate
- parser.addSwitch("quietupdate");
- parser.addShortOpt("quietupdate", 'U');
- parser.addDocumentation("quietupdate",
- "doesn't restart MultiMC after installing updates");
// WARNING: disabled until further notice
/*
// --launch
@@ -129,41 +116,76 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root)
m_status = MultiMC::Succeeded;
return;
}
-
- // update
- // Note: cwd is always the current executable path!
- if (!args["update"].isNull())
- {
- std::cout << "Performing MultiMC update: " << qPrintable(args["update"].toString())
- << std::endl;
- QString cwd = QDir::currentPath();
- QDir::setCurrent(applicationDirPath());
- QFile file(applicationFilePath());
- file.copy(args["update"].toString());
- if (args["quietupdate"].toBool())
- {
- m_status = MultiMC::Succeeded;
- return;
- }
- QDir::setCurrent(cwd);
- }
}
-
+ origcwdPath = QDir::currentPath();
+ binPath = applicationDirPath();
+ QString adjustedBy;
// change directory
- QDir::setCurrent(
- args["dir"].toString().isEmpty()
- ? (root.isEmpty() ? QDir::currentPath() : QDir::current().absoluteFilePath(root))
- : args["dir"].toString());
+ QString dirParam = args["dir"].toString();
+ if (!data_dir_override.isEmpty())
+ {
+ // the override is used for tests (although dirparam would be enough...)
+ // TODO: remove the need for this extra logic
+ adjustedBy += "Test override " + data_dir_override;
+ dataPath = data_dir_override;
+ }
+ else if (!dirParam.isEmpty())
+ {
+ // the dir param. it makes multimc data path point to whatever the user specified
+ // on command line
+ adjustedBy += "Command line " + dirParam;
+ dataPath = dirParam;
+ }
+ if(!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
+ {
+ // BAD STUFF. WHAT DO?
+ initLogger();
+ QLOG_ERROR() << "Failed to set work path. Will exit. NOW.";
+ m_status = MultiMC::Failed;
+ return;
+ }
+
+ {
+ #ifdef Q_OS_LINUX
+ QDir foo(PathCombine(binPath, ".."));
+ rootPath = foo.absolutePath();
+ #elif defined(Q_OS_WIN32)
+ QDir foo(PathCombine(binPath, ".."));
+ rootPath = foo.absolutePath();
+ #elif defined(Q_OS_MAC)
+ QDir foo(PathCombine(binPath, "../.."));
+ rootPath = foo.absolutePath();
+ #endif
+ }
// init the logger
initLogger();
+ QLOG_INFO() << "MultiMC 5, (c) 2013 MultiMC Contributors";
+ QLOG_INFO() << "Version : " << VERSION_STR;
+ QLOG_INFO() << "Git commit : " << GIT_COMMIT;
+ if (adjustedBy.size())
+ {
+ QLOG_INFO() << "Work dir before adjustment : " << origcwdPath;
+ QLOG_INFO() << "Work dir after adjustment : " << QDir::currentPath();
+ QLOG_INFO() << "Adjusted by : " << adjustedBy;
+ }
+ else
+ {
+ QLOG_INFO() << "Work dir : " << QDir::currentPath();
+ }
+ QLOG_INFO() << "Binary path : " << binPath;
+ QLOG_INFO() << "Application root path : " << rootPath;
+
// load settings
initGlobalSettings();
// initialize the updater
m_updateChecker.reset(new UpdateChecker());
+ // initialize the notification checker
+ m_notificationChecker.reset(new NotificationChecker());
+
// initialize the news checker
m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL));
@@ -319,7 +341,7 @@ void MultiMC::initLogger()
QsLogging::Logger &logger = QsLogging::Logger::instance();
logger.setLoggingLevel(QsLogging::TraceLevel);
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
- m_debugDestination = QsLogging::DestinationFactory::MakeQDebugDestination();
+ m_debugDestination = QsLogging::DestinationFactory::MakeDebugOutputDestination();
logger.addDestination(m_fileDestination.get());
logger.addDestination(m_debugDestination.get());
// log all the things
@@ -332,6 +354,7 @@ void MultiMC::initGlobalSettings()
// Updates
m_settings->registerSetting("UseDevBuilds", false);
m_settings->registerSetting("AutoUpdate", true);
+ m_settings->registerSetting("ShownNotifications", QString());
// FTB
m_settings->registerSetting("TrackFTBInstances", false);
diff --git a/MultiMC.h b/MultiMC.h
index 9ad276ff..3a25aa5e 100644
--- a/MultiMC.h
+++ b/MultiMC.h
@@ -17,6 +17,7 @@ class QNetworkAccessManager;
class ForgeVersionList;
class JavaVersionList;
class UpdateChecker;
+class NotificationChecker;
class NewsChecker;
#if defined(MMC)
@@ -90,6 +91,11 @@ public:
return m_updateChecker;
}
+ std::shared_ptr<NotificationChecker> notificationChecker()
+ {
+ return m_notificationChecker;
+ }
+
std::shared_ptr<NewsChecker> newsChecker()
{
return m_newsChecker;
@@ -125,6 +131,29 @@ public:
*/
bool openJsonEditor(const QString &filename);
+ /// this is the root of the 'installation'. Used for automatic updates
+ const QString &root()
+ {
+ return rootPath;
+ }
+ /// this is the where the binary files reside
+ const QString &bin()
+ {
+ return binPath;
+ }
+ /// this is the work/data path. All user data is here.
+ const QString &data()
+ {
+ return dataPath;
+ }
+ /**
+ * this is the original work path before it was changed by the adjustment mechanism
+ */
+ const QString &origcwd()
+ {
+ return origcwdPath;
+ }
+
private:
void initLogger();
@@ -143,6 +172,7 @@ private:
std::shared_ptr<SettingsObject> m_settings;
std::shared_ptr<InstanceList> m_instances;
std::shared_ptr<UpdateChecker> m_updateChecker;
+ std::shared_ptr<NotificationChecker> m_notificationChecker;
std::shared_ptr<NewsChecker> m_newsChecker;
std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<IconList> m_icons;
@@ -157,6 +187,11 @@ private:
QString m_updateOnExitPath;
+ QString rootPath;
+ QString binPath;
+ QString dataPath;
+ QString origcwdPath;
+
Status m_status = MultiMC::Failed;
MultiMCVersion m_version;
};
diff --git a/config.h.in b/config.h.in
index aa604056..9681b825 100644
--- a/config.h.in
+++ b/config.h.in
@@ -10,6 +10,12 @@
// URL for the updater's channel
#define CHANLIST_URL "@MultiMC_CHANLIST_URL@"
+// URL for notifications
+#define NOTIFICATION_URL "@MultiMC_NOTIFICATION_URL@"
+
+// Used for matching notifications
+#define FULL_VERSION_STR "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@"
+
// The commit hash of this build
#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index 75ebefe4..d1119028 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -92,6 +92,7 @@
#include "logic/assets/AssetsUtils.h"
#include "logic/assets/AssetsMigrateTask.h"
#include <logic/updater/UpdateChecker.h>
+#include <logic/updater/NotificationChecker.h>
#include <logic/tasks/ThreadTask.h>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
@@ -283,6 +284,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
// if automatic update checks are allowed, start one.
if (MMC->settings()->get("AutoUpdate").toBool())
on_actionCheckUpdate_triggered();
+
+ connect(MMC->notificationChecker().get(), &NotificationChecker::notificationCheckFinished,
+ this, &MainWindow::notificationsChanged);
}
const QString currentInstanceId = MMC->settings()->get("SelectedInstance").toString();
@@ -522,6 +526,63 @@ void MainWindow::updateAvailable(QString repo, QString versionName, int versionI
}
}
+QList<int> stringToIntList(const QString &string)
+{
+ QStringList split = string.split(',', QString::SkipEmptyParts);
+ QList<int> out;
+ for (int i = 0; i < split.size(); ++i)
+ {
+ out.append(split.at(i).toInt());
+ }
+ return out;
+}
+QString intListToString(const QList<int> &list)
+{
+ QStringList slist;
+ for (int i = 0; i < list.size(); ++i)
+ {
+ slist.append(QString::number(list.at(i)));
+ }
+ return slist.join(',');
+}
+void MainWindow::notificationsChanged()
+{
+ QList<NotificationChecker::NotificationEntry> entries =
+ MMC->notificationChecker()->notificationEntries();
+ QList<int> shownNotifications =
+ stringToIntList(MMC->settings()->get("ShownNotifications").toString());
+ for (auto it = entries.begin(); it != entries.end(); ++it)
+ {
+ NotificationChecker::NotificationEntry entry = *it;
+ if (!shownNotifications.contains(entry.id) && entry.applies())
+ {
+ QMessageBox::Icon icon;
+ switch (entry.type)
+ {
+ case NotificationChecker::NotificationEntry::Critical:
+ icon = QMessageBox::Critical;
+ break;
+ case NotificationChecker::NotificationEntry::Warning:
+ icon = QMessageBox::Warning;
+ break;
+ case NotificationChecker::NotificationEntry::Information:
+ icon = QMessageBox::Information;
+ break;
+ }
+
+ QMessageBox box(icon, tr("Notification"), entry.message, QMessageBox::Close, this);
+ QPushButton *dontShowAgainButton = box.addButton(tr("Don't show again"), QMessageBox::AcceptRole);
+ box.setDefaultButton(QMessageBox::Close);
+ box.exec();
+ if (box.clickedButton() == dontShowAgainButton)
+ {
+ shownNotifications.append(entry.id);
+ }
+ }
+ }
+ MMC->settings()->set("ShownNotifications", intListToString(shownNotifications));
+}
+
void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit)
{
QLOG_INFO() << "Downloading updates.";
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index ca11b380..af2f1dca 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -159,6 +159,8 @@ slots:
void updateAvailable(QString repo, QString versionName, int versionId);
+ void notificationsChanged();
+
void activeAccountChanged();
void changeActiveAccount();
diff --git a/gui/widgets/ModListView.cpp b/gui/widgets/ModListView.cpp
index 9d5950c3..358e6331 100644
--- a/gui/widgets/ModListView.cpp
+++ b/gui/widgets/ModListView.cpp
@@ -44,9 +44,19 @@ void ModListView::setModel ( QAbstractItemModel* model )
QTreeView::setModel ( model );
auto head = header();
head->setStretchLastSection(false);
- head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
- head->setSectionResizeMode(1, QHeaderView::Stretch);
- for(int i = 2; i < head->count(); i++)
- head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
- dropIndicatorPosition();
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
}
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
index fd41bcf7..499623bf 100644
--- a/logic/ModList.cpp
+++ b/logic/ModList.cpp
@@ -416,7 +416,7 @@ QVariant ModList::data(const QModelIndex &index, int role) const
switch (index.column())
{
case ActiveColumn:
- return mods[row].enabled();
+ return mods[row].enabled() ? Qt::Checked: Qt::Unchecked;
default:
return QVariant();
}
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
index 48a2865a..bfd183d9 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/lists/InstanceList.cpp
@@ -307,9 +307,10 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
return;
}
-
dir.cd("ModPacks");
- QFile f(dir.absoluteFilePath("modpacks.xml"));
+ auto fpath = dir.absoluteFilePath("modpacks.xml");
+ QFile f(fpath);
+ QLOG_INFO() << "Discovering FTB instances -- " << fpath;
if (!f.open(QFile::ReadOnly))
return;
@@ -326,6 +327,9 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
QXmlStreamAttributes attrs = reader.attributes();
FTBRecord record;
record.dir = attrs.value("dir").toString();
+ QDir test(dataDir.absoluteFilePath(record.dir));
+ if(!test.exists())
+ continue;
record.name = attrs.value("name").toString();
record.logo = attrs.value("logo").toString();
record.mcVersion = attrs.value("mcVersion").toString();
@@ -343,11 +347,17 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
}
}
f.close();
-
+ if(!records.size())
+ {
+ QLOG_INFO() << "No FTB instances to load.";
+ return;
+ }
+ QLOG_INFO() << "Loading FTB instances! -- got " << records.size();
// process the records we acquired.
for (auto record : records)
{
auto instanceDir = dataDir.absoluteFilePath(record.dir);
+ QLOG_INFO() << "Loading FTB instance from " << instanceDir;
auto templateDir = dir.absoluteFilePath(record.dir);
if (!QFileInfo(instanceDir).exists())
{
@@ -361,6 +371,7 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists())
{
+ QLOG_INFO() << "Converting " << record.name << " as new.";
BaseInstance *instPtr = NULL;
auto &factory = InstanceFactory::get();
auto version = MMC->minecraftlist()->findVersion(record.mcVersion);
@@ -386,6 +397,7 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
}
else
{
+ QLOG_INFO() << "Loading existing " << record.name;
BaseInstance *instPtr = NULL;
auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir);
if (!instPtr || error != InstanceFactory::NoCreateError)
@@ -419,7 +431,7 @@ InstanceList::InstListError InstanceList::loadList()
QString subDir = iter.next();
if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
continue;
-
+ QLOG_INFO() << "Loading MultiMC instance from " << subDir;
BaseInstance *instPtr = NULL;
auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
continueProcessInstance(instPtr, error, subDir, groupMap);
@@ -534,7 +546,7 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro
{
instPtr->setGroupInitial((*iter));
}
- QLOG_INFO() << "Loaded instance " << instPtr->name();
+ QLOG_INFO() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath();
instPtr->setParent(this);
m_instances.append(std::shared_ptr<BaseInstance>(instPtr));
connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp
index 6e0a92f0..029286dd 100644
--- a/logic/updater/DownloadUpdateTask.cpp
+++ b/logic/updater/DownloadUpdateTask.cpp
@@ -404,11 +404,10 @@ DownloadUpdateTask::processFileLists(NetJob *job,
{
auto cache_entry = MMC->metacache()->resolveEntry("root", entry.path);
QLOG_DEBUG() << "Updater will be in " << cache_entry->getFullPath();
- if(cache_entry->stale)
- {
- auto download = CacheDownload::make(QUrl(source.url), cache_entry);
- job->addNetAction(download);
- }
+ // force check.
+ cache_entry->stale = true;
+ auto download = CacheDownload::make(QUrl(source.url), cache_entry);
+ job->addNetAction(download);
}
else
{
diff --git a/logic/updater/NotificationChecker.cpp b/logic/updater/NotificationChecker.cpp
new file mode 100644
index 00000000..40367eac
--- /dev/null
+++ b/logic/updater/NotificationChecker.cpp
@@ -0,0 +1,120 @@
+#include "NotificationChecker.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "MultiMC.h"
+#include "logic/net/CacheDownload.h"
+#include "config.h"
+
+NotificationChecker::NotificationChecker(QObject *parent)
+ : QObject(parent), m_notificationsUrl(QUrl(NOTIFICATION_URL))
+{
+ // this will call checkForNotifications once the event loop is running
+ QMetaObject::invokeMethod(this, "checkForNotifications", Qt::QueuedConnection);
+}
+
+QUrl NotificationChecker::notificationsUrl() const
+{
+ return m_notificationsUrl;
+}
+void NotificationChecker::setNotificationsUrl(const QUrl &notificationsUrl)
+{
+ m_notificationsUrl = notificationsUrl;
+}
+
+QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const
+{
+ return m_entries;
+}
+
+void NotificationChecker::checkForNotifications()
+{
+ if (!m_notificationsUrl.isValid())
+ {
+ QLOG_ERROR() << "Failed to check for notifications. No notifications URL set."
+ << "If you'd like to use MultiMC's notification system, please pass the "
+ "URL to CMake at compile time.";
+ return;
+ }
+ if (m_checkJob)
+ {
+ return;
+ }
+ m_checkJob.reset(new NetJob("Checking for notifications"));
+ auto entry = MMC->metacache()->resolveEntry("root", "notifications.json");
+ entry->stale = true;
+ m_checkJob->addNetAction(m_download = CacheDownload::make(m_notificationsUrl, entry));
+ connect(m_download.get(), &CacheDownload::succeeded, this,
+ &NotificationChecker::downloadSucceeded);
+ m_checkJob->start();
+}
+
+void NotificationChecker::downloadSucceeded(int)
+{
+ m_entries.clear();
+
+ QFile file(m_download->m_output_file.fileName());
+ if (file.open(QFile::ReadOnly))
+ {
+ QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
+ for (auto it = root.begin(); it != root.end(); ++it)
+ {
+ QJsonObject obj = (*it).toObject();
+ NotificationEntry entry;
+ entry.id = obj.value("id").toDouble();
+ entry.message = obj.value("message").toString();
+ entry.channel = obj.value("channel").toString();
+ entry.buildtype = obj.value("buildtype").toString();
+ entry.from = obj.value("from").toString();
+ entry.to = obj.value("to").toString();
+ const QString type = obj.value("type").toString("critical");
+ if (type == "critical")
+ {
+ entry.type = NotificationEntry::Critical;
+ }
+ else if (type == "warning")
+ {
+ entry.type = NotificationEntry::Warning;
+ }
+ else if (type == "information")
+ {
+ entry.type = NotificationEntry::Information;
+ }
+ m_entries.append(entry);
+ }
+ }
+
+ m_checkJob.reset();
+
+ emit notificationCheckFinished();
+}
+
+bool NotificationChecker::NotificationEntry::applies() const
+{
+ bool channelApplies = channel.isEmpty() || channel == VERSION_CHANNEL;
+ bool buildtypeApplies = buildtype.isEmpty() || buildtype == VERSION_BUILD_TYPE;
+ bool fromApplies =
+ from.isEmpty() || from == FULL_VERSION_STR || !versionLessThan(FULL_VERSION_STR, from);
+ bool toApplies =
+ to.isEmpty() || to == FULL_VERSION_STR || !versionLessThan(to, FULL_VERSION_STR);
+ return channelApplies && buildtypeApplies && fromApplies && toApplies;
+}
+
+bool NotificationChecker::NotificationEntry::versionLessThan(const QString &v1,
+ const QString &v2)
+{
+ QStringList l1 = v1.split('.');
+ QStringList l2 = v2.split('.');
+ while (!l1.isEmpty() && !l2.isEmpty())
+ {
+ int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt();
+ int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt();
+ if (one != two)
+ {
+ return one < two;
+ }
+ }
+ return false;
+}
diff --git a/logic/updater/NotificationChecker.h b/logic/updater/NotificationChecker.h
new file mode 100644
index 00000000..20541757
--- /dev/null
+++ b/logic/updater/NotificationChecker.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <QObject>
+
+#include "logic/net/NetJob.h"
+#include "logic/net/CacheDownload.h"
+
+class NotificationChecker : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit NotificationChecker(QObject *parent = 0);
+
+ QUrl notificationsUrl() const;
+ void setNotificationsUrl(const QUrl &notificationsUrl);
+
+ struct NotificationEntry
+ {
+ int id;
+ QString message;
+ enum
+ {
+ Critical,
+ Warning,
+ Information
+ } type;
+ QString channel;
+ QString buildtype;
+ QString from;
+ QString to;
+ bool applies() const;
+ static bool versionLessThan(const QString &v1, const QString &v2);
+ };
+
+ QList<NotificationEntry> notificationEntries() const;
+
+public
+slots:
+ void checkForNotifications();
+
+private
+slots:
+ void downloadSucceeded(int);
+
+signals:
+ void notificationCheckFinished();
+
+private:
+ QList<NotificationEntry> m_entries;
+ QUrl m_notificationsUrl;
+ NetJobPtr m_checkJob;
+ CacheDownloadPtr m_download;
+};
diff --git a/tests/TestUtil.h b/tests/TestUtil.h
index fd25d24f..231ce7f6 100644
--- a/tests/TestUtil.h
+++ b/tests/TestUtil.h
@@ -39,7 +39,7 @@ int main(int argc, char *argv[]) \
{ \
char *argv_[] = { argv[0] _MMC_EXTRA_ARGV }; \
int argc_ = 1 + _MMC_EXTRA_ARGC; \
- MultiMC app(argc_, argv_, QDir::temp().absoluteFilePath("MultiMC_Test")); \
+ MultiMC app(argc_, argv_/*, QDir::temp().absoluteFilePath("MultiMC_Test")*/); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
TestObject tc; \
return QTest::qExec(&tc, argc, argv); \