diff --git a/.gitignore b/.gitignore
index 2be1234..99faba9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
!/script/jlink/*.bat
/script/jpackage/*
!/script/jpackage/build.*
+!/script/jpackage/LabelPlusFXDict.lnk
lpfx.iml
lpfx.ipr
diff --git a/CHANGELOG b/CHANGELOG.md
similarity index 91%
rename from CHANGELOG
rename to CHANGELOG.md
index b8270cf..d965c7f 100644
--- a/CHANGELOG
+++ b/CHANGELOG.md
@@ -12,10 +12,36 @@ TODO:
- 备份文件上限(Next)
- 保存specify(Next)
+# 2.3.4
+
+Add
+
+ - 编辑框添加快捷输入短语
+ - 树状图菜单添加复制文本和粘贴文本,并支持cv快捷键操作
+ - 选中右键树状图中的Label支持移动序号
+ - 图片区添加QW切换图片的快捷键
+ - 添加百度翻译key设置用于配置繁简体转换
+ - 现在更新版本会尝试加载旧版本的配置文件
+
+Fix
+
+- 尝试通过限制加载时图片的大小以及给予更多内存空间的方式修复图片文件尺寸过大导致卡死的问题
+- 尝试修复图片区数字快捷键报错、
+
+Change
+
+- 切换图片时默认选中第一个Label
+
+
# 2.3.3
Fix
- 修正了当换页后选中的Label索引值与上一页选中的Label索引值相同时无法输入翻译文本的问题;
+ - 修正了Label有时候会放歪的问题;
+ - 修正了框选Label后关闭文档会产生异常的问题;
+ - 修正了保存文件格式不匹配的问题;
+ - 修正了无法编辑项目图片的问题;
+ - 修正了排序报错的问题;
Add
- 现在可以直接使用滚轮来缩放图片(需要设置);
- 现在可以在启动时直接打开上次文件(需要设置);
diff --git a/README.md b/README.md
index 9613346..c0a8fc8 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@
+[简体中文](/README_ZH.md) | English
diff --git a/README_ZH.md b/README_ZH.md
new file mode 100644
index 0000000..5db6038
--- /dev/null
+++ b/README_ZH.md
@@ -0,0 +1,130 @@
+
+
+
+
+
+简体中文 | [English](/README.md)
+
+
+
+
+
+
Label Plus FX
+
+ 一个跨平台的Label Plus
+
+
+ 用户手册
+ ·
+ 反馈问题
+ ·
+ 提交建议
+
+
+
+
+
+
+ 目录
+
+ -
+ 关于本项目
+
+ -
+ 开始
+
+
+ - 说明
+ - 许可协议
+ - 联系方式
+
+
+
+
+
+## 关于本项目
+
+[![Product Screen Shot][product-screenshot]]()
+
+本项目受到 [LabelPlus](https://noodlefighter.com/label_plus/)的启发。
+
+
+
+## 开始
+
+复制本项目并启动需要以下几个简单的步骤
+
+### 环境
+
+ * [Liberica JDK 17 (完整版本)](https://bell-sw.com/pages/downloads/#/java-17-lts%20/%20current) : 用于主应用程序;
+
+ * [可选] [Visual Studio 2019](https://visualstudio.microsoft.com/zh-hans/downloads/) : 用于Windows IME JNI接口;
+
+
+### 启动步骤
+
+1.克隆仓库
+ ```sh
+ git clone https://github.com/Meodinger/LabelPlusFX.git
+ ```
+2. 运行Maven命令 `package`
+
+3. 运行脚本, `link.bat` `build.bat` 都可以
+
+4. 对于Windows用户, 构建封装器库 `IMEWrapper` 然后复制 `IMEInterface.dll` 和 `IMEWrapper.dll` 到 `LabelPlusFX.exe` (使用`jpackage`)或 `runtime\java.exe`(使用`jlink`) 所在的文件夹下.
+
+> 如果不想使用Windows IME JNI接口, 可以使用 `run.bat --disable-jni` 或`LabelPlusFX.exe --disable-jni`方式启动
+
+> 在IDE中运行LPFX, 可以执行 `exec:java@run` 命令
+
+
+## 说明
+
+Label Plus FX的功能设计基于 [LabelPlus](https://noodlefighter.com/label_plus/)
+
+更多示例,请参考用户手册和Wiki [User Manual](https://www.kdocs.cn/l/seRSJCKVOn0Y) 和 [Wiki](https://github.com/Meodinger/LabelPlusFX/wiki)
+
+
+
+## 贡献
+
+开源社区因贡献而变得如此美好,充满学习、启发和创造。非常感谢您所做的**任何贡献**
+
+1. Fork项目
+2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
+3. 提交更改 (`git commit -m '添加了一些很棒的改进'`)
+4. 推送到分支 (`git push origin feature/AmazingFeature`)
+5. 发起拉取请求
+
+
+
+## 许可协议
+
+根据AGPLv3许可证分发。有关更多信息,请参见`LICENSE`页面。
+
+
+
+## 联系方式
+
+Meodinger Wang - [@Meodinger_Wang](https://twitter.com/Meodinger_Wang) - meodinger@qq.com
+
+项目链接: [https://github.com/Meodinger/LabelPlusFX](https://github.com/Meodinger/LabelPlusFX)
+
+
+
+## 赞助
+
+
+
+
+
+[product-screenshot]: https://s2.loli.net/2022/02/04/2H7bguJ9rcyBjUO.png
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 4e5229d..839fc0f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
ink.meodinger
lpfx
- 2.3.3-SNAPSHOT
+ 2.3.4
jar
lpfx
@@ -118,6 +118,10 @@
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
+
+ 17
+ 17
+
org.apache.maven.plugins
diff --git a/script/jpackage/LabelPlusFXDict.lnk b/script/jpackage/LabelPlusFXDict.lnk
new file mode 100644
index 0000000..4d2445b
Binary files /dev/null and b/script/jpackage/LabelPlusFXDict.lnk differ
diff --git a/script/jpackage/build.bat b/script/jpackage/build.bat
index 45a30bc..d5b50ba 100644
--- a/script/jpackage/build.bat
+++ b/script/jpackage/build.bat
@@ -6,7 +6,7 @@ rd /S /Q ".\LabelPlusFX"
set MODULES="%DIR%\target\build"
set ICON="%DIR%\images\icons\cat.ico"
-jpackage --verbose --type app-image --app-version 2.3.3 --copyright "Meodinger Tech (C) 2022" --name LabelPlusFX --icon %ICON% --dest . --module-path %MODULES% --add-modules lpfx,jdk.crypto.cryptoki --module lpfx/ink.meodinger.lpfx.LauncherKt
+jpackage --verbose --type app-image --app-version 2.3.3 --copyright "Meodinger Tech (C) 2022" --name LabelPlusFX --icon %ICON% --dest . --module-path %MODULES% --add-modules lpfx,jdk.crypto.cryptoki --module lpfx/ink.meodinger.lpfx.LauncherKt --java-options "-Dprism.maxvram=2G"
echo:
-echo All completed, remember to copy dlls!
\ No newline at end of file
+echo All completed, remember to copy dlls!
diff --git a/src/main/kotlin/ink/meodinger/lpfx/Controller.kt b/src/main/kotlin/ink/meodinger/lpfx/Controller.kt
index 472e958..1d798f5 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/Controller.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/Controller.kt
@@ -104,7 +104,7 @@ class Controller(private val state: State) {
}
}
- private var accumulator: Long = 16 * 60 * 60 * ONE_SECOND
+ private var accumulator: Long = 16 * 60 * 60 * ONE_SECOND // Shift to 1970/01/02 00:00 GMT+8
private val accumulatorFormatter = SimpleDateFormat("HH:mm:ss")
private val accumulatorManager = TimerTaskManager(0, ONE_SECOND) {
if (state.isOpened) {
@@ -143,7 +143,13 @@ class Controller(private val state: State) {
// Opened and selected
val file = state.getPicFileNow()
if (file.exists()) {
- val imageByFX = Image(file.toURI().toURL().toString())
+ var imageByFX = Image(file.toURI().toURL().toString())
+
+ //if the image is too large,limit the size of image
+ if(imageByFX.width > 5000 || imageByFX.height > 5000) {
+ Logger.info("limit the size of image because `$file` is too large ", "Controller")
+ imageByFX = Image(file.toURI().toURL().toString(),5000.0,5000.0,true,true)
+ }
if (!imageByFX.isError) {
imageByFX
@@ -202,12 +208,7 @@ class Controller(private val state: State) {
Logger.info("Controller initialized", "Controller")
// Display default image
- cLabelPane.isVisible = false
- Platform.runLater {
- // re-locate after the initial rendering
- cLabelPane.moveToCenter()
- cLabelPane.isVisible = true
- }
+ cLabelPane.moveToCenter()
}
/**
@@ -353,7 +354,7 @@ class Controller(private val state: State) {
}
WorkMode.LabelMode -> {
val transLabel = state.transFile.getTransLabel(state.currentPicName, it.labelIndex)
- val transGroup = state.transFile.getTransGroup(transLabel.groupId)
+ val transGroup = state.transFile.groupList[transLabel.groupId]
cLabelPane.showText(transGroup.name, transGroup.color, it.displayX, it.displayY)
}
}
@@ -387,7 +388,7 @@ class Controller(private val state: State) {
WorkMode.LabelMode -> {
if (state.currentGroupId == NOT_FOUND) return@handler
- val transGroup = state.transFile.getTransGroup(state.currentGroupId)
+ val transGroup = state.transFile.groupList[state.currentGroupId]
cLabelPane.showText(transGroup.name, transGroup.color, it.displayX, it.displayY)
}
}
@@ -405,10 +406,10 @@ class Controller(private val state: State) {
if (it != NOT_FOUND) {
if (cTreeView.isFocused) {
// if the change is result of CTreeView selection, add
- cTreeView.selectGroup(state.transFile.getTransGroup(it).name, clear = false, scrollTo = false)
+ cTreeView.selectGroup(state.transFile.groupList[it].name, clear = false, scrollTo = false)
} else {
// if the change is result of GroupBar/Box selection, set
- cTreeView.selectGroup(state.transFile.getTransGroup(it).name, clear = true, scrollTo = true)
+ cTreeView.selectGroup(state.transFile.groupList[it].name, clear = true, scrollTo = true)
}
}
} else {
@@ -433,8 +434,10 @@ class Controller(private val state: State) {
cPicBox.itemsProperty().bind(picNamesBinding)
cPicBox.indexProperty().addListener(onNew {
if (state.isOpened) {
- // PicBox index should never be -1 (value should never be null)
- state.currentPicName = state.transFile.sortedPicNames[it]
+ if (it != NOT_FOUND) {
+ // PicBox index should never be -1 except when removing a picture
+ state.currentPicName = state.transFile.sortedPicNames[it]
+ }
} else {
// Closed, do nothing. Let State set current-pic-name to empty string
}
@@ -512,10 +515,13 @@ class Controller(private val state: State) {
// This could clear the label-index related bindings like TransArea text
state.currentPicNameProperty().addListener(onChange {
// If switch picture in CTreeView, the fours on TreeCell will not clear automatically
+ //clear selection
+ state.currentLabelIndex = NOT_FOUND
// So we should manually clear it to make sure we start from the first label
- cTreeView.selectRoot(clear = true, scrollTo = false)
+ cTreeView.selectFirst(clear = true, scrollTo = false)
+ cLabelPane.moveToLabel(cTreeView.selectedLabel)
// Clear here, because the already happened selection may change it
- state.currentLabelIndex = NOT_FOUND
+// state.currentLabelIndex = NOT_FOUND
})
Logger.info("Listened for current-pic-name change for clear label-index selection", "Controller")
@@ -598,7 +604,6 @@ class Controller(private val state: State) {
*/
private fun transform() {
Logger.info("Applying Transformations...", "Controller")
-
// Transform tab press in CTreeView to ViewModeBtn click
cTreeView.addEventFilter(KeyEvent.KEY_PRESSED) {
if (it.code == KeyCode.TAB) {
@@ -621,6 +626,22 @@ class Controller(private val state: State) {
}
Logger.info("Transformed Tab on CLabelPane", "Controller")
+ val changePicHandler = EventHandler handler@{
+ if (it.isControlDown || it.isMetaDown || it.isShiftDown || it.isAltDown || it.code.isDigitKey) return@handler
+ // Mark immediately when this event will be consumed
+ it.consume() // stop further propagation
+
+ when (it.code) {
+ KeyCode.Q -> cPicBox.back()
+ KeyCode.W -> cPicBox.next()
+ else -> return@handler
+ }
+ cTreeView.selectFirst()
+ it.consume() // Consume used event
+ }
+ cLabelPane.addEventHandler(KeyEvent.KEY_PRESSED, changePicHandler)
+ Logger.info("Transformed A/D", "Controller")
+
// Transform number key press to CTreeView select
val numberBuilder = StringBuilder()
view.addEventHandler(KeyEvent.KEY_PRESSED) handler@{
@@ -631,17 +652,17 @@ class Controller(private val state: State) {
// Mark immediately when this event will be consumed
it.consume() // stop further propagation
- val number = it.code.char.toInt()
+ val number = it.text.toInt()
if (numberBuilder.isEmpty()) {
// Not parsing
if (number == 0) {
// Start parse
numberBuilder.append(0)
- } else if (number in 1..state.transFile.groupCount) {
+ } else if (state.transFileProperty().isNotNull.value && number in 1..state.transFile.groupCount) {
// Try select
val index = number - 1
if (state.viewMode == ViewMode.GroupMode) {
- cTreeView.selectGroup(state.transFile.getTransGroup(index).name, clear = true, scrollTo = false)
+ cTreeView.selectGroup(state.transFile.groupList[index].name, clear = true, scrollTo = false)
} else {
state.currentGroupId = index
}
@@ -652,10 +673,10 @@ class Controller(private val state: State) {
// Parsing
numberBuilder.append(number)
val index = numberBuilder.toString().toInt() - 1
- if (index in 0 until state.transFile.groupCount) {
+ if ( state.transFileProperty().isNotNull.value &&index in 0 until state.transFile.groupCount) {
// Try select
if (state.viewMode == ViewMode.GroupMode) {
- cTreeView.selectGroup(state.transFile.getTransGroup(index).name, clear = true, scrollTo = false)
+ cTreeView.selectGroup(state.transFile.groupList[index].name, clear = true, scrollTo = false)
} else {
state.currentGroupId = index
}
@@ -668,23 +689,6 @@ class Controller(private val state: State) {
}
Logger.info("Transformed num-key pressed", "Controller")
- // Transform Ctrl + Left/Right KeyEvent to CPicBox button click
- val arrowKeyChangePicHandler = EventHandler handler@{
- if (!(it.isControlDown || it.isMetaDown)) return@handler
-
- when (it.code) {
- KeyCode.LEFT -> cPicBox.back()
- KeyCode.RIGHT -> cPicBox.next()
- else -> return@handler
- }
-
- it.consume() // Consume used event
- }
- cLabelPane.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
- cTransArea.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
- cTreeView.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
- Logger.info("Transformed Ctrl + Left/Right", "Controller")
-
/**
* Find next LabelItem as int index.
* @return NOT_FOUND when have no next
@@ -703,6 +707,40 @@ class Controller(private val state: State) {
}
}
+ fun moveCurrLabelTo(direction: Int) {
+ var itemIndex = getNextLabelItemIndex(cTreeView.selectionModel.selectedIndex, direction)
+ if (itemIndex == NOT_FOUND) {
+ // if selected first and try getting previous, return last;
+ // if selected last and try getting next, return first;
+ itemIndex = getNextLabelItemIndex(if (direction == 1) 0 else cTreeView.expandedItemCount, direction)
+ }
+ if(itemIndex == NOT_FOUND) {
+ return
+ }
+ val item = cTreeView.getTreeItem(itemIndex) as CTreeLabelItem
+
+ cLabelPane.moveToLabel(item.transLabel.index)
+ cTreeView.selectLabel(item.transLabel.index, clear = true, scrollTo = true)
+ }
+
+ // Transform Ctrl + Left/Right KeyEvent to CPicBox button click
+ val arrowKeyChangePicHandler = EventHandler handler@{
+ if (!(it.isControlDown || it.isMetaDown)) return@handler
+
+ when (it.code) {
+ KeyCode.LEFT -> cPicBox.back()
+ KeyCode.RIGHT -> cPicBox.next()
+ else -> return@handler
+ }
+ cTreeView.selectFirst()
+ it.consume() // Consume used event
+ }
+ cLabelPane.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
+ cTransArea.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
+ cTreeView.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangePicHandler)
+ Logger.info("Transformed Ctrl + Left/Right", "Controller")
+
+
// Transform Ctrl + Up/Down KeyEvent to CTreeView select (and have effect: move to label)
val arrowKeyChangeLabelHandler = EventHandler handler@{
if (!((it.isControlDown || it.isMetaDown) && it.code.isArrowKey)) return@handler
@@ -717,16 +755,7 @@ class Controller(private val state: State) {
// Mark immediately when this event will be consumed
it.consume() // stop further propagation
- var itemIndex = getNextLabelItemIndex(cTreeView.selectionModel.selectedIndex, itemShift)
- if (itemIndex == NOT_FOUND) {
- // if selected first and try getting previous, return last;
- // if selected last and try getting next, return first;
- itemIndex = getNextLabelItemIndex(if (itemShift == 1) 0 else cTreeView.expandedItemCount, itemShift)
- }
- val item = cTreeView.getTreeItem(itemIndex) as CTreeLabelItem
-
- cLabelPane.moveToLabel(item.transLabel.index)
- cTreeView.selectLabel(item.transLabel.index, clear = true, scrollTo = true)
+ moveCurrLabelTo(itemShift)
}
cLabelPane.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangeLabelHandler)
cTransArea.addEventHandler(KeyEvent.KEY_PRESSED, arrowKeyChangeLabelHandler)
@@ -742,20 +771,58 @@ class Controller(private val state: State) {
// transform
if (it.isShiftDown) {
// Met the bounds, change picture
- if (itemIndex == NOT_FOUND) cLabelPane.fireEvent(keyEvent(it, code = KeyCode.LEFT, character = "", text = ""))
- // Go to previous label
- cLabelPane.fireEvent(keyEvent(it, code = KeyCode.UP, character = "", text = ""))
+ if (itemIndex == NOT_FOUND) {
+ cPicBox.back()
+ cTreeView.selectLast()
+ } else {
+ // Go to previous label
+ moveCurrLabelTo(direction = -1)
+ }
} else {
// Met the bounds, change picture
- if (itemIndex == NOT_FOUND) cLabelPane.fireEvent(keyEvent(it, code = KeyCode.RIGHT, character = "", text = ""))
- // Go to previous label
- cLabelPane.fireEvent(keyEvent(it, code = KeyCode.DOWN, character = "", text = ""))
+ if (itemIndex == NOT_FOUND) {
+ cPicBox.next()
+ cTreeView.selectFirst()
+ } else {
+ // Go to previous label
+ moveCurrLabelTo(direction = 1)
+ }
}
}
cLabelPane.addEventHandler(KeyEvent.KEY_PRESSED, enterKeyTransformerHandler)
cTransArea.addEventHandler(KeyEvent.KEY_PRESSED, enterKeyTransformerHandler)
Logger.info("Transformed Ctrl + Enter", "Controller")
+
+
+
+ val copyLabelHandler = EventHandler handler@{
+ if (!(it.isControlDown || it.isMetaDown)) return@handler
+ when (it.code) {
+ KeyCode.C -> // cTreeView.pasteLabelsText(selectItems.map { it.transLabel.index },state)
+ {
+ @Suppress("UNCHECKED_CAST") val treeItem = cTreeView.getTreeItem(cTreeView.selectionModel.selectedIndex) as CTreeLabelItem
+ cTreeView.copyLabelText(treeItem.transLabel.index)
+ }
+ KeyCode.V -> {
+ @Suppress("UNCHECKED_CAST") val selectItems:Collection =
+ cTreeView.selectionModel.selectedIndices.map { cTreeView.getTreeItem(it)}
+ .filter { it is CTreeLabelItem } as List
+ cTreeView.pasteLabelsText(selectItems.map { it.transLabel.index },state)
+ }
+ else ->// cTreeView.pasteLabelsText(selectItems.map { it.transLabel.index },state)
+ // cTreeView.pasteLabelsText(selectItems.map { it.transLabel.index },state)
+ {
+ return@handler
+ }
+ }
+ it.consume() // Consume used event
+ }
+ cTreeView.addEventHandler(KeyEvent.KEY_PRESSED, copyLabelHandler)
+ Logger.info("Transformed Ctrl + C/V", "Controller")
+//
+
+
}
// Controller Methods
@@ -932,8 +999,8 @@ class Controller(private val state: State) {
state.currentLabelIndex = labelIndex.takeIf { state.transFile.getTransList(state.currentPicName).any { l -> l.index == it } } ?: NOT_FOUND
// Move to center
- // FIXME: May throw NoSuchElementException if render not complete
if (labelIndex != NOT_FOUND) {
+ // NotNow: May throw NoSuchElementException if render not complete
cTreeView.selectLabel(labelIndex, clear = true, scrollTo = true)
cLabelPane.moveToLabel(labelIndex)
}
@@ -965,7 +1032,7 @@ class Controller(private val state: State) {
}
// Use temp if overwrite
- val exportDest = if (overwrite) File.createTempFile(file.path, "temp").apply(File::deleteOnExit) else file
+ val exportDest = if (overwrite) File.createTempFile("LPFX", ".${file.extension}").apply(File::deleteOnExit) else file
// Export
try {
@@ -1140,38 +1207,38 @@ class Controller(private val state: State) {
val version = fetchLatestSync()
if (version != Version.V0) Logger.info("Got latest version: $version (current $V)", "Controller")
- if (version > V) Platform.runLater {
- val suppressNoticeButtonType = ButtonType(I18N["update.dialog.suppress"], ButtonBar.ButtonData.OK_DONE)
-
- val dialog = Dialog()
- dialog.initOwner(this@Controller.state.stage)
- dialog.title = I18N["update.dialog.title"]
- dialog.graphic = ImageView(IMAGE_INFO.resizeByRadius(GENERAL_ICON_RADIUS))
- dialog.dialogPane.buttonTypes.addAll(suppressNoticeButtonType, ButtonType.CLOSE)
- dialog.dialogPane.withContent(VBox()) {
- add(Label(String.format(I18N["update.dialog.content.s"], version)))
- add(Separator()) {
- padding = Insets(8.0, 0.0, 8.0, 0.0)
- }
- add(Hyperlink(I18N["update.dialog.link"])) {
- padding = Insets(0.0)
- setOnAction { this@Controller.state.application.hostServices.showDocument(release) }
+ Platform.runLater {
+ if (version > V) {
+ val suppressNoticeButtonType = ButtonType(I18N["update.dialog.suppress"], ButtonBar.ButtonData.OK_DONE)
+
+ val dialog = Dialog()
+ dialog.initOwner(this@Controller.state.stage)
+ dialog.title = I18N["update.dialog.title"]
+ dialog.graphic = ImageView(IMAGE_INFO.resizeByRadius(GENERAL_ICON_RADIUS))
+ dialog.dialogPane.buttonTypes.addAll(suppressNoticeButtonType, ButtonType.CLOSE)
+ dialog.dialogPane.withContent(VBox()) {
+ add(Label(String.format(I18N["update.dialog.content.s"], version)))
+ add(Separator()) {
+ padding = Insets(8.0, 0.0, 8.0, 0.0)
+ }
+ add(Hyperlink(I18N["update.dialog.link"])) {
+ padding = Insets(0.0)
+ setOnAction { this@Controller.state.application.hostServices.showDocument(release) }
+ }
}
- }
- val suppressButton = dialog.dialogPane.lookupButton(suppressNoticeButtonType)
- ButtonBar.setButtonUniformSize(suppressButton, false)
+ val suppressButton = dialog.dialogPane.lookupButton(suppressNoticeButtonType)
+ ButtonBar.setButtonUniformSize(suppressButton, false)
- dialog.showAndWait().ifPresent { type ->
- if (type == suppressNoticeButtonType) {
- Preference.lastUpdateNotice = time
- Logger.info("Check suppressed, next notice time is ${time + delay}",
- "Controller"
- )
+ dialog.showAndWait().ifPresent { type ->
+ if (type == suppressNoticeButtonType) {
+ Preference.lastUpdateNotice = time
+ Logger.info("Check suppressed, next notice time is ${time + delay}", "Controller")
+ }
}
+ } else if (showWhenUpdated) {
+ showInfo(this@Controller.state.stage, I18N["update.info.updated"])
}
- } else if (showWhenUpdated) Platform.runLater {
- showInfo(this@Controller.state.stage, I18N["update.info.updated"])
}
}()
}
diff --git a/src/main/kotlin/ink/meodinger/lpfx/LabelPlusFXDict.kt b/src/main/kotlin/ink/meodinger/lpfx/LabelPlusFXDict.kt
index c8a5017..9f6b950 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/LabelPlusFXDict.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/LabelPlusFXDict.kt
@@ -30,6 +30,7 @@ class LabelPlusFXDict: Application() {
showException(primaryStage, e)
}
// Set up the Stage
+ primaryStage.icons.add(ICON)
primaryStage.title = "LPFX Dictionary (Standalone)"
primaryStage.scene = OnlineDict().scene
primaryStage.width = 400.0
diff --git a/src/main/kotlin/ink/meodinger/lpfx/State.kt b/src/main/kotlin/ink/meodinger/lpfx/State.kt
index c2d88b5..13e27ec 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/State.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/State.kt
@@ -172,21 +172,21 @@ class State {
private val undoStack: Stack = ArrayStack()
private val redoStack: Stack = ArrayStack()
- private val canUndoProperty: BooleanProperty = SimpleBooleanProperty(false)
+ private val undoableProperty: BooleanProperty = SimpleBooleanProperty(false)
/**
* Whether undo is available
*/
- fun undoableProperty(): ReadOnlyBooleanProperty = canUndoProperty
+ fun undoableProperty(): ReadOnlyBooleanProperty = undoableProperty
/**
* @see undoableProperty
*/
- val isUndoable: Boolean by canUndoProperty
+ val isUndoable: Boolean by undoableProperty
private val redoableProperty: BooleanProperty = SimpleBooleanProperty(false)
/**
* Whether redo is available
*/
- fun canRedoProperty(): ReadOnlyBooleanProperty = redoableProperty
+ fun redoableProperty(): ReadOnlyBooleanProperty = redoableProperty
/**
* @see redoableProperty
*/
@@ -201,7 +201,7 @@ class State {
Logger.info("Action committed", "State")
isChanged = true
- canUndoProperty.set(true)
+ undoableProperty.set(true)
redoableProperty.set(false)
}
@@ -214,7 +214,7 @@ class State {
redoStack.push(undoStack.pop().apply(Action::revert))
Logger.info("Action reverted", "State")
- canUndoProperty.set(!undoStack.isEmpty())
+ undoableProperty.set(!undoStack.isEmpty())
redoableProperty.set(true)
}
@@ -227,18 +227,16 @@ class State {
undoStack.push(redoStack.pop().apply(Action::commit))
Logger.info("Action re-committed", "State")
- canUndoProperty.set(true)
+ undoableProperty.set(true)
redoableProperty.set(!redoStack.isEmpty())
}
// endregion
/**
- * Reset the entire worksapce, be ready to open another translation.
+ * Reset the entire workspace, be ready to open another translation.
*/
fun reset() {
- if (!isOpened) return
-
controller.reset()
undoStack.empty()
diff --git a/src/main/kotlin/ink/meodinger/lpfx/View.kt b/src/main/kotlin/ink/meodinger/lpfx/View.kt
index 0fbbfb2..f7264b2 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/View.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/View.kt
@@ -124,9 +124,11 @@ class View(private val state: State) : BorderPane() {
*/
val cTransArea: CLigatureArea = CLigatureArea()
+
// Private Components
private val statsBar: HBox = HBox()
private val cTreeMenu: CTreeMenu = CTreeMenu(state, cTreeView)
+ private val cTextMenu: CTextMenu = CTextMenu(cTransArea)
// endregion
@@ -230,6 +232,9 @@ class View(private val state: State) : BorderPane() {
item(I18N["m.bak_recovery"]) {
does { bakRecovery() }
}
+ item(I18N["m.settings"]) {
+ does { settings() }
+ }
separator()
item(I18N["m.exit"]) {
does { exitApplication() }
@@ -243,7 +248,7 @@ class View(private val state: State) : BorderPane() {
}
item(I18N["m.redo"]) {
does { state.redo() }
- disableProperty().bind(!state.canRedoProperty())
+ disableProperty().bind(!state.redoableProperty())
accelerator = KeyCodeCombination(KeyCode.Z, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN)
}
separator()
@@ -311,9 +316,6 @@ class View(private val state: State) : BorderPane() {
}
}
menu(I18N["mm.about"]) {
- item(I18N["m.settings"]) {
- does { settings() }
- }
item(I18N["m.logs"]) {
does { logs() }
}
@@ -771,6 +773,7 @@ class View(private val state: State) : BorderPane() {
Settings.DefaultGroupColorHexList -> Settings.defaultGroupColorHexList .setAll(value as List)
Settings.IsGroupCreateOnNewTrans -> Settings.isGroupCreateOnNewTransList .setAll(value as List)
Settings.LigatureRules -> Settings.ligatureRules .setAll(value as List>)
+ Settings.QuickInputTexts -> Settings.quickInputTexts .setAll(value as List)
Settings.ViewModes -> Settings.viewModes .setAll(value as List)
Settings.NewPictureScale -> Settings.newPictureScalePicture = value as CLabelPane.NewPictureScale
Settings.UseWheelToScale -> Settings.useWheelToScale = value as Boolean
@@ -784,6 +787,9 @@ class View(private val state: State) : BorderPane() {
Settings.UseMeoFileAsDefault -> Settings.useMeoFileAsDefault = value as Boolean
Settings.UseExportNameTemplate -> Settings.useExportNameTemplate = value as Boolean
Settings.ExportNameTemplate -> Settings.exportNameTemplate = value as String
+ Settings.UseCustomBaiduKey -> Settings.useCustomBaiduKey = value as Boolean
+ Settings.BaiduTransLateKey -> Settings.baiduTransLateKey = value as String
+ Settings.BaiduTransLateAppId -> Settings.baiduTransLateAppId = value as String
else -> doNothing()
}
}
diff --git a/src/main/kotlin/ink/meodinger/lpfx/action/GroupAction.kt b/src/main/kotlin/ink/meodinger/lpfx/action/GroupAction.kt
index 618d631..cd1dbdf 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/action/GroupAction.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/action/GroupAction.kt
@@ -58,21 +58,20 @@ class GroupAction(
throw IllegalArgumentException(String.format(I18N["exception.action.group_repeated.s"], transGroup.name))
state.transFile.groupListObservable.add(groupId, transGroup)
-
for (labels in state.transFile.transMapObservable.values) for (label in labels)
if (label.groupId >= groupId) label.groupId++
+ @Suppress("DEPRECATION") state.transFile.installGroup(transGroup)
Logger.info("Added TransGroup: $transGroup", "Action")
}
private fun removeTransGroup(transGroup: TransGroup) {
- val groupId = state.transFile.getGroupIdByName(transGroup.name)
- if (state.transFile.isGroupStillInUse(groupId))
+ if (state.transFile.isGroupStillInUse(transGroup.name))
throw IllegalArgumentException(String.format(I18N["exception.action.group_still_in_use.s"], transGroup.name))
+ @Suppress("DEPRECATION") state.transFile.disposeGroup(transGroup)
for (labels in state.transFile.transMapObservable.values) for (label in labels)
- if (label.groupId >= groupId) label.groupId--
-
- state.transFile.groupListObservable.removeAt(groupId)
+ if (label.groupId >= transGroup.index) label.groupId--
+ state.transFile.groupListObservable.removeAt(transGroup.index)
Logger.info("Removed TransGroup: $transGroup", "Action")
}
diff --git a/src/main/kotlin/ink/meodinger/lpfx/action/LabelAction.kt b/src/main/kotlin/ink/meodinger/lpfx/action/LabelAction.kt
index 7153f24..3b36243 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/action/LabelAction.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/action/LabelAction.kt
@@ -50,7 +50,16 @@ class LabelAction(
if (newLabelIndex != NOT_FOUND) {
builder.append("@index: ${targetTransLabel.index.pad(2)} -> ${index.pad(2)}; ")
- targetTransLabel.index = index
+ if (targetTransLabel.index == index) return
+ val transLabel = TransLabel(
+ index,
+ targetTransLabel.groupId,
+ targetTransLabel.x,
+ targetTransLabel.y,
+ targetTransLabel.text
+ )
+ removeTransLabel(targetPicName,targetTransLabel)
+ addTransLabel(targetPicName,transLabel)
}
if (newGroupId != NOT_FOUND) {
builder.append("@groupId: ${targetTransLabel.groupId.pad(2)} -> ${groupId.pad(2)}; ")
@@ -82,7 +91,7 @@ class LabelAction(
throw IllegalArgumentException(String.format(I18N["exception.action.label_group_invalid.i"], transLabel.groupId))
for (label in list) if (label.index >= transLabel.index) label.index++
- list.add(transLabel)
+ list.add(transLabel.index -1 ,transLabel)
@Suppress("DEPRECATION") state.transFile.installLabel(transLabel)
Logger.info("Added $picName @ $transLabel", "Action")
diff --git a/src/main/kotlin/ink/meodinger/lpfx/component/CLabelPane.kt b/src/main/kotlin/ink/meodinger/lpfx/component/CLabelPane.kt
index 7539762..3a6a82f 100644
--- a/src/main/kotlin/ink/meodinger/lpfx/component/CLabelPane.kt
+++ b/src/main/kotlin/ink/meodinger/lpfx/component/CLabelPane.kt
@@ -845,7 +845,8 @@ class CLabelPane : ScrollPane() {
* @param labelIndex Index of the label which will be displaye at the center
*/
fun moveToLabel(labelIndex: Int) {
- val label = labelNodes.first { it.index == labelIndex }
+ val label = labelNodes.firstOrNull { it.index == labelIndex } ?:return
+
vvalue = 0.0
hvalue = 0.0
diff --git a/src/main/kotlin/ink/meodinger/lpfx/component/CTextMenu.kt b/src/main/kotlin/ink/meodinger/lpfx/component/CTextMenu.kt
new file mode 100644
index 0000000..de36a29
--- /dev/null
+++ b/src/main/kotlin/ink/meodinger/lpfx/component/CTextMenu.kt
@@ -0,0 +1,99 @@
+package ink.meodinger.lpfx.component
+
+import ink.meodinger.lpfx.I18N
+import ink.meodinger.lpfx.get
+import ink.meodinger.lpfx.options.Logger
+import ink.meodinger.lpfx.options.Settings
+import ink.meodinger.lpfx.util.property.onChange
+import javafx.event.EventHandler
+import javafx.scene.control.*
+
+class CTextMenu(
+ private val textField: TextInputControl
+) : ContextMenu() {
+
+ // add default menu items
+ private val undoMI = MenuItem(I18N["input.menu.undo"]).apply {
+ onAction = EventHandler { textField.undo() }
+ }
+ private val redoMI = MenuItem(I18N["input.menu.redo"]).apply {
+ onAction = EventHandler { textField.redo() }
+ }
+ private val cutMI = MenuItem(I18N["input.menu.cut"]).apply {
+ onAction = EventHandler { textField.cut() }
+ }
+ private val copyMI = MenuItem(I18N["input.menu.copy"]).apply {
+ onAction = EventHandler { textField.copy() }
+ }
+ private val pasteMI = MenuItem(I18N["input.menu.paste"]).apply {
+ onAction = EventHandler { textField.paste() }
+ }
+ private val deleteMI = MenuItem(I18N["input.menu.delete_selection"]).apply {
+ onAction = EventHandler { deleteSelectedText(textField) }
+ }
+ private val selectAllMI = MenuItem(I18N["input.menu.select_all"]).apply {
+ onAction = EventHandler { textField.selectAll() }
+ }
+
+ // add custom menu items
+ private val quickInput = Menu(I18N["input.menu.quick_input"])
+
+
+ init {
+ textField.undoableProperty()
+ .addListener { _, _, newValue ->
+ undoMI.isDisable =
+ !newValue!!
+ }
+ textField.redoableProperty()
+ .addListener { _, _, newValue ->
+ redoMI.isDisable =
+ !newValue!!
+ }
+ textField.selectionProperty()
+ .addListener { _, _, newValue ->
+ cutMI.isDisable =
+ newValue.length == 0
+ copyMI.isDisable = newValue.length == 0
+ deleteMI.isDisable = newValue.length == 0
+ selectAllMI.isDisable = newValue.length == newValue.end
+ }
+
+ //init QuickInputItems
+ initQuickInputItems(quickInput)
+ textField.contextMenu = ContextMenu(
+ undoMI, redoMI, cutMI, copyMI, pasteMI, deleteMI, SeparatorMenuItem(), selectAllMI, quickInput
+ )
+ }
+
+ private fun deleteSelectedText(t: TextInputControl) {
+ val range = t.selection
+ if (range.length == 0) {
+ return
+ }
+ val text = t.text
+ val newText = text.substring(0, range.start) + text.substring(range.end)
+ t.text = newText
+ t.positionCaret(range.start)
+ }
+
+ private fun initQuickInputItems(menu: Menu) {
+ menu.items.clear()
+ menu.items.addAll(getQuickInputItems(textField))
+ Settings.quickInputTextsProperty.addListener( onChange {
+ Logger.info("refresh the quick input items", "CTextMenu")
+ menu.items.clear()
+ menu.items.addAll(getQuickInputItems(textField))
+ })
+ }
+
+ private fun getQuickInputItems(t: TextInputControl): List