package io.gitlab.jfronny.inceptum.gtk import io.gitlab.jfronny.commons.OSUtils import io.gitlab.jfronny.commons.io.JFiles import io.gitlab.jfronny.inceptum.common.MetaHolder import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder import io.gitlab.jfronny.inceptum.gtk.util.I18n import io.gitlab.jfronny.inceptum.gtk.util.Log import io.gitlab.jfronny.inceptum.gtk.window.AboutWindow import io.gitlab.jfronny.inceptum.gtk.window.create.NewInstanceWindow import io.gitlab.jfronny.inceptum.gtk.window.dialog.MicrosoftLoginDialog import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog import io.gitlab.jfronny.inceptum.gtk.window.settings.launcher.LauncherSettingsWindow import io.gitlab.jfronny.inceptum.launcher.LauncherEnv import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount import io.gitlab.jfronny.inceptum.launcher.system.importer.Importers import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceList import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps import io.gitlab.jfronny.inceptum.launcher.util.ProcessState import org.gnome.gio.Cancellable import org.gnome.gio.Menu import org.gnome.gtk.* import java.awt.Toolkit import java.awt.datatransfer.DataFlavor import java.io.IOException import java.nio.file.Path object GtkMenubar { @JvmField var newMenu: MenuBuilder? = null @JvmField var accountsMenu: MenuBuilder? = null @JvmField var launchMenu: MenuBuilder? = null @JvmStatic fun create(app: Application) { val menu = MenuBuilder(app) val file = menu.submenu("file") newMenu = file.submenu("new") generateNewMenu(app) file.button("redownload") { val state = ProcessState(3 + Steps.STEPS.size * InstanceList.size(), "Initializing") ProcessStateWatcherDialog.show( GtkEnvBackend.dialogParent, "Reloading data", "Could not execute refresh task", state ) { state.incrementStep("Clearing cache directories") JFiles.clearDirectory(MetaHolder.ASSETS_DIR) JFiles.clearDirectory(MetaHolder.LIBRARIES_DIR) { path: Path -> !path.startsWith(MetaHolder.LIBRARIES_DIR.resolve("io/gitlab/jfronny")) } JFiles.clearDirectory(MetaHolder.NATIVES_DIR) { path: Path -> !path.startsWith(MetaHolder.NATIVES_DIR.resolve("forceload")) } JFiles.clearDirectory(MetaHolder.CACHE_DIR) if (state.isCancelled) return@show state.incrementStep("Reloading instance list") InstanceList.reset() InstanceList.forEach { instance: Instance? -> if (state.isCancelled) return@forEach Steps.reDownload(instance, state) } } } file.button("exit") { app.quit() } launchMenu = menu.submenu("launch") generateLaunchMenu(app) accountsMenu = MenuBuilder(app, Menu(), "account") // this should ideally be menu.submenu("account"), but that causes a segfault generateAccountsMenu(app) val help = menu.submenu("help") help.button("about") { AboutWindow.createAndShow() } help.button("log") { //TODO } } @JvmStatic fun generateNewMenu(app: Application) { newMenu!!.clear() newMenu!!.button("new") { NewInstanceWindow(app).visible = true } newMenu!!.button("file") { val dialog = FileChooserNative( I18n["menu.file.new.file"], GtkEnvBackend.dialogParent, FileChooserAction.OPEN, "_" + I18n["select"], "_" + I18n["cancel"] ) val filter = FileFilter() filter.addPattern("*.zip") filter.addPattern("*.mrpack") dialog.addFilter(filter) dialog.onResponse { responseId: Int -> if (responseId == ResponseType.ACCEPT.value) { val file = dialog.file!!.path if (file == null) { LauncherEnv.showError("The path returned by the file dialog is null", "Could not import") return@onResponse } val state = ProcessState(Importers.MAX_STEPS, "Initializing") ProcessStateWatcherDialog.show( GtkEnvBackend.dialogParent, I18n["menu.file.new.file"], I18n["menu.file.new.file.error"], state ) { Importers.importPack(Path.of(file), state) } } } dialog.show() } newMenu!!.button("url") { readClipboard { clipboard -> LauncherEnv.getInput( I18n["menu.file.new.url"], I18n["menu.file.new.url.details"], clipboard ?: "", { s: String? -> val state = ProcessState(Importers.MAX_STEPS, "Initializing") ProcessStateWatcherDialog.show( GtkEnvBackend.dialogParent, I18n["menu.file.new.url"], I18n["menu.file.new.url.error"], state ) { Importers.importPack(s, state) } }, {}) } } } private fun readClipboard(continuation: (String?) -> Unit) { if (OSUtils.TYPE == OSUtils.Type.LINUX) { val clipboard = GtkEnvBackend.dialogParent!!.display.clipboard clipboard.readTextAsync(Cancellable()) { _, res, _ -> continuation(clipboard.readTextFinish(res)) } } else { continuation(Toolkit.getDefaultToolkit().systemClipboard.getData(DataFlavor.stringFlavor) as String?) } } @JvmStatic fun generateLaunchMenu(app: Application) { launchMenu!!.clear() try { InstanceList.forEach { entry: Instance -> launchMenu!!.literalButton(entry.id + ".launch", entry.toString()) { launch(entry, LaunchType.Client) } } } catch (e: IOException) { Log.error("Could not generate launch menu", e) } } @JvmStatic fun launch(instance: Instance, launchType: LaunchType) { if (instance.isSetupLocked) { LauncherEnv.showError(I18n["instance.launch.locked.setup"], I18n["instance.launch.locked"]) } else if (instance.isRunningLocked) { LauncherEnv.showOkCancel( I18n["instance.launch.locked.running"], I18n["instance.launch.locked"] ) { forceLaunch(instance, launchType) } } else forceLaunch(instance, launchType) } private fun forceLaunch(instance: Instance, launchType: LaunchType) { val state = Steps.createProcessState() ProcessStateWatcherDialog.show( GtkEnvBackend.dialogParent, I18n["instance.launch.title"], I18n["instance.launch.error"], state ) { try { Steps.reDownload(instance, state) } catch (e: IOException) { Log.error("Could not fetch instance, trying to start anyways", e) } if (state.isCancelled) return@show state.updateStep("Starting Game") try { if (launchType == LaunchType.Client) InstanceLauncher.launchClient(instance) else InstanceLauncher.launch( instance, launchType, false, AccountManager.NULL_AUTH ) } catch (e: Throwable) { LauncherEnv.showError("Could not start instance", e) } } } @JvmStatic fun generateAccountsMenu(app: Application) { accountsMenu!!.clear() accountsMenu!!.button("new") { MicrosoftLoginDialog(GtkEnvBackend.dialogParent).visible = true } accountsMenu!!.button("manage") { val window = LauncherSettingsWindow(app) window.activePage = "settings.accounts" window.visible = true } val accounts: MutableList = ArrayList(AccountManager.getAccounts()) accounts.add(null) accountsMenu!!.literalRadio( "account", accounts[AccountManager.getSelectedIndex()], accounts, { _, acc: MicrosoftAccount? -> if (acc == null) return@literalRadio I18n["account.none"] acc.minecraftUsername }) { account: MicrosoftAccount? -> AccountManager.switchAccount(account) } } }