mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
317 lines
10 KiB
Kotlin
317 lines
10 KiB
Kotlin
package app.termora
|
|
|
|
|
|
import app.termora.actions.*
|
|
import app.termora.findeverywhere.FindEverywhereProvider
|
|
import app.termora.findeverywhere.FindEverywhereResult
|
|
import app.termora.terminal.DataKey
|
|
import com.formdev.flatlaf.FlatClientProperties
|
|
import com.formdev.flatlaf.FlatLaf
|
|
import com.formdev.flatlaf.extras.FlatSVGIcon
|
|
import com.formdev.flatlaf.extras.components.FlatButton
|
|
import com.formdev.flatlaf.extras.components.FlatTextField
|
|
import org.apache.commons.lang3.StringUtils
|
|
import org.jdesktop.swingx.action.ActionManager
|
|
import java.awt.BorderLayout
|
|
import java.awt.Component
|
|
import java.awt.Dimension
|
|
import java.awt.KeyboardFocusManager
|
|
import java.awt.event.ActionEvent
|
|
import java.awt.event.ComponentAdapter
|
|
import java.awt.event.ComponentEvent
|
|
import javax.swing.*
|
|
import javax.swing.event.DocumentEvent
|
|
import kotlin.math.max
|
|
|
|
class WelcomePanel(private val windowScope: WindowScope) : JPanel(BorderLayout()), Disposable, TerminalTab,
|
|
DataProvider {
|
|
private val properties get() = Database.getDatabase().properties
|
|
private val rootPanel = JPanel(BorderLayout())
|
|
private val searchTextField = FlatTextField()
|
|
private val hostTree = NewHostTree()
|
|
private val bannerPanel = BannerPanel()
|
|
private val toggle = FlatButton()
|
|
private var fullContent = properties.getString("WelcomeFullContent", "false").toBoolean()
|
|
private val dataProviderSupport = DataProviderSupport()
|
|
private val hostTreeModel = hostTree.model as NewHostTreeModel
|
|
private var lastFocused: Component? = null
|
|
private val filterableHostTreeModel = FilterableHostTreeModel(hostTree) {
|
|
searchTextField.text.isBlank()
|
|
}
|
|
|
|
init {
|
|
initView()
|
|
initEvents()
|
|
}
|
|
|
|
|
|
private fun initView() {
|
|
putClientProperty(FlatClientProperties.TABBED_PANE_TAB_CLOSABLE, false)
|
|
putClientProperty(FindEverywhereProvider.SKIP_FIND_EVERYWHERE, true)
|
|
|
|
val panel = JPanel(BorderLayout())
|
|
panel.add(createSearchPanel(), BorderLayout.NORTH)
|
|
panel.add(createHostPanel(), BorderLayout.CENTER)
|
|
|
|
if (!fullContent) {
|
|
rootPanel.add(bannerPanel, BorderLayout.NORTH)
|
|
}
|
|
|
|
rootPanel.add(panel, BorderLayout.CENTER)
|
|
add(rootPanel, BorderLayout.CENTER)
|
|
|
|
dataProviderSupport.addData(DataProviders.Welcome.HostTree, hostTree)
|
|
|
|
}
|
|
|
|
private fun createSearchPanel(): JComponent {
|
|
searchTextField.focusTraversalKeysEnabled = false
|
|
searchTextField.preferredSize = Dimension(
|
|
searchTextField.preferredSize.width,
|
|
(UIManager.getInt("TitleBar.height") * 0.85).toInt()
|
|
)
|
|
|
|
|
|
val iconSize = (searchTextField.preferredSize.height * 0.65).toInt()
|
|
|
|
val newHost = FlatButton()
|
|
newHost.icon = FlatSVGIcon(
|
|
Icons.openNewTab.name,
|
|
iconSize,
|
|
iconSize
|
|
)
|
|
newHost.isFocusable = false
|
|
newHost.buttonType = FlatButton.ButtonType.toolBarButton
|
|
newHost.addActionListener { e ->
|
|
ActionManager.getInstance().getAction(NewHostAction.NEW_HOST)?.actionPerformed(e)
|
|
}
|
|
|
|
|
|
toggle.icon = FlatSVGIcon(
|
|
if (fullContent) Icons.collapseAll.name else Icons.collapseAll.name,
|
|
iconSize,
|
|
iconSize
|
|
)
|
|
toggle.isFocusable = false
|
|
toggle.buttonType = FlatButton.ButtonType.toolBarButton
|
|
|
|
val box = Box.createHorizontalBox()
|
|
box.add(searchTextField)
|
|
box.add(Box.createHorizontalStrut(4))
|
|
box.add(newHost)
|
|
box.add(Box.createHorizontalStrut(4))
|
|
box.add(toggle)
|
|
|
|
if (!fullContent) {
|
|
box.border = BorderFactory.createEmptyBorder(20, 0, 0, 0)
|
|
}
|
|
|
|
toggle.addActionListener {
|
|
fullContent = !fullContent
|
|
toggle.icon = FlatSVGIcon(
|
|
if (fullContent) Icons.collapseAll.name else Icons.collapseAll.name,
|
|
iconSize,
|
|
iconSize
|
|
)
|
|
if (fullContent) {
|
|
box.border = BorderFactory.createEmptyBorder()
|
|
} else {
|
|
box.border = BorderFactory.createEmptyBorder(20, 0, 0, 0)
|
|
}
|
|
perform()
|
|
}
|
|
|
|
return box
|
|
}
|
|
|
|
private fun createHostPanel(): JComponent {
|
|
val panel = JPanel(BorderLayout())
|
|
hostTree.actionMap.put("find", object : AnAction() {
|
|
override fun actionPerformed(evt: AnActionEvent) {
|
|
searchTextField.requestFocusInWindow()
|
|
}
|
|
})
|
|
hostTree.showsRootHandles = true
|
|
|
|
val scrollPane = JScrollPane(hostTree)
|
|
scrollPane.verticalScrollBar.maximumSize = Dimension(0, 0)
|
|
scrollPane.verticalScrollBar.preferredSize = Dimension(0, 0)
|
|
scrollPane.verticalScrollBar.minimumSize = Dimension(0, 0)
|
|
scrollPane.border = BorderFactory.createEmptyBorder()
|
|
|
|
|
|
panel.add(scrollPane, BorderLayout.CENTER)
|
|
panel.border = BorderFactory.createEmptyBorder(10, 0, 0, 0)
|
|
|
|
hostTree.model = filterableHostTreeModel
|
|
TreeUtils.loadExpansionState(
|
|
hostTree,
|
|
properties.getString("Welcome.HostTree.state", StringUtils.EMPTY)
|
|
)
|
|
|
|
return panel
|
|
}
|
|
|
|
|
|
private fun initEvents() {
|
|
|
|
addComponentListener(object : ComponentAdapter() {
|
|
override fun componentShown(e: ComponentEvent) {
|
|
if (!searchTextField.hasFocus()) {
|
|
searchTextField.requestFocusInWindow()
|
|
}
|
|
perform()
|
|
removeComponentListener(this)
|
|
}
|
|
})
|
|
|
|
|
|
addComponentListener(object : ComponentAdapter() {
|
|
override fun componentResized(e: ComponentEvent) {
|
|
perform()
|
|
}
|
|
})
|
|
|
|
|
|
FindEverywhereProvider.getFindEverywhereProviders(windowScope).add(object : FindEverywhereProvider {
|
|
override fun find(pattern: String): List<FindEverywhereResult> {
|
|
var filter = hostTreeModel.root.getAllChildren()
|
|
.map { it.host }
|
|
.filter { it.protocol != Protocol.Folder }
|
|
|
|
if (pattern.isNotBlank()) {
|
|
filter = filter.filter {
|
|
if (it.protocol == Protocol.SSH) {
|
|
it.name.contains(pattern, true) || it.host.contains(pattern, true)
|
|
} else {
|
|
it.name.contains(pattern, true)
|
|
}
|
|
}
|
|
}
|
|
|
|
return filter.map { HostFindEverywhereResult(it) }
|
|
}
|
|
|
|
override fun group(): String {
|
|
return I18n.getString("termora.find-everywhere.groups.open-new-hosts")
|
|
}
|
|
|
|
override fun order(): Int {
|
|
return Integer.MIN_VALUE + 2
|
|
}
|
|
})
|
|
|
|
|
|
filterableHostTreeModel.addFilter {
|
|
val text = searchTextField.text
|
|
val host = it.host
|
|
text.isBlank() || host.name.contains(text, true)
|
|
|| host.host.contains(text, true)
|
|
|| host.username.contains(text, true)
|
|
}
|
|
|
|
searchTextField.document.addDocumentListener(object : DocumentAdaptor() {
|
|
override fun changedUpdate(e: DocumentEvent) {
|
|
val text = searchTextField.text
|
|
filterableHostTreeModel.refresh()
|
|
if (text.isNotBlank()) {
|
|
hostTree.expandAll()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
private fun perform() {
|
|
rootPanel.remove(bannerPanel)
|
|
if (fullContent) {
|
|
rootPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
|
} else {
|
|
val top = max((height * 0.08).toInt(), 30)
|
|
val left = max((width * 0.25).toInt(), 30)
|
|
rootPanel.add(bannerPanel, BorderLayout.NORTH)
|
|
rootPanel.border = BorderFactory.createEmptyBorder(top, left, top / 2, left)
|
|
SwingUtilities.invokeLater {
|
|
rootPanel.revalidate()
|
|
rootPanel.repaint()
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
override fun getTitle(): String {
|
|
return I18n.getString("termora.title")
|
|
}
|
|
|
|
override fun getIcon(): Icon {
|
|
return Icons.homeFolder
|
|
}
|
|
|
|
override fun getJComponent(): JComponent {
|
|
return this
|
|
}
|
|
|
|
override fun canReconnect(): Boolean {
|
|
return false
|
|
}
|
|
|
|
override fun canClose(): Boolean {
|
|
return false
|
|
}
|
|
|
|
override fun canClone(): Boolean {
|
|
return false
|
|
}
|
|
|
|
override fun onLostFocus() {
|
|
lastFocused = KeyboardFocusManager.getCurrentKeyboardFocusManager().focusOwner
|
|
}
|
|
|
|
override fun onGrabFocus() {
|
|
SwingUtilities.invokeLater { lastFocused?.requestFocusInWindow() }
|
|
}
|
|
|
|
override fun dispose() {
|
|
properties.putString("WelcomeFullContent", fullContent.toString())
|
|
properties.putString("Welcome.HostTree.state", TreeUtils.saveExpansionState(hostTree))
|
|
}
|
|
|
|
private inner class HostFindEverywhereResult(val host: Host) : FindEverywhereResult {
|
|
private val showMoreInfo get() = properties.getString("HostTree.showMoreInfo", "false").toBoolean()
|
|
|
|
override fun actionPerformed(e: ActionEvent) {
|
|
ActionManager.getInstance()
|
|
.getAction(OpenHostAction.OPEN_HOST)
|
|
?.actionPerformed(OpenHostActionEvent(e.source, host, e))
|
|
}
|
|
|
|
override fun getIcon(isSelected: Boolean): Icon {
|
|
if (isSelected) {
|
|
if (!FlatLaf.isLafDark()) {
|
|
return Icons.terminal.dark
|
|
}
|
|
}
|
|
return Icons.terminal
|
|
}
|
|
|
|
override fun getText(isSelected: Boolean): String {
|
|
if (showMoreInfo) {
|
|
val color = UIManager.getColor(if (isSelected) "textHighlightText" else "textInactiveText")
|
|
val moreInfo = when (host.protocol) {
|
|
Protocol.SSH -> "${host.username}@${host.host}"
|
|
Protocol.Serial -> host.options.serialComm.port
|
|
else -> StringUtils.EMPTY
|
|
}
|
|
if (moreInfo.isNotBlank()) {
|
|
return "<html>${host.name} <font color=rgb(${color.red},${color.green},${color.blue})>${moreInfo}</font></html>"
|
|
}
|
|
}
|
|
return host.name
|
|
}
|
|
}
|
|
|
|
override fun <T : Any> getData(dataKey: DataKey<T>): T? {
|
|
return dataProviderSupport.getData(dataKey)
|
|
}
|
|
|
|
|
|
} |