mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: telnet support login scripts
This commit is contained in:
215
src/main/kotlin/app/termora/LoginScriptPanel.kt
Normal file
215
src/main/kotlin/app/termora/LoginScriptPanel.kt
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatTable
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatToolBar
|
||||||
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
|
import com.jgoodies.forms.layout.FormLayout
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Dimension
|
||||||
|
import java.awt.Window
|
||||||
|
import java.awt.event.ActionEvent
|
||||||
|
import javax.swing.*
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer
|
||||||
|
import javax.swing.table.DefaultTableModel
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
internal class LoginScriptPanel(private val loginScripts: MutableList<LoginScript>) : JPanel(BorderLayout()) {
|
||||||
|
|
||||||
|
private val owner get() = SwingUtilities.getWindowAncestor(this)
|
||||||
|
|
||||||
|
private val addBtn = JButton(I18n.getString("termora.new-host.tunneling.add"))
|
||||||
|
private val editBtn = JButton(I18n.getString("termora.new-host.tunneling.edit"))
|
||||||
|
private val deleteBtn = JButton(I18n.getString("termora.new-host.tunneling.delete"))
|
||||||
|
private val table = FlatTable()
|
||||||
|
private val model = object : DefaultTableModel() {
|
||||||
|
override fun getRowCount(): Int {
|
||||||
|
return loginScripts.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCellEditable(row: Int, column: Int): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRow(loginScript: LoginScript) {
|
||||||
|
val rowCount = super.getRowCount()
|
||||||
|
loginScripts.add(loginScript)
|
||||||
|
super.fireTableRowsInserted(rowCount, rowCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValueAt(row: Int, column: Int): Any {
|
||||||
|
val loginScript = loginScripts[row]
|
||||||
|
return when (column) {
|
||||||
|
0 -> loginScript.expect
|
||||||
|
1 -> loginScript.send
|
||||||
|
else -> super.getValueAt(row, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
|
||||||
|
addBtn.isFocusable = false
|
||||||
|
editBtn.isFocusable = false
|
||||||
|
deleteBtn.isFocusable = false
|
||||||
|
|
||||||
|
deleteBtn.isEnabled = false
|
||||||
|
editBtn.isEnabled = false
|
||||||
|
|
||||||
|
val scrollPane = JScrollPane(table)
|
||||||
|
|
||||||
|
model.addColumn(I18n.getString("termora.new-host.terminal.expect"))
|
||||||
|
model.addColumn(I18n.getString("termora.new-host.terminal.send"))
|
||||||
|
|
||||||
|
table.putClientProperty(
|
||||||
|
FlatClientProperties.STYLE, mapOf(
|
||||||
|
"showHorizontalLines" to true,
|
||||||
|
"showVerticalLines" to true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
table.model = model
|
||||||
|
table.autoResizeMode = JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
|
||||||
|
table.setDefaultRenderer(
|
||||||
|
Any::class.java,
|
||||||
|
DefaultTableCellRenderer().apply { horizontalAlignment = SwingConstants.CENTER })
|
||||||
|
table.fillsViewportHeight = true
|
||||||
|
scrollPane.border = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createEmptyBorder(4, 0, 4, 0),
|
||||||
|
BorderFactory.createMatteBorder(1, 1, 1, 1, DynamicColor.Companion.BorderColor)
|
||||||
|
)
|
||||||
|
table.border = BorderFactory.createEmptyBorder()
|
||||||
|
|
||||||
|
|
||||||
|
val box = Box.createHorizontalBox()
|
||||||
|
box.add(addBtn)
|
||||||
|
box.add(Box.createHorizontalStrut(4))
|
||||||
|
box.add(editBtn)
|
||||||
|
box.add(Box.createHorizontalStrut(4))
|
||||||
|
box.add(deleteBtn)
|
||||||
|
|
||||||
|
add(scrollPane, BorderLayout.CENTER)
|
||||||
|
add(box, BorderLayout.SOUTH)
|
||||||
|
border = BorderFactory.createEmptyBorder(6, 8, 6, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun initEvents() {
|
||||||
|
addBtn.addActionListener(object : AbstractAction() {
|
||||||
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
|
val dialog = LoginScriptDialog(owner)
|
||||||
|
dialog.isVisible = true
|
||||||
|
model.addRow(dialog.loginScript ?: return)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
editBtn.addActionListener(object : AbstractAction() {
|
||||||
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
|
val dialog = LoginScriptDialog(owner, loginScripts[table.selectedRow])
|
||||||
|
dialog.isVisible = true
|
||||||
|
loginScripts[table.selectedRow] = dialog.loginScript ?: return
|
||||||
|
model.fireTableRowsUpdated(table.selectedRow, table.selectedRow)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
deleteBtn.addActionListener(object : AbstractAction() {
|
||||||
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
|
val rows = table.selectedRows
|
||||||
|
if (rows.isEmpty()) return
|
||||||
|
rows.sortDescending()
|
||||||
|
for (row in rows) {
|
||||||
|
loginScripts.removeAt(row)
|
||||||
|
model.fireTableRowsDeleted(row, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
table.selectionModel.addListSelectionListener {
|
||||||
|
deleteBtn.isEnabled = table.selectedRowCount > 0
|
||||||
|
editBtn.isEnabled = deleteBtn.isEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class LoginScriptDialog(
|
||||||
|
owner: Window,
|
||||||
|
var loginScript: LoginScript? = null
|
||||||
|
) : DialogWrapper(owner) {
|
||||||
|
private val formMargin = "4dlu"
|
||||||
|
private val expectTextField = OutlineTextField()
|
||||||
|
private val sendTextField = OutlineTextField()
|
||||||
|
private val regexToggleBtn = JToggleButton(Icons.regex)
|
||||||
|
.apply { toolTipText = I18n.getString("termora.regex") }
|
||||||
|
private val matchCaseToggleBtn = JToggleButton(Icons.matchCase)
|
||||||
|
.apply { toolTipText = I18n.getString("termora.match-case") }
|
||||||
|
|
||||||
|
init {
|
||||||
|
isModal = true
|
||||||
|
title = I18n.getString("termora.new-host.terminal.login-scripts")
|
||||||
|
controlsVisible = false
|
||||||
|
|
||||||
|
init()
|
||||||
|
pack()
|
||||||
|
size = Dimension(max(UIManager.getInt("Dialog.width") - 300, 250), preferredSize.height)
|
||||||
|
setLocationRelativeTo(owner)
|
||||||
|
|
||||||
|
val toolbar = FlatToolBar().apply { isFloatable = false }
|
||||||
|
toolbar.add(regexToggleBtn)
|
||||||
|
toolbar.add(matchCaseToggleBtn)
|
||||||
|
expectTextField.trailingComponent = toolbar
|
||||||
|
expectTextField.placeholderText = I18n.getString("termora.optional")
|
||||||
|
|
||||||
|
val script = loginScript
|
||||||
|
if (script != null) {
|
||||||
|
expectTextField.text = script.expect
|
||||||
|
sendTextField.text = script.send
|
||||||
|
matchCaseToggleBtn.isSelected = script.matchCase
|
||||||
|
regexToggleBtn.isSelected = script.regex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doOKAction() {
|
||||||
|
if (sendTextField.text.isBlank()) {
|
||||||
|
sendTextField.outline = "error"
|
||||||
|
sendTextField.requestFocusInWindow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loginScript = LoginScript(
|
||||||
|
expect = expectTextField.text,
|
||||||
|
send = sendTextField.text,
|
||||||
|
matchCase = matchCaseToggleBtn.isSelected,
|
||||||
|
regex = regexToggleBtn.isSelected,
|
||||||
|
)
|
||||||
|
|
||||||
|
super.doOKAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doCancelAction() {
|
||||||
|
loginScript = null
|
||||||
|
super.doCancelAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCenterPanel(): JComponent {
|
||||||
|
val layout = FormLayout(
|
||||||
|
"left:pref, $formMargin, default:grow",
|
||||||
|
"pref, $formMargin, pref"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rows = 1
|
||||||
|
val step = 2
|
||||||
|
return FormBuilder.create().layout(layout).padding("0dlu, $formMargin, $formMargin, $formMargin")
|
||||||
|
.add("${I18n.getString("termora.new-host.terminal.expect")}:").xy(1, rows)
|
||||||
|
.add(expectTextField).xy(3, rows).apply { rows += step }
|
||||||
|
.add("${I18n.getString("termora.new-host.terminal.send")}:").xy(1, rows)
|
||||||
|
.add(sendTextField).xy(3, rows).apply { rows += step }
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,8 +11,6 @@ import app.termora.tree.NewHostTreeDialog
|
|||||||
import com.formdev.flatlaf.FlatClientProperties
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
import com.formdev.flatlaf.extras.components.FlatComboBox
|
import com.formdev.flatlaf.extras.components.FlatComboBox
|
||||||
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
||||||
import com.formdev.flatlaf.extras.components.FlatTable
|
|
||||||
import com.formdev.flatlaf.extras.components.FlatToolBar
|
|
||||||
import com.formdev.flatlaf.ui.FlatTextBorder
|
import com.formdev.flatlaf.ui.FlatTextBorder
|
||||||
import com.formdev.flatlaf.util.SystemInfo
|
import com.formdev.flatlaf.util.SystemInfo
|
||||||
import com.jgoodies.forms.builder.FormBuilder
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
@@ -27,7 +25,6 @@ import java.nio.charset.Charset
|
|||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
import javax.swing.table.DefaultTableCellRenderer
|
import javax.swing.table.DefaultTableCellRenderer
|
||||||
import javax.swing.table.DefaultTableModel
|
import javax.swing.table.DefaultTableModel
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
@Suppress("CascadeIf")
|
@Suppress("CascadeIf")
|
||||||
open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
|
open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
|
||||||
@@ -496,35 +493,7 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
|
|||||||
val environmentTextArea = FixedLengthTextArea(2048)
|
val environmentTextArea = FixedLengthTextArea(2048)
|
||||||
val loginScripts = mutableListOf<LoginScript>()
|
val loginScripts = mutableListOf<LoginScript>()
|
||||||
|
|
||||||
|
private val loginScriptPanel = LoginScriptPanel(loginScripts)
|
||||||
private val addBtn = JButton(I18n.getString("termora.new-host.tunneling.add"))
|
|
||||||
private val editBtn = JButton(I18n.getString("termora.new-host.tunneling.edit"))
|
|
||||||
private val deleteBtn = JButton(I18n.getString("termora.new-host.tunneling.delete"))
|
|
||||||
private val table = FlatTable()
|
|
||||||
private val model = object : DefaultTableModel() {
|
|
||||||
override fun getRowCount(): Int {
|
|
||||||
return loginScripts.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isCellEditable(row: Int, column: Int): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addRow(loginScript: LoginScript) {
|
|
||||||
val rowCount = super.getRowCount()
|
|
||||||
loginScripts.add(loginScript)
|
|
||||||
super.fireTableRowsInserted(rowCount, rowCount + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValueAt(row: Int, column: Int): Any {
|
|
||||||
val loginScript = loginScripts[row]
|
|
||||||
return when (column) {
|
|
||||||
0 -> loginScript.expect
|
|
||||||
1 -> loginScript.send
|
|
||||||
else -> super.getValueAt(row, column)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val tabbed = FlatTabbedPane()
|
private val tabbed = FlatTabbedPane()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -533,12 +502,7 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
addBtn.isFocusable = false
|
|
||||||
editBtn.isFocusable = false
|
|
||||||
deleteBtn.isFocusable = false
|
|
||||||
|
|
||||||
deleteBtn.isEnabled = false
|
|
||||||
editBtn.isEnabled = false
|
|
||||||
|
|
||||||
tabbed.styleMap = mapOf(
|
tabbed.styleMap = mapOf(
|
||||||
"focusColor" to DynamicColor("TabbedPane.background"),
|
"focusColor" to DynamicColor("TabbedPane.background"),
|
||||||
@@ -547,7 +511,7 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
|
|||||||
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
|
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
|
||||||
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
|
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
|
||||||
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
|
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
|
||||||
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), getLoginScriptsComponent())
|
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), loginScriptPanel)
|
||||||
add(tabbed, BorderLayout.CENTER)
|
add(tabbed, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
|
||||||
@@ -575,39 +539,7 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initEvents() {
|
private fun initEvents() {
|
||||||
addBtn.addActionListener(object : AbstractAction() {
|
|
||||||
override fun actionPerformed(e: ActionEvent) {
|
|
||||||
val dialog = LoginScriptDialog(owner)
|
|
||||||
dialog.isVisible = true
|
|
||||||
model.addRow(dialog.loginScript ?: return)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
editBtn.addActionListener(object : AbstractAction() {
|
|
||||||
override fun actionPerformed(e: ActionEvent) {
|
|
||||||
val dialog = LoginScriptDialog(owner, loginScripts[table.selectedRow])
|
|
||||||
dialog.isVisible = true
|
|
||||||
loginScripts[table.selectedRow] = dialog.loginScript ?: return
|
|
||||||
model.fireTableRowsUpdated(table.selectedRow, table.selectedRow)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
deleteBtn.addActionListener(object : AbstractAction() {
|
|
||||||
override fun actionPerformed(e: ActionEvent) {
|
|
||||||
val rows = table.selectedRows
|
|
||||||
if (rows.isEmpty()) return
|
|
||||||
rows.sortDescending()
|
|
||||||
for (row in rows) {
|
|
||||||
loginScripts.removeAt(row)
|
|
||||||
model.fireTableRowsDeleted(row, row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
table.selectionModel.addListSelectionListener {
|
|
||||||
deleteBtn.isEnabled = table.selectedRowCount > 0
|
|
||||||
editBtn.isEnabled = deleteBtn.isEnabled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -648,123 +580,6 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
|
|||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLoginScriptsComponent(): JComponent {
|
|
||||||
val panel = JPanel(BorderLayout())
|
|
||||||
val scrollPane = JScrollPane(table)
|
|
||||||
|
|
||||||
model.addColumn(I18n.getString("termora.new-host.terminal.expect"))
|
|
||||||
model.addColumn(I18n.getString("termora.new-host.terminal.send"))
|
|
||||||
|
|
||||||
table.putClientProperty(
|
|
||||||
FlatClientProperties.STYLE, mapOf(
|
|
||||||
"showHorizontalLines" to true,
|
|
||||||
"showVerticalLines" to true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
table.model = model
|
|
||||||
table.autoResizeMode = JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
|
|
||||||
table.setDefaultRenderer(
|
|
||||||
Any::class.java,
|
|
||||||
DefaultTableCellRenderer().apply { horizontalAlignment = SwingConstants.CENTER })
|
|
||||||
table.fillsViewportHeight = true
|
|
||||||
scrollPane.border = BorderFactory.createCompoundBorder(
|
|
||||||
BorderFactory.createEmptyBorder(4, 0, 4, 0),
|
|
||||||
BorderFactory.createMatteBorder(1, 1, 1, 1, DynamicColor.Companion.BorderColor)
|
|
||||||
)
|
|
||||||
table.border = BorderFactory.createEmptyBorder()
|
|
||||||
|
|
||||||
|
|
||||||
val box = Box.createHorizontalBox()
|
|
||||||
box.add(addBtn)
|
|
||||||
box.add(Box.createHorizontalStrut(4))
|
|
||||||
box.add(editBtn)
|
|
||||||
box.add(Box.createHorizontalStrut(4))
|
|
||||||
box.add(deleteBtn)
|
|
||||||
|
|
||||||
panel.add(scrollPane, BorderLayout.CENTER)
|
|
||||||
panel.add(box, BorderLayout.SOUTH)
|
|
||||||
panel.border = BorderFactory.createEmptyBorder(6, 8, 6, 8)
|
|
||||||
|
|
||||||
return panel
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class LoginScriptDialog(
|
|
||||||
owner: Window,
|
|
||||||
var loginScript: LoginScript? = null
|
|
||||||
) : DialogWrapper(owner) {
|
|
||||||
private val formMargin = "4dlu"
|
|
||||||
private val expectTextField = OutlineTextField()
|
|
||||||
private val sendTextField = OutlineTextField()
|
|
||||||
private val regexToggleBtn = JToggleButton(Icons.regex)
|
|
||||||
.apply { toolTipText = I18n.getString("termora.regex") }
|
|
||||||
private val matchCaseToggleBtn = JToggleButton(Icons.matchCase)
|
|
||||||
.apply { toolTipText = I18n.getString("termora.match-case") }
|
|
||||||
|
|
||||||
init {
|
|
||||||
isModal = true
|
|
||||||
title = I18n.getString("termora.new-host.terminal.login-scripts")
|
|
||||||
controlsVisible = false
|
|
||||||
|
|
||||||
init()
|
|
||||||
pack()
|
|
||||||
size = Dimension(max(UIManager.getInt("Dialog.width") - 300, 250), preferredSize.height)
|
|
||||||
setLocationRelativeTo(owner)
|
|
||||||
|
|
||||||
val toolbar = FlatToolBar().apply { isFloatable = false }
|
|
||||||
toolbar.add(regexToggleBtn)
|
|
||||||
toolbar.add(matchCaseToggleBtn)
|
|
||||||
expectTextField.trailingComponent = toolbar
|
|
||||||
expectTextField.placeholderText = I18n.getString("termora.optional")
|
|
||||||
|
|
||||||
val script = loginScript
|
|
||||||
if (script != null) {
|
|
||||||
expectTextField.text = script.expect
|
|
||||||
sendTextField.text = script.send
|
|
||||||
matchCaseToggleBtn.isSelected = script.matchCase
|
|
||||||
regexToggleBtn.isSelected = script.regex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doOKAction() {
|
|
||||||
if (sendTextField.text.isBlank()) {
|
|
||||||
sendTextField.outline = "error"
|
|
||||||
sendTextField.requestFocusInWindow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginScript = LoginScript(
|
|
||||||
expect = expectTextField.text,
|
|
||||||
send = sendTextField.text,
|
|
||||||
matchCase = matchCaseToggleBtn.isSelected,
|
|
||||||
regex = regexToggleBtn.isSelected,
|
|
||||||
)
|
|
||||||
|
|
||||||
super.doOKAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doCancelAction() {
|
|
||||||
loginScript = null
|
|
||||||
super.doCancelAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createCenterPanel(): JComponent {
|
|
||||||
val layout = FormLayout(
|
|
||||||
"left:pref, $formMargin, default:grow",
|
|
||||||
"pref, $formMargin, pref"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rows = 1
|
|
||||||
val step = 2
|
|
||||||
return FormBuilder.create().layout(layout).padding("0dlu, $formMargin, $formMargin, $formMargin")
|
|
||||||
.add("${I18n.getString("termora.new-host.terminal.expect")}:").xy(1, rows)
|
|
||||||
.add(expectTextField).xy(3, rows).apply { rows += step }
|
|
||||||
.add("${I18n.getString("termora.new-host.terminal.send")}:").xy(1, rows)
|
|
||||||
.add(sendTextField).xy(3, rows).apply { rows += step }
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected inner class SFTPOption : JPanel(BorderLayout()), Option {
|
protected inner class SFTPOption : JPanel(BorderLayout()), Option {
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import app.termora.*
|
|||||||
import app.termora.account.AccountOwner
|
import app.termora.account.AccountOwner
|
||||||
import app.termora.plugin.internal.BasicProxyOption
|
import app.termora.plugin.internal.BasicProxyOption
|
||||||
import com.formdev.flatlaf.FlatClientProperties
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
import com.formdev.flatlaf.extras.components.FlatComboBox
|
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
||||||
import com.formdev.flatlaf.ui.FlatTextBorder
|
import com.formdev.flatlaf.ui.FlatTextBorder
|
||||||
import com.jgoodies.forms.builder.FormBuilder
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
import com.jgoodies.forms.layout.FormLayout
|
import com.jgoodies.forms.layout.FormLayout
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.Component
|
|
||||||
import java.awt.KeyboardFocusManager
|
import java.awt.KeyboardFocusManager
|
||||||
import java.awt.event.ComponentAdapter
|
import java.awt.event.ComponentAdapter
|
||||||
import java.awt.event.ComponentEvent
|
import java.awt.event.ComponentEvent
|
||||||
@@ -35,16 +34,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
val protocol = TelnetProtocolProvider.PROTOCOL
|
val protocol = TelnetProtocolProvider.PROTOCOL
|
||||||
val host = generalOption.hostTextField.text
|
val host = generalOption.hostTextField.text
|
||||||
val port = (generalOption.portTextField.value ?: 23) as Int
|
val port = (generalOption.portTextField.value ?: 23) as Int
|
||||||
var authentication = Authentication.No
|
|
||||||
var proxy = Proxy.Companion.No
|
var proxy = Proxy.Companion.No
|
||||||
val authenticationType = generalOption.authenticationTypeComboBox.selectedItem as AuthenticationType
|
|
||||||
|
|
||||||
if (authenticationType == AuthenticationType.Password) {
|
|
||||||
authentication = authentication.copy(
|
|
||||||
type = authenticationType,
|
|
||||||
password = String(generalOption.passwordTextField.password)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxyOption.proxyTypeComboBox.selectedItem != ProxyType.No) {
|
if (proxyOption.proxyTypeComboBox.selectedItem != ProxyType.No) {
|
||||||
proxy = proxy.copy(
|
proxy = proxy.copy(
|
||||||
@@ -64,6 +54,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
||||||
env = terminalOption.environmentTextArea.text,
|
env = terminalOption.environmentTextArea.text,
|
||||||
startupCommand = terminalOption.startupCommandTextField.text,
|
startupCommand = terminalOption.startupCommandTextField.text,
|
||||||
|
loginScripts = terminalOption.loginScripts,
|
||||||
serialComm = serialComm,
|
serialComm = serialComm,
|
||||||
extras = mutableMapOf(
|
extras = mutableMapOf(
|
||||||
"backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name,
|
"backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name,
|
||||||
@@ -76,8 +67,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
protocol = protocol,
|
protocol = protocol,
|
||||||
host = host,
|
host = host,
|
||||||
port = port,
|
port = port,
|
||||||
username = generalOption.usernameTextField.text,
|
|
||||||
authentication = authentication,
|
|
||||||
proxy = proxy,
|
proxy = proxy,
|
||||||
sort = System.currentTimeMillis(),
|
sort = System.currentTimeMillis(),
|
||||||
remark = generalOption.remarkTextArea.text,
|
remark = generalOption.remarkTextArea.text,
|
||||||
@@ -88,13 +77,9 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
fun setHost(host: Host) {
|
fun setHost(host: Host) {
|
||||||
generalOption.portTextField.value = host.port
|
generalOption.portTextField.value = host.port
|
||||||
generalOption.nameTextField.text = host.name
|
generalOption.nameTextField.text = host.name
|
||||||
generalOption.usernameTextField.text = host.username
|
|
||||||
generalOption.hostTextField.text = host.host
|
generalOption.hostTextField.text = host.host
|
||||||
generalOption.remarkTextArea.text = host.remark
|
generalOption.remarkTextArea.text = host.remark
|
||||||
generalOption.authenticationTypeComboBox.selectedItem = host.authentication.type
|
|
||||||
if (host.authentication.type == AuthenticationType.Password) {
|
|
||||||
generalOption.passwordTextField.text = host.authentication.password
|
|
||||||
}
|
|
||||||
proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type
|
proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type
|
||||||
proxyOption.proxyHostTextField.text = host.proxy.host
|
proxyOption.proxyHostTextField.text = host.proxy.host
|
||||||
proxyOption.proxyPasswordTextField.text = host.proxy.password
|
proxyOption.proxyPasswordTextField.text = host.proxy.password
|
||||||
@@ -110,6 +95,8 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
terminalOption.characterAtATimeTextField.selectedItem =
|
terminalOption.characterAtATimeTextField.selectedItem =
|
||||||
host.options.extras["character-at-a-time"]?.toBooleanStrictOrNull() ?: false
|
host.options.extras["character-at-a-time"]?.toBooleanStrictOrNull() ?: false
|
||||||
|
|
||||||
|
terminalOption.loginScripts.clear()
|
||||||
|
terminalOption.loginScripts.addAll(host.options.loginScripts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateFields(): Boolean {
|
fun validateFields(): Boolean {
|
||||||
@@ -122,15 +109,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host.authentication.type == AuthenticationType.Password) {
|
|
||||||
if (validateField(generalOption.usernameTextField)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (validateField(generalOption.passwordTextField)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxy
|
// proxy
|
||||||
if (host.proxy.type != ProxyType.No) {
|
if (host.proxy.type != ProxyType.No) {
|
||||||
if (validateField(proxyOption.proxyHostTextField)
|
if (validateField(proxyOption.proxyHostTextField)
|
||||||
@@ -167,28 +145,11 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
textField.requestFocusInWindow()
|
textField.requestFocusInWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
inner class GeneralOption : JPanel(BorderLayout()), Option {
|
||||||
* 返回 true 表示有错误
|
|
||||||
*/
|
|
||||||
private fun validateField(comboBox: JComboBox<*>): Boolean {
|
|
||||||
val selectedItem = comboBox.selectedItem
|
|
||||||
if (comboBox.isEnabled && (selectedItem == null || (selectedItem is String && selectedItem.isBlank()))) {
|
|
||||||
selectOptionJComponent(comboBox)
|
|
||||||
comboBox.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
|
|
||||||
comboBox.requestFocusInWindow()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
protected inner class GeneralOption : JPanel(BorderLayout()), Option {
|
|
||||||
val portTextField = PortSpinner(23)
|
val portTextField = PortSpinner(23)
|
||||||
val nameTextField = OutlineTextField(128)
|
val nameTextField = OutlineTextField(128)
|
||||||
val usernameTextField = OutlineTextField(128)
|
|
||||||
val hostTextField = OutlineTextField(255)
|
val hostTextField = OutlineTextField(255)
|
||||||
val passwordTextField = OutlinePasswordField(255)
|
|
||||||
val remarkTextArea = FixedLengthTextArea(512)
|
val remarkTextArea = FixedLengthTextArea(512)
|
||||||
val authenticationTypeComboBox = FlatComboBox<AuthenticationType>()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView()
|
initView()
|
||||||
@@ -197,44 +158,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
add(getCenterComponent(), BorderLayout.CENTER)
|
add(getCenterComponent(), BorderLayout.CENTER)
|
||||||
|
|
||||||
authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() {
|
|
||||||
override fun getListCellRendererComponent(
|
|
||||||
list: JList<*>?,
|
|
||||||
value: Any?,
|
|
||||||
index: Int,
|
|
||||||
isSelected: Boolean,
|
|
||||||
cellHasFocus: Boolean
|
|
||||||
): Component {
|
|
||||||
var text = value?.toString() ?: ""
|
|
||||||
when (value) {
|
|
||||||
AuthenticationType.Password -> {
|
|
||||||
text = "Password"
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticationType.PublicKey -> {
|
|
||||||
text = "Public Key"
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticationType.KeyboardInteractive -> {
|
|
||||||
text = "Keyboard Interactive"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.getListCellRendererComponent(
|
|
||||||
list,
|
|
||||||
text,
|
|
||||||
index,
|
|
||||||
isSelected,
|
|
||||||
cellHasFocus
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticationTypeComboBox.addItem(AuthenticationType.No)
|
|
||||||
authenticationTypeComboBox.addItem(AuthenticationType.Password)
|
|
||||||
|
|
||||||
authenticationTypeComboBox.selectedItem = AuthenticationType.Password
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initEvents() {
|
private fun initEvents() {
|
||||||
@@ -261,7 +184,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
private fun getCenterComponent(): JComponent {
|
private fun getCenterComponent(): JComponent {
|
||||||
val layout = FormLayout(
|
val layout = FormLayout(
|
||||||
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref, $FORM_MARGIN, default",
|
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref, $FORM_MARGIN, default",
|
||||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||||
)
|
)
|
||||||
remarkTextArea.setFocusTraversalKeys(
|
remarkTextArea.setFocusTraversalKeys(
|
||||||
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
||||||
@@ -290,15 +213,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
.add("${I18n.getString("termora.new-host.general.port")}:").xy(5, rows)
|
.add("${I18n.getString("termora.new-host.general.port")}:").xy(5, rows)
|
||||||
.add(portTextField).xy(7, rows).apply { rows += step }
|
.add(portTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
|
||||||
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
|
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.authentication")}:").xy(1, rows)
|
|
||||||
.add(authenticationTypeComboBox).xyw(3, rows, 5).apply { rows += step }
|
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
|
||||||
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.remark")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.general.remark")}:").xy(1, rows)
|
||||||
.add(JScrollPane(remarkTextArea).apply { border = FlatTextBorder() })
|
.add(JScrollPane(remarkTextArea).apply { border = FlatTextBorder() })
|
||||||
.xyw(3, rows, 5).apply { rows += step }
|
.xyw(3, rows, 5).apply { rows += step }
|
||||||
@@ -318,7 +232,9 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
val startupCommandTextField = OutlineTextField()
|
val startupCommandTextField = OutlineTextField()
|
||||||
val characterAtATimeTextField = YesOrNoComboBox()
|
val characterAtATimeTextField = YesOrNoComboBox()
|
||||||
val environmentTextArea = FixedLengthTextArea(2048)
|
val environmentTextArea = FixedLengthTextArea(2048)
|
||||||
|
val loginScripts = mutableListOf<LoginScript>()
|
||||||
|
|
||||||
|
private val loginScriptPanel = LoginScriptPanel(loginScripts)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView()
|
initView()
|
||||||
@@ -326,7 +242,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
add(getCenterComponent(), BorderLayout.CENTER)
|
|
||||||
|
|
||||||
backspaceComboBox.addItem(Backspace.Delete)
|
backspaceComboBox.addItem(Backspace.Delete)
|
||||||
backspaceComboBox.addItem(Backspace.Backspace)
|
backspaceComboBox.addItem(Backspace.Backspace)
|
||||||
@@ -355,6 +270,17 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
|
|
||||||
charsetComboBox.selectedItem = "UTF-8"
|
charsetComboBox.selectedItem = "UTF-8"
|
||||||
|
|
||||||
|
val tabbed = FlatTabbedPane()
|
||||||
|
tabbed.styleMap = mapOf(
|
||||||
|
"focusColor" to DynamicColor("TabbedPane.background"),
|
||||||
|
"hoverColor" to DynamicColor("TabbedPane.background"),
|
||||||
|
)
|
||||||
|
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
|
||||||
|
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
|
||||||
|
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
|
||||||
|
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), loginScriptPanel)
|
||||||
|
add(tabbed, BorderLayout.CENTER)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initEvents() {
|
private fun initEvents() {
|
||||||
@@ -383,6 +309,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
|
|||||||
var rows = 1
|
var rows = 1
|
||||||
val step = 2
|
val step = 2
|
||||||
val panel = FormBuilder.create().layout(layout)
|
val panel = FormBuilder.create().layout(layout)
|
||||||
|
.border(BorderFactory.createEmptyBorder(6, 8, 6, 8))
|
||||||
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
||||||
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
||||||
.add("${I18n.getString("termora.new-host.terminal.backspace")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.terminal.backspace")}:").xy(1, rows)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package app.termora.plugin.internal.telnet
|
package app.termora.plugin.internal.telnet
|
||||||
|
|
||||||
import app.termora.*
|
import app.termora.Host
|
||||||
|
import app.termora.ProxyType
|
||||||
|
import app.termora.PtyHostTerminalTab
|
||||||
|
import app.termora.WindowScope
|
||||||
import app.termora.terminal.ControlCharacters
|
import app.termora.terminal.ControlCharacters
|
||||||
import app.termora.terminal.KeyEncoderImpl
|
import app.termora.terminal.KeyEncoderImpl
|
||||||
import app.termora.terminal.PtyConnector
|
import app.termora.terminal.PtyConnector
|
||||||
@@ -69,34 +72,4 @@ class TelnetTerminalTab(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun loginScriptsPtyConnector(host: Host, ptyConnector: PtyConnector): PtyConnector {
|
|
||||||
if (host.authentication.type != AuthenticationType.Password) {
|
|
||||||
return ptyConnector
|
|
||||||
}
|
|
||||||
|
|
||||||
val scripts = mutableListOf<LoginScript>()
|
|
||||||
scripts.add(
|
|
||||||
LoginScript(
|
|
||||||
expect = "login:",
|
|
||||||
send = host.username,
|
|
||||||
regex = false,
|
|
||||||
matchCase = false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
scripts.add(
|
|
||||||
LoginScript(
|
|
||||||
expect = "password:",
|
|
||||||
send = host.authentication.password,
|
|
||||||
regex = false,
|
|
||||||
matchCase = false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return super.loginScriptsPtyConnector(
|
|
||||||
host.copy(options = host.options.copy(loginScripts = scripts)),
|
|
||||||
ptyConnector
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user