mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: quick connect
This commit is contained in:
@@ -344,6 +344,11 @@ data class Host(
|
||||
|
||||
val isFolder get() = StringUtils.equalsIgnoreCase(protocol, "Folder")
|
||||
|
||||
/**
|
||||
* 临时的 SSH 不可以保存
|
||||
*/
|
||||
val isTemporary get() = options.extras["Temporary"] != null
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
@@ -21,9 +21,13 @@ class HostManager private constructor() : Disposable {
|
||||
*/
|
||||
fun addHost(host: Host, source: DatabaseChangedExtension.Source = DatabaseChangedExtension.Source.User) {
|
||||
assertEventDispatchThread()
|
||||
if (host.ownerType.isBlank()) {
|
||||
|
||||
if (host.isTemporary)
|
||||
throw IllegalArgumentException("Temporary host")
|
||||
|
||||
if (host.ownerType.isBlank())
|
||||
throw IllegalArgumentException("Owner type cannot be null")
|
||||
}
|
||||
|
||||
databaseManager.saveAndIncrementVersion(
|
||||
Data(
|
||||
id = host.id,
|
||||
|
||||
@@ -37,6 +37,8 @@ class RequestAuthenticationDialog(owner: Window, host: Host) : DialogWrapper(own
|
||||
preferredSize = size
|
||||
minimumSize = size
|
||||
|
||||
rememberCheckBox.isVisible = host.isTemporary.not()
|
||||
|
||||
publicKeyComboBox.renderer = object : DefaultListCellRenderer() {
|
||||
override fun getListCellRendererComponent(
|
||||
list: JList<*>?,
|
||||
@@ -84,7 +86,7 @@ class RequestAuthenticationDialog(owner: Window, host: Host) : DialogWrapper(own
|
||||
|
||||
switchPasswordComponent()
|
||||
|
||||
return FormBuilder.create().padding("$formMargin, $formMargin, $formMargin, $formMargin")
|
||||
return FormBuilder.create().padding("1dlu, $formMargin, $formMargin, $formMargin")
|
||||
.layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.general.authentication")}:").xy(1, 1)
|
||||
.add(authenticationTypeComboBox).xy(3, 1)
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.formdev.flatlaf.extras.components.FlatPopupMenu
|
||||
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.awt.*
|
||||
import java.awt.event.AWTEventListener
|
||||
import java.awt.event.ActionEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
@@ -32,11 +31,6 @@ class TerminalTabbed(
|
||||
private val layout: TermoraLayout,
|
||||
) : JPanel(BorderLayout()), Disposable, TerminalTabbedManager, DataProvider {
|
||||
private val tabs = mutableListOf<TerminalTab>()
|
||||
private val customizeToolBarAWTEventListener = object : AWTEventListener, Disposable {
|
||||
override fun eventDispatched(event: AWTEvent?) {
|
||||
|
||||
}
|
||||
}
|
||||
private val actionManager = ActionManager.getInstance()
|
||||
private val dataProviderSupport = DataProviderSupport()
|
||||
private val appearance get() = DatabaseManager.getInstance().appearance
|
||||
@@ -72,7 +66,6 @@ class TerminalTabbed(
|
||||
|
||||
|
||||
private fun initEvents() {
|
||||
Disposer.register(this, customizeToolBarAWTEventListener)
|
||||
|
||||
// 关闭 tab
|
||||
tabbedPane.setTabCloseCallback { _, i -> removeTabAt(i, true) }
|
||||
@@ -146,9 +139,6 @@ class TerminalTabbed(
|
||||
}
|
||||
}).let { Disposer.register(this, it) }
|
||||
|
||||
// 监听全局事件
|
||||
toolkit.addAWTEventListener(customizeToolBarAWTEventListener, AWTEvent.MOUSE_EVENT_MASK)
|
||||
|
||||
}
|
||||
|
||||
private fun removeTabAt(index: Int, disposable: Boolean = true) {
|
||||
@@ -301,9 +291,7 @@ class TerminalTabbed(
|
||||
|
||||
// 关闭
|
||||
val close = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.close"))
|
||||
close.addActionListener {
|
||||
tabbedPane.tabCloseCallback?.accept(tabbedPane, tabIndex)
|
||||
}
|
||||
close.addActionListener { tabbedPane.tabCloseCallback?.accept(tabbedPane, tabIndex) }
|
||||
|
||||
// 关闭其他标签页
|
||||
popupMenu.add(I18n.getString("termora.tabbed.contextmenu.close-other-tabs")).addActionListener {
|
||||
@@ -326,7 +314,7 @@ class TerminalTabbed(
|
||||
close.isEnabled = tab.canClose()
|
||||
rename.isEnabled = close.isEnabled
|
||||
clone.isEnabled = close.isEnabled
|
||||
edit.isEnabled = tab is HostTerminalTab && tab.host.id != "local"
|
||||
edit.isEnabled = tab is HostTerminalTab && tab.host.id != "local" && tab.host.isTemporary.not()
|
||||
openInNewWindow.isEnabled = close.isEnabled
|
||||
|
||||
// 如果不允许克隆
|
||||
@@ -337,12 +325,7 @@ class TerminalTabbed(
|
||||
if (close.isEnabled) {
|
||||
popupMenu.addSeparator()
|
||||
val reconnect = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.reconnect"))
|
||||
reconnect.addActionListener {
|
||||
if (tabIndex > 0) {
|
||||
tabs[tabIndex].reconnect()
|
||||
}
|
||||
}
|
||||
|
||||
reconnect.addActionListener { tabs[tabIndex].reconnect() }
|
||||
reconnect.isEnabled = tabs[tabIndex].canReconnect()
|
||||
}
|
||||
|
||||
@@ -384,60 +367,6 @@ class TerminalTabbed(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对着 ToolBar 右键
|
||||
*/
|
||||
/*private inner class CustomizeToolBarAWTEventListener : AWTEventListener, Disposable {
|
||||
override fun eventDispatched(event: AWTEvent) {
|
||||
if (event !is MouseEvent || event.id != MouseEvent.MOUSE_CLICKED || !SwingUtilities.isRightMouseButton(event)) return
|
||||
// 如果 ToolBar 没有显示
|
||||
if (!toolbar.isShowing) return
|
||||
// 如果不是作用于在 ToolBar 上面
|
||||
if (!Rectangle(toolbar.locationOnScreen, toolbar.size).contains(event.locationOnScreen)) return
|
||||
|
||||
// 显示右键菜单
|
||||
showContextMenu(event)
|
||||
}
|
||||
|
||||
private fun showContextMenu(event: MouseEvent) {
|
||||
val popupMenu = FlatPopupMenu()
|
||||
popupMenu.add(I18n.getString("termora.toolbar.customize-toolbar")).addActionListener {
|
||||
val owner = SwingUtilities.getWindowAncestor(this@TerminalTabbed)
|
||||
val dialog = CustomizeToolBarDialog(owner, windowScope, termoraToolBar)
|
||||
dialog.setLocationRelativeTo(owner)
|
||||
if (dialog.open()) {
|
||||
TermoraToolBar.rebuild()
|
||||
}
|
||||
}
|
||||
popupMenu.show(event.component, event.x, event.y)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
toolkit.removeAWTEventListener(this)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*private inner class CustomizeToolBarDialog(owner: Window) : DialogWrapper(owner) {
|
||||
init {
|
||||
size = Dimension(UIManager.getInt("Dialog.width"), UIManager.getInt("Dialog.height"))
|
||||
isModal = true
|
||||
title = I18n.getString("termora.setting")
|
||||
setLocationRelativeTo(null)
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
val model = DefaultListModel<String>()
|
||||
val checkBoxList = CheckBoxList(model)
|
||||
checkBoxList.fixedCellHeight = UIManager.getInt("Tree.rowHeight")
|
||||
model.addElement("Test")
|
||||
return checkBoxList
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
private inner class SwitchFindEverywhereResult(
|
||||
private val title: String,
|
||||
private val icon: Icon?,
|
||||
|
||||
@@ -28,6 +28,7 @@ class ActionManager : org.jdesktop.swingx.action.ActionManager() {
|
||||
private fun registerActions() {
|
||||
addAction(NewWindowAction.NEW_WINDOW, NewWindowAction())
|
||||
addAction(FindEverywhereAction.FIND_EVERYWHERE, FindEverywhereAction())
|
||||
addAction(QuickConnectAction.QUICK_CONNECT, QuickConnectAction.instance)
|
||||
|
||||
addAction(Actions.APP_UPDATE, AppUpdateAction.getInstance())
|
||||
addAction(Actions.KEYWORD_HIGHLIGHT, KeywordHighlightAction())
|
||||
|
||||
@@ -38,7 +38,7 @@ class OpenHostAction : AnAction() {
|
||||
if (providers.first { StringUtils.equalsIgnoreCase(it.getProtocol(), host.protocol) }
|
||||
.isTransfer()) {
|
||||
ActionManager.getInstance().getAction(Actions.SFTP)
|
||||
.actionPerformed(TransferActionEvent(evt.source, evt.host.id, evt.event))
|
||||
.actionPerformed(TransferActionEvent(evt.source, host, evt.event))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
174
src/main/kotlin/app/termora/actions/QuickConnectAction.kt
Normal file
174
src/main/kotlin/app/termora/actions/QuickConnectAction.kt
Normal file
@@ -0,0 +1,174 @@
|
||||
package app.termora.actions
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.Application.ohMyJson
|
||||
import app.termora.OptionsPane.Companion.FORM_MARGIN
|
||||
import app.termora.database.DatabaseManager
|
||||
import app.termora.protocol.ProtocolProvider
|
||||
import com.jgoodies.forms.builder.FormBuilder
|
||||
import com.jgoodies.forms.layout.FormLayout
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||
import java.awt.Dimension
|
||||
import java.awt.Window
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import javax.swing.*
|
||||
|
||||
class QuickConnectAction private constructor() : AnAction(I18n.getString("termora.actions.quick-connect"), Icons.find) {
|
||||
companion object {
|
||||
const val QUICK_CONNECT = "QuickConnectAction"
|
||||
val instance = QuickConnectAction()
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
putValue(SHORT_DESCRIPTION, I18n.getString("termora.actions.quick-connect"))
|
||||
}
|
||||
|
||||
override fun actionPerformed(evt: AnActionEvent) {
|
||||
val scope = evt.getData(DataProviders.WindowScope) ?: return
|
||||
val dialog = QuickConnectDialog(scope.window)
|
||||
dialog.isVisible = true
|
||||
}
|
||||
|
||||
private class QuickConnectDialog(owner: Window) : DialogWrapper(owner) {
|
||||
private val properties get() = DatabaseManager.getInstance().properties
|
||||
private val hostComboBox = OutlineComboBox<String>()
|
||||
private val usernameTextField = OutlineTextField(256)
|
||||
private val passwordTextField = OutlinePasswordField(256)
|
||||
|
||||
init {
|
||||
isModal = true
|
||||
title = I18n.getString("termora.actions.quick-connect")
|
||||
isResizable = false
|
||||
init()
|
||||
pack()
|
||||
size = Dimension(UIManager.getInt("Dialog.width") - 250, preferredSize.height)
|
||||
setLocationRelativeTo(owner)
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
hostComboBox.isEditable = true
|
||||
hostComboBox.placeholderText = "ssh://127.0.0.1:22"
|
||||
|
||||
val histories = getHistories()
|
||||
for (history in histories) {
|
||||
if (histories.first() == history) {
|
||||
usernameTextField.text = history.host.username
|
||||
passwordTextField.text = history.host.authentication.password
|
||||
}
|
||||
hostComboBox.addItem(history.url)
|
||||
}
|
||||
|
||||
usernameTextField.placeholderText = I18n.getString("termora.new-host.general.username")
|
||||
passwordTextField.placeholderText = I18n.getString("termora.new-host.general.password")
|
||||
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
|
||||
return FormBuilder.create().layout(layout)
|
||||
.border(BorderFactory.createEmptyBorder(0, 8, 8, 8))
|
||||
.add("${I18n.getString("termora.new-host.general.protocol")}:").xy(1, 1)
|
||||
.add(hostComboBox).xy(3, 1)
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, 3)
|
||||
.add(usernameTextField).xy(3, 3)
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, 5)
|
||||
.add(passwordTextField).xy(3, 5)
|
||||
.build()
|
||||
|
||||
}
|
||||
|
||||
override fun doOKAction() {
|
||||
val host = hostComboBox.selectedItem as? String
|
||||
if (host.isNullOrBlank()) {
|
||||
hostComboBox.requestFocusInWindow()
|
||||
return
|
||||
}
|
||||
|
||||
val historyHost: HistoryHost
|
||||
try {
|
||||
historyHost = getHistoryHost(host.trim())
|
||||
} catch (e: Exception) {
|
||||
hostComboBox.requestFocusInWindow()
|
||||
OptionPane.showMessageDialog(
|
||||
this,
|
||||
e.message ?: ExceptionUtils.getRootCauseMessage(e),
|
||||
messageType = JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val action = ActionManager.getInstance().getAction(OpenHostAction.OPEN_HOST)
|
||||
if (action is OpenHostAction) {
|
||||
SwingUtilities.invokeLater {
|
||||
action.actionPerformed(OpenHostActionEvent(this, historyHost.host, EventObject(this)))
|
||||
}
|
||||
}
|
||||
super.doOKAction()
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun getHistoryHost(host: String): HistoryHost {
|
||||
|
||||
|
||||
val uri = URI.create(host)
|
||||
val protocolProvider = ProtocolProvider.valueOf(uri.scheme)
|
||||
if (protocolProvider == null) {
|
||||
throw UnsupportedOperationException(I18n.getString("termora.protocol.not-supported", uri.scheme))
|
||||
}
|
||||
|
||||
val historyHost = HistoryHost(
|
||||
host, Host(
|
||||
name = uri.host,
|
||||
protocol = uri.scheme,
|
||||
host = uri.host,
|
||||
port = uri.port,
|
||||
username = usernameTextField.text.trim(),
|
||||
authentication = Authentication.No.copy(
|
||||
type = AuthenticationType.Password,
|
||||
password = String(passwordTextField.password)
|
||||
),
|
||||
options = Options.Default.copy(
|
||||
extras = mutableMapOf("Temporary" to "true")
|
||||
)
|
||||
)
|
||||
)
|
||||
val histories = getHistories().toMutableList()
|
||||
histories.removeIf { it.url == host }
|
||||
histories.addFirst(historyHost)
|
||||
|
||||
if (histories.size > 20) {
|
||||
histories.removeLast()
|
||||
}
|
||||
|
||||
properties.putString("QuickConnect.historyHosts", ohMyJson.encodeToString(histories))
|
||||
|
||||
return historyHost
|
||||
}
|
||||
|
||||
private fun getHistories(): List<HistoryHost> {
|
||||
val text = properties.getString("QuickConnect.historyHosts", "[]")
|
||||
return ohMyJson.runCatching { ohMyJson.decodeFromString<List<HistoryHost>>(text) }
|
||||
.getOrNull() ?: emptyList()
|
||||
}
|
||||
|
||||
override fun addNotify() {
|
||||
super.addNotify()
|
||||
controlsVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class HistoryHost(
|
||||
val url: String,
|
||||
val host: Host,
|
||||
)
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import app.termora.Icons
|
||||
import app.termora.Scope
|
||||
import app.termora.actions.NewHostAction
|
||||
import app.termora.actions.OpenLocalTerminalAction
|
||||
import app.termora.actions.QuickConnectAction
|
||||
import app.termora.snippet.SnippetAction
|
||||
import com.formdev.flatlaf.FlatLaf
|
||||
import org.jdesktop.swingx.action.ActionManager
|
||||
@@ -19,19 +20,13 @@ class QuickCommandFindEverywhereProvider : FindEverywhereProvider {
|
||||
actionManager.let { list.add(CreateHostFindEverywhereResult()) }
|
||||
|
||||
// Local terminal
|
||||
actionManager.getAction(OpenLocalTerminalAction.LOCAL_TERMINAL)?.let {
|
||||
list.add(ActionFindEverywhereResult(it))
|
||||
}
|
||||
|
||||
actionManager.getAction(OpenLocalTerminalAction.LOCAL_TERMINAL)?.let { list.add(ActionFindEverywhereResult(it)) }
|
||||
// Snippet
|
||||
actionManager.getAction(SnippetAction.SNIPPET)?.let {
|
||||
list.add(ActionFindEverywhereResult(it))
|
||||
}
|
||||
|
||||
actionManager.getAction(SnippetAction.SNIPPET)?.let { list.add(ActionFindEverywhereResult(it)) }
|
||||
// SFTP
|
||||
actionManager.getAction(Actions.SFTP)?.let {
|
||||
list.add(ActionFindEverywhereResult(it))
|
||||
}
|
||||
actionManager.getAction(Actions.SFTP)?.let { list.add(ActionFindEverywhereResult(it)) }
|
||||
// quick connect
|
||||
actionManager.getAction(QuickConnectAction.QUICK_CONNECT)?.let { list.add(ActionFindEverywhereResult(it)) }
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -250,7 +250,8 @@ object SshClients {
|
||||
|
||||
val session = client.connect(entry).verify(timeout).session
|
||||
if (host.authentication.type == AuthenticationType.Password) {
|
||||
session.addPasswordIdentity(host.authentication.password)
|
||||
if (StringUtils.isNotBlank(host.authentication.password))
|
||||
session.addPasswordIdentity(host.authentication.password)
|
||||
} else if (host.authentication.type == AuthenticationType.PublicKey) {
|
||||
session.keyIdentityProvider = OhKeyPairKeyPairProvider(host.authentication.password)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package app.termora.transfer
|
||||
|
||||
import app.termora.Host
|
||||
import app.termora.actions.AnActionEvent
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.util.*
|
||||
|
||||
class TransferActionEvent(
|
||||
source: Any,
|
||||
val hostId: String,
|
||||
val host: Host? = null,
|
||||
event: EventObject
|
||||
) : AnActionEvent(source, StringUtils.EMPTY, event)
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.termora.transfer
|
||||
|
||||
import app.termora.HostManager
|
||||
import app.termora.HostTerminalTab
|
||||
import app.termora.I18n
|
||||
import app.termora.Icons
|
||||
@@ -8,10 +7,8 @@ import app.termora.actions.AnAction
|
||||
import app.termora.actions.AnActionEvent
|
||||
import app.termora.actions.DataProviders
|
||||
import app.termora.protocol.TransferProtocolProvider
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
class TransferAnAction : AnAction(I18n.getString("termora.transport.sftp"), Icons.folder) {
|
||||
private val hostManager get() = HostManager.getInstance()
|
||||
override fun actionPerformed(evt: AnActionEvent) {
|
||||
val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager) ?: return
|
||||
|
||||
@@ -29,35 +26,32 @@ class TransferAnAction : AnAction(I18n.getString("termora.transport.sftp"), Icon
|
||||
terminalTabbedManager.addTerminalTab(sftpTab, false)
|
||||
}
|
||||
|
||||
var hostId = if (evt is TransferActionEvent) evt.hostId else StringUtils.EMPTY
|
||||
var host = if (evt is TransferActionEvent) evt.host else null
|
||||
|
||||
// 如果不是特定事件,那么尝试获取选中的Tab,如果是一个 Host 并且是 SSH 协议那么直接打开
|
||||
if (hostId.isBlank()) {
|
||||
if (host == null) {
|
||||
val tab = terminalTabbedManager.getSelectedTerminalTab()
|
||||
// 如果当前选中的是 Host 主机
|
||||
if (tab is HostTerminalTab) {
|
||||
if (TransferProtocolProvider.valueOf(tab.host.protocol) != null) {
|
||||
hostId = tab.host.id
|
||||
host = tab.host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
terminalTabbedManager.setSelectedTerminalTab(sftpTab)
|
||||
|
||||
if (hostId.isBlank()) return
|
||||
|
||||
val tabbed = sftpTab.rightTabbed
|
||||
|
||||
// 如果已经打开了 那么直接选中
|
||||
for (i in 0 until tabbed.tabCount) {
|
||||
val panel = tabbed.getTransportPanel(i) ?: continue
|
||||
if (panel.host.id == hostId) {
|
||||
tabbed.selectedIndex = i
|
||||
return
|
||||
if (host != null) {
|
||||
for (i in 0 until tabbed.tabCount) {
|
||||
val panel = tabbed.getTransportPanel(i) ?: continue
|
||||
if (panel.host.id == host.id) {
|
||||
tabbed.selectedIndex = i
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val host = hostManager.getHost(hostId) ?: return
|
||||
var selectionPane: TransportSelectionPanel? = null
|
||||
|
||||
for (i in 0 until tabbed.tabCount) {
|
||||
val c = tabbed.getComponentAt(i)
|
||||
if (c is TransportSelectionPanel) {
|
||||
@@ -72,8 +66,10 @@ class TransferAnAction : AnAction(I18n.getString("termora.transport.sftp"), Icon
|
||||
selectionPane = tabbed.addSelectionTab()
|
||||
}
|
||||
|
||||
selectionPane.connect(host)
|
||||
|
||||
if (host != null) {
|
||||
selectionPane.connect(host)
|
||||
}
|
||||
|
||||
terminalTabbedManager.setSelectedTerminalTab(sftpTab)
|
||||
}
|
||||
}
|
||||
@@ -481,12 +481,12 @@ class NewHostTree : SimpleTree(), Disposable {
|
||||
}
|
||||
|
||||
private fun openWithSFTP(evt: EventObject) {
|
||||
val nodes = getSelectionSimpleTreeNodes(true)
|
||||
val hosts = getSelectionSimpleTreeNodes(true)
|
||||
.map { it.host }.filter { TransferProtocolProvider.valueOf(it.protocol) != null }
|
||||
if (nodes.isEmpty()) return
|
||||
if (hosts.isEmpty()) return
|
||||
|
||||
for (node in nodes) {
|
||||
sftpAction.actionPerformed(TransferActionEvent(this, node.id, evt))
|
||||
for (host in hosts) {
|
||||
sftpAction.actionPerformed(TransferActionEvent(this, host, evt))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,7 @@ termora.actions.zoom-in-terminal=Zoom In Terminal
|
||||
termora.actions.zoom-out-terminal=Zoom Out Terminal
|
||||
termora.actions.zoom-reset-terminal=Reset Terminal Zoom
|
||||
termora.actions.open-local-terminal=Open Local Terminal
|
||||
termora.actions.quick-connect=Quick Connect
|
||||
termora.actions.open-find-everywhere=Open FindEverywhere
|
||||
termora.actions.open-new-window=Open new Window
|
||||
termora.actions.clear-screen=Clear Terminal Screen
|
||||
|
||||
@@ -417,6 +417,7 @@ termora.actions.zoom-in-terminal=放大终端
|
||||
termora.actions.zoom-out-terminal=缩小终端
|
||||
termora.actions.zoom-reset-terminal=重置终端缩放
|
||||
termora.actions.open-local-terminal=打开本地终端
|
||||
termora.actions.quick-connect=快速连接
|
||||
termora.actions.open-find-everywhere=打开全局查找
|
||||
termora.actions.open-new-window=打开新窗口
|
||||
termora.actions.clear-screen=清除终端屏幕
|
||||
|
||||
@@ -405,6 +405,7 @@ termora.actions.zoom-in-terminal=放大終端
|
||||
termora.actions.zoom-out-terminal=縮小終端
|
||||
termora.actions.zoom-reset-terminal=重置終端縮放
|
||||
termora.actions.open-local-terminal=開啟本地終端
|
||||
termora.actions.quick-connect=快速連接
|
||||
termora.actions.open-find-everywhere=開啟全域搜尋
|
||||
termora.actions.open-new-window=開啟新視窗
|
||||
termora.actions.clear-screen=清除終端機螢幕
|
||||
|
||||
Reference in New Issue
Block a user