mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-15 18:02:58 +08:00
feat: host tree supports copy and paste
This commit is contained in:
@@ -3,6 +3,8 @@ package app.termora.tree
|
|||||||
import app.termora.*
|
import app.termora.*
|
||||||
import app.termora.Application.ohMyJson
|
import app.termora.Application.ohMyJson
|
||||||
import app.termora.account.AccountManager
|
import app.termora.account.AccountManager
|
||||||
|
import app.termora.actions.AnAction
|
||||||
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.actions.OpenHostAction
|
import app.termora.actions.OpenHostAction
|
||||||
import app.termora.database.DatabaseChangedExtension
|
import app.termora.database.DatabaseChangedExtension
|
||||||
import app.termora.database.DatabaseManager
|
import app.termora.database.DatabaseManager
|
||||||
@@ -32,6 +34,10 @@ import org.jdesktop.swingx.action.ActionManager
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.NodeList
|
import org.w3c.dom.NodeList
|
||||||
|
import java.awt.datatransfer.DataFlavor
|
||||||
|
import java.awt.datatransfer.StringSelection
|
||||||
|
import java.awt.datatransfer.Transferable
|
||||||
|
import java.awt.datatransfer.UnsupportedFlavorException
|
||||||
import java.awt.event.*
|
import java.awt.event.*
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -140,6 +146,41 @@ class NewHostTree : SimpleTree(), Disposable {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
actionMap.put("copy", object : AnAction() {
|
||||||
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
|
toolkit.systemClipboard.setContents(StringSelection(StringUtils.EMPTY), null)
|
||||||
|
val nodes = getSelectionSimpleTreeNodes(false).toMutableList()
|
||||||
|
nodes.removeIf { e -> e.getParents().any { nodes.contains(it) } }
|
||||||
|
if (nodes.isEmpty() || nodes.any { it is TeamTreeNode }) return
|
||||||
|
if (nodes.any { it.id == "0" || it.id.isBlank() }) return
|
||||||
|
toolkit.systemClipboard.setContents(NodesTransferable(nodes), null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
actionMap.put("paste", object : AnAction() {
|
||||||
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
|
val lastNode = getLastSelectedPathNode() ?: return
|
||||||
|
val folder = if (lastNode.isFolder) lastNode.parent ?: simpleTreeModel.root
|
||||||
|
else lastNode.parent ?: return
|
||||||
|
|
||||||
|
if (toolkit.systemClipboard.isDataFlavorAvailable(NodesTransferable.FLAVOR).not()) return
|
||||||
|
val nodes = (toolkit.systemClipboard.getData(NodesTransferable.FLAVOR) as? List<*>)
|
||||||
|
?.filterIsInstance<HostTreeNode>() ?: return
|
||||||
|
|
||||||
|
for (node in nodes) {
|
||||||
|
val newNode = copyNode(node, folder.id)
|
||||||
|
// 复制的是文件夹,就在最后面
|
||||||
|
if (newNode.isFolder) {
|
||||||
|
simpleTreeModel.insertNodeInto(newNode, folder, folder.folderCount)
|
||||||
|
} else if (lastNode.isFolder) { // 用户选的节点是文件夹,那就在最后一个child下面
|
||||||
|
simpleTreeModel.insertNodeInto(newNode, folder, folder.childCount)
|
||||||
|
} else { // 用户选的是主机并且复制的是主机
|
||||||
|
simpleTreeModel.insertNodeInto(newNode, folder, folder.getIndex(lastNode) + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restoreExpansions() {
|
fun restoreExpansions() {
|
||||||
@@ -391,6 +432,20 @@ class NewHostTree : SimpleTree(), Disposable {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val mnemonics = mapOf(
|
||||||
|
refresh to KeyEvent.VK_R,
|
||||||
|
newMenu to KeyEvent.VK_W,
|
||||||
|
newFolder to KeyEvent.VK_F,
|
||||||
|
rename to KeyEvent.VK_M,
|
||||||
|
remove to KeyEvent.VK_D,
|
||||||
|
property to KeyEvent.VK_I,
|
||||||
|
)
|
||||||
|
|
||||||
|
for ((item, mnemonic) in mnemonics) {
|
||||||
|
item.text = "${item.text}(${KeyEvent.getKeyText(mnemonic)})"
|
||||||
|
item.setMnemonic(mnemonic)
|
||||||
|
}
|
||||||
|
|
||||||
popupMenu.show(this, evt.x, evt.y)
|
popupMenu.show(this, evt.x, evt.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,5 +1132,23 @@ class NewHostTree : SimpleTree(), Disposable {
|
|||||||
electerm,
|
electerm,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NodesTransferable(val nodes: List<HostTreeNode>) : Transferable {
|
||||||
|
companion object {
|
||||||
|
val FLAVOR = DataFlavor("termora/host-tree", "Termora host tree transfers")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTransferDataFlavors(): Array<out DataFlavor> {
|
||||||
|
return arrayOf(FLAVOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isDataFlavorSupported(flavor: DataFlavor?): Boolean {
|
||||||
|
return flavor == FLAVOR
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTransferData(flavor: DataFlavor?): Any {
|
||||||
|
return if (flavor == FLAVOR) nodes else throw UnsupportedFlavorException(flavor)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package app.termora.tree
|
|||||||
|
|
||||||
import javax.swing.Icon
|
import javax.swing.Icon
|
||||||
import javax.swing.tree.DefaultMutableTreeNode
|
import javax.swing.tree.DefaultMutableTreeNode
|
||||||
|
import javax.swing.tree.TreeNode
|
||||||
|
|
||||||
abstract class SimpleTreeNode<T>(data: T) : DefaultMutableTreeNode(data) {
|
abstract class SimpleTreeNode<T>(data: T) : DefaultMutableTreeNode(data) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -35,4 +36,15 @@ abstract class SimpleTreeNode<T>(data: T) : DefaultMutableTreeNode(data) {
|
|||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun getParents(): List<SimpleTreeNode<T>> {
|
||||||
|
val parents = mutableListOf<SimpleTreeNode<T>>()
|
||||||
|
var p = parent as TreeNode?
|
||||||
|
while (p != null) {
|
||||||
|
if (p is SimpleTreeNode<T>) {
|
||||||
|
parents.add(p)
|
||||||
|
}
|
||||||
|
p = p.parent
|
||||||
|
}
|
||||||
|
return parents
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user