Compare commits

..

10 Commits

14 changed files with 131 additions and 31 deletions

View File

@ -29,6 +29,7 @@ class WillowApplication : Application() {
private const val FONT_SIZE = 12.0
private const val JDBC_CONNECTION_STRING = "jdbc:sqlite:dictionary.db"
val resourceBundle: ResourceBundle = ResourceBundle.getBundle("i18n/willow", Locale.US)
}
override fun init() {
@ -45,7 +46,7 @@ class WillowApplication : Application() {
val model = Model(searchService, findWordsService)
val fxmlLoader = FXMLLoader()
fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", Locale.US)
fxmlLoader.resources = resourceBundle
fxmlLoader.controllerFactory = Callback { type ->
when (type) {
MainController::class.java -> MainController(model)
@ -71,7 +72,8 @@ class WillowApplication : Application() {
private fun loadFonts() {
Font.loadFont(javaClass.getResourceAsStream("/fonts/inter.ttf"), FONT_SIZE)
Font.loadFont(javaClass.getResourceAsStream("/fonts/tw-kai.ttf"), FONT_SIZE)
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc.ttf"), FONT_SIZE)
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc-regular.ttf"), FONT_SIZE)
Font.loadFont(javaClass.getResourceAsStream("/fonts/noto-sans-tc-bold.ttf"), FONT_SIZE)
}
}

View File

@ -27,6 +27,8 @@ fun main() {
pinyin_with_tone_marks TEXT NOT NULL,
pinyin_with_tone_numbers TEXT NOT NULL,
zhuyin TEXT NOT NULL,
searchable_pinyin TEXT NOT NULL,
searchable_pinyin_with_tone_numbers TEXT NOT NULL,
definitions JSON NOT NULL,
character_count INTEGER NOT NULL,
CONSTRAINT character_count_gte CHECK(character_count > 0)
@ -36,6 +38,10 @@ fun main() {
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_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 cedictEntries =
@ -45,7 +51,7 @@ fun main() {
val insertStatement =
connection.prepareStatement(
"INSERT OR IGNORE INTO cedict(traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions, character_count) VALUES(?,?,?,?,?,?,?)"
"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(?,?,?,?,?,?,?,?,?)"
)
for (entry in cedictEntries) {
try {
@ -71,12 +77,31 @@ fun main() {
)
insertStatement.setString(
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(
ListSerializer(ListSerializer(String.serializer())),
entry.definitions.map { it.glosses }
)
)
insertStatement.setInt(7, entry.traditional.length)
insertStatement.setInt(9, entry.traditional.length)
} catch (_: Exception) {
// no-op
}

View File

@ -20,9 +20,15 @@ fun main() {
val (shangWords, time3) = measureTimedValue {
sqliteDictionary.findWordsContaining(shang)
}
val (characters, time4) = measureTimedValue {
val searchi = sqliteDictionary.search("師範大學", SearchMode.TRADITIONAL).first()
sqliteDictionary.findCharacters(searchi)
}
println(searchResults)
println(shangWords)
println(characters)
println(time)
println(time2)
println(time3)
println(time4)
}

View File

@ -6,6 +6,7 @@ import java.sql.PreparedStatement
import java.sql.ResultSet
class SqliteDictionary(private val connection: Connection) : Dictionary {
private val whitespaceRegex = """\s+""".toRegex()
private val searchSimplifiedPreparedStatement: PreparedStatement by lazy {
connection.prepareStatement(
@ -29,6 +30,18 @@ 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 {
connection.prepareStatement(
"""
@ -40,8 +53,14 @@ 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) {
SearchMode.PINYIN -> TODO()
SearchMode.PINYIN -> searchPinyin(query)
SearchMode.SIMPLIFIED -> searchSimplified(query)
SearchMode.TRADITIONAL -> searchTraditional(query)
SearchMode.ENGLISH -> TODO()
@ -60,7 +79,16 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
}
override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
return emptyList()
val characterList = entry.traditional
.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> {
@ -71,6 +99,17 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
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> {
searchTraditionalPreparedStatement.setString(1, "$query*")

View File

@ -44,15 +44,13 @@ internal class EntryCell : ListCell<DictionaryEntryFx?>() {
init {
text = null
if (item != null) {
contextMenu = createContextMenuForEntry(item!!)
}
}
override fun updateItem(entry: DictionaryEntryFx?, empty: Boolean) {
super.updateItem(entry, empty)
if (empty || entry == null) {
graphic = null
contextMenu = null
} else {
labelHeadword.text = entry.traditionalProperty.value
labelPronunciation.text = entry.pinyinWithToneMarksProperty.value
@ -60,6 +58,7 @@ internal class EntryCell : ListCell<DictionaryEntryFx?>() {
val definition = entry.definitions.joinToString(separator = " / ") { it.joinToString(separator = "; ") }
labelDefinition.text = definition
contextMenu = createContextMenuForEntry(entry)
graphic = root
}
}

View File

@ -8,6 +8,12 @@ import javafx.scene.control.Label
import javafx.scene.control.ListView
import javafx.scene.control.TabPane
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) {
@FXML
@ -64,19 +70,19 @@ class DetailsController(private val model: Model) {
return@addListener
}
webViewDefinition.engine.loadContent(
buildString {
append("<html>")
append("<body>")
append("<h1>CC-CEDICT</h1>")
append("<ol>")
for (definition in newValue.definitions) {
append("<li>")
append(definition.joinToString(separator = "; "))
append("</li>")
createHTML().html {
body {
h1 {
+"CC-CEDICT"
}
ol {
for (definition in newValue.definitions) {
li {
+definition.joinToString(separator = "; ")
}
}
}
}
append("</ol>")
append("</body>")
append("</html>")
}
)
}

View File

@ -24,5 +24,14 @@ class SearchController(private val model: Model) {
val searchMode = searchModeToggleGroup.selectedToggle.userData as 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)
}
}
}

View File

@ -1,18 +1,21 @@
package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.WillowApplication
import com.marvinelsen.willow.ui.DictionaryEntryFx
import javafx.event.EventHandler
import javafx.scene.control.ContextMenu
import javafx.scene.control.MenuItem
fun createContextMenuForEntry(entry: DictionaryEntryFx) = ContextMenu().apply {
val menuItemCopyHeadword = MenuItem("Copy Headword").apply {
onAction = EventHandler { ClipboardHelper.copyHeadword(entry) }
}
val menuItemCopyHeadword =
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.headword")).apply {
onAction = EventHandler { ClipboardHelper.copyHeadword(entry) }
}
val menuItemCopyPronunciation = MenuItem("Copy Pronunciation").apply {
onAction = EventHandler { ClipboardHelper.copyPronunciation(entry) }
}
val menuItemCopyPronunciation =
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.pronunciation")).apply {
onAction = EventHandler { ClipboardHelper.copyPronunciation(entry) }
}
items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation)
}

View File

@ -15,6 +15,11 @@ h1 {
font-size: 1.25em;
}
ol {
padding-left: 0;
list-style-position: inside;
}
ol li:only-child {
list-style: none;
}

View File

@ -8,11 +8,11 @@
.list-view {
-fx-selection-bar: #B8EEFF;
-fx-font-family: "Noto Sans CJK TC";
/*-fx-selection-bar-non-focused: green;*/
}
.list-view-entry {
-fx-font-family: "Noto Sans TC";
-fx-font-size: 20;
-fx-font-weight: bold;
}
@ -34,7 +34,7 @@
}
.pronunciation {
-fx-font: 16 "Noto Sans CJK TC";
-fx-font: 16 "Noto Sans TC";
}
.settings-dialog .content {

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.web.WebView?>
@ -21,7 +22,11 @@
<ListView fx:id="listviewSentences"/>
</Tab>
<Tab id="tabWords" closable="false" disable="false" text="%tab.words">
<ListView fx:id="listViewWords"/>
<ListView fx:id="listViewWords">
<cellFactory>
<DictionaryEntryCellFactory/>
</cellFactory>
</ListView>
</Tab>
<Tab closable="false" disable="false" text="%tab.characters">
<ListView fx:id="listViewCharacters"/>

View File

@ -31,7 +31,8 @@
</userData>
</RadioButton>
<RadioButton mnemonicParsing="false" text="%search.mode.english"
toggleGroup="$searchModeToggleGroup">
toggleGroup="$searchModeToggleGroup"
disable="true">
<userData>
<SearchMode fx:value="ENGLISH"/>
</userData>