diff --git a/src/main/kotlin/app/termora/transfer/TransportPanel.kt b/src/main/kotlin/app/termora/transfer/TransportPanel.kt index 2005fb5..827546e 100644 --- a/src/main/kotlin/app/termora/transfer/TransportPanel.kt +++ b/src/main/kotlin/app/termora/transfer/TransportPanel.kt @@ -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> @@ -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>() var locally = false if (support.isDataFlavorSupported(TransferTransferable.FLAVOR)) { val transferTransferable = support.transferable.getTransferData(TransferTransferable.FLAVOR) as? TransferTransferable ?: return null - if (transferTransferable.component == panel) 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,13 +581,27 @@ 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, + insertRow = dropLocation.isInsertRow, + workdir = if (dropLocation.isInsertRow) workdir else model.getPath(row), + files = paths + ) + } + return TransferData( locally = locally, - row = row, - insertRow = dropLocation.isInsertRow, - workdir = if (dropLocation.isInsertRow) workdir else model.getPath(row), + insertRow = false, + workdir = workdir, files = paths ) + } override fun getSourceActions(c: JComponent?): Int { @@ -899,7 +925,7 @@ internal class TransportPanel( } } - private class TransferTransferable(val component: TransportPanel, val files: List>) : + class TransferTransferable(val component: TransportPanel, val files: List>) : 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>) : 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)) } } diff --git a/src/main/kotlin/app/termora/transfer/TransportPopupMenu.kt b/src/main/kotlin/app/termora/transfer/TransportPopupMenu.kt index 7b128fe..c10506e 100644 --- a/src/main/kotlin/app/termora/transfer/TransportPopupMenu.kt +++ b/src/main/kotlin/app/termora/transfer/TransportPopupMenu.kt @@ -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, val includeSubFolder: Boolean) diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index cb568eb..bb82afb 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -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 diff --git a/src/main/resources/i18n/messages_ru_RU.properties b/src/main/resources/i18n/messages_ru_RU.properties index 616bfa0..472895f 100644 --- a/src/main/resources/i18n/messages_ru_RU.properties +++ b/src/main/resources/i18n/messages_ru_RU.properties @@ -3,6 +3,7 @@ termora.confirm=Ок termora.exit=покидать termora.cancel=Отмена termora.copy=Копировать +termora.paste=Вставить termora.apply=Применить termora.save=Сохранить termora.remove=Удалить diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index a5f26c9..779765f 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -2,6 +2,7 @@ termora.confirm=确认 termora.exit=退出 termora.cancel=取消 termora.copy=复制 +termora.paste=粘贴 termora.apply=应用 termora.save=保存 termora.remove=删除 diff --git a/src/main/resources/i18n/messages_zh_TW.properties b/src/main/resources/i18n/messages_zh_TW.properties index 9b97a34..6698c04 100644 --- a/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -1,6 +1,8 @@ termora.confirm=確定 termora.exit=Exit termora.cancel=取消 +termora.copy=複製 +termora.paste=貼上 termora.apply=应用 termora.save=儲存 termora.remove=刪除