void AccountListDialog::on_setDefaultBtn_clicked() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); if (selection.size() > 0) { QModelIndex selected = selection.first(); MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>(); m_accounts->setActiveAccount(account->username()); } }
void MainWindow::activeAccountChanged() { repopulateAccountsMenu(); MojangAccountPtr account = MMC->accounts()->activeAccount(); if (account != nullptr && account->username() != "") { const AccountProfile *profile = account->currentProfile(); if (profile != nullptr) { accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name)); return; } } // Set the icon to the "no account" icon. accountMenuButton->setIcon(QIcon::fromTheme("noaccount")); }
void MainWindow::activeAccountChanged() { repopulateAccountsMenu(); MojangAccountPtr account = MMC->accounts()->activeAccount(); if (account != nullptr && account->username() != "") { const AccountProfile *profile = account->currentProfile(); if (profile != nullptr) { accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name)); return; } } // Set the icon to the "no account" icon. accountMenuButton->setIcon( QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio)); }
void AccountListDialog::addAccount(const QString& errMsg) { // TODO: We can use the login dialog for this for now, but we'll have to make something better for it eventually. EditAccountDialog loginDialog(errMsg, this, EditAccountDialog::UsernameField | EditAccountDialog::PasswordField); loginDialog.exec(); if (loginDialog.result() == QDialog::Accepted) { QString username(loginDialog.username()); QString password(loginDialog.password()); MojangAccountPtr account = MojangAccountPtr(new MojangAccount(username)); ProgressDialog progDialog(this); AuthenticateTask authTask(account, password, &progDialog); if (progDialog.exec(&authTask)) { // Add the authenticated account to the accounts list. MojangAccountPtr account = authTask.getMojangAccount(); m_accounts->addAccount(account); // Grab associated player skins auto job = new NetJob("Player skins: " + account->username()); for(AccountProfile profile : account->profiles()) { auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png"); auto action = CacheDownload::make( QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"), meta); job->addNetAction(action); meta->stale = true; } job->start(); } } }
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; } 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) { Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(account.get() != nullptr, "launchInstance", "account is NULL"); proc = instance->prepareForLaunch(account); if (!proc) return; this->hide(); console = new ConsoleWindow(proc); connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded())); proc->setLogin(account); proc->launch(); }
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. ProgressDialog progDialog(this); auto task = account->login(passDialog.password()); progDialog.exec(task.get()); if (task->successful()) return true; else { // If the authentication task failed, recurse with the task's error message. return loginWithPassword(account, task->failReason()); } } return false; }
void MainWindow::repopulateAccountsMenu() { accountMenu->clear(); std::shared_ptr<MojangAccountList> accounts = MMC->accounts(); MojangAccountPtr active_account = accounts->activeAccount(); QString active_username = ""; if (active_account != nullptr) { active_username = accounts->activeAccount()->username(); } if (accounts->count() <= 0) { QAction *action = new QAction(tr("No accounts added!"), this); action->setEnabled(false); accountMenu->addAction(action); accountMenu->addSeparator(); } else { // TODO: Nicer way to iterate? for (int i = 0; i < accounts->count(); i++) { MojangAccountPtr account = accounts->at(i); // Styling hack QAction *section = new QAction(account->username(), this); section->setEnabled(false); accountMenu->addAction(section); for (auto profile : account->profiles()) { QAction *action = new QAction(profile.name, this); action->setData(account->username()); action->setCheckable(true); if (active_username == account->username()) { action->setChecked(true); } action->setIcon(SkinUtils::getFaceFromCache(profile.name)); accountMenu->addAction(action); connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount())); } accountMenu->addSeparator(); } } QAction *action = new QAction(tr("No Default Account"), this); action->setCheckable(true); action->setIcon(QIcon::fromTheme("noaccount")); action->setData(""); if (active_username.isEmpty()) { action->setChecked(true); } accountMenu->addAction(action); connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount())); accountMenu->addSeparator(); accountMenu->addAction(manageAccountsAction); }
void MainWindow::doLaunch(bool online, BaseProfilerFactory *profiler) { if (!m_selectedInstance) return; // Find an account to use. std::shared_ptr<MojangAccountList> accounts = MMC->accounts(); MojangAccountPtr account = accounts->activeAccount(); 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?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec(); if (reply == QMessageBox::Yes) { // Open the account manager. on_actionManageAccounts_triggered(); } } else if (account.get() == nullptr) { // 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); selectDialog.exec(); // Launch the instance with the selected account. account = selectDialog.selectedAccount(); // If the user said to use the account as default, do that. if (selectDialog.useAsGlobalDefault() && account.get() != nullptr) accounts->setActiveAccount(account->username()); } // if no account is selected, we bail if (!account.get()) return; // we try empty password first :) QString password; // we loop until the user succeeds in logging in or gives up bool tryagain = true; // the failure. the default failure. QString failReason = tr("Your account is currently not logged in. Please enter " "your password to log in again."); while (tryagain) { AuthSessionPtr session(new AuthSession()); session->wants_online = online; auto task = account->login(session, password); if (task) { // We'll need to validate the access token to make sure the account // is still logged in. ProgressDialog progDialog(this); if (online) progDialog.setSkipButton(true, tr("Play Offline")); progDialog.exec(task.get()); if (!task->successful()) { failReason = task->failReason(); } } switch (session->status) { case AuthSession::Undetermined: { QLOG_ERROR() << "Received undetermined session status during login. Bye."; tryagain = false; break; } case AuthSession::RequiresPassword: { EditAccountDialog passDialog(failReason, this, EditAccountDialog::PasswordField); if (passDialog.exec() == QDialog::Accepted) { password = passDialog.password(); } else { tryagain = false; } break; } case AuthSession::PlayableOffline: { // we ask the user for a player name bool ok = false; QString usedname = session->player_name; QString name = QInputDialog::getText(this, tr("Player name"), tr("Choose your offline mode player name."), QLineEdit::Normal, session->player_name, &ok); if (!ok) { tryagain = false; break; } if (name.length()) { usedname = name; } session->MakeOffline(usedname); // offline flavored game from here :3 } case AuthSession::PlayableOnline: { // update first if the server actually responded if (session->auth_server_online) { updateInstance(m_selectedInstance, session, profiler); } else { launchInstance(m_selectedInstance, session, profiler); } tryagain = false; } } } }
void MainWindow::doLaunch() { if (!m_selectedInstance) return; // Find an account to use. std::shared_ptr<MojangAccountList> accounts = MMC->accounts(); MojangAccountPtr account = accounts->activeAccount(); 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?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec(); if (reply == QMessageBox::Yes) { // Open the account manager. on_actionManageAccounts_triggered(); } } else if (account.get() == nullptr) { // 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); selectDialog.exec(); // Launch the instance with the selected account. account = selectDialog.selectedAccount(); // If the user said to use the account as default, do that. if (selectDialog.useAsGlobalDefault() && account.get() != nullptr) accounts->setActiveAccount(account->username()); } // if no account is selected, we bail if (!account.get()) return; QString failReason = tr("Your account is currently not logged in. Please enter " "your password to log in again."); // do the login. if the account has an access token, try to refresh it first. if (account->accountStatus() != NotVerified) { // We'll need to validate the access token to make sure the account is still logged in. ProgressDialog progDialog(this); progDialog.setSkipButton(true, tr("Play Offline")); auto task = account->login(); progDialog.exec(task.get()); auto status = account->accountStatus(); if (status != NotVerified) { updateInstance(m_selectedInstance, account); } else { if (!task->successful()) { failReason = task->failReason(); } if (loginWithPassword(account, failReason)) updateInstance(m_selectedInstance, account); } // in any case, revert from online to verified. account->downgrade(); } else { if (loginWithPassword(account, failReason)) { updateInstance(m_selectedInstance, account); account->downgrade(); } // in any case, revert from online to verified. account->downgrade(); } }
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); setWindowTitle(QString("MultiMC %1").arg(MMC->version().toString())); // OSX magic. // setUnifiedTitleAndToolBarOnMac(true); // The instance action toolbar customizations { // disabled until we have an instance selected ui->instanceToolBar->setEnabled(false); // the rename label is inside the rename tool button renameButton = new LabeledToolButton(); renameButton->setText("Instance Name"); renameButton->setToolTip(ui->actionRenameInstance->toolTip()); connect(renameButton, SIGNAL(clicked(bool)), SLOT(on_actionRenameInstance_triggered())); ui->instanceToolBar->insertWidget(ui->actionLaunchInstance, renameButton); ui->instanceToolBar->insertSeparator(ui->actionLaunchInstance); renameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); } // Add the news label to the news toolbar. { newsLabel = new QToolButton(); newsLabel->setIcon(QIcon(":/icons/toolbar/news")); newsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel); QObject::connect(newsLabel, &QAbstractButton::clicked, this, &MainWindow::newsButtonClicked); QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this, &MainWindow::updateNewsLabel); updateNewsLabel(); } // Create the instance list widget { view = new KCategorizedView(ui->centralWidget); drawer = new KCategoryDrawer(view); view->setSelectionMode(QAbstractItemView::SingleSelection); view->setCategoryDrawer(drawer); view->setCollapsibleBlocks(true); view->setViewMode(QListView::IconMode); view->setFlow(QListView::LeftToRight); view->setWordWrap(true); view->setMouseTracking(true); view->viewport()->setAttribute(Qt::WA_Hover); auto delegate = new ListViewDelegate(); view->setItemDelegate(delegate); view->setSpacing(10); view->setUniformItemWidths(true); // do not show ugly blue border on the mac view->setAttribute(Qt::WA_MacShowFocusRect, false); view->installEventFilter(this); proxymodel = new InstanceProxyModel(this); // proxymodel->setSortRole(KCategorizedSortFilterProxyModel::CategorySortRole); // proxymodel->setFilterRole(KCategorizedSortFilterProxyModel::CategorySortRole); // proxymodel->setDynamicSortFilter ( true ); // FIXME: instList should be global-ish, or at least not tied to the main window... // maybe the application itself? proxymodel->setSourceModel(MMC->instances().get()); proxymodel->sort(0); view->setFrameShape(QFrame::NoFrame); view->setModel(proxymodel); view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showInstanceContextMenu(const QPoint&))); ui->horizontalLayout->addWidget(view); } // The cat background { bool cat_enable = MMC->settings()->get("TheCat").toBool(); ui->actionCAT->setChecked(cat_enable); connect(ui->actionCAT, SIGNAL(toggled(bool)), SLOT(onCatToggled(bool))); setCatBackground(cat_enable); } // start instance when double-clicked connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(instanceActivated(const QModelIndex &))); // track the selection -- update the instance toolbar connect(view->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(instanceChanged(const QModelIndex &, const QModelIndex &))); // track icon changes and update the toolbar! connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString))); // model reset -> selection is invalid. All the instance pointers are wrong. // FIXME: stop using POINTERS everywhere connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad())); m_statusLeft = new QLabel(tr("No instance selected"), this); statusBar()->addPermanentWidget(m_statusLeft, 1); // Add "manage accounts" button, right align 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())); repopulateAccountsMenu(); accountMenuButton = new QToolButton(this); accountMenuButton->setText(tr("Accounts")); accountMenuButton->setMenu(accountMenu); accountMenuButton->setPopupMode(QToolButton::InstantPopup); accountMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); accountMenuButton->setIcon( QPixmap(":/icons/toolbar/noaccount").scaled(48, 48, Qt::KeepAspectRatio)); QWidgetAction *accountMenuButtonAction = new QWidgetAction(this); accountMenuButtonAction->setDefaultWidget(accountMenuButton); 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(); }); std::shared_ptr<MojangAccountList> accounts = MMC->accounts(); // TODO: Nicer way to iterate? for (int i = 0; i < accounts->count(); i++) { MojangAccountPtr account = accounts->at(i); if (account != nullptr) { auto job = new NetJob("Startup player skins: " + account->username()); 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); job->addNetAction(action); meta->stale = true; } connect(job, SIGNAL(succeeded()), SLOT(activeAccountChanged())); job->start(); } } // run the things that load and download other things... FIXME: this is NOT the place // FIXME: invisible actions in the background = NOPE. { if (!MMC->minecraftlist()->isLoaded()) { m_versionLoadTask = MMC->minecraftlist()->getLoadTask(); startTask(m_versionLoadTask); } if (!MMC->lwjgllist()->isLoaded()) { MMC->lwjgllist()->loadList(); } MMC->newsChecker()->reloadNews(); updateNewsLabel(); // set up the updater object. auto updater = MMC->updateChecker(); connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable); connect(updater.get(), &UpdateChecker::noUpdateFound, [this]() { CustomMessageBox::selectable( this, tr("No update found."), tr("No MultiMC update was found!\nYou are using the latest version."))->exec(); }); // 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(); if (!currentInstanceId.isNull()) { const QModelIndex index = MMC->instances()->getInstanceIndexById(currentInstanceId); if (index.isValid()) { const QModelIndex mappedIndex = proxymodel->mapFromSource(index); view->setCurrentIndex(mappedIndex); } else { view->setCurrentIndex(proxymodel->index(0, 0)); } } else { view->setCurrentIndex(proxymodel->index(0, 0)); } // removing this looks stupid view->setFocus(); }