From 6779bb64d7158764c414c4ac9eff8e513b0159d8 Mon Sep 17 00:00:00 2001 From: Yiqun Zhang Date: Fri, 9 Aug 2024 22:50:39 +0800 Subject: [PATCH] :lipstick: Add shortcut for pasting plain text (#1640) --- .../com/crosspaste/paste/PasteboardService.kt | 7 +++ .../crosspaste/paste/TransferableProducer.kt | 7 +++ .../ui/settings/ShortcutKeysView.kt | 4 ++ .../listen/DesktopShortKeysAction.kt | 24 +++++++++++ .../crosspaste/listen/DesktopShortcutKeys.kt | 43 +++++++++++++++---- .../paste/AbstractPasteboardService.kt | 18 ++++++++ .../paste/DesktopTransferableProducer.kt | 28 ++++++++++++ .../desktopMain/resources/i18n/en.properties | 1 + .../desktopMain/resources/i18n/es.properties | 1 + .../desktopMain/resources/i18n/jp.properties | 1 + .../desktopMain/resources/i18n/zh.properties | 1 + .../resources/shortcut_keys/Linux.properties | 1 + .../resources/shortcut_keys/Macos.properties | 1 + .../shortcut_keys/Windows.properties | 1 + 14 files changed, 129 insertions(+), 9 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/crosspaste/paste/PasteboardService.kt b/composeApp/src/commonMain/kotlin/com/crosspaste/paste/PasteboardService.kt index 40bd639b..12d8dcdf 100644 --- a/composeApp/src/commonMain/kotlin/com/crosspaste/paste/PasteboardService.kt +++ b/composeApp/src/commonMain/kotlin/com/crosspaste/paste/PasteboardService.kt @@ -4,6 +4,7 @@ import com.crosspaste.app.AppWindowManager import com.crosspaste.config.ConfigManager import com.crosspaste.dao.paste.PasteDao import com.crosspaste.dao.paste.PasteData +import com.crosspaste.dao.paste.PasteItem import io.github.oshai.kotlinlogging.KLogger import kotlinx.coroutines.channels.Channel @@ -21,6 +22,12 @@ interface PasteboardService : PasteboardMonitor { val pasteboardChannel: Channel Unit> + suspend fun tryWritePasteboard( + pasteItem: PasteItem, + localOnly: Boolean = false, + filterFile: Boolean = false, + ) + suspend fun tryWritePasteboard( pasteData: PasteData, localOnly: Boolean = false, diff --git a/composeApp/src/commonMain/kotlin/com/crosspaste/paste/TransferableProducer.kt b/composeApp/src/commonMain/kotlin/com/crosspaste/paste/TransferableProducer.kt index baaca1ee..2ee843a4 100644 --- a/composeApp/src/commonMain/kotlin/com/crosspaste/paste/TransferableProducer.kt +++ b/composeApp/src/commonMain/kotlin/com/crosspaste/paste/TransferableProducer.kt @@ -1,9 +1,16 @@ package com.crosspaste.paste import com.crosspaste.dao.paste.PasteData +import com.crosspaste.dao.paste.PasteItem interface TransferableProducer { + fun produce( + pasteItem: PasteItem, + localOnly: Boolean = false, + filterFile: Boolean = false, + ): PasteTransferable? + fun produce( pasteData: PasteData, localOnly: Boolean = false, diff --git a/composeApp/src/commonMain/kotlin/com/crosspaste/ui/settings/ShortcutKeysView.kt b/composeApp/src/commonMain/kotlin/com/crosspaste/ui/settings/ShortcutKeysView.kt index 6ac10b6e..fd5acf65 100644 --- a/composeApp/src/commonMain/kotlin/com/crosspaste/ui/settings/ShortcutKeysView.kt +++ b/composeApp/src/commonMain/kotlin/com/crosspaste/ui/settings/ShortcutKeysView.kt @@ -92,6 +92,10 @@ fun ShortcutKeysContentView() { .clip(RoundedCornerShape(8.dp)) .background(MaterialTheme.colors.background), ) { + ShortcutKeyRow("paste_plain_text") + + Divider(modifier = Modifier.padding(start = 15.dp)) + ShortcutKeyRow("paste_local_last") Divider(modifier = Modifier.padding(start = 15.dp)) diff --git a/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortKeysAction.kt b/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortKeysAction.kt index ff39abcf..7e45c4ad 100644 --- a/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortKeysAction.kt +++ b/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortKeysAction.kt @@ -7,6 +7,7 @@ import com.crosspaste.dao.paste.PasteDao import com.crosspaste.dao.paste.PasteData import com.crosspaste.listen.DesktopShortcutKeys.Companion.HIDE_WINDOW import com.crosspaste.listen.DesktopShortcutKeys.Companion.PASTE_LOCAL_LAST +import com.crosspaste.listen.DesktopShortcutKeys.Companion.PASTE_PLAIN_TEXT import com.crosspaste.listen.DesktopShortcutKeys.Companion.PASTE_REMOTE_LAST import com.crosspaste.listen.DesktopShortcutKeys.Companion.SHOW_MAIN import com.crosspaste.listen.DesktopShortcutKeys.Companion.SHOW_SEARCH @@ -15,6 +16,7 @@ import com.crosspaste.listen.DesktopShortcutKeys.Companion.SWITCH_MONITOR_PASTEB import com.crosspaste.listener.ShortcutKeysAction import com.crosspaste.paste.PasteSearchService import com.crosspaste.paste.PasteboardService +import com.crosspaste.paste.item.PasteText import com.crosspaste.utils.GlobalCoroutineScopeImpl.mainCoroutineDispatcher import com.crosspaste.utils.ioDispatcher import io.github.oshai.kotlinlogging.KotlinLogging @@ -35,6 +37,7 @@ class DesktopShortKeysAction( override val action: (String) -> Unit = { actionName -> when (actionName) { + PASTE_PLAIN_TEXT -> pastePlainText() PASTE_LOCAL_LAST -> pasteLast(true) PASTE_REMOTE_LAST -> pasteLast(false) SHOW_MAIN -> showMainWindow() @@ -75,6 +78,27 @@ class DesktopShortKeysAction( } } + private fun pastePlainText() { + logger.info { "Paste Plain Text" } + mainCoroutineDispatcher.launch(CoroutineName("PastePlainText")) { + val result = + pasteDao.searchPasteData( + searchTerms = listOf(), + favorite = null, + limit = 1, + ) + + if (result.size > 0) { + mainCoroutineDispatcher.launch(ioDispatcher) { + result[0].getPasteAppearItems().firstOrNull { it is PasteText }?.let { + pasteboardService.tryWritePasteboard(it, localOnly = true) + appWindowManager.toPaste() + } + } + } + } + } + private fun pasteLast(local: Boolean) { logger.info { "paste ${if (local) "Local" else "Remote"} Last" } diff --git a/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortcutKeys.kt b/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortcutKeys.kt index b4df9410..413d298a 100644 --- a/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortcutKeys.kt +++ b/composeApp/src/desktopMain/kotlin/com/crosspaste/listen/DesktopShortcutKeys.kt @@ -9,16 +9,17 @@ import com.crosspaste.listener.ShortcutKeys import com.crosspaste.listener.ShortcutKeysCore import com.crosspaste.path.DesktopAppPathProvider import com.crosspaste.platform.currentPlatform -import com.crosspaste.presist.DesktopOneFilePersist -import com.crosspaste.utils.getResourceUtils +import com.crosspaste.utils.DesktopResourceUtils import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import okio.FileSystem +import okio.Path import okio.Path.Companion.toOkioPath import java.io.FileOutputStream import java.io.InputStreamReader import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets +import java.util.Date import java.util.Properties class DesktopShortcutKeys( @@ -27,6 +28,7 @@ class DesktopShortcutKeys( companion object { const val PASTE = "paste" + const val PASTE_PLAIN_TEXT = "paste_plain_text" const val PASTE_LOCAL_LAST = "paste_local_last" const val PASTE_REMOTE_LAST = "paste_remote_last" const val SHOW_MAIN = "show_main" @@ -38,6 +40,8 @@ class DesktopShortcutKeys( private val logger: KLogger = KotlinLogging.logger {} + private val platform = currentPlatform() + override var shortcutKeysCore by mutableStateOf(defaultKeysCore()) init { @@ -51,7 +55,6 @@ class DesktopShortcutKeys( } private fun defaultKeysCore(): ShortcutKeysCore { - val platform = currentPlatform() return shortcutKeysLoader.load(platform.name) } @@ -60,13 +63,24 @@ class DesktopShortcutKeys( val shortcutKeysPropertiesPath = DesktopAppPathProvider .resolve("shortcut-keys.properties", AppFileType.USER) + + val platformProperties = + DesktopResourceUtils.loadProperties( + "shortcut_keys/${platform.name}.properties", + ) + if (!FileSystem.SYSTEM.exists(shortcutKeysPropertiesPath)) { - val filePersist = DesktopOneFilePersist(shortcutKeysPropertiesPath) - val platform = currentPlatform() - val bytes = - getResourceUtils() - .readResourceBytes("shortcut_keys/${platform.name}.properties") - filePersist.saveBytes(bytes) + writeProperties(platformProperties, shortcutKeysPropertiesPath) + } else { + val properties = Properties() + InputStreamReader(shortcutKeysPropertiesPath.toFile().inputStream(), StandardCharsets.UTF_8) + .use { inputStreamReader -> properties.load(inputStreamReader) } + for (key in platformProperties.keys) { + if (!properties.containsKey(key)) { + properties.setProperty(key.toString(), platformProperties.getProperty(key.toString())) + } + } + writeProperties(properties, shortcutKeysPropertiesPath) } val path = shortcutKeysPropertiesPath.toFile().toOkioPath() @@ -78,6 +92,17 @@ class DesktopShortcutKeys( } } + private fun writeProperties( + properties: Properties, + path: Path, + ) { + path.toFile().outputStream().use { fileOutputStream -> + OutputStreamWriter(fileOutputStream, Charsets.UTF_8).use { writer -> + properties.store(writer, Date().toString()) + } + } + } + override fun update( keyName: String, keys: List, diff --git a/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/AbstractPasteboardService.kt b/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/AbstractPasteboardService.kt index c99fed5b..d180ac34 100644 --- a/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/AbstractPasteboardService.kt +++ b/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/AbstractPasteboardService.kt @@ -1,6 +1,7 @@ package com.crosspaste.paste import com.crosspaste.dao.paste.PasteData +import com.crosspaste.dao.paste.PasteItem import java.awt.datatransfer.Clipboard import java.awt.datatransfer.ClipboardOwner import java.awt.datatransfer.Transferable @@ -28,6 +29,23 @@ abstract class AbstractPasteboardService : PasteboardService, ClipboardOwner { } } + override suspend fun tryWritePasteboard( + pasteItem: PasteItem, + localOnly: Boolean, + filterFile: Boolean, + ) { + try { + pasteProducer.produce(pasteItem, localOnly, filterFile)?.let { + it as DesktopWriteTransferable + ownerTransferable = it + owner = true + systemClipboard.setContents(ownerTransferable, this) + } + } catch (e: Exception) { + logger.error(e) { "tryWritePasteboard error" } + } + } + override suspend fun tryWritePasteboard( pasteData: PasteData, localOnly: Boolean, diff --git a/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/DesktopTransferableProducer.kt b/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/DesktopTransferableProducer.kt index a3fd3c27..248de0ed 100644 --- a/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/DesktopTransferableProducer.kt +++ b/composeApp/src/desktopMain/kotlin/com/crosspaste/paste/DesktopTransferableProducer.kt @@ -1,6 +1,7 @@ package com.crosspaste.paste import com.crosspaste.dao.paste.PasteData +import com.crosspaste.dao.paste.PasteItem import com.crosspaste.paste.item.PasteFiles import com.crosspaste.paste.plugin.type.PasteTypePlugin import java.awt.datatransfer.DataFlavor @@ -12,6 +13,33 @@ class DesktopTransferableProducer( private val pasteTypePluginMap: Map = pasteTypePlugins.associateBy { it.getPasteType() } + override fun produce( + pasteItem: PasteItem, + localOnly: Boolean, + filterFile: Boolean, + ): DesktopWriteTransferable? { + val builder = DesktopWriteTransferableBuilder() + + if (filterFile) { + if (pasteItem is PasteFiles) { + return null + } + } else { + pasteTypePluginMap[pasteItem.getPasteType()]?.let { + builder.add(it, pasteItem) + } + } + + return if (builder.isEmpty()) { + null + } else { + if (localOnly) { + builder.add(LocalOnlyFlavor.toPasteDataFlavor(), true) + } + builder.build() + } + } + override fun produce( pasteData: PasteData, localOnly: Boolean, diff --git a/composeApp/src/desktopMain/resources/i18n/en.properties b/composeApp/src/desktopMain/resources/i18n/en.properties index 0e340ef3..b1916e2c 100644 --- a/composeApp/src/desktopMain/resources/i18n/en.properties +++ b/composeApp/src/desktopMain/resources/i18n/en.properties @@ -101,6 +101,7 @@ open_search_window=Open Search Window paste=Paste paste_control=Paste Control paste_local_last=Paste Local Last +paste_plain_text=Paste Plain Text paste_remote_last=Paste Remote Last paste_to=Paste To pasteboard=Pasteboard diff --git a/composeApp/src/desktopMain/resources/i18n/es.properties b/composeApp/src/desktopMain/resources/i18n/es.properties index d52bb53e..413a769e 100644 --- a/composeApp/src/desktopMain/resources/i18n/es.properties +++ b/composeApp/src/desktopMain/resources/i18n/es.properties @@ -101,6 +101,7 @@ open_search_window=Abrir ventana de búsqueda paste=Pegar paste_control=Control de pegado paste_local_last=Pegar último local +paste_plain_text=Pegar texto sin formato paste_remote_last=Pegar último remoto paste_to=Pegar en pasteboard=Portapapeles diff --git a/composeApp/src/desktopMain/resources/i18n/jp.properties b/composeApp/src/desktopMain/resources/i18n/jp.properties index e53378c3..039aab88 100644 --- a/composeApp/src/desktopMain/resources/i18n/jp.properties +++ b/composeApp/src/desktopMain/resources/i18n/jp.properties @@ -101,6 +101,7 @@ open_search_window=検索ウィンドウを開く paste=ペースト paste_control=ペースト制御 paste_local_last=最後にローカルペースト +paste_plain_text=プレーンテキストをペースト paste_remote_last=最後にリモートペースト paste_to=ペースト先 pasteboard=ペーストボード diff --git a/composeApp/src/desktopMain/resources/i18n/zh.properties b/composeApp/src/desktopMain/resources/i18n/zh.properties index 15032d15..34fe08f5 100644 --- a/composeApp/src/desktopMain/resources/i18n/zh.properties +++ b/composeApp/src/desktopMain/resources/i18n/zh.properties @@ -101,6 +101,7 @@ open_search_window=打开搜索窗口 paste=粘贴数据 paste_control=粘贴控制 paste_local_last=粘贴本地最新数据 +paste_plain_text=粘贴纯文本 paste_remote_last=粘贴远程最新数据 paste_to=粘贴到 pasteboard=粘贴板 diff --git a/composeApp/src/desktopMain/resources/shortcut_keys/Linux.properties b/composeApp/src/desktopMain/resources/shortcut_keys/Linux.properties index 944bc569..edcb047f 100644 --- a/composeApp/src/desktopMain/resources/shortcut_keys/Linux.properties +++ b/composeApp/src/desktopMain/resources/shortcut_keys/Linux.properties @@ -1,4 +1,5 @@ paste=29+47 +paste_plain_text=42+3675+20 paste_local_last=42+3675+38 paste_remote_last=42+3675+52 show_main=42+3675+51 diff --git a/composeApp/src/desktopMain/resources/shortcut_keys/Macos.properties b/composeApp/src/desktopMain/resources/shortcut_keys/Macos.properties index 5f03dd1a..a2878fa5 100644 --- a/composeApp/src/desktopMain/resources/shortcut_keys/Macos.properties +++ b/composeApp/src/desktopMain/resources/shortcut_keys/Macos.properties @@ -1,4 +1,5 @@ paste=3675+47 +paste_plain_text=42+3675+20 paste_local_last=42+3675+38 paste_remote_last=42+3675+52 show_main=42+3675+51 diff --git a/composeApp/src/desktopMain/resources/shortcut_keys/Windows.properties b/composeApp/src/desktopMain/resources/shortcut_keys/Windows.properties index 944bc569..edcb047f 100644 --- a/composeApp/src/desktopMain/resources/shortcut_keys/Windows.properties +++ b/composeApp/src/desktopMain/resources/shortcut_keys/Windows.properties @@ -1,4 +1,5 @@ paste=29+47 +paste_plain_text=42+3675+20 paste_local_last=42+3675+38 paste_remote_last=42+3675+52 show_main=42+3675+51