Compare commits
No commits in common. "cdcfd92f606b577ec51decdc772a1d23c92d9721" and "1ee597f93455b486f6017e6c74925e8d0398dcf3" have entirely different histories.
cdcfd92f60
...
1ee597f934
@ -29,7 +29,6 @@ class WillowApplication : Application() {
|
|||||||
private const val FONT_SIZE = 12.0
|
private const val FONT_SIZE = 12.0
|
||||||
|
|
||||||
private const val JDBC_CONNECTION_STRING = "jdbc:sqlite:dictionary.db"
|
private const val JDBC_CONNECTION_STRING = "jdbc:sqlite:dictionary.db"
|
||||||
val resourceBundle: ResourceBundle = ResourceBundle.getBundle("i18n/willow", Locale.US)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
@ -46,7 +45,7 @@ class WillowApplication : Application() {
|
|||||||
val model = Model(searchService, findWordsService)
|
val model = Model(searchService, findWordsService)
|
||||||
|
|
||||||
val fxmlLoader = FXMLLoader()
|
val fxmlLoader = FXMLLoader()
|
||||||
fxmlLoader.resources = resourceBundle
|
fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", Locale.US)
|
||||||
fxmlLoader.controllerFactory = Callback { type ->
|
fxmlLoader.controllerFactory = Callback { type ->
|
||||||
when (type) {
|
when (type) {
|
||||||
MainController::class.java -> MainController(model)
|
MainController::class.java -> MainController(model)
|
||||||
@ -72,8 +71,7 @@ class WillowApplication : Application() {
|
|||||||
private fun loadFonts() {
|
private fun loadFonts() {
|
||||||
Font.loadFont(javaClass.getResourceAsStream("/fonts/inter.ttf"), FONT_SIZE)
|
Font.loadFont(javaClass.getResourceAsStream("/fonts/inter.ttf"), FONT_SIZE)
|
||||||
Font.loadFont(javaClass.getResourceAsStream("/fonts/tw-kai.ttf"), FONT_SIZE)
|
Font.loadFont(javaClass.getResourceAsStream("/fonts/tw-kai.ttf"), FONT_SIZE)
|
||||||
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc-regular.ttf"), FONT_SIZE)
|
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc.ttf"), FONT_SIZE)
|
||||||
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc-bold.ttf"), FONT_SIZE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ fun main() {
|
|||||||
pinyin_with_tone_marks TEXT NOT NULL,
|
pinyin_with_tone_marks TEXT NOT NULL,
|
||||||
pinyin_with_tone_numbers TEXT NOT NULL,
|
pinyin_with_tone_numbers TEXT NOT NULL,
|
||||||
zhuyin TEXT NOT NULL,
|
zhuyin TEXT NOT NULL,
|
||||||
searchable_pinyin TEXT NOT NULL,
|
|
||||||
searchable_pinyin_with_tone_numbers TEXT NOT NULL,
|
|
||||||
definitions JSON NOT NULL,
|
definitions JSON NOT NULL,
|
||||||
character_count INTEGER NOT NULL,
|
character_count INTEGER NOT NULL,
|
||||||
CONSTRAINT character_count_gte CHECK(character_count > 0)
|
CONSTRAINT character_count_gte CHECK(character_count > 0)
|
||||||
@ -38,10 +36,6 @@ fun main() {
|
|||||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_traditional ON cedict (traditional)")
|
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_traditional ON cedict (traditional)")
|
||||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_simplified ON cedict (simplified)")
|
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_simplified ON cedict (simplified)")
|
||||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_character_count ON cedict (character_count)")
|
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_character_count ON cedict (character_count)")
|
||||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS idx_cedict_searchable_pinyin ON cedict (searchable_pinyin)")
|
|
||||||
statement.executeUpdate(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_cedict_searchable_pinyin_with_tone_numbers ON cedict (searchable_pinyin_with_tone_numbers)"
|
|
||||||
)
|
|
||||||
|
|
||||||
val cedictParser = CedictParser.instance
|
val cedictParser = CedictParser.instance
|
||||||
val cedictEntries =
|
val cedictEntries =
|
||||||
@ -51,7 +45,7 @@ fun main() {
|
|||||||
|
|
||||||
val insertStatement =
|
val insertStatement =
|
||||||
connection.prepareStatement(
|
connection.prepareStatement(
|
||||||
"INSERT OR IGNORE INTO cedict(traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, searchable_pinyin, searchable_pinyin_with_tone_numbers, definitions, character_count) VALUES(?,?,?,?,?,?,?,?,?)"
|
"INSERT OR IGNORE INTO cedict(traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions, character_count) VALUES(?,?,?,?,?,?,?)"
|
||||||
)
|
)
|
||||||
for (entry in cedictEntries) {
|
for (entry in cedictEntries) {
|
||||||
try {
|
try {
|
||||||
@ -77,31 +71,12 @@ fun main() {
|
|||||||
)
|
)
|
||||||
insertStatement.setString(
|
insertStatement.setString(
|
||||||
6,
|
6,
|
||||||
entry.pinyinSyllables.joinToString(
|
|
||||||
separator = ""
|
|
||||||
) {
|
|
||||||
it
|
|
||||||
.format(TransliterationSystem.PINYIN_WITH_TONE_NUMBERS)
|
|
||||||
.lowercase()
|
|
||||||
.replace("""\d""".toRegex(), "")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
insertStatement.setString(
|
|
||||||
7,
|
|
||||||
entry.pinyinSyllables.joinToString(
|
|
||||||
separator = ""
|
|
||||||
) {
|
|
||||||
it.format(TransliterationSystem.PINYIN_WITH_TONE_NUMBERS).lowercase()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
insertStatement.setString(
|
|
||||||
8,
|
|
||||||
Json.encodeToString(
|
Json.encodeToString(
|
||||||
ListSerializer(ListSerializer(String.serializer())),
|
ListSerializer(ListSerializer(String.serializer())),
|
||||||
entry.definitions.map { it.glosses }
|
entry.definitions.map { it.glosses }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
insertStatement.setInt(9, entry.traditional.length)
|
insertStatement.setInt(7, entry.traditional.length)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,9 @@ fun main() {
|
|||||||
val (shangWords, time3) = measureTimedValue {
|
val (shangWords, time3) = measureTimedValue {
|
||||||
sqliteDictionary.findWordsContaining(shang)
|
sqliteDictionary.findWordsContaining(shang)
|
||||||
}
|
}
|
||||||
val (characters, time4) = measureTimedValue {
|
|
||||||
val searchi = sqliteDictionary.search("師範大學", SearchMode.TRADITIONAL).first()
|
|
||||||
sqliteDictionary.findCharacters(searchi)
|
|
||||||
}
|
|
||||||
println(searchResults)
|
println(searchResults)
|
||||||
println(shangWords)
|
println(shangWords)
|
||||||
println(characters)
|
|
||||||
println(time)
|
println(time)
|
||||||
println(time2)
|
println(time2)
|
||||||
println(time3)
|
println(time3)
|
||||||
println(time4)
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import java.sql.PreparedStatement
|
|||||||
import java.sql.ResultSet
|
import java.sql.ResultSet
|
||||||
|
|
||||||
class SqliteDictionary(private val connection: Connection) : Dictionary {
|
class SqliteDictionary(private val connection: Connection) : Dictionary {
|
||||||
private val whitespaceRegex = """\s+""".toRegex()
|
|
||||||
|
|
||||||
private val searchSimplifiedPreparedStatement: PreparedStatement by lazy {
|
private val searchSimplifiedPreparedStatement: PreparedStatement by lazy {
|
||||||
connection.prepareStatement(
|
connection.prepareStatement(
|
||||||
@ -30,18 +29,6 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val searchPinyinPreparedStatement: PreparedStatement by lazy {
|
|
||||||
connection.prepareStatement(
|
|
||||||
"""
|
|
||||||
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions
|
|
||||||
FROM cedict
|
|
||||||
WHERE searchable_pinyin GLOB ?
|
|
||||||
OR searchable_pinyin_with_tone_numbers GLOB ?
|
|
||||||
ORDER BY character_count ASC
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findWordsContaining: PreparedStatement by lazy {
|
private val findWordsContaining: PreparedStatement by lazy {
|
||||||
connection.prepareStatement(
|
connection.prepareStatement(
|
||||||
"""
|
"""
|
||||||
@ -53,14 +40,8 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val findCharacters = """
|
|
||||||
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions
|
|
||||||
FROM cedict
|
|
||||||
WHERE traditional IN (?)
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
|
override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
|
||||||
SearchMode.PINYIN -> searchPinyin(query)
|
SearchMode.PINYIN -> TODO()
|
||||||
SearchMode.SIMPLIFIED -> searchSimplified(query)
|
SearchMode.SIMPLIFIED -> searchSimplified(query)
|
||||||
SearchMode.TRADITIONAL -> searchTraditional(query)
|
SearchMode.TRADITIONAL -> searchTraditional(query)
|
||||||
SearchMode.ENGLISH -> TODO()
|
SearchMode.ENGLISH -> TODO()
|
||||||
@ -79,16 +60,7 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
|
override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
|
||||||
val characterList = entry.traditional
|
return emptyList()
|
||||||
.split("")
|
|
||||||
.filter { it.isNotBlank() }
|
|
||||||
.joinToString(",") { "'$it'" }
|
|
||||||
|
|
||||||
val query = findCharacters.replace("?", characterList)
|
|
||||||
|
|
||||||
val resultSet: ResultSet = connection.createStatement().executeQuery(query)
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchSimplified(query: String): List<DictionaryEntry> {
|
private fun searchSimplified(query: String): List<DictionaryEntry> {
|
||||||
@ -99,17 +71,6 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
return resultSet.toListOfDictionaryEntries()
|
return resultSet.toListOfDictionaryEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchPinyin(query: String): List<DictionaryEntry> {
|
|
||||||
val sanitizedQuery = query.lowercase().replace(whitespaceRegex, "")
|
|
||||||
|
|
||||||
searchPinyinPreparedStatement.setString(1, "$sanitizedQuery*")
|
|
||||||
searchPinyinPreparedStatement.setString(2, "$sanitizedQuery*")
|
|
||||||
|
|
||||||
val resultSet: ResultSet = searchPinyinPreparedStatement.executeQuery()
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun searchTraditional(query: String): List<DictionaryEntry> {
|
private fun searchTraditional(query: String): List<DictionaryEntry> {
|
||||||
searchTraditionalPreparedStatement.setString(1, "$query*")
|
searchTraditionalPreparedStatement.setString(1, "$query*")
|
||||||
|
|
||||||
|
@ -44,13 +44,15 @@ internal class EntryCell : ListCell<DictionaryEntryFx?>() {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
text = null
|
text = null
|
||||||
|
if (item != null) {
|
||||||
|
contextMenu = createContextMenuForEntry(item!!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateItem(entry: DictionaryEntryFx?, empty: Boolean) {
|
override fun updateItem(entry: DictionaryEntryFx?, empty: Boolean) {
|
||||||
super.updateItem(entry, empty)
|
super.updateItem(entry, empty)
|
||||||
if (empty || entry == null) {
|
if (empty || entry == null) {
|
||||||
graphic = null
|
graphic = null
|
||||||
contextMenu = null
|
|
||||||
} else {
|
} else {
|
||||||
labelHeadword.text = entry.traditionalProperty.value
|
labelHeadword.text = entry.traditionalProperty.value
|
||||||
labelPronunciation.text = entry.pinyinWithToneMarksProperty.value
|
labelPronunciation.text = entry.pinyinWithToneMarksProperty.value
|
||||||
@ -58,7 +60,6 @@ internal class EntryCell : ListCell<DictionaryEntryFx?>() {
|
|||||||
val definition = entry.definitions.joinToString(separator = " / ") { it.joinToString(separator = "; ") }
|
val definition = entry.definitions.joinToString(separator = " / ") { it.joinToString(separator = "; ") }
|
||||||
labelDefinition.text = definition
|
labelDefinition.text = definition
|
||||||
|
|
||||||
contextMenu = createContextMenuForEntry(entry)
|
|
||||||
graphic = root
|
graphic = root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,6 @@ import javafx.scene.control.Label
|
|||||||
import javafx.scene.control.ListView
|
import javafx.scene.control.ListView
|
||||||
import javafx.scene.control.TabPane
|
import javafx.scene.control.TabPane
|
||||||
import javafx.scene.web.WebView
|
import javafx.scene.web.WebView
|
||||||
import kotlinx.html.body
|
|
||||||
import kotlinx.html.h1
|
|
||||||
import kotlinx.html.html
|
|
||||||
import kotlinx.html.li
|
|
||||||
import kotlinx.html.ol
|
|
||||||
import kotlinx.html.stream.createHTML
|
|
||||||
|
|
||||||
class DetailsController(private val model: Model) {
|
class DetailsController(private val model: Model) {
|
||||||
@FXML
|
@FXML
|
||||||
@ -70,19 +64,19 @@ class DetailsController(private val model: Model) {
|
|||||||
return@addListener
|
return@addListener
|
||||||
}
|
}
|
||||||
webViewDefinition.engine.loadContent(
|
webViewDefinition.engine.loadContent(
|
||||||
createHTML().html {
|
buildString {
|
||||||
body {
|
append("<html>")
|
||||||
h1 {
|
append("<body>")
|
||||||
+"CC-CEDICT"
|
append("<h1>CC-CEDICT</h1>")
|
||||||
}
|
append("<ol>")
|
||||||
ol {
|
for (definition in newValue.definitions) {
|
||||||
for (definition in newValue.definitions) {
|
append("<li>")
|
||||||
li {
|
append(definition.joinToString(separator = "; "))
|
||||||
+definition.joinToString(separator = "; ")
|
append("</li>")
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
append("</ol>")
|
||||||
|
append("</body>")
|
||||||
|
append("</html>")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,5 @@ class SearchController(private val model: Model) {
|
|||||||
val searchMode = searchModeToggleGroup.selectedToggle.userData as SearchMode
|
val searchMode = searchModeToggleGroup.selectedToggle.userData as SearchMode
|
||||||
model.search(newValue, searchMode)
|
model.search(newValue, searchMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
searchModeToggleGroup.selectedToggleProperty().addListener { _, _, newValue ->
|
|
||||||
if (textFieldSearch.text.isNullOrBlank()) {
|
|
||||||
return@addListener
|
|
||||||
}
|
|
||||||
|
|
||||||
val searchMode = newValue.userData as SearchMode
|
|
||||||
model.search(textFieldSearch.text, searchMode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
package com.marvinelsen.willow.ui.util
|
package com.marvinelsen.willow.ui.util
|
||||||
|
|
||||||
import com.marvinelsen.willow.WillowApplication
|
|
||||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
||||||
import javafx.event.EventHandler
|
import javafx.event.EventHandler
|
||||||
import javafx.scene.control.ContextMenu
|
import javafx.scene.control.ContextMenu
|
||||||
import javafx.scene.control.MenuItem
|
import javafx.scene.control.MenuItem
|
||||||
|
|
||||||
fun createContextMenuForEntry(entry: DictionaryEntryFx) = ContextMenu().apply {
|
fun createContextMenuForEntry(entry: DictionaryEntryFx) = ContextMenu().apply {
|
||||||
val menuItemCopyHeadword =
|
val menuItemCopyHeadword = MenuItem("Copy Headword").apply {
|
||||||
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.headword")).apply {
|
onAction = EventHandler { ClipboardHelper.copyHeadword(entry) }
|
||||||
onAction = EventHandler { ClipboardHelper.copyHeadword(entry) }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val menuItemCopyPronunciation =
|
val menuItemCopyPronunciation = MenuItem("Copy Pronunciation").apply {
|
||||||
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.pronunciation")).apply {
|
onAction = EventHandler { ClipboardHelper.copyPronunciation(entry) }
|
||||||
onAction = EventHandler { ClipboardHelper.copyPronunciation(entry) }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation)
|
items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation)
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,6 @@ h1 {
|
|||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol {
|
|
||||||
padding-left: 0;
|
|
||||||
list-style-position: inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol li:only-child {
|
ol li:only-child {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
.list-view {
|
.list-view {
|
||||||
-fx-selection-bar: #B8EEFF;
|
-fx-selection-bar: #B8EEFF;
|
||||||
|
-fx-font-family: "Noto Sans CJK TC";
|
||||||
/*-fx-selection-bar-non-focused: green;*/
|
/*-fx-selection-bar-non-focused: green;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-view-entry {
|
.list-view-entry {
|
||||||
-fx-font-family: "Noto Sans TC";
|
|
||||||
-fx-font-size: 20;
|
-fx-font-size: 20;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pronunciation {
|
.pronunciation {
|
||||||
-fx-font: 16 "Noto Sans TC";
|
-fx-font: 16 "Noto Sans CJK TC";
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-dialog .content {
|
.settings-dialog .content {
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory?>
|
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.web.WebView?>
|
<?import javafx.scene.web.WebView?>
|
||||||
@ -22,11 +21,7 @@
|
|||||||
<ListView fx:id="listviewSentences"/>
|
<ListView fx:id="listviewSentences"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="tabWords" closable="false" disable="false" text="%tab.words">
|
<Tab id="tabWords" closable="false" disable="false" text="%tab.words">
|
||||||
<ListView fx:id="listViewWords">
|
<ListView fx:id="listViewWords"/>
|
||||||
<cellFactory>
|
|
||||||
<DictionaryEntryCellFactory/>
|
|
||||||
</cellFactory>
|
|
||||||
</ListView>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab closable="false" disable="false" text="%tab.characters">
|
<Tab closable="false" disable="false" text="%tab.characters">
|
||||||
<ListView fx:id="listViewCharacters"/>
|
<ListView fx:id="listViewCharacters"/>
|
||||||
|
@ -31,8 +31,7 @@
|
|||||||
</userData>
|
</userData>
|
||||||
</RadioButton>
|
</RadioButton>
|
||||||
<RadioButton mnemonicParsing="false" text="%search.mode.english"
|
<RadioButton mnemonicParsing="false" text="%search.mode.english"
|
||||||
toggleGroup="$searchModeToggleGroup"
|
toggleGroup="$searchModeToggleGroup">
|
||||||
disable="true">
|
|
||||||
<userData>
|
<userData>
|
||||||
<SearchMode fx:value="ENGLISH"/>
|
<SearchMode fx:value="ENGLISH"/>
|
||||||
</userData>
|
</userData>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user