diff --git a/src/main/java/com/codedead/opal/controller/MainWindowController.java b/src/main/java/com/codedead/opal/controller/MainWindowController.java index a2cfaf4..0c3d44b 100644 --- a/src/main/java/com/codedead/opal/controller/MainWindowController.java +++ b/src/main/java/com/codedead/opal/controller/MainWindowController.java @@ -19,6 +19,8 @@ import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; import javafx.scene.media.MediaPlayer; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -38,6 +40,10 @@ public final class MainWindowController implements IAudioTimer, TrayIconListener { + @FXML + private GridPane grpCustom; + @FXML + private TitledPane pneCustom; @FXML private TitledPane pneOther; @FXML @@ -269,8 +275,8 @@ private List getSoundPanes(final GridPane parent) { private void openFile(final String path) { if (path == null) throw new NullPointerException("Path cannot be null!"); - if (path.isEmpty()) - throw new IllegalArgumentException("Path cannot be empty!"); + if (path.isBlank()) + throw new IllegalArgumentException("Path cannot be blank!"); try { helpUtils.openFile(new RunnableFileOpener(path, new IRunnableHelper() { @@ -319,6 +325,19 @@ private void initialize() { e.setVisible(true); e.setManaged(true); }); + + final List customSoundPanes = getSoundPanes(grpCustom); + if (!customSoundPanes.isEmpty()) { + pneCustom.setExpanded(true); + pneCustom.setVisible(true); + pneCustom.setManaged(true); + + customSoundPanes.forEach(e -> { + e.setVisible(true); + e.setManaged(true); + }); + } + btnClearSearch.setVisible(false); btnClearSearch.setManaged(false); @@ -344,6 +363,7 @@ private void initialize() { }); return; } + Platform.runLater(() -> { getAllSoundPanes() .forEach(e -> { @@ -351,8 +371,28 @@ private void initialize() { e.setManaged(e.isVisible()); }); + getSoundPanes(grpCustom) + .forEach(e -> { + e.setVisible(e.getName().toLowerCase().contains(newValue.trim().toLowerCase())); + e.setManaged(e.isVisible()); + }); + + // Check if there are still active sound panes on pneCustom + final List customSoundPanes = getSoundPanes(grpCustom); + if (!customSoundPanes.isEmpty()) { + customSoundPanes.stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { + pneCustom.setExpanded(true); + pneCustom.setVisible(true); + pneCustom.setManaged(true); + }, () -> { + pneCustom.setExpanded(false); + pneCustom.setVisible(false); + pneCustom.setManaged(false); + }); + } + // Check if there are still active sound panes on pneNature - getSoundPanes(grpNature).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(e -> { + getSoundPanes(grpNature).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { pneNature.setExpanded(true); pneNature.setVisible(true); pneNature.setManaged(true); @@ -363,7 +403,7 @@ private void initialize() { }); // Check if there are still active sound panes on pneOffice - getSoundPanes(grpOffice).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(e -> { + getSoundPanes(grpOffice).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { pneOffice.setExpanded(true); pneOffice.setVisible(true); pneOffice.setManaged(true); @@ -374,7 +414,7 @@ private void initialize() { }); // Check if there are still active sound panes on pneAudiences - getSoundPanes(grpAudiences).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(e -> { + getSoundPanes(grpAudiences).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { pneAudiences.setExpanded(true); pneAudiences.setVisible(true); pneAudiences.setManaged(true); @@ -385,7 +425,7 @@ private void initialize() { }); // Check if there are still active sound panes on pneRadioFrequencyStatic - getSoundPanes(grpRadioFrequencyStatic).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(e -> { + getSoundPanes(grpRadioFrequencyStatic).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { pneRadioFrequencyStatic.setExpanded(true); pneRadioFrequencyStatic.setVisible(true); pneRadioFrequencyStatic.setManaged(true); @@ -396,7 +436,7 @@ private void initialize() { }); // Check if there are still active sound panes on pneOther - getSoundPanes(grpOther).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(e -> { + getSoundPanes(grpOther).stream().filter(Node::isVisible).findFirst().ifPresentOrElse(_ -> { pneOther.setExpanded(true); pneOther.setVisible(true); pneOther.setManaged(true); @@ -442,6 +482,52 @@ private void openSoundPresetAction() { } } + /** + * Open a custom sound + */ + @FXML + private void addCustomSound() { + logger.info("Opening a custom sound"); + final FileChooser chooser = new FileChooser(); + + final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("MP3 (*.mp3)", "*.mp3"); + chooser.getExtensionFilters().add(extFilter); + + final File file = chooser.showOpenDialog(new Stage()); + + if (file != null && file.exists()) { + if (file.getAbsolutePath().isBlank()) + throw new IllegalArgumentException("Path cannot be blank!"); + + logger.info("Loading custom sound from {}", file.getAbsolutePath()); + + try { + final RowConstraints row = new RowConstraints(); + row.setVgrow(Priority.ALWAYS); + + grpCustom.getRowConstraints().add(row); + + final SoundPane customSoundPane = new SoundPane(); + final int count = getSoundPanes(grpCustom).size() + 1; + + customSoundPane.setName(translationBundle.getString("CustomSound") + " #" + count); + customSoundPane.setMediaKey("custom" + count); + customSoundPane.setMediaPath(file.toURI().toURL().toString()); + customSoundPane.setImage("/images/customsound.png"); + customSoundPane.setResourceFile(false); + + grpCustom.add(customSoundPane, 0, count - 1); + + pneCustom.setExpanded(true); + pneCustom.setVisible(true); + pneCustom.setManaged(true); + } catch (final IOException ex) { + logger.error("Unable to open the custom sound from {}", file.getAbsolutePath(), ex); + FxUtils.showErrorAlert(translationBundle.getString("OpenCustomSoundError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + } + } + } + /** * Open a sound preset * @@ -450,8 +536,8 @@ private void openSoundPresetAction() { private void openSoundPreset(final String path) { if (path == null) throw new NullPointerException("Path cannot be null!"); - if (path.isEmpty()) - throw new IllegalArgumentException("Path cannot be empty!"); + if (path.isBlank()) + throw new IllegalArgumentException("Path cannot be blank!"); logger.info("Loading sound preset from {}", path); @@ -459,8 +545,8 @@ private void openSoundPreset(final String path) { final Path filePath = Path.of(path); final String actual = Files.readString(filePath); - if (actual.isEmpty()) - throw new IllegalArgumentException("Sound preset cannot be empty!"); + if (actual.isBlank()) + throw new IllegalArgumentException("Sound preset cannot be blank!"); final TypeReference> typeRef = new TypeReference<>() { }; @@ -518,6 +604,10 @@ private void playPauseAction() { for (final SoundPane soundPane : getAllSoundPanes()) { soundPane.playPause(); } + + for (final SoundPane soundPane : getSoundPanes(grpCustom)) { + soundPane.playPause(); + } } catch (final MediaPlayerException ex) { logger.error("Unable to play / pause MediaPlayer", ex); FxUtils.showErrorAlert(translationBundle.getString("PlayPauseError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); @@ -531,6 +621,13 @@ private void playPauseAction() { private void resetAction() { logger.info("Resetting all audio sliders"); getAllSoundPanes().forEach(e -> e.getSlider().setValue(0)); + + getSoundPanes(grpCustom).forEach(SoundPane::disposeMediaPlayer); + grpCustom.getChildren().clear(); + + pneCustom.setExpanded(false); + pneCustom.setVisible(false); + pneCustom.setManaged(false); } /** @@ -684,6 +781,7 @@ private void clearSearchAction() { public void fired() { cancelTimer(); getAllSoundPanes().forEach(SoundPane::pause); + getSoundPanes(grpCustom).forEach(SoundPane::pause); if (Boolean.parseBoolean(settingsController.getProperties().getProperty("timerComputerShutdown", "false"))) { final String command = switch (platformName.toLowerCase()) { diff --git a/src/main/java/com/codedead/opal/controller/SettingsController.java b/src/main/java/com/codedead/opal/controller/SettingsController.java index cb19420..cd1da93 100644 --- a/src/main/java/com/codedead/opal/controller/SettingsController.java +++ b/src/main/java/com/codedead/opal/controller/SettingsController.java @@ -86,8 +86,10 @@ public String getPropertiesResourceLocation() { * @param propertiesResourceLocation The resource location of the default properties file */ public void setPropertiesResourceLocation(final String propertiesResourceLocation) { - if (propertiesResourceLocation == null || propertiesResourceLocation.isEmpty()) - throw new IllegalArgumentException("Properties resource location cannot be null!"); + if (propertiesResourceLocation == null) + throw new NullPointerException("Properties resource location cannot be null!"); + if (propertiesResourceLocation.isBlank()) + throw new IllegalArgumentException("Properties resource location cannot be blank!"); this.propertiesResourceLocation = propertiesResourceLocation; } @@ -107,8 +109,10 @@ public String getPropertiesFileLocation() { * @param propertiesFileLocation The properties file location */ public void setPropertiesFileLocation(final String propertiesFileLocation) { - if (propertiesFileLocation == null || propertiesFileLocation.isEmpty()) - throw new IllegalArgumentException("Properties file location cannot be null or empty!"); + if (propertiesFileLocation == null) + throw new NullPointerException("Properties file location cannot be null!"); + if (propertiesFileLocation.isBlank()) + throw new IllegalArgumentException("Properties file location cannot be blank!"); this.propertiesFileLocation = propertiesFileLocation; } diff --git a/src/main/java/com/codedead/opal/controller/UpdateController.java b/src/main/java/com/codedead/opal/controller/UpdateController.java index 03d2894..126eed0 100644 --- a/src/main/java/com/codedead/opal/controller/UpdateController.java +++ b/src/main/java/com/codedead/opal/controller/UpdateController.java @@ -60,8 +60,8 @@ public UpdateController(final String updateUrl, final String currentVersion) { public Optional checkForUpdates(final String currentPlatform, final boolean isPortable) throws InterruptedException, InvalidHttpResponseCodeException, IOException { if (currentPlatform == null) throw new NullPointerException("Current platform cannot be null!"); - if (currentPlatform.isEmpty()) - throw new IllegalArgumentException("Current platform cannot be empty!"); + if (currentPlatform.isBlank()) + throw new IllegalArgumentException("Current platform cannot be blank!"); logger.info("Attempting to retrieve the latest PlatformUpdate objects"); @@ -151,12 +151,12 @@ public List getUpdates() throws IOException, InterruptedExceptio public void downloadFile(final String url, final String path) throws IOException, URISyntaxException { if (url == null) throw new NullPointerException("URL cannot be null!"); - if (url.isEmpty()) - throw new IllegalArgumentException("URL cannot be empty!"); + if (url.isBlank()) + throw new IllegalArgumentException("URL cannot be blank!"); if (path == null) throw new NullPointerException("Path cannot be null!"); - if (path.isEmpty()) - throw new IllegalArgumentException("Path cannot be empty!"); + if (path.isBlank()) + throw new IllegalArgumentException("Path cannot be blank!"); logger.info("Attempting to download file from {} and store it at {}", url, path); @@ -187,8 +187,8 @@ public String getUpdateUrl() { public void setUpdateUrl(final String updateUrl) { if (updateUrl == null) throw new NullPointerException("Update URL cannot be null!"); - if (updateUrl.isEmpty()) - throw new IllegalArgumentException("Update URL cannot be empty!"); + if (updateUrl.isBlank()) + throw new IllegalArgumentException("Update URL cannot be blank!"); this.updateUrl = updateUrl; } @@ -201,8 +201,8 @@ public void setUpdateUrl(final String updateUrl) { public void setCurrentVersion(final String currentVersion) { if (currentVersion == null) throw new NullPointerException("currentVersion cannot be null!"); - if (currentVersion.isEmpty()) - throw new IllegalArgumentException("currentVersion cannot be empty!"); + if (currentVersion.isBlank()) + throw new IllegalArgumentException("currentVersion cannot be blank!"); this.currentVersion = currentVersion; } diff --git a/src/main/java/com/codedead/opal/domain/NumberTextField.java b/src/main/java/com/codedead/opal/domain/NumberTextField.java index 300ef97..a4b5cbf 100644 --- a/src/main/java/com/codedead/opal/domain/NumberTextField.java +++ b/src/main/java/com/codedead/opal/domain/NumberTextField.java @@ -16,14 +16,14 @@ public NumberTextField() { this.setOnKeyPressed(e -> { if (e.getCode() == KeyCode.UP) { int currentValue = 0; - if (!this.getText().isEmpty()) { + if (!this.getText().isBlank()) { currentValue = Integer.parseInt(this.getText()); } currentValue++; this.setText(Integer.toString(currentValue)); } else if (e.getCode() == KeyCode.DOWN) { int currentValue = 0; - if (!this.getText().isEmpty()) { + if (!this.getText().isBlank()) { currentValue = Integer.parseInt(this.getText()); } if (currentValue - 1 >= getMin()) { diff --git a/src/main/java/com/codedead/opal/domain/SoundPane.java b/src/main/java/com/codedead/opal/domain/SoundPane.java index d2e1359..3c169b8 100644 --- a/src/main/java/com/codedead/opal/domain/SoundPane.java +++ b/src/main/java/com/codedead/opal/domain/SoundPane.java @@ -38,6 +38,8 @@ public final class SoundPane extends GridPane { @FXML private String mediaPath; @FXML + private boolean resourceFile; + @FXML private String mediaKey; private double balance; private MediaPlayer mediaPlayer; @@ -86,12 +88,12 @@ private void initialize() { private void initializeMediaPlayer(final String value) throws URISyntaxException { if (value == null) throw new NullPointerException("Value cannot be null!"); - if (value.isEmpty()) - throw new IllegalArgumentException("Value cannot be empty!"); + if (value.isBlank()) + throw new IllegalArgumentException("Value cannot be blank!"); disposeMediaPlayer(); - mediaPlayer = new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource(value)).toURI().toString())); + mediaPlayer = new MediaPlayer(new Media(isResourceFile() ? Objects.requireNonNull(getClass().getResource(value)).toURI().toString() : value)); mediaPlayer.setOnEndOfMedia(() -> { if (mediaPlayer != null) { mediaPlayer.seek(Duration.ZERO); @@ -206,12 +208,32 @@ public String getMediaPath() { public void setMediaPath(final String mediaPath) { if (mediaPath == null) throw new NullPointerException("Media path cannot be null!"); - if (mediaPath.isEmpty()) - throw new IllegalArgumentException("Media path cannot be empty!"); + if (mediaPath.isBlank()) + throw new IllegalArgumentException("Media path cannot be blank!"); this.mediaPath = mediaPath; } + /** + * Get whether the media file is a resource file + * + * @return True if the media file is a resource file, otherwise false + */ + @FXML + public boolean isResourceFile() { + return resourceFile; + } + + /** + * Set whether the media file is a resource file + * + * @param resourceFile True if the media file is a resource file, otherwise false + */ + @FXML + public void setResourceFile(final boolean resourceFile) { + this.resourceFile = resourceFile; + } + /** * Get the media key * @@ -231,8 +253,8 @@ public String getMediaKey() { public void setMediaKey(final String mediaKey) { if (mediaKey == null) throw new NullPointerException("Media key cannot be null!"); - if (mediaKey.isEmpty()) - throw new IllegalArgumentException("Media key cannot be empty!"); + if (mediaKey.isBlank()) + throw new IllegalArgumentException("Media key cannot be blank!"); this.mediaKey = mediaKey; } @@ -280,7 +302,7 @@ public void playPause() throws MediaPlayerException { /** * Dispose of the {@link MediaPlayer} object and all bindings */ - private void disposeMediaPlayer() { + public void disposeMediaPlayer() { if (mediaPlayer != null) { mediaPlayer.volumeProperty().unbindBidirectional(sldVolume.valueProperty()); mediaPlayer.stop(); diff --git a/src/main/java/com/codedead/opal/utils/HelpUtils.java b/src/main/java/com/codedead/opal/utils/HelpUtils.java index 6fd3a7c..1164c36 100644 --- a/src/main/java/com/codedead/opal/utils/HelpUtils.java +++ b/src/main/java/com/codedead/opal/utils/HelpUtils.java @@ -58,8 +58,8 @@ public void openFileFromResources(final RunnableFileOpener runnableFileOpener, f throw new NullPointerException("RunnableFileOpener cannot be null!"); if (resource == null) throw new NullPointerException("Resource cannot be null!"); - if (resource.isEmpty()) - throw new IllegalArgumentException("Resource cannot be empty!"); + if (resource.isBlank()) + throw new IllegalArgumentException("Resource cannot be blank!"); logger.info("Attempting to open file from resources {}", resource); @@ -123,8 +123,8 @@ public void run() { public void openWebsite(final String url, final ResourceBundle resourceBundle) { if (url == null) throw new NullPointerException("URL cannot be null!"); - if (url.isEmpty()) - throw new IllegalArgumentException("URL cannot be empty!"); + if (url.isBlank()) + throw new IllegalArgumentException("URL cannot be blank!"); logger.info("Opening the website {}", url); diff --git a/src/main/java/com/codedead/opal/utils/RunnableFileOpener.java b/src/main/java/com/codedead/opal/utils/RunnableFileOpener.java index 4040652..b0f54ff 100644 --- a/src/main/java/com/codedead/opal/utils/RunnableFileOpener.java +++ b/src/main/java/com/codedead/opal/utils/RunnableFileOpener.java @@ -20,8 +20,8 @@ public final class RunnableFileOpener implements Runnable { public RunnableFileOpener(final String fileLocation, final IRunnableHelper iRunnableHelper) { if (fileLocation == null) throw new NullPointerException("File location cannot be null!"); - if (fileLocation.isEmpty()) - throw new IllegalArgumentException("File location cannot be empty!"); + if (fileLocation.isBlank()) + throw new IllegalArgumentException("File location cannot be blank!"); this.fileLocation = fileLocation; this.iRunnableHelper = iRunnableHelper; diff --git a/src/main/java/com/codedead/opal/utils/RunnableSiteOpener.java b/src/main/java/com/codedead/opal/utils/RunnableSiteOpener.java index 10488f6..b13d515 100644 --- a/src/main/java/com/codedead/opal/utils/RunnableSiteOpener.java +++ b/src/main/java/com/codedead/opal/utils/RunnableSiteOpener.java @@ -23,8 +23,8 @@ public final class RunnableSiteOpener implements Runnable { public RunnableSiteOpener(final String url, final IRunnableHelper iRunnableHelper) { if (url == null) throw new NullPointerException("URL cannot be null!"); - if (url.isEmpty()) - throw new IllegalArgumentException("URL cannot be empty!"); + if (url.isBlank()) + throw new IllegalArgumentException("URL cannot be blank!"); this.url = url; this.iRunnableHelper = iRunnableHelper; diff --git a/src/main/resources/images/customsound.png b/src/main/resources/images/customsound.png new file mode 100644 index 0000000..2f6057b Binary files /dev/null and b/src/main/resources/images/customsound.png differ diff --git a/src/main/resources/translations/OpalApplication.properties b/src/main/resources/translations/OpalApplication.properties index 0946c7a..396d20c 100644 --- a/src/main/resources/translations/OpalApplication.properties +++ b/src/main/resources/translations/OpalApplication.properties @@ -102,3 +102,7 @@ Dolphins=Dolphins RollerCoaster=Roller coaster LargeCrowd=Large crowd Search=Search +CustomSounds=Custom sounds +AddCustomSound=Add custom sound +OpenCustomSoundError=Unable to open custom sound! +CustomSound=Custom sound diff --git a/src/main/resources/translations/OpalApplication_de_DE.properties b/src/main/resources/translations/OpalApplication_de_DE.properties index f1951ef..69b5d32 100644 --- a/src/main/resources/translations/OpalApplication_de_DE.properties +++ b/src/main/resources/translations/OpalApplication_de_DE.properties @@ -102,3 +102,7 @@ Dolphins=Delfine RollerCoaster=Achterbahn LargeCrowd=Große Menschenmenge Search=Suche +CustomSounds=Benutzerdefinierte Klänge +AddCustomSound=Benutzerdefinierten Klang hinzufügen +OpenCustomSoundError=Benutzerdefinierten Klang konnte nicht geöffnet werden! +CustomSound=Benutzerdefinierter Klang diff --git a/src/main/resources/translations/OpalApplication_es_ES.properties b/src/main/resources/translations/OpalApplication_es_ES.properties index 1f78414..e98c440 100644 --- a/src/main/resources/translations/OpalApplication_es_ES.properties +++ b/src/main/resources/translations/OpalApplication_es_ES.properties @@ -102,3 +102,7 @@ Dolphins=Delfines RollerCoaster=Montaña rusa LargeCrowd=Multitud grande Search=Buscar +CustomSounds=Sonidos personalizados +AddCustomSound=Añadir sonido personalizado +OpenCustomSoundError=¡No se puede abrir el sonido personalizado! +CustomSound=Sonido personalizado diff --git a/src/main/resources/translations/OpalApplication_fr_FR.properties b/src/main/resources/translations/OpalApplication_fr_FR.properties index 2c3cd86..865ce7a 100644 --- a/src/main/resources/translations/OpalApplication_fr_FR.properties +++ b/src/main/resources/translations/OpalApplication_fr_FR.properties @@ -102,3 +102,7 @@ Dolphins=Dauphins RollerCoaster=Montagnes russes LargeCrowd=Grande foule Search=Chercher +CustomSounds=Sons personnalisés +AddCustomSound=Ajouter un son personnalisé +OpenCustomSoundError=Impossible d'ouvrir le son personnalisé! +CustomSound=Son personnalisé diff --git a/src/main/resources/translations/OpalApplication_hi_IN.properties b/src/main/resources/translations/OpalApplication_hi_IN.properties index 4f1a7f2..2657f76 100644 --- a/src/main/resources/translations/OpalApplication_hi_IN.properties +++ b/src/main/resources/translations/OpalApplication_hi_IN.properties @@ -102,3 +102,7 @@ Dolphins=डाल्फिन RollerCoaster=रोलर कोस्टर LargeCrowd=बड़ी भीड़ Search=खोज +CustomSounds=कस्टम ध्वनियाँ +AddCustomSound=कस्टम ध्वनि जोड़ें +OpenCustomSoundError=कस्टम ध्वनि खोलने में असमर्थ! +CustomSound=कस्टम ध्वनि diff --git a/src/main/resources/translations/OpalApplication_jp_JP.properties b/src/main/resources/translations/OpalApplication_jp_JP.properties index f91b284..f1a640f 100644 --- a/src/main/resources/translations/OpalApplication_jp_JP.properties +++ b/src/main/resources/translations/OpalApplication_jp_JP.properties @@ -102,3 +102,7 @@ Dolphins=イルカ RollerCoaster=ローラーコースター LargeCrowd=大観衆 Search=検索 +CustomSounds=カスタム サウンド +AddCustomSound=カスタム サウンドを追加 +OpenCustomSoundError=カスタム サウンドを開けません! +CustomSound=カスタム サウンド diff --git a/src/main/resources/translations/OpalApplication_nl_NL.properties b/src/main/resources/translations/OpalApplication_nl_NL.properties index 2e256ad..b8e8e36 100644 --- a/src/main/resources/translations/OpalApplication_nl_NL.properties +++ b/src/main/resources/translations/OpalApplication_nl_NL.properties @@ -102,3 +102,7 @@ Dolphins=Dolfijnen RollerCoaster=Achtbaan LargeCrowd=Grote menigte Search=Zoeken +CustomSounds=Aangepaste geluiden +AddCustomSound=Aangepast geluid toevoegen +OpenCustomSoundError=Kan aangepast geluid niet openen! +CustomSound=Aangepast geluid diff --git a/src/main/resources/translations/OpalApplication_ru_RU.properties b/src/main/resources/translations/OpalApplication_ru_RU.properties index 85a02a7..e4e0c50 100644 --- a/src/main/resources/translations/OpalApplication_ru_RU.properties +++ b/src/main/resources/translations/OpalApplication_ru_RU.properties @@ -102,3 +102,7 @@ Dolphins=Дельфины RollerCoaster=Американские горки LargeCrowd=Большая толпа Search=Поиск +CustomSounds=Пользовательские звуки +AddCustomSound=Добавить пользовательский звук +OpenCustomSoundError=Невозможно открыть пользовательский звук! +CustomSound=Пользовательский звук diff --git a/src/main/resources/translations/OpalApplication_tr_TR.properties b/src/main/resources/translations/OpalApplication_tr_TR.properties index fdc75ce..fc99408 100644 --- a/src/main/resources/translations/OpalApplication_tr_TR.properties +++ b/src/main/resources/translations/OpalApplication_tr_TR.properties @@ -102,3 +102,7 @@ Dolphins=Yunuslar RollerCoaster=Lunapark treni LargeCrowd=Kalabalık Search=Ara +CustomSounds=Özel sesler +AddCustomSound=Özel ses ekle +OpenCustomSoundError=Özel ses açılamıyor! +CustomSound=Özel ses diff --git a/src/main/resources/translations/OpalApplication_zh_CN.properties b/src/main/resources/translations/OpalApplication_zh_CN.properties index 742cda2..987f431 100644 --- a/src/main/resources/translations/OpalApplication_zh_CN.properties +++ b/src/main/resources/translations/OpalApplication_zh_CN.properties @@ -102,3 +102,7 @@ Dolphins=海豚 RollerCoaster=过山车 LargeCrowd=大群众 Search=搜索 +CustomSounds=自定义声音 +AddCustomSound=添加自定义声音 +OpenCustomSoundError=无法打开自定义声音! +CustomSound=自定义声音 diff --git a/src/main/resources/windows/MainWindow.fxml b/src/main/resources/windows/MainWindow.fxml index f71cffd..038e953 100644 --- a/src/main/resources/windows/MainWindow.fxml +++ b/src/main/resources/windows/MainWindow.fxml @@ -57,6 +57,14 @@ + + + + + + + + @@ -192,6 +200,16 @@ + + + + + + + + + + @@ -215,40 +233,40 @@ + image="/images/rain.png" resourceFile="true"/> + image="/images/wind.png" resourceFile="true"/> + image="/images/thunder.png" resourceFile="true"/> + image="/images/bird.png" resourceFile="true"/> + image="/images/bird.png" resourceFile="true"/> + image="/images/ocean.png" resourceFile="true"/> + image="/images/river.png" resourceFile="true"/> + image="/images/fire.png" resourceFile="true"/> + image="/images/cave.png" resourceFile="true"/> + image="/images/frogs.png" resourceFile="true"/> + image="/images/zoo.png" resourceFile="true"/> + image="/images/ocean.png" resourceFile="true"/> @@ -269,25 +287,25 @@ + image="/images/keyboard.png" resourceFile="true"/> + image="/images/phone.png" resourceFile="true"/> + image="/images/group.png" resourceFile="true"/> + image="/images/car.png" resourceFile="true"/> + image="/images/clock.png" resourceFile="true"/> + image="/images/fan.png" resourceFile="true"/> + image="/images/coffee.png" resourceFile="true"/> @@ -307,22 +325,22 @@ + image="/images/networking.png" resourceFile="true"/> + image="/images/group.png" resourceFile="true"/> + image="/images/restaurant.png" resourceFile="true"/> + image="/images/tribal.png" resourceFile="true"/> + image="/images/tribal2.png" resourceFile="true"/> + image="/images/rugbyfootball.png" resourceFile="true"/> @@ -343,16 +361,16 @@ + image="/images/radio.png" resourceFile="true"/> + image="/images/radio.png" resourceFile="true"/> + image="/images/radio.png" resourceFile="true"/> + image="/images/radio.png" resourceFile="true"/> @@ -379,34 +397,34 @@ + image="/images/star.png" resourceFile="true"/> + image="/images/zen.png" resourceFile="true"/> + image="/images/sleepy.png" resourceFile="true"/> + image="/images/train.png" resourceFile="true"/> + image="/images/gong.png" resourceFile="true"/> + image="/images/belltower.png" resourceFile="true"/> + image="/images/space.png" resourceFile="true"/> + image="/images/train.png" resourceFile="true"/> + image="/images/metronome.png" resourceFile="true"/> + image="/images/metronome.png" resourceFile="true"/>