diff --git a/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt b/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt index ed60b62..5960cd7 100644 --- a/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt +++ b/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt @@ -8,6 +8,7 @@ import com.marvinelsen.willow.ui.controllers.MenuController import com.marvinelsen.willow.ui.controllers.SearchController import com.marvinelsen.willow.ui.controllers.SearchResultsController import javafx.application.Application +import javafx.application.HostServices import javafx.fxml.FXMLLoader import javafx.scene.Scene import javafx.scene.image.Image @@ -46,15 +47,17 @@ class WillowApplication : Application() { val config = Config() config.load() + val hostServices: HostServices = hostServices + val fxmlLoader = FXMLLoader() fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", config.locale.value) fxmlLoader.controllerFactory = Callback { type -> when (type) { MainController::class.java -> MainController(model) MenuController::class.java -> MenuController(model, config) - DetailsController::class.java -> DetailsController(model, config) + DetailsController::class.java -> DetailsController(model, config, hostServices) SearchController::class.java -> SearchController(model) - SearchResultsController::class.java -> SearchResultsController(model, config) + SearchResultsController::class.java -> SearchResultsController(model, config, hostServices) else -> error("Trying to instantiate unknown controller type $type") } } diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/cells/DictionaryEntryCellFactory.kt b/src/main/kotlin/com/marvinelsen/willow/ui/cells/DictionaryEntryCellFactory.kt index b3fff59..0f7c817 100644 --- a/src/main/kotlin/com/marvinelsen/willow/ui/cells/DictionaryEntryCellFactory.kt +++ b/src/main/kotlin/com/marvinelsen/willow/ui/cells/DictionaryEntryCellFactory.kt @@ -5,6 +5,7 @@ import com.marvinelsen.willow.config.Pronunciation import com.marvinelsen.willow.config.Script import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.util.createContextMenuForEntry +import javafx.application.HostServices import javafx.beans.binding.Bindings import javafx.geometry.VPos import javafx.scene.control.Label @@ -18,10 +19,11 @@ import java.util.ResourceBundle class DictionaryEntryCellFactory( private val resources: ResourceBundle, private val config: Config, + private val hostServices: HostServices, ) : Callback, ListCell> { override fun call(listView: ListView): ListCell { - val entryCell = EntryCell(resources, config) + val entryCell = EntryCell(resources, config, hostServices) entryCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING)) return entryCell } @@ -34,6 +36,7 @@ class DictionaryEntryCellFactory( private class EntryCell( private val resources: ResourceBundle, private val config: Config, + private val hostServices: HostServices, ) : ListCell() { private val labelHeadword = Label().apply { @@ -133,7 +136,7 @@ private class EntryCell( } labelDefinition.text = definition - contextMenu = createContextMenuForEntry(entry, resources) + contextMenu = createContextMenuForEntry(entry, resources, hostServices) graphic = root } } diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/cells/SentenceCellFactory.kt b/src/main/kotlin/com/marvinelsen/willow/ui/cells/SentenceCellFactory.kt index dbfca87..0025dfc 100644 --- a/src/main/kotlin/com/marvinelsen/willow/ui/cells/SentenceCellFactory.kt +++ b/src/main/kotlin/com/marvinelsen/willow/ui/cells/SentenceCellFactory.kt @@ -4,6 +4,7 @@ import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Script import com.marvinelsen.willow.ui.SentenceFx import com.marvinelsen.willow.ui.util.createContextMenuForSentence +import javafx.application.HostServices import javafx.beans.binding.Bindings import javafx.scene.control.Label import javafx.scene.control.ListCell @@ -15,10 +16,11 @@ import java.util.ResourceBundle class SentenceCellFactory( private val resources: ResourceBundle, private val config: Config, + private val hostServices: HostServices, ) : Callback, ListCell> { override fun call(listView: ListView): ListCell { - val sentenceCell = SentenceCell(resources, config) + val sentenceCell = SentenceCell(resources, config, hostServices) sentenceCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING)) return sentenceCell } @@ -31,6 +33,7 @@ class SentenceCellFactory( private class SentenceCell( private val resources: ResourceBundle, private val config: Config, + private val hostServices: HostServices, ) : ListCell() { private val labelSentence = Label().apply { @@ -60,7 +63,7 @@ private class SentenceCell( ) ) - contextMenu = createContextMenuForSentence(sentence, resources) + contextMenu = createContextMenuForSentence(sentence, resources, hostServices) graphic = root } } diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/controllers/DetailsController.kt b/src/main/kotlin/com/marvinelsen/willow/ui/controllers/DetailsController.kt index 56c47d1..50fad3c 100644 --- a/src/main/kotlin/com/marvinelsen/willow/ui/controllers/DetailsController.kt +++ b/src/main/kotlin/com/marvinelsen/willow/ui/controllers/DetailsController.kt @@ -9,6 +9,7 @@ import com.marvinelsen.willow.ui.SentenceFx import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory import com.marvinelsen.willow.ui.cells.SentenceCellFactory import com.marvinelsen.willow.ui.util.createContextMenuForEntry +import javafx.application.HostServices import javafx.beans.binding.Bindings import javafx.fxml.FXML import javafx.scene.control.Label @@ -31,7 +32,12 @@ import kotlinx.html.stream.createHTML import java.util.ResourceBundle @Suppress("UnusedPrivateMember", "TooManyFunctions") -class DetailsController(private val model: Model, private val config: Config) { +class DetailsController( + private val model: Model, + private val config: Config, + private val hostServices: HostServices, +) { + @FXML private lateinit var resources: ResourceBundle @@ -192,7 +198,7 @@ class DetailsController(private val model: Model, private val config: Config) { private fun initializeListViewSentences() { listViewSentences.apply { - cellFactory = SentenceCellFactory(resources, config) + cellFactory = SentenceCellFactory(resources, config, hostServices) items = model.sentences disableProperty().bind(Bindings.or(model.isFindingSentences, Bindings.isEmpty(model.sentences))) @@ -205,7 +211,7 @@ class DetailsController(private val model: Model, private val config: Config) { private fun initializeListViewWordsContaining() { listViewWordsContaining.apply { - cellFactory = DictionaryEntryCellFactory(resources, config) + cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) items = model.wordsContaining disableProperty().bind(Bindings.or(model.isFindingWordsContaining, Bindings.isEmpty(model.wordsContaining))) @@ -218,7 +224,7 @@ class DetailsController(private val model: Model, private val config: Config) { private fun initializeListViewWordsBeginning() { listViewWordsBeginning.apply { - cellFactory = DictionaryEntryCellFactory(resources, config) + cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) items = model.wordsBeginning disableProperty().bind(Bindings.or(model.isFindingWordsBeginning, Bindings.isEmpty(model.wordsBeginning))) @@ -231,7 +237,7 @@ class DetailsController(private val model: Model, private val config: Config) { private fun initializeListViewCharacters() { listViewCharacters.apply { - cellFactory = DictionaryEntryCellFactory(resources, config) + cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) items = model.characters disableProperty().bind(Bindings.or(model.isFindingCharacters, Bindings.isEmpty(model.characters))) @@ -258,7 +264,7 @@ class DetailsController(private val model: Model, private val config: Config) { private fun headerOnContextMenuRequested(contextMenuEvent: ContextMenuEvent) { if (model.selectedEntry.value == null) return - createContextMenuForEntry(model.selectedEntry.value, resources).show( + createContextMenuForEntry(model.selectedEntry.value, resources, hostServices).show( flowPaneHeader.scene.window, contextMenuEvent.screenX, contextMenuEvent.screenY diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/controllers/SearchResultsController.kt b/src/main/kotlin/com/marvinelsen/willow/ui/controllers/SearchResultsController.kt index 35e75d7..abc2456 100644 --- a/src/main/kotlin/com/marvinelsen/willow/ui/controllers/SearchResultsController.kt +++ b/src/main/kotlin/com/marvinelsen/willow/ui/controllers/SearchResultsController.kt @@ -4,6 +4,7 @@ import com.marvinelsen.willow.Model import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory +import javafx.application.HostServices import javafx.beans.binding.Bindings import javafx.fxml.FXML import javafx.scene.control.Label @@ -11,7 +12,12 @@ import javafx.scene.control.ListView import javafx.scene.control.ProgressIndicator import java.util.ResourceBundle -class SearchResultsController(private val model: Model, private val config: Config) { +class SearchResultsController( + private val model: Model, + private val config: Config, + private val hostServices: HostServices, +) { + @FXML private lateinit var resources: ResourceBundle @@ -28,7 +34,7 @@ class SearchResultsController(private val model: Model, private val config: Conf @FXML @Suppress("UnusedPrivateMember") private fun initialize() { - listViewSearchResults.cellFactory = DictionaryEntryCellFactory(resources, config) + listViewSearchResults.cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) listViewSearchResults.items = model.searchResults listViewSearchResults .disableProperty() diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/util/ContextMenuUtils.kt b/src/main/kotlin/com/marvinelsen/willow/ui/util/ContextMenuUtils.kt index 20b4da6..b24663f 100644 --- a/src/main/kotlin/com/marvinelsen/willow/ui/util/ContextMenuUtils.kt +++ b/src/main/kotlin/com/marvinelsen/willow/ui/util/ContextMenuUtils.kt @@ -2,12 +2,19 @@ package com.marvinelsen.willow.ui.util import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.SentenceFx +import javafx.application.HostServices import javafx.event.EventHandler import javafx.scene.control.ContextMenu import javafx.scene.control.MenuItem +import java.net.URLEncoder +import java.nio.charset.StandardCharsets import java.util.ResourceBundle -fun createContextMenuForEntry(entry: DictionaryEntryFx, resourceBundle: ResourceBundle) = ContextMenu().apply { +fun createContextMenuForEntry( + entry: DictionaryEntryFx, + resourceBundle: ResourceBundle, + hostServices: HostServices, +) = ContextMenu().apply { val menuItemCopyHeadword = MenuItem(resourceBundle.getString("menubar.edit.copy.headword")).apply { onAction = EventHandler { ClipboardHelper.copyString(entry.traditionalProperty.value) } @@ -18,14 +25,34 @@ fun createContextMenuForEntry(entry: DictionaryEntryFx, resourceBundle: Resource onAction = EventHandler { ClipboardHelper.copyString(entry.pinyinWithToneMarksProperty.value) } } - items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation) + val menuItemSearchOnWeb = + MenuItem(resourceBundle.getString("menubar.edit.search-web")).apply { + onAction = EventHandler { + val query = URLEncoder.encode(entry.traditionalProperty.value, StandardCharsets.UTF_8) + hostServices.showDocument("https://duckduckgo.com/?q=$query") + } + } + + items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation, menuItemSearchOnWeb) } -fun createContextMenuForSentence(sentence: SentenceFx, resourceBundle: ResourceBundle) = ContextMenu().apply { +fun createContextMenuForSentence( + sentence: SentenceFx, + resourceBundle: ResourceBundle, + hostServices: HostServices, +) = ContextMenu().apply { val menuItemCopySentence = MenuItem(resourceBundle.getString("menubar.edit.copy.sentence")).apply { onAction = EventHandler { ClipboardHelper.copyString(sentence.traditionalProperty.value) } } - items.addAll(menuItemCopySentence) + val menuItemSearchOnWeb = + MenuItem(resourceBundle.getString("menubar.edit.search-web")).apply { + onAction = EventHandler { + val query = URLEncoder.encode(sentence.traditionalProperty.value, StandardCharsets.UTF_8) + hostServices.showDocument("https://duckduckgo.com/?q=$query") + } + } + + items.addAll(menuItemCopySentence, menuItemSearchOnWeb) } diff --git a/src/main/resources/i18n/willow.properties b/src/main/resources/i18n/willow.properties index b65410d..d50a1e9 100644 --- a/src/main/resources/i18n/willow.properties +++ b/src/main/resources/i18n/willow.properties @@ -26,6 +26,7 @@ menubar.edit=_Edit menubar.edit.copy.headword=Copy Headword menubar.edit.copy.pronunciation=Copy Pronunciation menubar.edit.copy.sentence=Copy Sentence +menubar.edit.search-web=Search on Web... menubar.help=_Help menubar.help.about=_About… diff --git a/src/main/resources/i18n/willow_de.properties b/src/main/resources/i18n/willow_de.properties index dd05813..0dbe290 100644 --- a/src/main/resources/i18n/willow_de.properties +++ b/src/main/resources/i18n/willow_de.properties @@ -26,6 +26,7 @@ menubar.edit=_Bearbeiten menubar.edit.copy.headword=Kopiere Wort menubar.edit.copy.pronunciation=Kopiere Aussprache menubar.edit.copy.sentence=Copy Sentence +menubar.edit.search-web=Search on Web... menubar.help=_Hilfe menubar.help.about=_Über… diff --git a/src/main/resources/i18n/willow_en.properties b/src/main/resources/i18n/willow_en.properties index b65410d..d50a1e9 100644 --- a/src/main/resources/i18n/willow_en.properties +++ b/src/main/resources/i18n/willow_en.properties @@ -26,6 +26,7 @@ menubar.edit=_Edit menubar.edit.copy.headword=Copy Headword menubar.edit.copy.pronunciation=Copy Pronunciation menubar.edit.copy.sentence=Copy Sentence +menubar.edit.search-web=Search on Web... menubar.help=_Help menubar.help.about=_About… diff --git a/src/main/resources/i18n/willow_zh_CN.properties b/src/main/resources/i18n/willow_zh_CN.properties index 88dc688..242a6bf 100644 --- a/src/main/resources/i18n/willow_zh_CN.properties +++ b/src/main/resources/i18n/willow_zh_CN.properties @@ -26,6 +26,7 @@ menubar.edit=_編輯 menubar.edit.copy.headword=複製 Wort menubar.edit.copy.pronunciation=複製 Aussprache menubar.edit.copy.sentence=Copy Sentence +menubar.edit.search-web=Search on Web... menubar.help=_說明 menubar.help.about=_關於 Willow… diff --git a/src/main/resources/i18n/willow_zh_TW.properties b/src/main/resources/i18n/willow_zh_TW.properties index 21ef796..32c3acf 100644 --- a/src/main/resources/i18n/willow_zh_TW.properties +++ b/src/main/resources/i18n/willow_zh_TW.properties @@ -26,6 +26,7 @@ menubar.edit=_編輯 menubar.edit.copy.headword=複製 Wort menubar.edit.copy.pronunciation=複製 Aussprache menubar.edit.copy.sentence=Copy Sentence +menubar.edit.search-web=Search on Web... menubar.help=_說明 menubar.help.about=_關於 Willow…