mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: transfer supports copy and paste
This commit is contained in:
@@ -467,6 +467,15 @@ internal class TransportPanel(
|
||||
}
|
||||
})
|
||||
|
||||
table.actionMap.put("copy", object : AbstractAction() {
|
||||
override fun actionPerformed(e: ActionEvent) {
|
||||
val rows = table.selectedRows.map { sorter.convertRowIndexToModel(it) }.toTypedArray()
|
||||
val files = rows.map { model.getPath(it) to model.getAttributes(it) }
|
||||
if (files.any { it.second.isParent }) return
|
||||
toolkit.systemClipboard.setContents(TransferTransferable(panel, files), null)
|
||||
}
|
||||
})
|
||||
|
||||
table.actionMap.put("Reload", object : AbstractAction() {
|
||||
override fun actionPerformed(e: ActionEvent) {
|
||||
reload()
|
||||
@@ -514,7 +523,6 @@ internal class TransportPanel(
|
||||
data class TransferData(
|
||||
// true 就是本地拖拽上传
|
||||
val locally: Boolean,
|
||||
val row: Int,
|
||||
val insertRow: Boolean,
|
||||
val workdir: Path,
|
||||
val files: List<Pair<Path, Attributes>>
|
||||
@@ -540,18 +548,22 @@ internal class TransportPanel(
|
||||
|
||||
private fun getTransferData(support: TransferSupport, load: Boolean): TransferData? {
|
||||
val workdir = workdir ?: return null
|
||||
val dropLocation = support.dropLocation as? JTable.DropLocation ?: return null
|
||||
val row = if (dropLocation.isInsertRow) 0 else sorter.convertRowIndexToModel(dropLocation.row)
|
||||
if (dropLocation.isInsertRow.not() && dropLocation.column != TransportTableModel.COLUMN_NAME) return null
|
||||
if (dropLocation.isInsertRow.not() && model.getAttributes(row).isDirectory.not()) return null
|
||||
if (hasParent && dropLocation.row == 0) return null
|
||||
val paths = mutableListOf<Pair<Path, Attributes>>()
|
||||
var locally = false
|
||||
|
||||
if (support.isDataFlavorSupported(TransferTransferable.FLAVOR)) {
|
||||
val transferTransferable = support.transferable.getTransferData(TransferTransferable.FLAVOR)
|
||||
as? TransferTransferable ?: return null
|
||||
if (support.isDrop) {
|
||||
if (transferTransferable.component == panel) return null
|
||||
} else {
|
||||
// 如果在一个目录,那么是不允许粘贴的
|
||||
for (pair in transferTransferable.files) {
|
||||
if (pair.first.parent?.pathString == workdir.pathString) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
paths.addAll(transferTransferable.files)
|
||||
} else if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
|
||||
if (loader.isLoaded() && loader.getSyncTransportSupport().getFileSystem().isLocallyFileSystem())
|
||||
@@ -569,15 +581,29 @@ internal class TransportPanel(
|
||||
return null
|
||||
}
|
||||
|
||||
if (support.isDrop) {
|
||||
val dropLocation = support.dropLocation as? JTable.DropLocation ?: return null
|
||||
val row = if (dropLocation.isInsertRow) 0 else sorter.convertRowIndexToModel(dropLocation.row)
|
||||
if (dropLocation.isInsertRow.not() && dropLocation.column != TransportTableModel.COLUMN_NAME) return null
|
||||
if (dropLocation.isInsertRow.not() && model.getAttributes(row).isDirectory.not()) return null
|
||||
if (hasParent && dropLocation.row == 0) return null
|
||||
return TransferData(
|
||||
locally = locally,
|
||||
row = row,
|
||||
insertRow = dropLocation.isInsertRow,
|
||||
workdir = if (dropLocation.isInsertRow) workdir else model.getPath(row),
|
||||
files = paths
|
||||
)
|
||||
}
|
||||
|
||||
return TransferData(
|
||||
locally = locally,
|
||||
insertRow = false,
|
||||
workdir = workdir,
|
||||
files = paths
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
override fun getSourceActions(c: JComponent?): Int {
|
||||
return COPY
|
||||
}
|
||||
@@ -899,7 +925,7 @@ internal class TransportPanel(
|
||||
}
|
||||
}
|
||||
|
||||
private class TransferTransferable(val component: TransportPanel, val files: List<Pair<Path, Attributes>>) :
|
||||
class TransferTransferable(val component: TransportPanel, val files: List<Pair<Path, Attributes>>) :
|
||||
Transferable {
|
||||
companion object {
|
||||
val FLAVOR = DataFlavor("termora/transfers", "Termora transfers")
|
||||
@@ -1041,7 +1067,6 @@ internal class TransportPanel(
|
||||
}
|
||||
|
||||
private inner class PopupMenuActionListener(private val files: List<Pair<Path, Attributes>>) : ActionListener {
|
||||
@Suppress("CascadeIf")
|
||||
override fun actionPerformed(e: ActionEvent) {
|
||||
val actionCommand = TransportPopupMenu.ActionCommand.valueOf(e.actionCommand)
|
||||
if (actionCommand == TransportPopupMenu.ActionCommand.Transfer) {
|
||||
@@ -1089,6 +1114,12 @@ internal class TransportPanel(
|
||||
Files.setPosixFilePermissions(path, c.permissions)
|
||||
}
|
||||
}
|
||||
} else if (actionCommand == TransportPopupMenu.ActionCommand.Copy) {
|
||||
val transferable = TransferTransferable(panel, files)
|
||||
toolkit.systemClipboard.setContents(transferable, null)
|
||||
} else if (actionCommand == TransportPopupMenu.ActionCommand.Paste) {
|
||||
val transferable = toolkit.systemClipboard.getContents(null) ?: return
|
||||
table.transferHandler.importData(TransferHandler.TransferSupport(table, transferable))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import javax.swing.JMenu
|
||||
import javax.swing.JMenuItem
|
||||
import javax.swing.JOptionPane
|
||||
import javax.swing.event.EventListenerList
|
||||
import javax.swing.event.PopupMenuEvent
|
||||
import javax.swing.event.PopupMenuListener
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.name
|
||||
|
||||
@@ -39,6 +41,8 @@ internal class TransportPopupMenu(
|
||||
private val transferMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.transfer"))
|
||||
private val editMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.edit"))
|
||||
private val copyPathMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.copy-path"))
|
||||
private val copyMenu = JMenuItem(I18n.getString("termora.copy"))
|
||||
private val pasteMenu = JMenuItem(I18n.getString("termora.paste"))
|
||||
private val openInFinderMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.open-in-folder"))
|
||||
private val renameMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.rename"))
|
||||
private val deleteMenu = JMenuItem(I18n.getString("termora.transport.table.contextmenu.delete"))
|
||||
@@ -82,6 +86,9 @@ internal class TransportPopupMenu(
|
||||
add(transferMenu)
|
||||
add(editMenu)
|
||||
addSeparator()
|
||||
add(copyMenu)
|
||||
add(pasteMenu)
|
||||
addSeparator()
|
||||
add(copyPathMenu)
|
||||
if (fileSystem?.isLocallyFileSystem() == true) {
|
||||
add(openInFinderMenu)
|
||||
@@ -133,6 +140,7 @@ internal class TransportPopupMenu(
|
||||
renameMenu.isEnabled = hasParent.not() && files.size == 1
|
||||
deleteMenu.isEnabled = hasParent.not() && files.isNotEmpty()
|
||||
changePermissionsMenu.isVisible = hasParent.not() && fileSystem is SftpFileSystem && files.size == 1
|
||||
copyMenu.isEnabled = hasParent.not() && files.isNotEmpty()
|
||||
|
||||
for ((item, mnemonic) in mnemonics) {
|
||||
item.text = "${item.text}(${KeyEvent.getKeyText(mnemonic)})"
|
||||
@@ -166,6 +174,22 @@ internal class TransportPopupMenu(
|
||||
sb.deleteCharAt(sb.length - 1)
|
||||
toolkit.systemClipboard.setContents(StringSelection(sb.toString()), null)
|
||||
}
|
||||
copyMenu.addActionListener { fireActionPerformed(it, ActionCommand.Copy) }
|
||||
pasteMenu.addActionListener { fireActionPerformed(it, ActionCommand.Paste) }
|
||||
|
||||
addPopupMenuListener(object : PopupMenuListener {
|
||||
override fun popupMenuWillBecomeVisible(e: PopupMenuEvent?) {
|
||||
pasteMenu.isEnabled = toolkit.systemClipboard
|
||||
.isDataFlavorAvailable(TransportPanel.TransferTransferable.FLAVOR)
|
||||
}
|
||||
|
||||
override fun popupMenuWillBecomeInvisible(e: PopupMenuEvent?) {
|
||||
}
|
||||
|
||||
override fun popupMenuCanceled(e: PopupMenuEvent?) {
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun fireActionPerformed(evt: ActionEvent, command: ActionCommand) {
|
||||
@@ -241,6 +265,8 @@ internal class TransportPopupMenu(
|
||||
ChangePermissions,
|
||||
Rmrf,
|
||||
Reconnect,
|
||||
Paste,
|
||||
Copy,
|
||||
}
|
||||
|
||||
data class ChangePermission(val permissions: Set<PosixFilePermission>, val includeSubFolder: Boolean)
|
||||
|
||||
@@ -3,6 +3,7 @@ termora.confirm=OK
|
||||
termora.exit=退出
|
||||
termora.cancel=Cancel
|
||||
termora.copy=Copy
|
||||
termora.paste=Paste
|
||||
termora.apply=Apply
|
||||
termora.save=Save
|
||||
termora.remove=Delete
|
||||
|
||||
@@ -3,6 +3,7 @@ termora.confirm=Ок
|
||||
termora.exit=покидать
|
||||
termora.cancel=Отмена
|
||||
termora.copy=Копировать
|
||||
termora.paste=Вставить
|
||||
termora.apply=Применить
|
||||
termora.save=Сохранить
|
||||
termora.remove=Удалить
|
||||
|
||||
@@ -2,6 +2,7 @@ termora.confirm=确认
|
||||
termora.exit=退出
|
||||
termora.cancel=取消
|
||||
termora.copy=复制
|
||||
termora.paste=粘贴
|
||||
termora.apply=应用
|
||||
termora.save=保存
|
||||
termora.remove=删除
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
termora.confirm=確定
|
||||
termora.exit=Exit
|
||||
termora.cancel=取消
|
||||
termora.copy=複製
|
||||
termora.paste=貼上
|
||||
termora.apply=应用
|
||||
termora.save=儲存
|
||||
termora.remove=刪除
|
||||
|
||||
Reference in New Issue
Block a user