mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: send command to the current window sessions
This commit is contained in:
@@ -2,12 +2,6 @@ package app.termora
|
|||||||
|
|
||||||
object Actions {
|
object Actions {
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将命令发送到多个会话
|
|
||||||
*/
|
|
||||||
const val MULTIPLE = "MultipleAction"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关键词高亮
|
* 关键词高亮
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package app.termora
|
|
||||||
|
|
||||||
|
|
||||||
import app.termora.terminal.PtyConnector
|
|
||||||
import app.termora.terminal.PtyConnectorDelegate
|
|
||||||
import org.jdesktop.swingx.action.ActionManager
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当开启转发时,会获取到所有的 [PtyConnector] 然后跳过中间层,直接找到最近的一个 [MultiplePtyConnector],如果找不到那就以最后一个匹配不到的为准 [getMultiplePtyConnector]。
|
|
||||||
*/
|
|
||||||
class MultiplePtyConnector(
|
|
||||||
private val myConnector: PtyConnector
|
|
||||||
) : PtyConnectorDelegate(myConnector) {
|
|
||||||
|
|
||||||
private val isMultiple get() = ActionManager.getInstance().isSelected(Actions.MULTIPLE)
|
|
||||||
private val ptyConnectors
|
|
||||||
get() = PtyConnectorFactory.getInstance().getPtyConnectors()
|
|
||||||
|
|
||||||
override fun write(buffer: ByteArray, offset: Int, len: Int) {
|
|
||||||
if (isMultiple) {
|
|
||||||
for (connector in ptyConnectors) {
|
|
||||||
getMultiplePtyConnector(connector).write(buffer, offset, len)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
myConnector.write(buffer, offset, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun getMultiplePtyConnector(connector: PtyConnector): PtyConnector {
|
|
||||||
if (connector is MultiplePtyConnector) {
|
|
||||||
val c = connector.myConnector
|
|
||||||
if (c is MultiplePtyConnector) {
|
|
||||||
return getMultiplePtyConnector(c)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connector is PtyConnectorDelegate) {
|
|
||||||
val c = connector.ptyConnector
|
|
||||||
if (c != null) {
|
|
||||||
return getMultiplePtyConnector(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return connector
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package app.termora
|
package app.termora
|
||||||
|
|
||||||
|
|
||||||
import app.termora.actions.ActionManager
|
import app.termora.actions.AnActionEvent
|
||||||
|
import app.termora.actions.DataProviders
|
||||||
|
import app.termora.actions.MultipleAction
|
||||||
import app.termora.terminal.Terminal
|
import app.termora.terminal.Terminal
|
||||||
import app.termora.terminal.TerminalColor
|
import app.termora.terminal.TerminalColor
|
||||||
import app.termora.terminal.TextStyle
|
import app.termora.terminal.TextStyle
|
||||||
@@ -9,8 +11,10 @@ import app.termora.terminal.panel.FloatingToolbarPanel
|
|||||||
import app.termora.terminal.panel.TerminalDisplay
|
import app.termora.terminal.panel.TerminalDisplay
|
||||||
import app.termora.terminal.panel.TerminalPaintListener
|
import app.termora.terminal.panel.TerminalPaintListener
|
||||||
import app.termora.terminal.panel.TerminalPanel
|
import app.termora.terminal.panel.TerminalPanel
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Graphics
|
import java.awt.Graphics
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class MultipleTerminalListener : TerminalPaintListener {
|
class MultipleTerminalListener : TerminalPaintListener {
|
||||||
override fun after(
|
override fun after(
|
||||||
@@ -21,9 +25,9 @@ class MultipleTerminalListener : TerminalPaintListener {
|
|||||||
terminalDisplay: TerminalDisplay,
|
terminalDisplay: TerminalDisplay,
|
||||||
terminal: Terminal
|
terminal: Terminal
|
||||||
) {
|
) {
|
||||||
if (!ActionManager.getInstance().isSelected(Actions.MULTIPLE)) {
|
val windowScope = AnActionEvent(terminalPanel, StringUtils.EMPTY, EventObject(terminalPanel))
|
||||||
return
|
.getData(DataProviders.WindowScope) ?: return
|
||||||
}
|
if (!MultipleAction.getInstance(windowScope).isSelected) return
|
||||||
|
|
||||||
val oldFont = g.font
|
val oldFont = g.font
|
||||||
val colorPalette = terminal.getTerminalModel().getColorPalette()
|
val colorPalette = terminal.getTerminalModel().getColorPalette()
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ class PtyConnectorFactory : Disposable {
|
|||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(PtyConnectorFactory::class.java)
|
private val log = LoggerFactory.getLogger(PtyConnectorFactory::class.java)
|
||||||
fun getInstance(): PtyConnectorFactory {
|
fun getInstance(): PtyConnectorFactory {
|
||||||
return ApplicationScope.forApplicationScope().getOrCreate(PtyConnectorFactory::class) { PtyConnectorFactory() }
|
return ApplicationScope.forApplicationScope()
|
||||||
|
.getOrCreate(PtyConnectorFactory::class) { PtyConnectorFactory() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,20 +87,14 @@ class PtyConnectorFactory : Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun decorate(ptyConnector: PtyConnector): PtyConnector {
|
fun decorate(ptyConnector: PtyConnector): PtyConnector {
|
||||||
// 集成转发,如果PtyConnector支持转发那么应该在当前注释行前面代理
|
// 宏
|
||||||
val multiplePtyConnector = MultiplePtyConnector(ptyConnector)
|
val macroPtyConnector = MacroPtyConnector(ptyConnector)
|
||||||
// 宏应该在转发前面执行,不然会导致重复录制
|
|
||||||
val macroPtyConnector = MacroPtyConnector(multiplePtyConnector)
|
|
||||||
// 集成自动删除
|
// 集成自动删除
|
||||||
val autoRemovePtyConnector = AutoRemovePtyConnector(macroPtyConnector)
|
val autoRemovePtyConnector = AutoRemovePtyConnector(macroPtyConnector)
|
||||||
ptyConnectors.add(autoRemovePtyConnector)
|
ptyConnectors.add(autoRemovePtyConnector)
|
||||||
return autoRemovePtyConnector
|
return autoRemovePtyConnector
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPtyConnectors(): List<PtyConnector> {
|
|
||||||
return ptyConnectors
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class AutoRemovePtyConnector(connector: PtyConnector) : PtyConnectorDelegate(connector) {
|
private inner class AutoRemovePtyConnector(connector: PtyConnector) : PtyConnectorDelegate(connector) {
|
||||||
override fun close() {
|
override fun close() {
|
||||||
ptyConnectors.remove(this)
|
ptyConnectors.remove(this)
|
||||||
|
|||||||
@@ -23,16 +23,9 @@ abstract class PtyHostTerminalTab(
|
|||||||
|
|
||||||
private var readerJob: Job? = null
|
private var readerJob: Job? = null
|
||||||
private val ptyConnectorDelegate = PtyConnectorDelegate()
|
private val ptyConnectorDelegate = PtyConnectorDelegate()
|
||||||
|
protected val terminalPanel = TerminalPanelFactory.getInstance().createTerminalPanel(terminal, ptyConnectorDelegate)
|
||||||
private val terminalPanelFactory = TerminalPanelFactory.getInstance()
|
|
||||||
protected val terminalPanel = terminalPanelFactory.createTerminalPanel(terminal, ptyConnectorDelegate)
|
|
||||||
.apply { Disposer.register(this@PtyHostTerminalTab, this) }
|
|
||||||
protected val ptyConnectorFactory get() = PtyConnectorFactory.getInstance()
|
protected val ptyConnectorFactory get() = PtyConnectorFactory.getInstance()
|
||||||
|
|
||||||
init {
|
|
||||||
terminal.getTerminalModel().setData(DataKey.PtyConnector, ptyConnectorDelegate)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
|
||||||
@@ -122,10 +115,9 @@ abstract class PtyHostTerminalTab(
|
|||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
stop()
|
stop()
|
||||||
terminalPanel
|
Disposer.dispose(terminalPanel)
|
||||||
super.dispose()
|
super.dispose()
|
||||||
|
|
||||||
|
|
||||||
if (log.isInfoEnabled) {
|
if (log.isInfoEnabled) {
|
||||||
log.info("Host: {} disposed", host.name)
|
log.info("Host: {} disposed", host.name)
|
||||||
}
|
}
|
||||||
@@ -141,6 +133,8 @@ abstract class PtyHostTerminalTab(
|
|||||||
override fun <T : Any> getData(dataKey: DataKey<T>): T? {
|
override fun <T : Any> getData(dataKey: DataKey<T>): T? {
|
||||||
if (dataKey == DataProviders.TerminalPanel) {
|
if (dataKey == DataProviders.TerminalPanel) {
|
||||||
return terminalPanel as T?
|
return terminalPanel as T?
|
||||||
|
} else if (dataKey == DataProviders.TerminalWriter) {
|
||||||
|
return terminalPanel.getData(DataKey.TerminalWriter) as T?
|
||||||
}
|
}
|
||||||
return super.getData(dataKey)
|
return super.getData(dataKey)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
package app.termora
|
package app.termora
|
||||||
|
|
||||||
|
import app.termora.actions.AnActionEvent
|
||||||
|
import app.termora.actions.DataProviders
|
||||||
|
import app.termora.actions.MultipleAction
|
||||||
import app.termora.highlight.KeywordHighlightPaintListener
|
import app.termora.highlight.KeywordHighlightPaintListener
|
||||||
import app.termora.terminal.DataKey
|
|
||||||
import app.termora.terminal.PtyConnector
|
import app.termora.terminal.PtyConnector
|
||||||
import app.termora.terminal.Terminal
|
import app.termora.terminal.Terminal
|
||||||
import app.termora.terminal.panel.TerminalHyperlinkPaintListener
|
import app.termora.terminal.panel.TerminalHyperlinkPaintListener
|
||||||
import app.termora.terminal.panel.TerminalPanel
|
import app.termora.terminal.panel.TerminalPanel
|
||||||
|
import app.termora.terminal.panel.TerminalWriter
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
import java.awt.event.ComponentEvent
|
import java.awt.event.ComponentEvent
|
||||||
import java.awt.event.ComponentListener
|
import java.awt.event.ComponentListener
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.JComponent
|
||||||
import javax.swing.SwingUtilities
|
import javax.swing.SwingUtilities
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
@@ -17,8 +24,6 @@ class TerminalPanelFactory : Disposable {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val Factory = DataKey(TerminalPanelFactory::class)
|
|
||||||
|
|
||||||
fun getInstance(): TerminalPanelFactory {
|
fun getInstance(): TerminalPanelFactory {
|
||||||
return ApplicationScope.forApplicationScope()
|
return ApplicationScope.forApplicationScope()
|
||||||
.getOrCreate(TerminalPanelFactory::class) { TerminalPanelFactory() }
|
.getOrCreate(TerminalPanelFactory::class) { TerminalPanelFactory() }
|
||||||
@@ -32,17 +37,15 @@ class TerminalPanelFactory : Disposable {
|
|||||||
|
|
||||||
|
|
||||||
fun createTerminalPanel(terminal: Terminal, ptyConnector: PtyConnector): TerminalPanel {
|
fun createTerminalPanel(terminal: Terminal, ptyConnector: PtyConnector): TerminalPanel {
|
||||||
val terminalPanel = TerminalPanel(terminal, ptyConnector)
|
val writer = MyTerminalWriter(ptyConnector)
|
||||||
|
val terminalPanel = TerminalPanel(terminal, writer)
|
||||||
terminalPanel.addTerminalPaintListener(MultipleTerminalListener())
|
terminalPanel.addTerminalPaintListener(MultipleTerminalListener())
|
||||||
terminalPanel.addTerminalPaintListener(KeywordHighlightPaintListener.getInstance())
|
terminalPanel.addTerminalPaintListener(KeywordHighlightPaintListener.getInstance())
|
||||||
terminalPanel.addTerminalPaintListener(TerminalHyperlinkPaintListener.getInstance())
|
terminalPanel.addTerminalPaintListener(TerminalHyperlinkPaintListener.getInstance())
|
||||||
terminal.getTerminalModel().setData(Factory, this)
|
|
||||||
|
|
||||||
Disposer.register(terminalPanel, object : Disposable {
|
Disposer.register(terminalPanel, object : Disposable {
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
if (terminal.getTerminalModel().hasData(Factory)) {
|
removeTerminalPanel(terminalPanel)
|
||||||
terminal.getTerminalModel().getData(Factory).removeTerminalPanel(terminalPanel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -70,13 +73,12 @@ class TerminalPanelFactory : Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeTerminalPanel(terminalPanel: TerminalPanel) {
|
private fun removeTerminalPanel(terminalPanel: TerminalPanel) {
|
||||||
terminalPanels.remove(terminalPanel)
|
terminalPanels.remove(terminalPanel)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addTerminalPanel(terminalPanel: TerminalPanel) {
|
private fun addTerminalPanel(terminalPanel: TerminalPanel) {
|
||||||
terminalPanels.add(terminalPanel)
|
terminalPanels.add(terminalPanel)
|
||||||
terminalPanel.terminal.getTerminalModel().setData(Factory, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Painter : Disposable {
|
private class Painter : Disposable {
|
||||||
@@ -102,4 +104,49 @@ class TerminalPanelFactory : Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MyTerminalWriter(private val ptyConnector: PtyConnector) : TerminalWriter {
|
||||||
|
private lateinit var evt: AnActionEvent
|
||||||
|
|
||||||
|
override fun onMounted(c: JComponent) {
|
||||||
|
evt = AnActionEvent(c, StringUtils.EMPTY, EventObject(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(request: TerminalWriter.WriteRequest) {
|
||||||
|
val windowScope = evt.getData(DataProviders.WindowScope)
|
||||||
|
if (windowScope == null) {
|
||||||
|
ptyConnector.write(request.buffer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val multipleAction = MultipleAction.getInstance(windowScope)
|
||||||
|
if (!multipleAction.isSelected) {
|
||||||
|
ptyConnector.write(request.buffer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager)
|
||||||
|
if (terminalTabbedManager == null) {
|
||||||
|
ptyConnector.write(request.buffer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tab in terminalTabbedManager.getTerminalTabs()) {
|
||||||
|
val writer = tab.getData(DataProviders.TerminalWriter) ?: continue
|
||||||
|
if (writer is MyTerminalWriter) {
|
||||||
|
writer.ptyConnector.write(request.buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resize(rows: Int, cols: Int) {
|
||||||
|
ptyConnector.resize(rows, cols)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCharset(): Charset {
|
||||||
|
return ptyConnector.getCharset()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
private val windowScope = ApplicationScope.forWindowScope(this)
|
private val windowScope = ApplicationScope.forWindowScope(this)
|
||||||
private val titleBar = LogicCustomTitleBar.createCustomTitleBar(this)
|
private val titleBar = LogicCustomTitleBar.createCustomTitleBar(this)
|
||||||
private val tabbedPane = MyTabbedPane()
|
private val tabbedPane = MyTabbedPane()
|
||||||
private val toolbar = TermoraToolBar(titleBar, tabbedPane)
|
private val toolbar = TermoraToolBar(windowScope, titleBar, tabbedPane)
|
||||||
private val terminalTabbed = TerminalTabbed(windowScope, toolbar, tabbedPane)
|
private val terminalTabbed = TerminalTabbed(windowScope, toolbar, tabbedPane)
|
||||||
private val isWindowDecorationsSupported by lazy { JBR.isWindowDecorationsSupported() }
|
private val isWindowDecorationsSupported by lazy { JBR.isWindowDecorationsSupported() }
|
||||||
private val dataProviderSupport = DataProviderSupport()
|
private val dataProviderSupport = DataProviderSupport()
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package app.termora
|
package app.termora
|
||||||
|
|
||||||
import app.termora.Application.ohMyJson
|
import app.termora.Application.ohMyJson
|
||||||
import app.termora.actions.ActionManager
|
import app.termora.actions.*
|
||||||
import app.termora.actions.AnAction
|
|
||||||
import app.termora.actions.AnActionEvent
|
|
||||||
import app.termora.actions.SettingsAction
|
|
||||||
import app.termora.findeverywhere.FindEverywhereAction
|
import app.termora.findeverywhere.FindEverywhereAction
|
||||||
import app.termora.snippet.SnippetAction
|
import app.termora.snippet.SnippetAction
|
||||||
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
import com.formdev.flatlaf.extras.components.FlatTabbedPane
|
||||||
@@ -27,6 +24,7 @@ data class ToolBarAction(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class TermoraToolBar(
|
class TermoraToolBar(
|
||||||
|
private val windowScope: WindowScope,
|
||||||
private val titleBar: WindowDecorations.CustomTitleBar,
|
private val titleBar: WindowDecorations.CustomTitleBar,
|
||||||
private val tabbedPane: FlatTabbedPane
|
private val tabbedPane: FlatTabbedPane
|
||||||
) {
|
) {
|
||||||
@@ -49,7 +47,7 @@ class TermoraToolBar(
|
|||||||
ToolBarAction(Actions.MACRO, true),
|
ToolBarAction(Actions.MACRO, true),
|
||||||
ToolBarAction(Actions.KEYWORD_HIGHLIGHT, true),
|
ToolBarAction(Actions.KEYWORD_HIGHLIGHT, true),
|
||||||
ToolBarAction(Actions.KEY_MANAGER, true),
|
ToolBarAction(Actions.KEY_MANAGER, true),
|
||||||
ToolBarAction(Actions.MULTIPLE, true),
|
ToolBarAction(MultipleAction.MULTIPLE, true),
|
||||||
ToolBarAction(FindEverywhereAction.FIND_EVERYWHERE, true),
|
ToolBarAction(FindEverywhereAction.FIND_EVERYWHERE, true),
|
||||||
ToolBarAction(SettingsAction.SETTING, true),
|
ToolBarAction(SettingsAction.SETTING, true),
|
||||||
)
|
)
|
||||||
@@ -126,8 +124,13 @@ class TermoraToolBar(
|
|||||||
// 获取显示的Action,如果不是 false 那么就是显示出来
|
// 获取显示的Action,如果不是 false 那么就是显示出来
|
||||||
for (action in getActions()) {
|
for (action in getActions()) {
|
||||||
if (action.visible) {
|
if (action.visible) {
|
||||||
actionManager.getAction(action.id)?.let {
|
val ac = actionManager.getAction(action.id)
|
||||||
toolbar.add(actionContainerFactory.createButton(it))
|
if (ac == null) {
|
||||||
|
if (action.id == MultipleAction.MULTIPLE) {
|
||||||
|
toolbar.add(actionContainerFactory.createButton(MultipleAction.getInstance(windowScope)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toolbar.add(actionContainerFactory.createButton(ac))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ class ActionManager : org.jdesktop.swingx.action.ActionManager() {
|
|||||||
addAction(NewWindowAction.NEW_WINDOW, NewWindowAction())
|
addAction(NewWindowAction.NEW_WINDOW, NewWindowAction())
|
||||||
addAction(FindEverywhereAction.FIND_EVERYWHERE, FindEverywhereAction())
|
addAction(FindEverywhereAction.FIND_EVERYWHERE, FindEverywhereAction())
|
||||||
|
|
||||||
addAction(Actions.MULTIPLE, MultipleAction())
|
|
||||||
addAction(Actions.APP_UPDATE, AppUpdateAction.getInstance())
|
addAction(Actions.APP_UPDATE, AppUpdateAction.getInstance())
|
||||||
addAction(Actions.KEYWORD_HIGHLIGHT, KeywordHighlightAction())
|
addAction(Actions.KEYWORD_HIGHLIGHT, KeywordHighlightAction())
|
||||||
addAction(Actions.TERMINAL_LOGGER, TerminalLoggerAction())
|
addAction(Actions.TERMINAL_LOGGER, TerminalLoggerAction())
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.termora.terminal.DataKey
|
|||||||
object DataProviders {
|
object DataProviders {
|
||||||
val TerminalPanel = DataKey(app.termora.terminal.panel.TerminalPanel::class)
|
val TerminalPanel = DataKey(app.termora.terminal.panel.TerminalPanel::class)
|
||||||
val Terminal = DataKey(app.termora.terminal.Terminal::class)
|
val Terminal = DataKey(app.termora.terminal.Terminal::class)
|
||||||
val PtyConnector get() = DataKey.PtyConnector
|
val TerminalWriter get() = DataKey.TerminalWriter
|
||||||
|
|
||||||
val TabbedPane = DataKey(app.termora.MyTabbedPane::class)
|
val TabbedPane = DataKey(app.termora.MyTabbedPane::class)
|
||||||
val TerminalTabbed = DataKey(app.termora.TerminalTabbed::class)
|
val TerminalTabbed = DataKey(app.termora.TerminalTabbed::class)
|
||||||
|
|||||||
@@ -3,11 +3,25 @@ package app.termora.actions
|
|||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
import app.termora.Icons
|
import app.termora.Icons
|
||||||
import app.termora.TerminalPanelFactory
|
import app.termora.TerminalPanelFactory
|
||||||
|
import app.termora.WindowScope
|
||||||
|
|
||||||
class MultipleAction : AnAction(
|
class MultipleAction private constructor() : AnAction(
|
||||||
I18n.getString("termora.tools.multiple"),
|
I18n.getString("termora.tools.multiple"),
|
||||||
Icons.vcs
|
Icons.vcs
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将命令发送到多个会话
|
||||||
|
*/
|
||||||
|
const val MULTIPLE = "MultipleAction"
|
||||||
|
|
||||||
|
fun getInstance(windowScope: WindowScope): MultipleAction {
|
||||||
|
return windowScope.getOrCreate(MultipleAction::class) { MultipleAction() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setStateAction()
|
setStateAction()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package app.termora.findeverywhere
|
|||||||
import app.termora.DialogWrapper
|
import app.termora.DialogWrapper
|
||||||
import app.termora.DynamicColor
|
import app.termora.DynamicColor
|
||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
|
import app.termora.WindowScope
|
||||||
import app.termora.actions.AnAction
|
import app.termora.actions.AnAction
|
||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.macro.MacroFindEverywhereProvider
|
import app.termora.macro.MacroFindEverywhereProvider
|
||||||
@@ -18,7 +19,7 @@ import javax.swing.*
|
|||||||
import javax.swing.event.DocumentEvent
|
import javax.swing.event.DocumentEvent
|
||||||
import javax.swing.event.DocumentListener
|
import javax.swing.event.DocumentListener
|
||||||
|
|
||||||
class FindEverywhere(owner: Window) : DialogWrapper(owner) {
|
class FindEverywhere(owner: Window, windowScope: WindowScope) : DialogWrapper(owner) {
|
||||||
private val searchTextField = FlatTextField()
|
private val searchTextField = FlatTextField()
|
||||||
private val model = DefaultListModel<FindEverywhereResult>()
|
private val model = DefaultListModel<FindEverywhereResult>()
|
||||||
private val resultList = FindEverywhereXList(model)
|
private val resultList = FindEverywhereXList(model)
|
||||||
@@ -26,7 +27,7 @@ class FindEverywhere(owner: Window) : DialogWrapper(owner) {
|
|||||||
private val providers = mutableListOf<FindEverywhereProvider>(
|
private val providers = mutableListOf<FindEverywhereProvider>(
|
||||||
BasicFilterFindEverywhereProvider(QuickCommandFindEverywhereProvider()),
|
BasicFilterFindEverywhereProvider(QuickCommandFindEverywhereProvider()),
|
||||||
BasicFilterFindEverywhereProvider(SettingsFindEverywhereProvider()),
|
BasicFilterFindEverywhereProvider(SettingsFindEverywhereProvider()),
|
||||||
BasicFilterFindEverywhereProvider(QuickActionsFindEverywhereProvider()),
|
BasicFilterFindEverywhereProvider(QuickActionsFindEverywhereProvider(windowScope)),
|
||||||
BasicFilterFindEverywhereProvider(MacroFindEverywhereProvider()),
|
BasicFilterFindEverywhereProvider(MacroFindEverywhereProvider()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class FindEverywhereAction : AnAction(StringUtils.EMPTY, Icons.find) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val dialog = FindEverywhere(owner)
|
val dialog = FindEverywhere(owner, scope)
|
||||||
for (provider in FindEverywhereProvider.getFindEverywhereProviders(scope)) {
|
for (provider in FindEverywhereProvider.getFindEverywhereProviders(scope)) {
|
||||||
dialog.registerProvider(provider)
|
dialog.registerProvider(provider)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,32 @@ package app.termora.findeverywhere
|
|||||||
|
|
||||||
import app.termora.Actions
|
import app.termora.Actions
|
||||||
import app.termora.I18n
|
import app.termora.I18n
|
||||||
|
import app.termora.WindowScope
|
||||||
|
import app.termora.actions.MultipleAction
|
||||||
|
|
||||||
import org.jdesktop.swingx.action.ActionManager
|
import org.jdesktop.swingx.action.ActionManager
|
||||||
|
|
||||||
class QuickActionsFindEverywhereProvider : FindEverywhereProvider {
|
class QuickActionsFindEverywhereProvider(private val windowScope: WindowScope) : FindEverywhereProvider {
|
||||||
private val actions = listOf(
|
private val actions = listOf(
|
||||||
Actions.KEY_MANAGER,
|
Actions.KEY_MANAGER,
|
||||||
Actions.KEYWORD_HIGHLIGHT,
|
Actions.KEYWORD_HIGHLIGHT,
|
||||||
Actions.MULTIPLE,
|
MultipleAction.MULTIPLE,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun find(pattern: String): List<FindEverywhereResult> {
|
override fun find(pattern: String): List<FindEverywhereResult> {
|
||||||
val actionManager = ActionManager.getInstance()
|
val actionManager = ActionManager.getInstance()
|
||||||
return actions
|
val results = ArrayList<FindEverywhereResult>()
|
||||||
.mapNotNull { actionManager.getAction(it) }
|
for (action in actions) {
|
||||||
.map { ActionFindEverywhereResult(it) }
|
val ac = actionManager.getAction(action)
|
||||||
|
if (ac == null) {
|
||||||
|
if (action == MultipleAction.MULTIPLE) {
|
||||||
|
results.add(ActionFindEverywhereResult(MultipleAction.getInstance(windowScope)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(ActionFindEverywhereResult(ac))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import app.termora.Icons
|
|||||||
import app.termora.actions.AnAction
|
import app.termora.actions.AnAction
|
||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.terminal.ControlCharacters
|
import app.termora.terminal.ControlCharacters
|
||||||
import app.termora.terminal.DataKey
|
import app.termora.terminal.panel.TerminalWriter
|
||||||
import app.termora.terminal.Terminal
|
|
||||||
|
|
||||||
class SnippetAction private constructor() : AnAction(I18n.getString("termora.snippet.title"), Icons.codeSpan) {
|
class SnippetAction private constructor() : AnAction(I18n.getString("termora.snippet.title"), Icons.codeSpan) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -23,9 +22,8 @@ class SnippetAction private constructor() : AnAction(I18n.getString("termora.sni
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun runSnippet(snippet: Snippet, terminal: Terminal) {
|
fun runSnippet(snippet: Snippet, writer: TerminalWriter) {
|
||||||
if (snippet.type != SnippetType.Snippet) return
|
if (snippet.type != SnippetType.Snippet) return
|
||||||
val terminalModel = terminal.getTerminalModel()
|
|
||||||
val map = mapOf(
|
val map = mapOf(
|
||||||
"\\r" to ControlCharacters.CR,
|
"\\r" to ControlCharacters.CR,
|
||||||
"\\n" to ControlCharacters.LF,
|
"\\n" to ControlCharacters.LF,
|
||||||
@@ -35,13 +33,10 @@ class SnippetAction private constructor() : AnAction(I18n.getString("termora.sni
|
|||||||
"\\b" to ControlCharacters.BS,
|
"\\b" to ControlCharacters.BS,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (terminalModel.hasData(DataKey.PtyConnector)) {
|
|
||||||
var text = snippet.snippet
|
var text = snippet.snippet
|
||||||
for (e in map.entries) {
|
for (e in map.entries) {
|
||||||
text = text.replace(e.key, e.value.toString())
|
text = text.replace(e.key, e.value.toString())
|
||||||
}
|
}
|
||||||
val ptyConnector = terminalModel.getData(DataKey.PtyConnector)
|
writer.write(TerminalWriter.WriteRequest.fromBytes(text.toByteArray(writer.getCharset())))
|
||||||
ptyConnector.write(text.toByteArray(ptyConnector.getCharset()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,12 @@ import app.termora.*
|
|||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
import java.awt.Window
|
import java.awt.Window
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import javax.swing.BorderFactory
|
import javax.swing.BorderFactory
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.JScrollPane
|
import javax.swing.JScrollPane
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
class SnippetTreeDialog(owner: Window) : DialogWrapper(owner) {
|
class SnippetTreeDialog(owner: Window) : DialogWrapper(owner) {
|
||||||
private val snippetTree = SnippetTree()
|
private val snippetTree = SnippetTree()
|
||||||
@@ -23,6 +26,15 @@ class SnippetTreeDialog(owner: Window) : DialogWrapper(owner) {
|
|||||||
setLocationRelativeTo(null)
|
setLocationRelativeTo(null)
|
||||||
init()
|
init()
|
||||||
|
|
||||||
|
snippetTree.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseClicked(e: MouseEvent) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(e) && e.clickCount % 2 == 0) {
|
||||||
|
val node = snippetTree.getLastSelectedPathNode() ?: return
|
||||||
|
if (node.isFolder) return
|
||||||
|
doOKAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Disposer.register(disposable, object : Disposable {
|
Disposer.register(disposable, object : Disposable {
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app.termora.terminal
|
package app.termora.terminal
|
||||||
|
|
||||||
import app.termora.assertEventDispatchThread
|
import app.termora.assertEventDispatchThread
|
||||||
|
import app.termora.terminal.panel.TerminalWriter
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -476,20 +477,20 @@ class ControlSequenceIntroducerProcessor(terminal: Terminal, reader: TerminalRea
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!terminalModel.hasData(DataKey.PtyConnector)) {
|
if (!terminalModel.hasData(DataKey.TerminalWriter)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val ptyConnector = terminalModel.getData(DataKey.PtyConnector)
|
val writer = terminalModel.getData(DataKey.TerminalWriter)
|
||||||
|
|
||||||
val m = args.first()
|
val m = args.first()
|
||||||
if (m == '6') {
|
if (m == '6') {
|
||||||
val position = terminal.getCursorModel().getPosition()
|
val position = terminal.getCursorModel().getPosition()
|
||||||
val bytes = "${ControlCharacters.ESC}[${position.y};${position.x}R".toByteArray(ptyConnector.getCharset())
|
val bytes = "${ControlCharacters.ESC}[${position.y};${position.x}R".toByteArray(writer.getCharset())
|
||||||
ptyConnector.write(bytes)
|
writer.write(TerminalWriter.WriteRequest.fromBytes(bytes))
|
||||||
} else if (m == '5') {
|
} else if (m == '5') {
|
||||||
val bytes = "${ControlCharacters.ESC}[0n".toByteArray(ptyConnector.getCharset())
|
val bytes = "${ControlCharacters.ESC}[0n".toByteArray(writer.getCharset())
|
||||||
ptyConnector.write(bytes)
|
writer.write(TerminalWriter.WriteRequest.fromBytes(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,9 +189,9 @@ class DataKey<T : Any>(val clazz: KClass<T>) {
|
|||||||
val CursorStyle = DataKey(app.termora.terminal.CursorStyle::class)
|
val CursorStyle = DataKey(app.termora.terminal.CursorStyle::class)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pty Connector
|
* TerminalWriter
|
||||||
*/
|
*/
|
||||||
val PtyConnector = DataKey(app.termora.terminal.PtyConnector::class)
|
val TerminalWriter = DataKey(app.termora.terminal.panel.TerminalWriter::class)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -157,13 +157,13 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
btn.addActionListener(object : AnAction() {
|
btn.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
||||||
val terminal = tab.getData(DataProviders.Terminal) ?: return
|
val writer = tab.getData(DataProviders.TerminalWriter) ?: return
|
||||||
val dialog = SnippetTreeDialog(evt.window)
|
val dialog = SnippetTreeDialog(evt.window)
|
||||||
dialog.setLocationRelativeTo(btn)
|
dialog.setLocationRelativeTo(btn)
|
||||||
dialog.setLocation(dialog.x, btn.locationOnScreen.y + height + 2)
|
dialog.setLocation(dialog.x, btn.locationOnScreen.y + height + 2)
|
||||||
dialog.isVisible = true
|
dialog.isVisible = true
|
||||||
val node = dialog.getSelectedNode() ?: return
|
val node = dialog.getSelectedNode() ?: return
|
||||||
SnippetAction.getInstance().runSnippet(node.data, terminal)
|
SnippetAction.getInstance().runSnippet(node.data, writer)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return btn
|
return btn
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import kotlin.time.Duration
|
|||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
|
||||||
class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnector) :
|
class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter) :
|
||||||
JPanel(BorderLayout()), DataProvider, Disposable, VisualWindowManager {
|
JPanel(BorderLayout()), DataProvider, Disposable, VisualWindowManager {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -117,6 +117,9 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
|
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
|
|
||||||
|
writer.onMounted(this)
|
||||||
|
|
||||||
isFocusable = true
|
isFocusable = true
|
||||||
isRequestFocusEnabled = true
|
isRequestFocusEnabled = true
|
||||||
focusTraversalKeysEnabled = false
|
focusTraversalKeysEnabled = false
|
||||||
@@ -146,13 +149,13 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
// DataProviders
|
// DataProviders
|
||||||
dataProviderSupport.addData(DataProviders.TerminalPanel, this)
|
dataProviderSupport.addData(DataProviders.TerminalPanel, this)
|
||||||
dataProviderSupport.addData(DataProviders.Terminal, terminal)
|
dataProviderSupport.addData(DataProviders.Terminal, terminal)
|
||||||
dataProviderSupport.addData(DataProviders.PtyConnector, ptyConnector)
|
dataProviderSupport.addData(DataProviders.TerminalWriter, writer)
|
||||||
dataProviderSupport.addData(FloatingToolbarPanel.FloatingToolbar, floatingToolbar)
|
dataProviderSupport.addData(FloatingToolbarPanel.FloatingToolbar, floatingToolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initEvents() {
|
private fun initEvents() {
|
||||||
|
|
||||||
this.addKeyListener(TerminalPanelKeyAdapter(this, terminal, ptyConnector))
|
this.addKeyListener(TerminalPanelKeyAdapter(this, terminal, writer))
|
||||||
|
|
||||||
this.addFocusListener(object : FocusAdapter() {
|
this.addFocusListener(object : FocusAdapter() {
|
||||||
override fun focusLost(e: FocusEvent) {
|
override fun focusLost(e: FocusEvent) {
|
||||||
@@ -165,7 +168,7 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
repaintImmediate()
|
repaintImmediate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.addComponentListener(TerminalPanelComponentAdapter(this, terminalDisplay, terminal, ptyConnector))
|
this.addComponentListener(TerminalPanelComponentAdapter(this, terminalDisplay, terminal, writer))
|
||||||
|
|
||||||
// 选中相关
|
// 选中相关
|
||||||
val mouseAdapter = TerminalPanelMouseSelectionAdapter(this, terminal)
|
val mouseAdapter = TerminalPanelMouseSelectionAdapter(this, terminal)
|
||||||
@@ -177,7 +180,7 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
this.addMouseListener(hyperlinkAdapter)
|
this.addMouseListener(hyperlinkAdapter)
|
||||||
|
|
||||||
// 鼠标跟踪
|
// 鼠标跟踪
|
||||||
val trackingAdapter = TerminalPanelMouseTrackingAdapter(this, terminal, ptyConnector)
|
val trackingAdapter = TerminalPanelMouseTrackingAdapter(this, terminal, writer)
|
||||||
this.addMouseListener(trackingAdapter)
|
this.addMouseListener(trackingAdapter)
|
||||||
this.addMouseWheelListener(trackingAdapter)
|
this.addMouseWheelListener(trackingAdapter)
|
||||||
|
|
||||||
@@ -329,7 +332,11 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
|
|
||||||
// 输入法提交
|
// 输入法提交
|
||||||
if (committedCharacterCount > 0) {
|
if (committedCharacterCount > 0) {
|
||||||
ptyConnector.write(sb.toString().toByteArray(ptyConnector.getCharset()))
|
writer.write(
|
||||||
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
|
sb.toString().toByteArray(writer.getCharset())
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val breakIterator = BreakIterator.getCharacterInstance()
|
val breakIterator = BreakIterator.getCharacterInstance()
|
||||||
val chars = mutableListOf<Char>()
|
val chars = mutableListOf<Char>()
|
||||||
@@ -439,13 +446,17 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect
|
|||||||
content = content.replace('\n', '\r')
|
content = content.replace('\n', '\r')
|
||||||
|
|
||||||
if (terminal.getTerminalModel().getData(DataKey.BracketedPasteMode, false)) {
|
if (terminal.getTerminalModel().getData(DataKey.BracketedPasteMode, false)) {
|
||||||
ptyConnector.write(
|
writer.write(
|
||||||
"${ControlCharacters.ESC}[200~${content}${ControlCharacters.ESC}[201~".toByteArray(
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
ptyConnector.getCharset()
|
"${ControlCharacters.ESC}[200~${content}${ControlCharacters.ESC}[201~".toByteArray(writer.getCharset())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ptyConnector.write(content.toByteArray(ptyConnector.getCharset()))
|
writer.write(
|
||||||
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
|
content.toByteArray(writer.getCharset())
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.getScrollingModel().scrollToRow(
|
terminal.getScrollingModel().scrollToRow(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.termora.terminal.panel
|
package app.termora.terminal.panel
|
||||||
|
|
||||||
import app.termora.terminal.PtyConnector
|
|
||||||
import app.termora.terminal.Terminal
|
import app.termora.terminal.Terminal
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.awt.event.ComponentAdapter
|
import java.awt.event.ComponentAdapter
|
||||||
@@ -11,7 +10,7 @@ class TerminalPanelComponentAdapter(
|
|||||||
private val terminalPanel: TerminalPanel,
|
private val terminalPanel: TerminalPanel,
|
||||||
private val terminalDisplay: TerminalDisplay,
|
private val terminalDisplay: TerminalDisplay,
|
||||||
private val terminal: Terminal,
|
private val terminal: Terminal,
|
||||||
private val ptyConnector: PtyConnector
|
private val writer: TerminalWriter
|
||||||
) : ComponentAdapter() {
|
) : ComponentAdapter() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -30,7 +29,7 @@ class TerminalPanelComponentAdapter(
|
|||||||
// 修改大小
|
// 修改大小
|
||||||
terminal.getTerminalModel().resize(rows = rows, cols = cols)
|
terminal.getTerminalModel().resize(rows = rows, cols = cols)
|
||||||
// 修改终端大小
|
// 修改终端大小
|
||||||
ptyConnector.resize(rows, cols)
|
writer.resize(rows, cols)
|
||||||
|
|
||||||
if (log.isTraceEnabled) {
|
if (log.isTraceEnabled) {
|
||||||
log.trace("size: {} , cols: {} , rows: {}", terminalPanel.size, cols, rows)
|
log.trace("size: {} , cols: {} , rows: {}", terminalPanel.size, cols, rows)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package app.termora.terminal.panel
|
|||||||
|
|
||||||
import app.termora.keymap.KeyShortcut
|
import app.termora.keymap.KeyShortcut
|
||||||
import app.termora.keymap.KeymapManager
|
import app.termora.keymap.KeymapManager
|
||||||
import app.termora.terminal.PtyConnector
|
|
||||||
import app.termora.terminal.Terminal
|
import app.termora.terminal.Terminal
|
||||||
import com.formdev.flatlaf.util.SystemInfo
|
import com.formdev.flatlaf.util.SystemInfo
|
||||||
import java.awt.event.InputEvent
|
import java.awt.event.InputEvent
|
||||||
@@ -13,7 +12,7 @@ import javax.swing.KeyStroke
|
|||||||
class TerminalPanelKeyAdapter(
|
class TerminalPanelKeyAdapter(
|
||||||
private val terminalPanel: TerminalPanel,
|
private val terminalPanel: TerminalPanel,
|
||||||
private val terminal: Terminal,
|
private val terminal: Terminal,
|
||||||
private val ptyConnector: PtyConnector
|
private val writer: TerminalWriter
|
||||||
) : KeyAdapter() {
|
) : KeyAdapter() {
|
||||||
|
|
||||||
private val activeKeymap get() = KeymapManager.getInstance().getActiveKeymap()
|
private val activeKeymap get() = KeymapManager.getInstance().getActiveKeymap()
|
||||||
@@ -24,7 +23,13 @@ class TerminalPanelKeyAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
terminal.getSelectionModel().clearSelection()
|
terminal.getSelectionModel().clearSelection()
|
||||||
ptyConnector.write("${e.keyChar}".toByteArray(ptyConnector.getCharset()))
|
|
||||||
|
writer.write(
|
||||||
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
|
"${e.keyChar}".toByteArray(writer.getCharset())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
terminal.getScrollingModel().scrollTo(Int.MAX_VALUE)
|
terminal.getScrollingModel().scrollTo(Int.MAX_VALUE)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -47,7 +52,11 @@ class TerminalPanelKeyAdapter(
|
|||||||
|
|
||||||
val encode = terminal.getKeyEncoder().encode(AWTTerminalKeyEvent(e))
|
val encode = terminal.getKeyEncoder().encode(AWTTerminalKeyEvent(e))
|
||||||
if (encode.isNotEmpty()) {
|
if (encode.isNotEmpty()) {
|
||||||
ptyConnector.write(encode.toByteArray(ptyConnector.getCharset()))
|
writer.write(
|
||||||
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
|
"${e.keyChar}".toByteArray(writer.getCharset())
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/TermoraDev/termora/issues/52
|
// https://github.com/TermoraDev/termora/issues/52
|
||||||
@@ -64,7 +73,11 @@ class TerminalPanelKeyAdapter(
|
|||||||
terminal.getSelectionModel().clearSelection()
|
terminal.getSelectionModel().clearSelection()
|
||||||
// 如果不为空表示已经发送过了,所以这里为空的时候再发送
|
// 如果不为空表示已经发送过了,所以这里为空的时候再发送
|
||||||
if (encode.isEmpty()) {
|
if (encode.isEmpty()) {
|
||||||
ptyConnector.write("${e.keyChar}".toByteArray(ptyConnector.getCharset()))
|
writer.write(
|
||||||
|
TerminalWriter.WriteRequest.fromBytes(
|
||||||
|
"${e.keyChar}".toByteArray(writer.getCharset())
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
terminal.getScrollingModel().scrollTo(Int.MAX_VALUE)
|
terminal.getScrollingModel().scrollTo(Int.MAX_VALUE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import kotlin.math.abs
|
|||||||
class TerminalPanelMouseTrackingAdapter(
|
class TerminalPanelMouseTrackingAdapter(
|
||||||
private val terminalPanel: TerminalPanel,
|
private val terminalPanel: TerminalPanel,
|
||||||
private val terminal: Terminal,
|
private val terminal: Terminal,
|
||||||
private val ptyConnector: PtyConnector
|
private val writer: TerminalWriter
|
||||||
) : MouseAdapter() {
|
) : MouseAdapter() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -70,14 +70,18 @@ class TerminalPanelMouseTrackingAdapter(
|
|||||||
val encode = terminal.getKeyEncoder()
|
val encode = terminal.getKeyEncoder()
|
||||||
.encode(TerminalKeyEvent(if (e.wheelRotation < 0) KeyEvent.VK_UP else KeyEvent.VK_DOWN))
|
.encode(TerminalKeyEvent(if (e.wheelRotation < 0) KeyEvent.VK_UP else KeyEvent.VK_DOWN))
|
||||||
if (encode.isBlank()) return
|
if (encode.isBlank()) return
|
||||||
val bytes = encode.toByteArray(ptyConnector.getCharset())
|
val bytes = encode.toByteArray(writer.getCharset())
|
||||||
for (i in 0 until abs(unitsToScroll)) {
|
for (i in 0 until abs(unitsToScroll)) {
|
||||||
ptyConnector.write(bytes)
|
writer.write(TerminalWriter.WriteRequest.fromBytes(bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendMouseEvent(position: Position, event: TerminalMouseEvent, eventType: TerminalMouseEventType) {
|
private fun sendMouseEvent(
|
||||||
|
position: Position,
|
||||||
|
event: TerminalMouseEvent,
|
||||||
|
eventType: TerminalMouseEventType
|
||||||
|
) {
|
||||||
if (event.button == TerminalMouseButton.None) {
|
if (event.button == TerminalMouseButton.None) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -112,7 +116,9 @@ class TerminalPanelMouseTrackingAdapter(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mouseReport(cb: Int, x: Int, y: Int) {
|
private fun mouseReport(
|
||||||
|
cb: Int, x: Int, y: Int
|
||||||
|
) {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
var charset = Charsets.UTF_8
|
var charset = Charsets.UTF_8
|
||||||
|
|
||||||
@@ -143,7 +149,7 @@ class TerminalPanelMouseTrackingAdapter(
|
|||||||
.append((32 + cb).toChar()).append((32 + x).toChar()).append(x).append((32 + y).toChar())
|
.append((32 + cb).toChar()).append((32 + x).toChar()).append(x).append((32 + y).toChar())
|
||||||
}
|
}
|
||||||
|
|
||||||
ptyConnector.write(sb.toString().toByteArray(charset))
|
writer.write(TerminalWriter.WriteRequest.fromBytes(sb.toString().toByteArray(charset)))
|
||||||
|
|
||||||
if (log.isTraceEnabled) {
|
if (log.isTraceEnabled) {
|
||||||
log.trace("Send ESC{}", sb.substring(1))
|
log.trace("Send ESC{}", sb.substring(1))
|
||||||
|
|||||||
46
src/main/kotlin/app/termora/terminal/panel/TerminalWriter.kt
Normal file
46
src/main/kotlin/app/termora/terminal/panel/TerminalWriter.kt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package app.termora.terminal.panel
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import javax.swing.JComponent
|
||||||
|
|
||||||
|
interface TerminalWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 挂载
|
||||||
|
*/
|
||||||
|
fun onMounted(c: JComponent)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数据写入
|
||||||
|
*/
|
||||||
|
fun write(request: WriteRequest)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置大小
|
||||||
|
*/
|
||||||
|
fun resize(rows: Int, cols: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符集
|
||||||
|
*/
|
||||||
|
fun getCharset(): Charset = Charsets.UTF_8
|
||||||
|
|
||||||
|
class WriteRequest private constructor(val buffer: ByteArray) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBytes(bytes: ByteArray): WriteRequest {
|
||||||
|
return WriteRequest(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromBytes(buffer: ByteArray, offset: Int, len: Int): WriteRequest {
|
||||||
|
return WriteRequest(buffer.copyOfRange(offset, offset + len))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromInt(buffer: Int): WriteRequest {
|
||||||
|
return fromBytes(ByteBuffer.allocate(Integer.BYTES).putInt(buffer).flip().array())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -258,12 +258,10 @@ termora.snippet=Snippet
|
|||||||
termora.snippet.title=Snippets
|
termora.snippet.title=Snippets
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
termora.tools.multiple=Send commands to multiple sessions
|
termora.tools.multiple=Send command to the current window sessions
|
||||||
|
|
||||||
# Transport
|
# Transport
|
||||||
termora.transport.local=Local
|
termora.transport.local=Local
|
||||||
termora.transport.parent-folder=Parent Folder
|
|
||||||
termora.transport.show-hidden-files=Show hidden files
|
|
||||||
termora.transport.file-already-exists=The file {0} already exists
|
termora.transport.file-already-exists=The file {0} already exists
|
||||||
|
|
||||||
termora.transport.bookmarks=Bookmarks Manager
|
termora.transport.bookmarks=Bookmarks Manager
|
||||||
@@ -272,7 +270,6 @@ termora.transport.bookmarks.down=Down
|
|||||||
|
|
||||||
termora.transport.table.filename=Filename
|
termora.transport.table.filename=Filename
|
||||||
termora.transport.table.type=Type
|
termora.transport.table.type=Type
|
||||||
termora.transport.table.type.folder=${termora.welcome.contextmenu.new.folder}
|
|
||||||
termora.transport.table.type.symbolic-link=Symbolic Link
|
termora.transport.table.type.symbolic-link=Symbolic Link
|
||||||
termora.transport.table.size=Size
|
termora.transport.table.size=Size
|
||||||
termora.transport.table.modified-time=Modified
|
termora.transport.table.modified-time=Modified
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ termora.keymgr.ssh-copy-id.failed=复制失败
|
|||||||
termora.keymgr.ssh-copy-id.end=复制公钥结束
|
termora.keymgr.ssh-copy-id.end=复制公钥结束
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
termora.tools.multiple=将命令发送到所有会话
|
termora.tools.multiple=将命令发送到当前窗口会话
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -254,8 +254,6 @@ termora.snippet.title=代码片段
|
|||||||
|
|
||||||
# Transport
|
# Transport
|
||||||
termora.transport.local=本机
|
termora.transport.local=本机
|
||||||
termora.transport.parent-folder=父文件夹
|
|
||||||
termora.transport.show-hidden-files=显示隐藏文件
|
|
||||||
termora.transport.file-already-exists=文件 {0} 已存在
|
termora.transport.file-already-exists=文件 {0} 已存在
|
||||||
|
|
||||||
termora.transport.bookmarks=书签管理
|
termora.transport.bookmarks=书签管理
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ termora.keymgr.ssh-copy-id.failed=複製失敗
|
|||||||
termora.keymgr.ssh-copy-id.end=複製公鑰結束
|
termora.keymgr.ssh-copy-id.end=複製公鑰結束
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
termora.tools.multiple=將指令傳送到所有會話
|
termora.tools.multiple=將命令傳送到目前視窗會話
|
||||||
|
|
||||||
|
|
||||||
# Tabbed
|
# Tabbed
|
||||||
@@ -250,8 +250,6 @@ termora.snippet.title=程式碼片段
|
|||||||
|
|
||||||
# Transport
|
# Transport
|
||||||
termora.transport.local=本機
|
termora.transport.local=本機
|
||||||
termora.transport.parent-folder=父資料夾
|
|
||||||
termora.transport.show-hidden-files=顯示隱藏文件
|
|
||||||
termora.transport.file-already-exists=檔案 {0} 已存在
|
termora.transport.file-already-exists=檔案 {0} 已存在
|
||||||
|
|
||||||
termora.transport.bookmarks=書籤管理
|
termora.transport.bookmarks=書籤管理
|
||||||
|
|||||||
Reference in New Issue
Block a user