diff --git a/src/main/java/com/codedead/opal/controller/MainWindowController.java b/src/main/java/com/codedead/opal/controller/MainWindowController.java index 0c3d44b..4b5480f 100644 --- a/src/main/java/com/codedead/opal/controller/MainWindowController.java +++ b/src/main/java/com/codedead/opal/controller/MainWindowController.java @@ -40,6 +40,118 @@ public final class MainWindowController implements IAudioTimer, TrayIconListener { + @FXML + private SoundPane snpFastMetronome; + @FXML + private SoundPane snpSlowMetronome; + @FXML + private SoundPane snpTrain; + @FXML + private SoundPane snpSpace; + @FXML + private SoundPane snpBellTower; + @FXML + private SoundPane snpGong; + @FXML + private SoundPane snpRollerCoaster; + @FXML + private SoundPane snpSleepy; + @FXML + private SoundPane snpZen; + @FXML + private SoundPane snpFantasy; + @FXML + private SoundPane snpBrownNoise; + @FXML + private SoundPane snpPinkNoise; + @FXML + private SoundPane snpWhiteNoise; + @FXML + private SoundPane snpStatic; + @FXML + private SoundPane snpRugbyFootball; + @FXML + private SoundPane snpDrumTribalFestival; + @FXML + private SoundPane snpTribalFestival; + @FXML + private SoundPane snpRestaurant; + @FXML + private SoundPane snpLargeCrowd; + @FXML + private SoundPane snpNetworkingEvent; + @FXML + private SoundPane snpCoffee; + @FXML + private SoundPane snpFan; + @FXML + private SoundPane snpClock; + @FXML + private SoundPane snpTraffic; + @FXML + private SoundPane snpChatter; + @FXML + private SoundPane snpPhone; + @FXML + private SoundPane snpTyping; + @FXML + private SoundPane snpDolphins; + @FXML + private SoundPane snpZoo; + @FXML + private SoundPane snpFrogs; + @FXML + private SoundPane snpCave; + @FXML + private SoundPane snpFireplace; + @FXML + private SoundPane snpRiver; + @FXML + private SoundPane snpOcean; + @FXML + private SoundPane snpSeagulls; + @FXML + private SoundPane snpBirds; + @FXML + private SoundPane snpThunder; + @FXML + private SoundPane snpWind; + @FXML + private SoundPane snpRain; + @FXML + private MenuItem mniAbout; + @FXML + private MenuItem mniDonate; + @FXML + private MenuItem mniLicense; + @FXML + private MenuItem mniHomePage; + @FXML + private MenuItem mniCheckForUpdates; + @FXML + private MenuItem mniHelp; + @FXML + private Menu mnuHelp; + @FXML + private Menu mnuTimer; + @FXML + private MenuItem mniSettings; + @FXML + private Menu mnuTools; + @FXML + private MenuItem mniExit; + @FXML + private MenuItem mniReset; + @FXML + private MenuItem mniPlayPause; + @FXML + private MenuItem mniAddCustomSound; + @FXML + private MenuItem mniSaveSoundPreset; + @FXML + private MenuItem mniOpenSoundPreset; + @FXML + private Menu mnuFile; @FXML private GridPane grpCustom; @FXML @@ -76,7 +188,6 @@ public final class MainWindowController implements IAudioTimer, TrayIconListener private TrayIconController trayIconController; private SettingsController settingsController; private UpdateController updateController; - private ResourceBundle translationBundle; private TimerTask timerTask; private TimerTask countDownTask; private final String platformName; @@ -86,6 +197,7 @@ public final class MainWindowController implements IAudioTimer, TrayIconListener private final Timer countDownTimer; private final IAudioTimer audioTimer; private final Logger logger; + private final ObservableResourceFactory resourceFactory; /** * Initialize a new MainWindowController @@ -101,6 +213,8 @@ public MainWindowController() { this.countDownTimer = new Timer(); this.audioTimer = this; this.objectMapper = new ObjectMapper(); + + resourceFactory = new ObservableResourceFactory(); } /** @@ -135,11 +249,11 @@ public void setControllers(final SettingsController settingsController, final Up logger.info("Attempting to load the ResourceBundle for locale {}", languageTag); final Locale locale = Locale.forLanguageTag(languageTag); - translationBundle = ResourceBundle.getBundle("translations.OpalApplication", locale); + resourceFactory.setResources(ResourceBundle.getBundle("translations.OpalApplication", locale)); final boolean mediaButtons = Boolean.parseBoolean(properties.getProperty("mediaButtons", "true")); - trayIconController = new TrayIconController(translationBundle, this); + trayIconController = new TrayIconController(resourceFactory, this); // Load tray icons after displaying the main stage to display the proper icon in the task bar / activities bar (linux) if (Boolean.parseBoolean(properties.getProperty("trayIcon", "false"))) { @@ -147,7 +261,7 @@ public void setControllers(final SettingsController settingsController, final Up trayIconController.showTrayIcon(); } catch (final IOException ex) { logger.error("Unable to create tray icon", ex); - FxUtils.showErrorAlert(translationBundle.getString("TrayIconError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("TrayIconError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -160,6 +274,77 @@ public void setControllers(final SettingsController settingsController, final Up } setAudioBalance(Double.parseDouble(properties.getProperty("audioBalance", "0.0"))); + + // Locale + mnuFile.textProperty().bind(resourceFactory.getStringBinding("File")); + mniOpenSoundPreset.textProperty().bind(resourceFactory.getStringBinding("OpenSoundPreset")); + mniSaveSoundPreset.textProperty().bind(resourceFactory.getStringBinding("SaveSoundPreset")); + mniAddCustomSound.textProperty().bind(resourceFactory.getStringBinding("AddCustomSound")); + mniPlayPause.textProperty().bind(resourceFactory.getStringBinding("PlayPause")); + mniReset.textProperty().bind(resourceFactory.getStringBinding("Reset")); + mniExit.textProperty().bind(resourceFactory.getStringBinding("Exit")); + mnuTools.textProperty().bind(resourceFactory.getStringBinding("Tools")); + mniSettings.textProperty().bind(resourceFactory.getStringBinding("Settings")); + mnuTimer.textProperty().bind(resourceFactory.getStringBinding("Timer")); + mniTimerEnabled.textProperty().bind(resourceFactory.getStringBinding("Enabled")); + mnuHelp.textProperty().bind(resourceFactory.getStringBinding("HelpMenu")); + mniHelp.textProperty().bind(resourceFactory.getStringBinding("Help")); + mniCheckForUpdates.textProperty().bind(resourceFactory.getStringBinding("CheckForUpdates")); + mniHomePage.textProperty().bind(resourceFactory.getStringBinding("Homepage")); + mniLicense.textProperty().bind(resourceFactory.getStringBinding("License")); + mniDonate.textProperty().bind(resourceFactory.getStringBinding("Donate")); + mniAbout.textProperty().bind(resourceFactory.getStringBinding("About")); + txtSearch.promptTextProperty().bind(resourceFactory.getStringBinding("Search")); + pneCustom.textProperty().bind(resourceFactory.getStringBinding("CustomSounds")); + pneNature.textProperty().bind(resourceFactory.getStringBinding("Nature")); + + snpRain.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Rain")); + snpWind.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Wind")); + snpThunder.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Thunder")); + snpBirds.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Birds")); + snpSeagulls.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Seagulls")); + snpOcean.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Ocean")); + snpRiver.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("River")); + snpFireplace.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Fireplace")); + snpCave.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Cave")); + snpFrogs.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Frogs")); + snpZoo.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Zoo")); + snpDolphins.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Dolphins")); + + pneOffice.textProperty().bind(resourceFactory.getStringBinding("Office")); + snpTyping.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Typing")); + snpPhone.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Phone")); + snpChatter.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Chatter")); + snpTraffic.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Traffic")); + snpClock.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Clock")); + snpFan.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Fan")); + snpCoffee.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Coffee")); + + pneAudiences.textProperty().bind(resourceFactory.getStringBinding("Audiences")); + snpNetworkingEvent.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("NetworkingEvent")); + snpLargeCrowd.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("LargeCrowd")); + snpRestaurant.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Restaurant")); + snpTribalFestival.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("TribalFestival")); + snpDrumTribalFestival.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("DrumTribalFestival")); + snpRugbyFootball.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("RugbyFootball")); + + pneRadioFrequencyStatic.textProperty().bind(resourceFactory.getStringBinding("RadioFrequencyStatic")); + snpStatic.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Static")); + snpWhiteNoise.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("WhiteNoise")); + snpPinkNoise.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("PinkNoise")); + snpBrownNoise.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("BrownNoise")); + + pneOther.textProperty().bind(resourceFactory.getStringBinding("Other")); + snpFantasy.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Fantasy")); + snpZen.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Zen")); + snpSleepy.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Sleepy")); + snpRollerCoaster.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("RollerCoaster")); + snpGong.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Gong")); + snpBellTower.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("BellTower")); + snpSpace.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Space")); + snpTrain.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("Train")); + snpSlowMetronome.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("SlowMetronome")); + snpFastMetronome.getNameLabel().textProperty().bind(resourceFactory.getStringBinding("FastMetronome")); } /** @@ -187,7 +372,8 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro logger.info("Version {}.{}.{}.{} is available", update.getMajorVersion(), update.getMinorVersion(), update.getBuildVersion(), update.getRevisionVersion()); - if (FxUtils.showConfirmationAlert(translationBundle + if (FxUtils.showConfirmationAlert(resourceFactory + .getResources() .getString("NewUpdateAvailable") .replace("{v}", String.format("%1$s.%2$s.%3$s.%4$s", update.getMajorVersion(), update.getMinorVersion(), update.getBuildVersion(), update.getRevisionVersion())), getClass().getResourceAsStream(SharedVariables.ICON_URL))) { @@ -216,7 +402,7 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro } else { logger.info("No updates available"); if (showNoUpdates) { - FxUtils.showInformationAlert(translationBundle.getString("NoUpdateAvailable"), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showInformationAlert(resourceFactory.getResources().getString("NoUpdateAvailable"), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } } catch (final InterruptedException | IOException | InvalidHttpResponseCodeException | URISyntaxException ex) { @@ -225,7 +411,7 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro logger.error("Unable to check for updates", ex); if (showErrors) { - FxUtils.showErrorAlert(translationBundle.getString("UpdateError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("UpdateError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } } @@ -291,14 +477,14 @@ public void exceptionOccurred(final Exception ex) { @Override public void run() { logger.error("Error opening the file", ex); - FxUtils.showErrorAlert(translationBundle.getString("FileExecutionError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("FileExecutionError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } }); } })); } catch (final IOException ex) { logger.error("Error opening the file", ex); - FxUtils.showErrorAlert(translationBundle.getString("FileExecutionError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("FileExecutionError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -510,7 +696,7 @@ private void addCustomSound() { final SoundPane customSoundPane = new SoundPane(); final int count = getSoundPanes(grpCustom).size() + 1; - customSoundPane.setName(translationBundle.getString("CustomSound") + " #" + count); + customSoundPane.setName(resourceFactory.getResources().getString("CustomSound") + " #" + count); customSoundPane.setMediaKey("custom" + count); customSoundPane.setMediaPath(file.toURI().toURL().toString()); customSoundPane.setImage("/images/customsound.png"); @@ -523,7 +709,7 @@ private void addCustomSound() { 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)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("OpenCustomSoundError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } } @@ -557,7 +743,7 @@ private void openSoundPreset(final String path) { mediaVolumes.forEach((key, value) -> soundPanes.stream().filter(e -> e.getMediaKey().equals(key)).forEach(e -> e.getSlider().setValue(value))); } catch (final IOException ex) { logger.error("Unable to open the sound preset from {}", path, ex); - FxUtils.showErrorAlert(translationBundle.getString("OpenSoundPresetError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("OpenSoundPresetError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -587,7 +773,7 @@ private void saveSoundPresetAction() { objectMapper.writeValue(new File(filePath), mediaVolumes); } catch (final IOException ex) { logger.error("Unable to save the sound settings to {}", filePath, ex); - FxUtils.showErrorAlert(translationBundle.getString("SaveSoundPresetError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("SaveSoundPresetError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } else { logger.info("Cancelled saving a sound settings"); @@ -610,7 +796,7 @@ private void playPauseAction() { } } catch (final MediaPlayerException ex) { logger.error("Unable to play / pause MediaPlayer", ex); - FxUtils.showErrorAlert(translationBundle.getString("PlayPauseError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("PlayPauseError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -647,7 +833,7 @@ private void settingsAction() { logger.info("Attempting to open the SettingsWindow"); try { - final FXMLLoader loader = new FXMLLoader(getClass().getResource("/windows/SettingsWindow.fxml"), translationBundle); + final FXMLLoader loader = new FXMLLoader(getClass().getResource("/windows/SettingsWindow.fxml"), resourceFactory.getResources()); final Parent root = loader.load(); final SettingsWindowController settingsWindowController = loader.getController(); @@ -657,7 +843,7 @@ private void settingsAction() { final Stage primaryStage = new Stage(); - primaryStage.setTitle(translationBundle.getString("SettingsWindowTitle")); + primaryStage.setTitle(resourceFactory.getResources().getString("SettingsWindowTitle")); primaryStage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream(SharedVariables.ICON_URL)))); primaryStage.setScene(new Scene(root)); @@ -669,7 +855,7 @@ private void settingsAction() { primaryStage.setHeight(350); } catch (final IOException ex) { logger.error("Unable to open the SettingsWindow", ex); - FxUtils.showErrorAlert(translationBundle.getString("SettingsWindowError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("SettingsWindowError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -693,14 +879,14 @@ public void exceptionOccurred(final Exception ex) { @Override public void run() { logger.error("Error opening the help file", ex); - FxUtils.showErrorAlert(translationBundle.getString("HelpFileError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("HelpFileError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } }); } }), SharedVariables.HELP_DOCUMENTATION_RESOURCE_LOCATION); } catch (final IOException ex) { logger.error("Error opening the help file", ex); - FxUtils.showErrorAlert(translationBundle.getString("HelpFileError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("HelpFileError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -709,7 +895,7 @@ public void run() { */ @FXML private void homepageAction() { - helpUtils.openWebsite("https://codedead.com", translationBundle); + helpUtils.openWebsite("https://codedead.com", resourceFactory.getResources()); } /** @@ -717,7 +903,7 @@ private void homepageAction() { */ @FXML private void licenseAction() { - helpUtils.openLicenseFile(translationBundle); + helpUtils.openLicenseFile(resourceFactory.getResources()); } /** @@ -725,7 +911,7 @@ private void licenseAction() { */ @FXML private void donateAction() { - helpUtils.openWebsite("https://codedead.com/donate", translationBundle); + helpUtils.openWebsite("https://codedead.com/donate", resourceFactory.getResources()); } /** @@ -736,15 +922,15 @@ private void aboutAction() { logger.info("Attempting to open the AboutWindow"); try { - final FXMLLoader loader = new FXMLLoader(getClass().getResource("/windows/AboutWindow.fxml"), translationBundle); + final FXMLLoader loader = new FXMLLoader(getClass().getResource("/windows/AboutWindow.fxml"), resourceFactory.getResources()); final Parent root = loader.load(); final AboutWindowController aboutWindowController = loader.getController(); - aboutWindowController.setResourceBundle(translationBundle); + aboutWindowController.setResourceBundle(resourceFactory.getResources()); final Stage primaryStage = new Stage(); - primaryStage.setTitle(translationBundle.getString("AboutWindowTitle")); + primaryStage.setTitle(resourceFactory.getResources().getString("AboutWindowTitle")); primaryStage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/opal.png")))); primaryStage.setScene(new Scene(root)); @@ -754,7 +940,7 @@ private void aboutAction() { primaryStage.setHeight(280); } catch (final IOException ex) { logger.error("Unable to open the AboutWindow", ex); - FxUtils.showErrorAlert(translationBundle.getString("AboutWindowError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); + FxUtils.showErrorAlert(resourceFactory.getResources().getString("AboutWindowError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -955,6 +1141,15 @@ public void setAudioBalance(final double audioBalance) { getAllSoundPanes().forEach(s -> s.setBalance(audioBalance)); } + /** + * Update the {@link ResourceBundle} object + * + * @param resourceBundle The {@link ResourceBundle} object + */ + public void updateResourceBundle(final ResourceBundle resourceBundle) { + resourceFactory.setResources(resourceBundle); + } + /** * Method that is called when the Window should be hidden or shown */ diff --git a/src/main/java/com/codedead/opal/controller/SettingsWindowController.java b/src/main/java/com/codedead/opal/controller/SettingsWindowController.java index 8ac6b0e..324d11d 100644 --- a/src/main/java/com/codedead/opal/controller/SettingsWindowController.java +++ b/src/main/java/com/codedead/opal/controller/SettingsWindowController.java @@ -184,19 +184,23 @@ private void loadSettings() { private void resetSettingsAction() { logger.info("Attempting to reset all settings"); if (FxUtils.showConfirmationAlert(translationBundle.getString("ConfirmReset"), getClass().getResourceAsStream(SharedVariables.ICON_URL))) { - showAlertIfLanguageMismatch(DEFAULT_LOCALE); - Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet()); try { settingsController.createDefaultProperties(); settingsController.setProperties(settingsController.readPropertiesFile()); + final Locale locale = Locale.forLanguageTag(DEFAULT_LOCALE); + final ResourceBundle translationBundle = ResourceBundle.getBundle("translations.OpalApplication", locale); + + mainWindowController.updateResourceBundle(translationBundle); + mainWindowController.loadMediaButtonVisibility(Boolean.parseBoolean(settingsController.getProperties().getProperty("mediaButtons", "true"))); mainWindowController.setAudioBalance(Double.parseDouble(settingsController.getProperties().getProperty("audioBalance", "0.0"))); trayIconController.hideTrayIcon(); - loadSettings(); + Stage stage = (Stage) sldAudioBalance.getScene().getWindow(); + stage.close(); } catch (final IOException ex) { logger.error("Unable to reset all settings", ex); FxUtils.showErrorAlert(translationBundle.getString("ResetSettingsError"), ex.toString(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); @@ -228,9 +232,13 @@ private void saveSettingsAction() { trayIconController.hideTrayIcon(); } - showAlertIfLanguageMismatch(settingsController.getProperties().getProperty("locale", DEFAULT_LOCALE)); + final String languageTag = LanguageController.getLocaleFromLanguageIndex(cboLanguage.getSelectionModel().getSelectedIndex()); + final Locale locale = Locale.forLanguageTag(languageTag); + final ResourceBundle translationBundle = ResourceBundle.getBundle("translations.OpalApplication", locale); + + mainWindowController.updateResourceBundle(translationBundle); - settingsController.getProperties().setProperty("locale", LanguageController.getLocaleFromLanguageIndex(cboLanguage.getSelectionModel().getSelectedIndex())); + settingsController.getProperties().setProperty("locale", languageTag); settingsController.getProperties().setProperty("theme", cboTheme.getSelectionModel().getSelectedItem()); settingsController.getProperties().setProperty("loglevel", cboLogLevel.getValue()); @@ -281,19 +289,6 @@ private void saveSettingsAction() { } } - /** - * Show an information alert if a restart is required - * - * @param languageToMatch The language that needs to be matched to the combobox - */ - private void showAlertIfLanguageMismatch(final String languageToMatch) { - final String newLanguage = LanguageController.getLocaleFromLanguageIndex(cboLanguage.getSelectionModel().getSelectedIndex()); - - if (!languageToMatch.equals(newLanguage)) { - FxUtils.showInformationAlert(translationBundle.getString("RestartRequired"), getClass().getResourceAsStream(SharedVariables.ICON_URL)); - } - } - /** * Method that is invoked when the window should be closed * diff --git a/src/main/java/com/codedead/opal/controller/TrayIconController.java b/src/main/java/com/codedead/opal/controller/TrayIconController.java index 0ae8e48..041d928 100644 --- a/src/main/java/com/codedead/opal/controller/TrayIconController.java +++ b/src/main/java/com/codedead/opal/controller/TrayIconController.java @@ -1,5 +1,6 @@ package com.codedead.opal.controller; +import com.codedead.opal.domain.ObservableResourceFactory; import com.codedead.opal.interfaces.TrayIconListener; import javafx.application.Platform; import org.apache.logging.log4j.LogManager; @@ -11,28 +12,27 @@ import java.io.IOException; import java.util.Arrays; import java.util.Objects; -import java.util.ResourceBundle; public final class TrayIconController { private TrayIcon trayIcon; - private final ResourceBundle resourceBundle; + private final ObservableResourceFactory resourceFactory; private final TrayIconListener trayIconListener; private final Logger logger; /** * Initialize a new TrayIconController * - * @param resourceBundle The {@link ResourceBundle} object + * @param resourceFactory The {@link ObservableResourceFactory} object * @param trayIconListener The {@link TrayIconListener} interface */ - public TrayIconController(final ResourceBundle resourceBundle, final TrayIconListener trayIconListener) { - if (resourceBundle == null) - throw new NullPointerException("ResourceBundle cannot be null!"); + public TrayIconController(final ObservableResourceFactory resourceFactory, final TrayIconListener trayIconListener) { + if (resourceFactory == null) + throw new NullPointerException("ResourceFactory cannot be null!"); if (trayIconListener == null) throw new NullPointerException("TrayIconListener cannot be null!"); - this.resourceBundle = resourceBundle; + this.resourceFactory = resourceFactory; this.trayIconListener = trayIconListener; this.logger = LogManager.getLogger(TrayIconController.class); } @@ -54,10 +54,10 @@ private void createTrayIcon() throws IOException { final PopupMenu popup = new PopupMenu(); final BufferedImage trayIconImage = ImageIO.read(Objects.requireNonNull(getClass().getResource("/images/opal.png"))); final TrayIcon localTrayIcon = new TrayIcon(trayIconImage.getScaledInstance(trayIconSize.width, trayIconSize.height, java.awt.Image.SCALE_SMOOTH)); - final java.awt.MenuItem displayItem = new java.awt.MenuItem(resourceBundle.getString("Display")); - final java.awt.MenuItem settingsItem = new java.awt.MenuItem(resourceBundle.getString("Settings")); - final java.awt.MenuItem aboutItem = new java.awt.MenuItem(resourceBundle.getString("About")); - final java.awt.MenuItem exitItem = new java.awt.MenuItem(resourceBundle.getString("Exit")); + final java.awt.MenuItem displayItem = new java.awt.MenuItem(resourceFactory.getResources().getString("Display")); + final java.awt.MenuItem settingsItem = new java.awt.MenuItem(resourceFactory.getResources().getString("Settings")); + final java.awt.MenuItem aboutItem = new java.awt.MenuItem(resourceFactory.getResources().getString("About")); + final java.awt.MenuItem exitItem = new java.awt.MenuItem(resourceFactory.getResources().getString("Exit")); if (trayIconListener != null) { // Platform.runLater to run on the JavaFX thread diff --git a/src/main/java/com/codedead/opal/domain/ObservableResourceFactory.java b/src/main/java/com/codedead/opal/domain/ObservableResourceFactory.java new file mode 100644 index 0000000..7849942 --- /dev/null +++ b/src/main/java/com/codedead/opal/domain/ObservableResourceFactory.java @@ -0,0 +1,65 @@ +package com.codedead.opal.domain; + +import javafx.beans.binding.StringBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +import java.util.ResourceBundle; + +public final class ObservableResourceFactory { + + private final ObjectProperty resources; + + /** + * Initialize a new ObservableResourceFactory + */ + public ObservableResourceFactory() { + resources = new SimpleObjectProperty<>(); + } + + /** + * Get the resources property + * + * @return The resources property + */ + public ObjectProperty resourcesProperty() { + return resources; + } + + /** + * Get the resources + * + * @return The resources + */ + public ResourceBundle getResources() { + return resourcesProperty().get(); + } + + /** + * Set the resources + * + * @param resources The resources + */ + public void setResources(final ResourceBundle resources) { + resourcesProperty().set(resources); + } + + /** + * Get a string binding for the given key + * + * @param key The key + * @return The {@link StringBinding} for the given key + */ + public StringBinding getStringBinding(final String key) { + return new StringBinding() { + { + bind(resourcesProperty()); + } + + @Override + public String computeValue() { + return getResources().getString(key); + } + }; + } +} diff --git a/src/main/java/com/codedead/opal/domain/SoundPane.java b/src/main/java/com/codedead/opal/domain/SoundPane.java index 3c169b8..1dd4f84 100644 --- a/src/main/java/com/codedead/opal/domain/SoundPane.java +++ b/src/main/java/com/codedead/opal/domain/SoundPane.java @@ -148,6 +148,15 @@ public String getName() { return lblName.getText(); } + /** + * Get the name {@link Label} object + * + * @return The name {@link Label} object + */ + public Label getNameLabel() { + return lblName; + } + /** * Set the name * diff --git a/src/main/resources/translations/OpalApplication.properties b/src/main/resources/translations/OpalApplication.properties index 218d865..26b9bfc 100644 --- a/src/main/resources/translations/OpalApplication.properties +++ b/src/main/resources/translations/OpalApplication.properties @@ -33,7 +33,6 @@ Phone=Phone Rain=Rain Reset=Reset ResetSettingsError=Unable to reset all settings! -RestartRequired=A restart is required in order to change the language! Save=Save SaveSettingsError=Unable to save settings! SaveSoundPreset=Save sound settings diff --git a/src/main/resources/translations/OpalApplication_de_DE.properties b/src/main/resources/translations/OpalApplication_de_DE.properties index 97d90cc..66cd929 100644 --- a/src/main/resources/translations/OpalApplication_de_DE.properties +++ b/src/main/resources/translations/OpalApplication_de_DE.properties @@ -33,7 +33,6 @@ Phone=Telefon Rain=Regen Reset=Zurücksetzen ResetSettingsError=Einstellungen konnten nicht zurückgesetzt werden! -RestartRequired=Zur Änderung der Sprache muss das Programm neu gestartet werden! Save=Speichern SaveSettingsError=Einstellungen konnten nicht gespeichert werden! SaveSoundPreset=Toneinstellungen speichern diff --git a/src/main/resources/translations/OpalApplication_en_US.properties b/src/main/resources/translations/OpalApplication_en_US.properties index 218d865..26b9bfc 100644 --- a/src/main/resources/translations/OpalApplication_en_US.properties +++ b/src/main/resources/translations/OpalApplication_en_US.properties @@ -33,7 +33,6 @@ Phone=Phone Rain=Rain Reset=Reset ResetSettingsError=Unable to reset all settings! -RestartRequired=A restart is required in order to change the language! Save=Save SaveSettingsError=Unable to save settings! SaveSoundPreset=Save sound settings diff --git a/src/main/resources/translations/OpalApplication_es_ES.properties b/src/main/resources/translations/OpalApplication_es_ES.properties index 646d483..cdf76ac 100644 --- a/src/main/resources/translations/OpalApplication_es_ES.properties +++ b/src/main/resources/translations/OpalApplication_es_ES.properties @@ -33,7 +33,6 @@ Phone=Teléfono Rain=Lluvia Reset=Restablecer ResetSettingsError=¡No se pueden restablecer todas las configuraciones! -RestartRequired=¡Es necesario reiniciar para cambiar el idioma! Save=Guardar SaveSettingsError=¡No se puede guardar la configuración! SaveSoundPreset=Guardar configuración de sonido diff --git a/src/main/resources/translations/OpalApplication_fr_FR.properties b/src/main/resources/translations/OpalApplication_fr_FR.properties index 5163751..756b6bf 100644 --- a/src/main/resources/translations/OpalApplication_fr_FR.properties +++ b/src/main/resources/translations/OpalApplication_fr_FR.properties @@ -33,7 +33,6 @@ Phone=Téléphone Rain=Pluie Reset=Réinitialiser ResetSettingsError=Impossible de réinitialiser tous les paramètres! -RestartRequired=Un redémarrage est nécessaire pour changer la langue! Save=Sauvegarder SaveSettingsError=Impossible d'enregistrer les paramètres! SaveSoundPreset=Enregistrer les paramètres sonores diff --git a/src/main/resources/translations/OpalApplication_hi_IN.properties b/src/main/resources/translations/OpalApplication_hi_IN.properties index d9a4b7a..95b23e3 100644 --- a/src/main/resources/translations/OpalApplication_hi_IN.properties +++ b/src/main/resources/translations/OpalApplication_hi_IN.properties @@ -33,7 +33,6 @@ Phone=फ़ोन Rain=बारिश Reset=रीसेट ResetSettingsError=सभी सेटिंग्स रीसेट करने में असमर्थ! -RestartRequired=भाषा बदलने के लिए पुनः आरंभ करना आवश्यक है! Save=बचाना SaveSettingsError=सेटिंग्स सहेजने में असमर्थ! SaveSoundPreset=ध्वनि सेटिंग सहेजें diff --git a/src/main/resources/translations/OpalApplication_jp_JP.properties b/src/main/resources/translations/OpalApplication_jp_JP.properties index 0975710..6b40b05 100644 --- a/src/main/resources/translations/OpalApplication_jp_JP.properties +++ b/src/main/resources/translations/OpalApplication_jp_JP.properties @@ -33,7 +33,6 @@ Phone=電話 Rain=雨 Reset=リセット ResetSettingsError=すべての設定をリセットできません! -RestartRequired=言語を変更するには再起動が必要です! Save=保存 SaveSettingsError=設定を保存できません! SaveSoundPreset=サウンド設定を保存 diff --git a/src/main/resources/translations/OpalApplication_nl_NL.properties b/src/main/resources/translations/OpalApplication_nl_NL.properties index 9fcb32a..6c5235b 100644 --- a/src/main/resources/translations/OpalApplication_nl_NL.properties +++ b/src/main/resources/translations/OpalApplication_nl_NL.properties @@ -33,7 +33,6 @@ Phone=Telefoon Rain=Regen Reset=Reset ResetSettingsError=Kan de instellingen niet resetten! -RestartRequired=Gelieve de applicatie te herstarten om te taal te wijzigen! Save=Opslaan SaveSettingsError=Kan instellingen niet opslaan! SaveSoundPreset=Sla geluidsinstellingen op diff --git a/src/main/resources/translations/OpalApplication_ru_RU.properties b/src/main/resources/translations/OpalApplication_ru_RU.properties index 45e03cd..b778c45 100644 --- a/src/main/resources/translations/OpalApplication_ru_RU.properties +++ b/src/main/resources/translations/OpalApplication_ru_RU.properties @@ -33,7 +33,6 @@ Phone=Телефон Rain=Дождь Reset=Перезагрузить ResetSettingsError=Невозможно сбросить все настройки! -RestartRequired=Для смены языка требуется перезагрузка! Save=Сохранять SaveSettingsError=Не удалось сохранить настройки! SaveSoundPreset=Сохранить настройки звука diff --git a/src/main/resources/translations/OpalApplication_tr_TR.properties b/src/main/resources/translations/OpalApplication_tr_TR.properties index 787da82..77fb1cd 100644 --- a/src/main/resources/translations/OpalApplication_tr_TR.properties +++ b/src/main/resources/translations/OpalApplication_tr_TR.properties @@ -33,7 +33,6 @@ Phone=Telefon Rain=Yağmur Reset=Sıfırla ResetSettingsError=Tüm ayarlar sıfırlanamıyor! -RestartRequired=Dili değiştirmek için yeniden başlatma gereklidir! Save=Kaydetmek SaveSettingsError=Ayarlar kaydedilemiyor! SaveSoundPreset=Ses ayarlarını kaydet diff --git a/src/main/resources/translations/OpalApplication_uk_UA.properties b/src/main/resources/translations/OpalApplication_uk_UA.properties index 0dcfe96..80db46e 100644 --- a/src/main/resources/translations/OpalApplication_uk_UA.properties +++ b/src/main/resources/translations/OpalApplication_uk_UA.properties @@ -33,7 +33,6 @@ Phone=Телефон Rain=Дощ Reset=Скинути ResetSettingsError=Неможливо скинути всі налаштування! -RestartRequired=Щоб змінити мову, потрібен перезапуск! Save=Збережіть SaveSettingsError=Не вдалося зберегти налаштування! SaveSoundPreset=Зберегти налаштування звуку diff --git a/src/main/resources/translations/OpalApplication_zh_CN.properties b/src/main/resources/translations/OpalApplication_zh_CN.properties index 521ae2b..4b745af 100644 --- a/src/main/resources/translations/OpalApplication_zh_CN.properties +++ b/src/main/resources/translations/OpalApplication_zh_CN.properties @@ -33,7 +33,6 @@ Phone=电话 Rain=雨 Reset=重置 ResetSettingsError=无法重置所有设置! -RestartRequired=需要重新启动才能更改语言! Save=节省 SaveSettingsError=无法保存设置! SaveSoundPreset=保存声音设置 diff --git a/src/main/resources/windows/MainWindow.fxml b/src/main/resources/windows/MainWindow.fxml index 038e953..077e418 100644 --- a/src/main/resources/windows/MainWindow.fxml +++ b/src/main/resources/windows/MainWindow.fxml @@ -41,15 +41,16 @@ - - + + - + @@ -57,7 +58,8 @@ - + @@ -65,14 +67,14 @@ - + - + @@ -80,7 +82,7 @@ - + @@ -88,15 +90,15 @@ - - + + - + @@ -106,8 +108,8 @@ - - + + @@ -115,7 +117,7 @@ - + @@ -123,14 +125,14 @@ - + - + @@ -138,14 +140,14 @@ - + - + @@ -231,40 +233,40 @@ - - - - - - - - - - - - @@ -285,25 +287,25 @@ - - - - - - - @@ -323,22 +325,22 @@ - - - - - - @@ -359,16 +361,16 @@ - - - - @@ -395,34 +397,34 @@ - - - - - - - - - -