Exemplo n.º 1
0
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());
	}	
}
Exemplo n.º 2
0
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"));
}
Exemplo n.º 3
0
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));
}
Exemplo n.º 4
0
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();
		}
	}
}
Exemplo n.º 5
0
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());
}
Exemplo n.º 6
0
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();
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
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;
		}
		}
	}
}
Exemplo n.º 10
0
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();
	}
}
Exemplo n.º 11
0
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();
}