diff --git a/src/main/kotlin/app/termora/Database.kt b/src/main/kotlin/app/termora/Database.kt index 5b588b0..d78f0cd 100644 --- a/src/main/kotlin/app/termora/Database.kt +++ b/src/main/kotlin/app/termora/Database.kt @@ -2,7 +2,6 @@ package app.termora import app.termora.Application.ohMyJson import app.termora.highlight.KeywordHighlight -import app.termora.keymap.KeyShortcut import app.termora.keymap.Keymap import app.termora.keymgr.OhKeyPair import app.termora.macro.Macro @@ -14,12 +13,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.* import org.apache.commons.io.IOUtils import org.slf4j.LoggerFactory import java.io.File import java.util.* -import javax.swing.KeyStroke import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set @@ -65,36 +62,17 @@ class Database private constructor(private val env: Environment) : Disposable { fun getKeymaps(): Collection { val array = env.computeInTransaction { tx -> - openCursor(tx, KEYMAP_STORE) { _, value -> - ohMyJson.decodeFromString(value) + openCursor(tx, KEYMAP_STORE) { _, value -> + value }.values } - val shortcuts = mutableListOf() - for (json in array.iterator()) { - val name = json["name"]?.jsonPrimitive?.content ?: continue - val readonly = json["readonly"]?.jsonPrimitive?.booleanOrNull ?: false - val keymap = Keymap(name, null, readonly) - - for (shortcut in (json["shortcuts"]?.jsonArray ?: emptyList()).map { it.jsonObject }) { - val keyStroke = shortcut["keyStroke"]?.jsonPrimitive?.contentOrNull ?: continue - val keyboard = shortcut["keyboard"]?.jsonPrimitive?.booleanOrNull ?: true - val actionIds = ohMyJson.decodeFromJsonElement>( - shortcut["actionIds"]?.jsonArray - ?: continue - ) - if (keyboard) { - val keyShortcut = KeyShortcut(KeyStroke.getKeyStroke(keyStroke)) - for (actionId in actionIds) { - keymap.addShortcut(actionId, keyShortcut) - } - } - } - - shortcuts.add(keymap) + val keymaps = mutableListOf() + for (text in array.iterator()) { + keymaps.add(Keymap.fromJSON(text) ?: continue) } - return shortcuts + return keymaps } fun addKeymap(keymap: Keymap) { @@ -599,6 +577,7 @@ class Database private constructor(private val env: Environment) : Disposable { var rangeKeyPairs by BooleanPropertyDelegate(true) var rangeKeywordHighlights by BooleanPropertyDelegate(true) var rangeMacros by BooleanPropertyDelegate(true) + var rangeKeymap by BooleanPropertyDelegate(true) /** * Token diff --git a/src/main/kotlin/app/termora/SettingsOptionsPane.kt b/src/main/kotlin/app/termora/SettingsOptionsPane.kt index 111031d..8d9103c 100644 --- a/src/main/kotlin/app/termora/SettingsOptionsPane.kt +++ b/src/main/kotlin/app/termora/SettingsOptionsPane.kt @@ -402,6 +402,7 @@ class SettingsOptionsPane : OptionsPane() { val keysCheckBox = JCheckBox(I18n.getString("termora.settings.sync.range.keys")) val keywordHighlightsCheckBox = JCheckBox(I18n.getString("termora.settings.sync.range.keyword-highlights")) val macrosCheckBox = JCheckBox(I18n.getString("termora.macro")) + val keymapCheckBox = JCheckBox(I18n.getString("termora.settings.keymap")) val visitGistBtn = JButton(Icons.externalLink) val getTokenBtn = JButton(Icons.externalLink) @@ -593,6 +594,9 @@ class SettingsOptionsPane : OptionsPane() { if (macrosCheckBox.isSelected) { range.add(SyncRange.Macros) } + if (keymapCheckBox.isSelected) { + range.add(SyncRange.Keymap) + } return SyncConfig( type = typeComboBox.selectedItem as SyncType, token = String(tokenTextField.password), @@ -664,6 +668,7 @@ class SettingsOptionsPane : OptionsPane() { tokenTextField.isEnabled = false keysCheckBox.isEnabled = false macrosCheckBox.isEnabled = false + keymapCheckBox.isEnabled = false keywordHighlightsCheckBox.isEnabled = false hostsCheckBox.isEnabled = false domainTextField.isEnabled = false @@ -696,6 +701,7 @@ class SettingsOptionsPane : OptionsPane() { hostsCheckBox.isEnabled = true typeComboBox.isEnabled = true macrosCheckBox.isEnabled = true + keymapCheckBox.isEnabled = true gistTextField.isEnabled = true tokenTextField.isEnabled = true domainTextField.isEnabled = true @@ -756,11 +762,13 @@ class SettingsOptionsPane : OptionsPane() { keysCheckBox.isFocusable = false keywordHighlightsCheckBox.isFocusable = false macrosCheckBox.isFocusable = false + keymapCheckBox.isFocusable = false hostsCheckBox.isSelected = sync.rangeHosts keysCheckBox.isSelected = sync.rangeKeyPairs keywordHighlightsCheckBox.isSelected = sync.rangeKeywordHighlights macrosCheckBox.isSelected = sync.rangeMacros + keymapCheckBox.isSelected = sync.rangeKeymap typeComboBox.selectedItem = sync.type gistTextField.text = sync.gist @@ -823,6 +831,7 @@ class SettingsOptionsPane : OptionsPane() { .add(keysCheckBox).xy(3, 1) .add(keywordHighlightsCheckBox).xy(5, 1) .add(macrosCheckBox).xy(1, 3) + .add(keymapCheckBox).xy(3, 3) .build() var rows = 1 diff --git a/src/main/kotlin/app/termora/actions/SettingsAction.kt b/src/main/kotlin/app/termora/actions/SettingsAction.kt index d4b6b96..3bfaf04 100644 --- a/src/main/kotlin/app/termora/actions/SettingsAction.kt +++ b/src/main/kotlin/app/termora/actions/SettingsAction.kt @@ -1,5 +1,6 @@ package app.termora.actions +import app.termora.ApplicationScope import app.termora.I18n import app.termora.Icons import app.termora.SettingsDialog @@ -27,7 +28,8 @@ class SettingsAction : AnAction( init { FlatDesktop.setPreferencesHandler { val owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().focusOwner - if (owner != null) { + // Doorman 的情况下不允许打开 + if (owner != null && ApplicationScope.windowScopes().isNotEmpty()) { actionPerformed(ActionEvent(owner, ActionEvent.ACTION_PERFORMED, StringUtils.EMPTY)) } } diff --git a/src/main/kotlin/app/termora/keymap/Keymap.kt b/src/main/kotlin/app/termora/keymap/Keymap.kt index b680935..f6348c3 100644 --- a/src/main/kotlin/app/termora/keymap/Keymap.kt +++ b/src/main/kotlin/app/termora/keymap/Keymap.kt @@ -2,10 +2,8 @@ package app.termora.keymap import app.termora.Application.ohMyJson import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.buildJsonArray -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.encodeToJsonElement -import kotlinx.serialization.json.put +import kotlinx.serialization.json.* +import javax.swing.KeyStroke open class Keymap( val name: String, @@ -16,6 +14,37 @@ open class Keymap( val isReadonly: Boolean = false, ) { + companion object { + fun fromJSON(text: String): Keymap? { + return fromJSON(ohMyJson.decodeFromString(text)) + } + + fun fromJSON(json: JsonObject): Keymap? { + val shortcuts = mutableListOf() + val name = json["name"]?.jsonPrimitive?.content ?: return null + val readonly = json["readonly"]?.jsonPrimitive?.booleanOrNull ?: return null + val keymap = Keymap(name, null, readonly) + + for (shortcut in (json["shortcuts"]?.jsonArray ?: emptyList()).map { it.jsonObject }) { + val keyStroke = shortcut["keyStroke"]?.jsonPrimitive?.contentOrNull ?: continue + val keyboard = shortcut["keyboard"]?.jsonPrimitive?.booleanOrNull ?: true + val actionIds = ohMyJson.decodeFromJsonElement>( + shortcut["actionIds"]?.jsonArray + ?: continue + ) + if (keyboard) { + val keyShortcut = KeyShortcut(KeyStroke.getKeyStroke(keyStroke)) + for (actionId in actionIds) { + keymap.addShortcut(actionId, keyShortcut) + } + } + } + + shortcuts.add(keymap) + return keymap + } + } + private val shortcuts = mutableMapOf>() open fun addShortcut(actionId: String, shortcut: Shortcut) { @@ -66,7 +95,11 @@ open class Keymap( fun toJSON(): String { - return ohMyJson.encodeToString(buildJsonObject { + return ohMyJson.encodeToString(toJSONObject()) + } + + fun toJSONObject(): JsonObject { + return buildJsonObject { put("name", name) put("readonly", isReadonly) parent?.let { put("parent", it.name) } @@ -81,7 +114,7 @@ open class Keymap( }) } }) - }) + } } } \ No newline at end of file diff --git a/src/main/kotlin/app/termora/sync/GitSyncer.kt b/src/main/kotlin/app/termora/sync/GitSyncer.kt index 903f6ad..53c249d 100644 --- a/src/main/kotlin/app/termora/sync/GitSyncer.kt +++ b/src/main/kotlin/app/termora/sync/GitSyncer.kt @@ -8,11 +8,14 @@ import app.termora.AES.encodeBase64String import app.termora.Application.ohMyJson import app.termora.highlight.KeywordHighlight import app.termora.highlight.KeywordHighlightManager +import app.termora.keymap.Keymap +import app.termora.keymap.KeymapManager import app.termora.keymgr.KeyManager import app.termora.keymgr.OhKeyPair import app.termora.macro.Macro import app.termora.macro.MacroManager import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import okhttp3.Request @@ -32,6 +35,7 @@ abstract class GitSyncer : Syncer { protected val keyManager get() = KeyManager.getInstance() protected val keywordHighlightManager get() = KeywordHighlightManager.getInstance() protected val macroManager get() = MacroManager.getInstance() + protected val keymapManager get() = KeymapManager.getInstance() override fun pull(config: SyncConfig): GistResponse { @@ -74,6 +78,13 @@ abstract class GitSyncer : Syncer { } } + // decode keymaps + if (config.ranges.contains(SyncRange.Macros)) { + gistResponse.gists.findLast { it.filename == "Keymaps" }?.let { + decodeKeymaps(it.content, config) + } + } + if (log.isInfoEnabled) { log.info("Type: ${config.type} , Gist: ${config.gistId} Pulled") } @@ -231,6 +242,17 @@ abstract class GitSyncer : Syncer { } } + private fun decodeKeymaps(text: String, config: SyncConfig) { + + for (keymap in ohMyJson.decodeFromString>(text).mapNotNull { Keymap.fromJSON(it) }) { + keymapManager.addKeymap(keymap) + } + + if (log.isDebugEnabled) { + log.debug("Decode Keymaps: {}", text) + } + } + private fun getKey(config: SyncConfig): ByteArray { return ArrayUtils.subarray(config.token.padEnd(16, '0').toByteArray(), 0, 16) } @@ -244,6 +266,7 @@ abstract class GitSyncer : Syncer { // aes key val key = ArrayUtils.subarray(config.token.padEnd(16, '0').toByteArray(), 0, 16) + // Hosts if (config.ranges.contains(SyncRange.Hosts)) { val encryptedHosts = mutableListOf() for (host in hostManager.hosts()) { @@ -282,6 +305,7 @@ abstract class GitSyncer : Syncer { } + // KeyPairs if (config.ranges.contains(SyncRange.KeyPairs)) { val encryptedKeys = mutableListOf() for (keyPair in keyManager.getOhKeyPairs()) { @@ -306,6 +330,7 @@ abstract class GitSyncer : Syncer { gistFiles.add(GistFile("KeyPairs", keysContent)) } + // Highlights if (config.ranges.contains(SyncRange.KeywordHighlights)) { val keywordHighlights = mutableListOf() for (keywordHighlight in keywordHighlightManager.getKeywordHighlights()) { @@ -324,6 +349,7 @@ abstract class GitSyncer : Syncer { gistFiles.add(GistFile("KeywordHighlights", keywordHighlightsContent)) } + // Macros if (config.ranges.contains(SyncRange.Macros)) { val macros = mutableListOf() for (macro in macroManager.getMacros()) { @@ -342,6 +368,26 @@ abstract class GitSyncer : Syncer { gistFiles.add(GistFile("Macros", macrosContent)) } + // Keymap + if (config.ranges.contains(SyncRange.Keymap)) { + val keymaps = mutableListOf() + for (keymap in keymapManager.getKeymaps()) { + // 只读的是内置的 + if (keymap.isReadonly) { + continue + } + keymaps.add(keymap.toJSONObject()) + } + + if (keymaps.isNotEmpty()) { + val keymapsContent = ohMyJson.encodeToString(keymaps) + if (log.isDebugEnabled) { + log.debug("Push keymaps: {}", keymapsContent) + } + gistFiles.add(GistFile("Keymaps", keymapsContent)) + } + } + if (gistFiles.isEmpty()) { throw IllegalArgumentException("No gist files found") } diff --git a/src/main/kotlin/app/termora/sync/SyncConfig.kt b/src/main/kotlin/app/termora/sync/SyncConfig.kt index 24b4908..2f2247b 100644 --- a/src/main/kotlin/app/termora/sync/SyncConfig.kt +++ b/src/main/kotlin/app/termora/sync/SyncConfig.kt @@ -11,6 +11,7 @@ enum class SyncRange { KeyPairs, KeywordHighlights, Macros, + Keymap, } data class SyncConfig(