Improve group changing, update instance on version change
Gives a list of existing groups to choose from. Instances are updated as long as there is at least one valid account.
This commit is contained in:
parent
5a3043398e
commit
dd9e04000c
@ -20,7 +20,6 @@
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "ui_MainWindow.h"
|
||||
#include "keyring.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
@ -177,14 +176,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
statusBar()->addPermanentWidget(m_statusRight, 0);
|
||||
|
||||
// Add "manage accounts" button, right align
|
||||
QWidget* spacer = new QWidget();
|
||||
QWidget *spacer = new QWidget();
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
ui->mainToolBar->addWidget(spacer);
|
||||
|
||||
accountMenu = new QMenu(this);
|
||||
manageAccountsAction = new QAction(tr("Manage Accounts"), this);
|
||||
manageAccountsAction->setCheckable(false);
|
||||
connect(manageAccountsAction, SIGNAL(triggered(bool)), this, SLOT(on_actionManageAccounts_triggered()));
|
||||
connect(manageAccountsAction, SIGNAL(triggered(bool)), this,
|
||||
SLOT(on_actionManageAccounts_triggered()));
|
||||
|
||||
repopulateAccountsMenu();
|
||||
|
||||
@ -193,7 +193,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
accountMenuButton->setMenu(accountMenu);
|
||||
accountMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
accountMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
accountMenuButton->setIcon(QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
accountMenuButton->setIcon(
|
||||
QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
|
||||
QWidgetAction *accountMenuButtonAction = new QWidgetAction(this);
|
||||
accountMenuButtonAction->setDefaultWidget(accountMenuButton);
|
||||
@ -201,26 +202,28 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
ui->mainToolBar->addAction(accountMenuButtonAction);
|
||||
|
||||
// Update the menu when the active account changes.
|
||||
// Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit. Template hell sucks...
|
||||
connect(MMC->accounts().get(), &MojangAccountList::activeAccountChanged, [this] { activeAccountChanged(); });
|
||||
connect(MMC->accounts().get(), &MojangAccountList::listChanged, [this] { repopulateAccountsMenu(); });
|
||||
// Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit.
|
||||
// Template hell sucks...
|
||||
connect(MMC->accounts().get(), &MojangAccountList::activeAccountChanged, [this]
|
||||
{ activeAccountChanged(); });
|
||||
connect(MMC->accounts().get(), &MojangAccountList::listChanged, [this]
|
||||
{ repopulateAccountsMenu(); });
|
||||
|
||||
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
|
||||
|
||||
// TODO: Nicer way to iterate?
|
||||
for(int i = 0; i < accounts->count(); i++)
|
||||
for (int i = 0; i < accounts->count(); i++)
|
||||
{
|
||||
MojangAccountPtr account = accounts->at(i);
|
||||
if(account != nullptr)
|
||||
if (account != nullptr)
|
||||
{
|
||||
auto job = new NetJob("Startup player skins: " + account->username());
|
||||
|
||||
for(AccountProfile profile : account->profiles())
|
||||
for (AccountProfile profile : account->profiles())
|
||||
{
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"),
|
||||
meta);
|
||||
QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"), meta);
|
||||
job->addNetAction(action);
|
||||
meta->stale = true;
|
||||
}
|
||||
@ -245,9 +248,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
|
||||
// set up the updater object.
|
||||
auto updater = MMC->updateChecker();
|
||||
QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable);
|
||||
QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this,
|
||||
&MainWindow::updateAvailable);
|
||||
// if automatic update checks are allowed, start one.
|
||||
if(MMC->settings()->get("AutoUpdate").toBool())
|
||||
if (MMC->settings()->get("AutoUpdate").toBool())
|
||||
on_actionCheckUpdate_triggered();
|
||||
}
|
||||
|
||||
@ -321,7 +325,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
QAction *action = new QAction(profile.name, this);
|
||||
action->setData(account->username());
|
||||
action->setCheckable(true);
|
||||
if(active_username == account->username())
|
||||
if (active_username == account->username())
|
||||
{
|
||||
action->setChecked(true);
|
||||
}
|
||||
@ -339,7 +343,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
action->setCheckable(true);
|
||||
action->setIcon(QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
action->setData("");
|
||||
if(active_username.isEmpty())
|
||||
if (active_username.isEmpty())
|
||||
{
|
||||
action->setChecked(true);
|
||||
}
|
||||
@ -356,10 +360,11 @@ void MainWindow::repopulateAccountsMenu()
|
||||
*/
|
||||
void MainWindow::changeActiveAccount()
|
||||
{
|
||||
QAction* sAction = (QAction*) sender();
|
||||
QAction *sAction = (QAction *)sender();
|
||||
// Profile's associated Mojang username
|
||||
// Will need to change when profiles are properly implemented
|
||||
if (sAction->data().type() != QVariant::Type::String) return;
|
||||
if (sAction->data().type() != QVariant::Type::String)
|
||||
return;
|
||||
|
||||
QVariant data = sAction->data();
|
||||
QString id = "";
|
||||
@ -390,7 +395,8 @@ void MainWindow::activeAccountChanged()
|
||||
}
|
||||
|
||||
// Set the icon to the "no account" icon.
|
||||
accountMenuButton->setIcon(QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
accountMenuButton->setIcon(
|
||||
QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio));
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
|
||||
@ -426,26 +432,28 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
|
||||
void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
|
||||
{
|
||||
UpdateDialog dlg;
|
||||
UpdateAction action = (UpdateAction) dlg.exec();
|
||||
switch(action)
|
||||
UpdateAction action = (UpdateAction)dlg.exec();
|
||||
switch (action)
|
||||
{
|
||||
case UPDATE_LATER:
|
||||
QLOG_INFO() << "Update will be installed later.";
|
||||
break;
|
||||
case UPDATE_NOW:
|
||||
downloadUpdates(repo, versionId);
|
||||
break;
|
||||
case UPDATE_ONEXIT:
|
||||
downloadUpdates(repo, versionId, true);
|
||||
break;
|
||||
case UPDATE_LATER:
|
||||
QLOG_INFO() << "Update will be installed later.";
|
||||
break;
|
||||
case UPDATE_NOW:
|
||||
downloadUpdates(repo, versionId);
|
||||
break;
|
||||
case UPDATE_ONEXIT:
|
||||
downloadUpdates(repo, versionId, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit)
|
||||
{
|
||||
QLOG_INFO() << "Downloading updates.";
|
||||
// TODO: If the user chooses to update on exit, we should download updates in the background.
|
||||
// Doing so is a bit complicated, because we'd have to make sure it finished downloading before actually exiting MultiMC.
|
||||
// TODO: If the user chooses to update on exit, we should download updates in the
|
||||
// background.
|
||||
// Doing so is a bit complicated, because we'd have to make sure it finished downloading
|
||||
// before actually exiting MultiMC.
|
||||
ProgressDialog updateDlg(this);
|
||||
DownloadUpdateTask updateTask(repo, versionId, &updateDlg);
|
||||
// If the task succeeds, install the updates.
|
||||
@ -539,15 +547,15 @@ void MainWindow::on_actionAddInstance_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
|
||||
MojangAccountPtr account = accounts->activeAccount();
|
||||
if(account.get() != nullptr && account->accountStatus() != NotVerified)
|
||||
if (MMC->accounts()->anyAccountIsValid())
|
||||
{
|
||||
ProgressDialog loadDialog(this);
|
||||
auto update = newInstance->doUpdate(false);
|
||||
connect(update.get(), &Task::failed , [this](QString reason) {
|
||||
connect(update.get(), &Task::failed, [this](QString reason)
|
||||
{
|
||||
QString error = QString("Instance load failed: %1").arg(reason);
|
||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)
|
||||
->show();
|
||||
});
|
||||
loadDialog.exec(update.get());
|
||||
}
|
||||
@ -625,8 +633,14 @@ void MainWindow::on_actionChangeInstGroup_triggered()
|
||||
|
||||
bool ok = false;
|
||||
QString name(m_selectedInstance->group());
|
||||
name = QInputDialog::getText(this, tr("Group name"), tr("Enter a new group name."),
|
||||
QLineEdit::Normal, name, &ok);
|
||||
auto groups = MMC->instances()->getGroups();
|
||||
groups.insert(0,"");
|
||||
groups.sort(Qt::CaseInsensitive);
|
||||
int foo = groups.indexOf(name);
|
||||
|
||||
name = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups,
|
||||
foo, true, &ok);
|
||||
name = name.simplified();
|
||||
if (ok)
|
||||
m_selectedInstance->setGroupPost(name);
|
||||
}
|
||||
@ -810,9 +824,11 @@ void MainWindow::doLaunch()
|
||||
if (accounts->count() <= 0)
|
||||
{
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(this, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
this, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
|
||||
if (reply == QMessageBox::Yes)
|
||||
@ -825,7 +841,7 @@ void MainWindow::doLaunch()
|
||||
{
|
||||
// If no default account is set, ask the user which one to use.
|
||||
AccountSelectDialog selectDialog(tr("Which account would you like to use?"),
|
||||
AccountSelectDialog::GlobalDefaultCheckbox, this);
|
||||
AccountSelectDialog::GlobalDefaultCheckbox, this);
|
||||
|
||||
selectDialog.exec();
|
||||
|
||||
@ -842,7 +858,7 @@ void MainWindow::doLaunch()
|
||||
return;
|
||||
|
||||
// do the login. if the account has an access token, try to refresh it first.
|
||||
if(account->accountStatus() != NotVerified)
|
||||
if (account->accountStatus() != NotVerified)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account is still logged in.
|
||||
ProgressDialog progDialog(this);
|
||||
@ -851,7 +867,7 @@ void MainWindow::doLaunch()
|
||||
progDialog.exec(task.get());
|
||||
|
||||
auto status = account->accountStatus();
|
||||
if(status != NotVerified)
|
||||
if (status != NotVerified)
|
||||
{
|
||||
updateInstance(m_selectedInstance, account);
|
||||
}
|
||||
@ -859,20 +875,22 @@ void MainWindow::doLaunch()
|
||||
account->downgrade();
|
||||
return;
|
||||
}
|
||||
if (loginWithPassword(account, tr("Your account is currently not logged in. Please enter your password to log in again.")))
|
||||
if (loginWithPassword(account, tr("Your account is currently not logged in. Please enter "
|
||||
"your password to log in again.")))
|
||||
updateInstance(m_selectedInstance, account);
|
||||
}
|
||||
|
||||
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString& errorMsg)
|
||||
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
|
||||
{
|
||||
EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
// To refresh the token, we just create an authenticate task with the given account and the user's password.
|
||||
// To refresh the token, we just create an authenticate task with the given account and
|
||||
// the user's password.
|
||||
ProgressDialog progDialog(this);
|
||||
auto task = account->login(passDialog.password());
|
||||
progDialog.exec(task.get());
|
||||
if(task->successful())
|
||||
if (task->successful())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
@ -883,21 +901,20 @@ bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString& erro
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::updateInstance(BaseInstance* instance, MojangAccountPtr account)
|
||||
void MainWindow::updateInstance(BaseInstance *instance, MojangAccountPtr account)
|
||||
{
|
||||
bool only_prepare = account->accountStatus() != Online;
|
||||
auto updateTask = instance->doUpdate(only_prepare);
|
||||
if (!updateTask)
|
||||
{
|
||||
launchInstance(instance, account);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgressDialog tDialog(this);
|
||||
connect(updateTask.get(), &Task::succeeded, [this, instance, account] { launchInstance(instance, account); });
|
||||
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
||||
tDialog.exec(updateTask.get());
|
||||
}
|
||||
ProgressDialog tDialog(this);
|
||||
connect(updateTask.get(), &Task::succeeded, [this, instance, account]
|
||||
{ launchInstance(instance, account); });
|
||||
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
||||
tDialog.exec(updateTask.get());
|
||||
}
|
||||
|
||||
void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account)
|
||||
@ -985,13 +1002,24 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
||||
this, tr("Are you sure?"),
|
||||
tr("This will remove any library/version customization you did previously. "
|
||||
"This includes things like Forge install and similar."),
|
||||
QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Abort)->exec();
|
||||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
|
||||
QMessageBox::Abort)->exec();
|
||||
|
||||
if (result != QMessageBox::Ok)
|
||||
return;
|
||||
}
|
||||
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
||||
}
|
||||
if (!MMC->accounts()->anyAccountIsValid())
|
||||
return;
|
||||
auto updateTask = m_selectedInstance->doUpdate(false /*only_prepare*/);
|
||||
if (!updateTask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ProgressDialog tDialog(this);
|
||||
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
||||
tDialog.exec(updateTask.get());
|
||||
}
|
||||
|
||||
void MainWindow::on_actionChangeInstLWJGLVersion_triggered()
|
||||
|
@ -149,7 +149,7 @@ public:
|
||||
*/
|
||||
virtual SettingsObject &settings() const;
|
||||
|
||||
/// returns a valid update task if update is needed, NULL otherwise
|
||||
/// returns a valid update task
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) = 0;
|
||||
|
||||
/// returns a valid minecraft process, ready for launch with the given account.
|
||||
|
@ -54,11 +54,9 @@ void OneSixUpdate::executeTask()
|
||||
|
||||
if (m_only_prepare)
|
||||
{
|
||||
if (m_inst->shouldUpdate())
|
||||
{
|
||||
emitFailed("Unable to update instance in offline mode.");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* FIXME: in offline mode, do not proceed!
|
||||
*/
|
||||
setStatus("Testing the Java installation.");
|
||||
QString java_path = m_inst->settings().get("JavaPath").toString();
|
||||
|
||||
|
@ -414,3 +414,13 @@ void MojangAccountList::setListFilePath(QString path, bool autosave)
|
||||
m_listFilePath = path;
|
||||
m_autosave = autosave;
|
||||
}
|
||||
|
||||
bool MojangAccountList::anyAccountIsValid()
|
||||
{
|
||||
for(auto account:m_accounts)
|
||||
{
|
||||
if(account->accountStatus() != NotVerified)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -126,6 +126,11 @@ public:
|
||||
* If the username given is an empty string, sets the active account to nothing.
|
||||
*/
|
||||
virtual void setActiveAccount(const QString &username);
|
||||
|
||||
/*!
|
||||
* Returns true if any of the account is at least Validated
|
||||
*/
|
||||
bool anyAccountIsValid();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
|
@ -117,6 +117,11 @@ void InstanceList::groupChanged()
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
QStringList InstanceList::getGroups()
|
||||
{
|
||||
return m_groups.toList();
|
||||
}
|
||||
|
||||
void InstanceList::saveGroupList()
|
||||
{
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
@ -126,7 +131,7 @@ void InstanceList::saveGroupList()
|
||||
if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
// An error occurred. Ignore it.
|
||||
QLOG_ERROR() << "Failed to read instance group file.";
|
||||
QLOG_ERROR() << "Failed to save instance group file.";
|
||||
return;
|
||||
}
|
||||
QTextStream out(&groupFile);
|
||||
@ -137,6 +142,10 @@ void InstanceList::saveGroupList()
|
||||
QString group = instance->group();
|
||||
if (group.isEmpty())
|
||||
continue;
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
m_groups.insert(group);
|
||||
|
||||
if (!groupMap.count(group))
|
||||
{
|
||||
QSet<QString> set;
|
||||
@ -253,6 +262,9 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
m_groups.insert(groupName);
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include "categorizedsortfilterproxymodel.h"
|
||||
#include <QIcon>
|
||||
|
||||
@ -97,6 +98,9 @@ public:
|
||||
InstancePtr getInstanceById(QString id) const;
|
||||
|
||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||
|
||||
// FIXME: instead of iterating through all instances and forming a set, keep the set around
|
||||
QStringList getGroups();
|
||||
signals:
|
||||
void dataIsInvalid();
|
||||
|
||||
@ -116,6 +120,7 @@ private:
|
||||
protected:
|
||||
QString m_instDir;
|
||||
QList<InstancePtr> m_instances;
|
||||
QSet<QString> m_groups;
|
||||
};
|
||||
|
||||
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
||||
|
Loading…
x
Reference in New Issue
Block a user