package io.gitlab.jfronny.inceptum.gtk.control import io.gitlab.jfronny.commons.io.JFiles import io.gitlab.jfronny.commons.ref.R import io.gitlab.jfronny.inceptum.common.MetaHolder import io.gitlab.jfronny.inceptum.common.Utils import io.gitlab.jfronny.inceptum.gtk.GtkMenubar import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder import io.gitlab.jfronny.inceptum.gtk.util.I18n import io.gitlab.jfronny.inceptum.gtk.util.fixSubtitle import io.gitlab.jfronny.inceptum.gtk.util.get import io.gitlab.jfronny.inceptum.gtk.util.margin import io.gitlab.jfronny.inceptum.gtk.window.settings.instance.InstanceSettingsWindow import io.gitlab.jfronny.inceptum.launcher.LauncherEnv import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceNameTool import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType import org.gnome.adw.ActionRow import org.gnome.gdk.Gdk import org.gnome.gio.Menu import org.gnome.gtk.* import java.io.IOException class InstanceListEntryFactory( private val app: Application?, private val instanceList: List ) : KSignalListItemFactory() { override fun setup(): ActionRow { val thumbnail = InstanceThumbnail() thumbnail.name = "inceptum-thumbnail" val launch = Button.newFromIconName("media-playback-start-symbolic") launch.addCssClass("flat") launch.name = "inceptum-launch" launch.tooltipText = I18n["instance.launch"] val menu = MenuButton() menu.addCssClass("flat") menu.iconName = "view-more-symbolic" menu.setPopover(PopoverMenu.newFromModel(Menu())) val row = ActionRow() row.margin = 8 row.name = "inceptum-row" row.removeCssClass("activatable") //TODO remove this workaround if a better way to support opening the menu is found row.addPrefix(thumbnail) row.addSuffix(launch) row.addSuffix(menu) row.fixSubtitle() val rightClicked = GestureClick() rightClicked.button = Gdk.BUTTON_SECONDARY rightClicked.onPressed { nPress, _, _ -> if (nPress == 1) menu.emitActivate() } row.addController(rightClicked) return row } override fun BindContext.bind(widget: ActionRow, data: Decomposed) { if (data.instance?.isLocked ?: true) { data.item.activatable = false widget.subtitle = if (data.instance?.isRunningLocked ?: false) I18n["instance.launch.locked.running"] else I18n["instance.launch.locked.setup"] } widget.title = data.instance.toString() data.thumbnail.bind(data.instance!!) val menuBuilder = MenuBuilder(data.popoverMenu, data.instanceId) val launchSection = menuBuilder.literalSection("launch", null) val kill = launchSection.literalButton("kill", I18n["instance.kill"]) { //TODO test LauncherEnv.showOkCancel(I18n["instance.kill.prompt"], I18n["instance.kill.details"]) { if (!data.instance.kill()) LauncherEnv.showError(I18n["instance.kill.fail"], I18n["failed"]) } } kill.enabled = data.instance.isRunningLocked launchSection.literalButton( "launch.client", I18n["instance.launch.client"] ) { GtkMenubar.launch(data.instance, LaunchType.Client) }.iconName = "media-playback-start-symbolic" launchSection.literalButton( "launch.server", I18n["instance.launch.server"] ) { GtkMenubar.launch(data.instance, LaunchType.Server) }.iconName = "network-server-symbolic" val settingsSection = menuBuilder.literalSection("settings", null) settingsSection.literalButton("settings", I18n["instance.settings"]) { //TODO keep track of properties windows and don't allow opening two InstanceSettingsWindow(app, data.instance).visible = true }.iconName = "document-edit-symbolic" settingsSection.literalButton( "directory", I18n["instance.directory"] ) { Utils.openFile(data.instance.path.toFile()) }.iconName = "folder-symbolic" settingsSection.literalButton("copy", I18n["instance.copy"]) { LauncherEnv.getInput( I18n["instance.copy.prompt"], I18n["instance.copy.details"], InstanceNameTool.getNextValid(data.instance.name), { s: String? -> try { JFiles.copyRecursive( data.instance.path, MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(s)) ) } catch (e: IOException) { LauncherEnv.showError(I18n["instance.copy.fail"], e) } }) { R.nop() } }.iconName = "edit-copy-symbolic" settingsSection.literalButton("delete", I18n["instance.delete"]) { LauncherEnv.showOkCancel( I18n["instance.delete.confirm"], I18n["instance.delete.confirm.title"] ) { try { JFiles.deleteRecursive(data.instance.path) } catch (e: IOException) { LauncherEnv.showError(I18n["instance.delete.fail"], e) } } }.iconName = "edit-delete-symbolic" registerForUnbind(data.launch.onClicked { GtkMenubar.launch(data.instance, LaunchType.Client) }) } override fun UnbindContext.unbind(widget: ActionRow, data: Decomposed) { data.popoverMenu.insertActionGroup(data.instanceId, null) } override fun ActionContext.castWidget(widget: Widget): ActionRow = widget as ActionRow override fun ActionContext.lookup(id: String, widget: ActionRow): Decomposed { val instance = instanceList[id] val prefixes = widget.firstChild!!.firstChild as Box val suffixes = widget.firstChild!!.lastChild as Box val thumbnail = InstanceThumbnail.castFrom(prefixes.firstChild as Stack) val launch = suffixes.firstChild as Button val menuButton = launch.nextSibling as MenuButton val popoverMenu = menuButton.popover as PopoverMenu return Decomposed(listItem, id, instance, thumbnail, launch, popoverMenu) } class Decomposed( val item: ListItem, val instanceId: String, val instance: Instance?, val thumbnail: InstanceThumbnail, val launch: Button, val popoverMenu: PopoverMenu ) }