feat: ftp plugin

This commit is contained in:
hstyi
2025-07-08 11:32:13 +08:00
committed by hstyi
parent ecf61bedc4
commit 165d544448
33 changed files with 826 additions and 88 deletions

View File

@@ -153,7 +153,8 @@ class TermoraFencePanel(
override fun dispose() {
enableManager.setFlag("Termora.Fence.dividerLocation", max(splitPane.dividerLocation, 10))
if (leftTreePanel.isVisible)
enableManager.setFlag("Termora.Fence.dividerLocation", max(splitPane.dividerLocation, 10))
}
fun getHostTree(): NewHostTree {

View File

@@ -78,8 +78,6 @@ class BasicProxyOption(
proxyAuthenticationTypeComboBox.addItem(type)
}
proxyUsernameTextField.text = "root"
refreshStates()
}

View File

@@ -8,6 +8,7 @@ import com.formdev.flatlaf.FlatLaf
import com.formdev.flatlaf.util.UIScale
import com.github.weisj.jsvg.SVGDocument
import com.github.weisj.jsvg.parser.SVGLoader
import com.github.weisj.jsvg.parser.impl.MutableLoaderContext
import java.awt.Component
import java.awt.Graphics
import java.awt.Graphics2D
@@ -21,8 +22,8 @@ class PluginSVGIcon(input: InputStream, dark: InputStream? = null) : Icon {
}
private val document = svgLoader.load(input)
private val darkDocument = dark?.let { svgLoader.load(it) }
private val document = svgLoader.load(input, null, MutableLoaderContext.createDefault())
private val darkDocument = dark?.let { svgLoader.load(it, null, MutableLoaderContext.createDefault()) }
override fun getIconHeight(): Int {
return 32

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing
import okio.withLock
import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode
import org.jdesktop.swingx.treetable.DefaultTreeTableModel
import org.slf4j.LoggerFactory
@@ -500,6 +501,10 @@ class TransferTableModel(private val coroutineScope: CoroutineScope) :
}
override fun dispose() {
removeTransfer(StringUtils.EMPTY)
}
private class UserCanceledException : RuntimeException()

View File

@@ -9,6 +9,7 @@ import app.termora.plugin.ExtensionManager
import app.termora.plugin.internal.wsl.WSLHostTerminalTab
import app.termora.terminal.DataKey
import app.termora.transfer.TransportTableModel.Attributes
import app.termora.transfer.s3.S3FileAttributes
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatToolBar
import com.formdev.flatlaf.icons.FlatTreeClosedIcon
@@ -48,6 +49,7 @@ import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong
import java.util.stream.Stream
import javax.swing.*
import javax.swing.TransferHandler
@@ -83,6 +85,7 @@ internal class TransportPanel(
}
private val mod = AtomicLong(0)
private val owner get() = SwingUtilities.getWindowAncestor(this)
private val lru = object : LinkedHashMap<String, Icon?>() {
override fun removeEldestEntry(eldest: Map.Entry<String?, Icon?>?): Boolean {
@@ -113,7 +116,7 @@ internal class TransportPanel(
get() = enableManager.getFlag(showHiddenFilesKey, true)
set(value) = enableManager.setFlag(showHiddenFilesKey, value)
private val navigator get() = this
private val nextReloadCallbacks = mutableListOf<() -> Unit>()
private val nextReloadCallbacks = Collections.synchronizedMap(mutableMapOf<Long, MutableList<() -> Unit>>())
private val history = linkedSetOf<Path>()
private val undoManager = MyUndoManager()
private val editTransferListener = EditTransferListener()
@@ -301,7 +304,7 @@ internal class TransportPanel(
}
if (target.pathString == workdir?.pathString || target.parent.pathString == workdir?.pathString) {
if (loading) {
nextReloadCallbacks.add { reload(requestFocus = false) }
registerNextReloadCallback { reload(requestFocus = false) }
} else {
reload(requestFocus = false)
}
@@ -620,7 +623,7 @@ internal class TransportPanel(
}
fun registerSelectRow(name: String) {
nextReloadCallbacks.add {
registerNextReloadCallback {
for (i in 0 until model.rowCount) {
if (model.getAttributes(i).name == name) {
val c = sorter.convertRowIndexToView(i)
@@ -633,6 +636,11 @@ internal class TransportPanel(
}
}
private fun registerNextReloadCallback(block: () -> Unit) {
nextReloadCallbacks.computeIfAbsent(mod.get()) { mutableListOf() }
.add(block)
}
fun reload(
oldPath: String? = workdir?.absolutePathString(),
newPath: String? = workdir?.absolutePathString(),
@@ -643,6 +651,8 @@ internal class TransportPanel(
if (loading) return false
loading = true
val mod = mod.getAndAdd(1)
coroutineScope.launch {
try {
@@ -650,7 +660,7 @@ internal class TransportPanel(
withContext(Dispatchers.Swing) {
setNewWorkdir(workdir)
nextReloadCallbacks.forEach { runCatching { it.invoke() } }
nextReloadCallbacks[mod]?.forEach { runCatching { it.invoke() } }
}
} catch (e: Exception) {
@@ -665,7 +675,7 @@ internal class TransportPanel(
} finally {
withContext(Dispatchers.Swing) {
loading = false
nextReloadCallbacks.clear()
nextReloadCallbacks.entries.removeIf { it.key <= mod }
}
}
}
@@ -730,6 +740,13 @@ internal class TransportPanel(
if (files.isNotEmpty())
consume.invoke()
if (first.compareAndSet(false, true)) {
withContext(Dispatchers.Swing) {
model.clear()
table.scrollRectToVisible(Rectangle())
}
}
if (requestFocus)
coroutineScope.launch(Dispatchers.Swing) { table.requestFocusInWindow() }
@@ -776,7 +793,9 @@ internal class TransportPanel(
.getOrNull()
val fileSize = basicAttributes?.size() ?: 0
val permissions = posixFileAttribute?.permissions() ?: emptySet()
val permissions = posixFileAttribute?.permissions()
?: if (basicAttributes is S3FileAttributes) basicAttributes.permissions
else emptySet()
val owner = fileOwnerAttribute?.name ?: StringUtils.EMPTY
val lastModifiedTime = basicAttributes?.lastModifiedTime()?.toMillis() ?: 0
val isDirectory = basicAttributes?.isDirectory ?: false

View File

@@ -68,6 +68,7 @@ internal class TransportViewer : JPanel(BorderLayout()), DataProvider, Disposabl
}
private fun initEvents() {
splitPane.addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
removeComponentListener(this)
@@ -91,6 +92,8 @@ internal class TransportViewer : JPanel(BorderLayout()), DataProvider, Disposabl
}
}
Disposer.register(this, transferManager)
Disposer.register(this, transferTable)
Disposer.register(this, leftTabbed)
Disposer.register(this, rightTabbed)
}

View File

@@ -2,6 +2,7 @@ package app.termora.transfer.s3
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
import java.nio.file.attribute.PosixFilePermission
data class S3FileAttributes(
private val lastModifiedTime: Long = 0,
@@ -13,7 +14,12 @@ data class S3FileAttributes(
private val symbolicLink: Boolean = false,
private val other: Boolean = false,
private val size: Long = 0,
) : BasicFileAttributes {
) : BasicFileAttributes {
var permissions: Set<PosixFilePermission> = emptySet()
override fun lastModifiedTime(): FileTime {
return FileTime.fromMillis(lastModifiedTime)
}
@@ -49,4 +55,6 @@ data class S3FileAttributes(
override fun fileKey(): Any? {
return null
}
}

View File

@@ -1 +1 @@
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#6C707E"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#6C707E"></path></svg>
<svg t="1751945417827" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M876.77610666 73.728H150.63722666A85.54496 85.54496 0 0 0 65.19466666 159.17056v531.57888a85.56544 85.56544 0 0 0 85.44256 85.46304h726.13888c47.104 0 85.44256-38.33856 85.44256-85.46304V159.17056c0-47.104-38.33856-85.44256-85.44256-85.44256zM904.87466666 690.74944c0 15.50336-12.61568 28.09856-28.09856 28.09856H150.63722666A28.13952 28.13952 0 0 1 122.53866666 690.74944V159.17056c0-15.48288 12.61568-28.09856 28.09856-28.09856h726.13888c15.48288 0 28.09856 12.61568 28.09856 28.09856v531.57888zM749.22666666 862.208h-471.04a28.672 28.672 0 0 0 0 57.344h471.04a28.672 28.672 0 0 0 0-57.344z" p-id="1613" fill="#6C707E"></path><path d="M227.04810666 557.568h50.03264v-101.45792h88.92416v-41.90208h-88.92416v-65.26976h104.1408v-41.59488H227.04810666zM404.28202666 348.93824h70.32832v208.62976h50.05312V348.93824h71.00416v-41.59488h-191.3856zM718.46570666 307.34336h-82.16576v250.20416h50.03264v-88.92416h33.4848c53.76 0 96.70656-25.68192 96.70656-82.8416 0-59.16672-42.5984-78.4384-98.05824-78.4384z m-2.02752 121.73312h-30.1056v-82.16576h28.40576c34.48832 0 52.736 9.80992 52.736 38.87104 0.02048 28.75392-16.54784 43.29472-51.03616 43.29472z" p-id="1614" fill="#6C707E"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +1 @@
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#CED0D6"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#CED0D6"></path></svg>
<svg t="1751945417827" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M876.77610666 73.728H150.63722666A85.54496 85.54496 0 0 0 65.19466666 159.17056v531.57888a85.56544 85.56544 0 0 0 85.44256 85.46304h726.13888c47.104 0 85.44256-38.33856 85.44256-85.46304V159.17056c0-47.104-38.33856-85.44256-85.44256-85.44256zM904.87466666 690.74944c0 15.50336-12.61568 28.09856-28.09856 28.09856H150.63722666A28.13952 28.13952 0 0 1 122.53866666 690.74944V159.17056c0-15.48288 12.61568-28.09856 28.09856-28.09856h726.13888c15.48288 0 28.09856 12.61568 28.09856 28.09856v531.57888zM749.22666666 862.208h-471.04a28.672 28.672 0 0 0 0 57.344h471.04a28.672 28.672 0 0 0 0-57.344z" p-id="1613" fill="#CED0D6"></path><path d="M227.04810666 557.568h50.03264v-101.45792h88.92416v-41.90208h-88.92416v-65.26976h104.1408v-41.59488H227.04810666zM404.28202666 348.93824h70.32832v208.62976h50.05312V348.93824h71.00416v-41.59488h-191.3856zM718.46570666 307.34336h-82.16576v250.20416h50.03264v-88.92416h33.4848c53.76 0 96.70656-25.68192 96.70656-82.8416 0-59.16672-42.5984-78.4384-98.05824-78.4384z m-2.02752 121.73312h-30.1056v-82.16576h28.40576c34.48832 0 52.736 9.80992 52.736 38.87104 0.02048 28.75392-16.54784 43.29472-51.03616 43.29472z" p-id="1614" fill="#CED0D6"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB