/*
* Copyright 2016 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.launcher.gui.javafx;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.launcher.game.GameJob;
import org.terasology.launcher.game.JobItem;
import org.terasology.launcher.game.TerasologyGameVersion;
import org.terasology.launcher.game.TerasologyGameVersions;
import org.terasology.launcher.game.VersionItem;
import org.terasology.launcher.settings.BaseLauncherSettings;
import org.terasology.launcher.util.BundleUtils;
import org.terasology.launcher.util.DirectoryUtils;
import org.terasology.launcher.util.GuiUtils;
import org.terasology.launcher.util.JavaHeapSize;
import org.terasology.launcher.util.Languages;
import org.terasology.launcher.util.LogLevel;
import java.io.File;
import java.io.IOException;
import java.text.Collator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
public class SettingsController {
private static final Logger logger = LoggerFactory.getLogger(SettingsController.class);
private File launcherDirectory;
private File downloadDirectory;
private BaseLauncherSettings launcherSettings;
private TerasologyGameVersions gameVersions;
private File gameDirectory;
private File gameDataDirectory;
private Stage stage;
@FXML
private Tab gameTab;
@FXML
private Label maxHeapSizeLabel;
@FXML
private Label initialHeapSizeLabel;
@FXML
private Label jobLabel;
@FXML
private Label buildVersionLabel;
@FXML
private Button gameDirectoryOpenButton;
@FXML
private Button gameDirectoryEditButton;
@FXML
private Button gameDataDirectoryOpenButton;
@FXML
private Button gameDataDirectoryEditButton;
@FXML
private Label gameDirectoryLabel;
@FXML
private Label gameDataDirectoryLabel;
@FXML
private Label javaParametersLabel;
@FXML
private Label gameParametersLabel;
@FXML
private Label logLevelLabel;
@FXML
private Tab launcherTab;
@FXML
private Label chooseLanguageLabel;
@FXML
private Label closeLauncherLabel;
@FXML
private Label saveDownloadedFilesLabel;
@FXML
private Label launcherDirectoryLabel;
@FXML
private Label downloadDirectoryLabel;
@FXML
private Button launcherDirectoryOpenButton;
@FXML
private Button downloadDirectoryOpenButton;
@FXML
private Label searchForUpdatesLabel;
@FXML
private Button saveSettingsButton;
@FXML
private Button cancelSettingsButton;
@FXML
private ComboBox<JobItem> jobBox;
@FXML
private ComboBox<VersionItem> buildVersionBox;
@FXML
private ComboBox<JavaHeapSize> maxHeapSizeBox;
@FXML
private ComboBox<JavaHeapSize> initialHeapSizeBox;
@FXML
private ComboBox<String> languageBox;
@FXML
private CheckBox saveDownloadedFilesBox;
@FXML
private CheckBox searchForUpdatesBox;
@FXML
private CheckBox closeAfterStartBox;
@FXML
private Label gameDirectoryPath;
@FXML
private Label gameDataDirectoryPath;
@FXML
private Label launcherDirectoryPath;
@FXML
private Label downloadDirectoryPath;
@FXML
private TextField userJavaParametersField;
@FXML
private TextField userGameParametersField;
@FXML
private ComboBox<LogLevel> logLevelBox;
@FXML
protected void cancelSettingsAction(ActionEvent event) {
((Node) event.getSource()).getScene().getWindow().hide();
}
@FXML
protected void saveSettingsAction(ActionEvent event) {
// save job
final JobItem jobItem = jobBox.getSelectionModel().getSelectedItem();
launcherSettings.setJob(jobItem.getJob());
// save build version
final VersionItem versionItem = buildVersionBox.getSelectionModel().getSelectedItem();
launcherSettings.setBuildVersion(versionItem.getVersion(), jobItem.getJob());
// save gameDirectory
launcherSettings.setGameDirectory(gameDirectory);
// save gameDataDirectory
launcherSettings.setGameDataDirectory(gameDataDirectory);
// save heap size settings
launcherSettings.setMaxHeapSize(maxHeapSizeBox.getSelectionModel().getSelectedItem());
launcherSettings.setInitialHeapSize(initialHeapSizeBox.getSelectionModel().getSelectedItem());
// save log level settings
launcherSettings.setLogLevel(logLevelBox.getSelectionModel().getSelectedItem());
// save languageBox settings
Languages.update(Languages.SUPPORTED_LOCALES.get(languageBox.getSelectionModel().getSelectedIndex()));
launcherSettings.setLocale(Languages.getCurrentLocale());
// save searchForLauncherUpdates
launcherSettings.setSearchForLauncherUpdates(searchForUpdatesBox.isSelected());
// save closeLauncherAfterGameStart
launcherSettings.setCloseLauncherAfterGameStart(closeAfterStartBox.isSelected());
// save saveDownloadedFiles
launcherSettings.setKeepDownloadedFiles(saveDownloadedFilesBox.isSelected());
//save userParameters (java & game), if textfield is empty then set to defaults
if (userJavaParametersField.getText().isEmpty()) {
launcherSettings.setUserJavaParameters(BaseLauncherSettings.USER_JAVA_PARAMETERS_DEFAULT);
} else {
logger.debug("User defined Java parameters: {}", userJavaParametersField.getText());
launcherSettings.setUserJavaParameters(userJavaParametersField.getText());
}
if (userGameParametersField.getText().isEmpty()) {
launcherSettings.setUserGameParameters(BaseLauncherSettings.USER_GAME_PARAMETERS_DEFAULT);
} else {
logger.debug("User defined game parameters: {}", userGameParametersField.getText());
launcherSettings.setUserGameParameters(userGameParametersField.getText());
}
// store changed settings
try {
launcherSettings.store();
} catch (IOException e) {
logger.error("The launcher settings can not be stored! '{}'", launcherSettings.getLauncherSettingsFilePath(), e);
GuiUtils.showErrorMessageDialog(stage, BundleUtils.getLabel("message_error_storeSettings"));
} finally {
((Node) event.getSource()).getScene().getWindow().hide();
}
}
@FXML
protected void openGameDirectoryAction() {
GuiUtils.openFileBrowser(stage, gameDirectory, BundleUtils.getLabel("message_error_gameDirectory"));
}
@FXML
protected void editGameDirectoryAction() {
final File selectedFile = GuiUtils.chooseDirectoryDialog(stage, gameDirectory, BundleUtils.getLabel("settings_game_gameDirectory_edit_title"));
if (selectedFile != null) {
try {
DirectoryUtils.checkDirectory(selectedFile);
gameDirectory = selectedFile;
updateDirectoryPathLabels();
} catch (IOException e) {
logger.error("The game directory can not be created or used! '{}'", gameDirectory, e);
GuiUtils.showErrorMessageDialog(stage, BundleUtils.getLabel("message_error_gameDirectory") + "\n" + gameDirectory);
}
}
}
@FXML
protected void openGameDataDirectoryAction() {
GuiUtils.openFileBrowser(stage, gameDataDirectory, BundleUtils.getLabel("message_error_gameDataDirectory"));
}
@FXML
protected void editGameDataDirectoryAction() {
final File selectedFile = GuiUtils.chooseDirectoryDialog(stage, gameDataDirectory, BundleUtils.getLabel("settings_game_gameDataDirectory_edit_title"));
if (selectedFile != null) {
try {
DirectoryUtils.checkDirectory(selectedFile);
gameDataDirectory = selectedFile;
updateDirectoryPathLabels();
} catch (IOException e) {
logger.error("The game data directory can not be created or used! '{}'", gameDataDirectory, e);
GuiUtils.showErrorMessageDialog(stage, BundleUtils.getLabel("message_error_gameDataDirectory") + "\n" + gameDataDirectory);
}
}
}
@FXML
protected void openLauncherDirectoryAction() {
GuiUtils.openFileBrowser(stage, launcherDirectory, BundleUtils.getLabel("message_error_launcherDirectory"));
}
@FXML
protected void openDownloadDirectoryAction() {
GuiUtils.openFileBrowser(stage, downloadDirectory, BundleUtils.getLabel("message_error_downloadDirectory"));
}
@FXML
protected void updateMaxHeapSizeBox() {
final JavaHeapSize initialHeapSize = initialHeapSizeBox.getSelectionModel().getSelectedItem();
final JavaHeapSize maxHeapSize = maxHeapSizeBox.getSelectionModel().getSelectedItem();
if ((initialHeapSize != null) && (maxHeapSize != null) && (maxHeapSize.compareTo(initialHeapSize) < 0)) {
initialHeapSizeBox.getSelectionModel().select(maxHeapSize);
}
}
@FXML
protected void updateInitialHeapSizeBox() {
final JavaHeapSize initialHeapSize = initialHeapSizeBox.getSelectionModel().getSelectedItem();
final JavaHeapSize maxHeapSize = maxHeapSizeBox.getSelectionModel().getSelectedItem();
if ((initialHeapSize != null) && (maxHeapSize != null) && (maxHeapSize.compareTo(initialHeapSize) < 0)) {
maxHeapSizeBox.getSelectionModel().select(initialHeapSize);
}
}
public void initialize(final File newLauncherDirectory, final File newDownloadDirectory, final BaseLauncherSettings newLauncherSettings,
final TerasologyGameVersions newGameVersions, final Stage newStage) {
this.launcherDirectory = newLauncherDirectory;
this.downloadDirectory = newDownloadDirectory;
this.launcherSettings = newLauncherSettings;
this.gameVersions = newGameVersions;
this.stage = newStage;
populateJobBox();
populateHeapSize();
populateLanguageValues();
populateLanguageIcons();
populateSearchForLauncherUpdates();
populateCloseLauncherAfterGameStart();
populateSaveDownloadedFiles();
populateLogLevel();
gameDirectory = newLauncherSettings.getGameDirectory();
gameDataDirectory = newLauncherSettings.getGameDataDirectory();
updateDirectoryPathLabels();
initUserParameterFields();
setLabelStrings();
}
/**
* Used to assign localized label strings via BundleUtils.
* Allows for fallback strings to be assigned if the localization-specific ones
* are absent/empty
*/
private void setLabelStrings() {
// Game settings
gameTab.setText(BundleUtils.getLabel("settings_game_title"));
maxHeapSizeLabel.setText(BundleUtils.getLabel("settings_game_maxHeapSize"));
initialHeapSizeLabel.setText(BundleUtils.getLabel("settings_game_initialHeapSize"));
jobLabel.setText(BundleUtils.getLabel("settings_game_job"));
buildVersionLabel.setText(BundleUtils.getLabel("settings_game_buildVersion"));
gameDirectoryOpenButton.setText(BundleUtils.getLabel("settings_game_gameDirectory_open"));
gameDirectoryEditButton.setText(BundleUtils.getLabel("settings_game_gameDirectory_edit"));
gameDataDirectoryOpenButton.setText(BundleUtils.getLabel("settings_game_gameDataDirectory_open"));
gameDataDirectoryEditButton.setText(BundleUtils.getLabel("settings_game_gameDataDirectory_edit"));
gameDirectoryLabel.setText(BundleUtils.getLabel("settings_game_gameDirectory"));
gameDataDirectoryLabel.setText(BundleUtils.getLabel("settings_game_gameDataDirectory"));
userJavaParametersField.setPromptText(BundleUtils.getLabel("settings_game_javaParsPrompt"));
userGameParametersField.setPromptText(BundleUtils.getLabel("settings_game_gameParsPrompt"));
javaParametersLabel.setText(BundleUtils.getLabel("settings_game_javaParameters"));
gameParametersLabel.setText(BundleUtils.getLabel("settings_game_gameParameters"));
logLevelLabel.setText(BundleUtils.getLabel("settings_game_logLevel"));
// Launcher settings
launcherTab.setText(BundleUtils.getLabel("settings_launcher_title"));
chooseLanguageLabel.setText(BundleUtils.getLabel("settings_launcher_chooseLanguage"));
closeLauncherLabel.setText(BundleUtils.getLabel("settings_launcher_closeLauncherAfterGameStart"));
saveDownloadedFilesLabel.setText(BundleUtils.getLabel("settings_launcher_saveDownloadedFiles"));
launcherDirectoryLabel.setText(BundleUtils.getLabel("settings_launcher_launcherDirectory"));
downloadDirectoryLabel.setText(BundleUtils.getLabel("settings_launcher_downloadDirectory"));
launcherDirectoryOpenButton.setText(BundleUtils.getLabel("settings_launcher_launcherDirectory_open"));
downloadDirectoryOpenButton.setText(BundleUtils.getLabel("settings_launcher_downloadDirectory_open"));
searchForUpdatesLabel.setText(BundleUtils.getLabel("settings_launcher_searchForLauncherUpdates"));
saveSettingsButton.setText(BundleUtils.getLabel("settings_save"));
cancelSettingsButton.setText(BundleUtils.getLabel("settings_cancel"));
}
private void populateJobBox() {
jobBox.getItems().clear();
for (GameJob job : GameJob.values()) {
if (job.isOnlyInstalled() && (launcherSettings.getJob() != job)) {
boolean foundInstalled = false;
final List<TerasologyGameVersion> gameVersionList = gameVersions.getGameVersionList(job);
for (TerasologyGameVersion gameVersion : gameVersionList) {
if (gameVersion.isInstalled()) {
foundInstalled = true;
break;
}
}
if (!foundInstalled) {
continue;
}
}
final JobItem jobItem = new JobItem(job);
jobBox.getItems().add(jobItem);
if (launcherSettings.getJob() == job) {
jobBox.getSelectionModel().select(jobItem);
}
}
updateBuildVersionBox();
// add change listeners
jobBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<JobItem>() {
@Override
public void changed(final ObservableValue<? extends JobItem> observableValue, final JobItem oldItem, final JobItem newItem) {
if (jobBox.getItems().isEmpty()) {
return;
}
launcherSettings.setJob(newItem.getJob());
updateBuildVersionBox();
logger.debug("Selected gamejob: {} -- {}", launcherSettings.getJob(), launcherSettings.getBuildVersion(launcherSettings.getJob()));
}
});
buildVersionBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<VersionItem>() {
@Override
public void changed(final ObservableValue<? extends VersionItem> observableValue, final VersionItem oldVersionItem, final VersionItem newVersionItem) {
if (newVersionItem != null) {
final Integer version = newVersionItem.getVersion();
launcherSettings.setBuildVersion(version, launcherSettings.getJob());
logger.debug("Selected gamejob: {} -- {}", launcherSettings.getJob(), launcherSettings.getBuildVersion(launcherSettings.getJob()));
}
}
});
}
private void updateBuildVersionBox() {
buildVersionBox.getItems().clear();
final JobItem jobItem = jobBox.getSelectionModel().getSelectedItem();
final int buildVersion = launcherSettings.getBuildVersion(jobItem.getJob());
for (TerasologyGameVersion version : gameVersions.getGameVersionList(jobItem.getJob())) {
final VersionItem versionItem = new VersionItem(version);
buildVersionBox.getItems().add(versionItem);
if (versionItem.getVersion() == buildVersion) {
buildVersionBox.getSelectionModel().select(versionItem);
}
}
}
private void populateHeapSize() {
maxHeapSizeBox.getItems().clear();
initialHeapSizeBox.getItems().clear();
for (JavaHeapSize heapSize : JavaHeapSize.values()) {
maxHeapSizeBox.getItems().add(heapSize);
initialHeapSizeBox.getItems().add(heapSize);
}
updateHeapSizeSelection();
}
private void populateLanguageValues() {
languageBox.getItems().clear();
for (Locale locale : Languages.SUPPORTED_LOCALES) {
String item = locale.toLanguageTag() + " : " + BundleUtils.getLabel(locale, Languages.SETTINGS_LABEL_KEYS.get(locale));
if (!locale.equals(Languages.getCurrentLocale())) {
item += " (" + BundleUtils.getLabel(Languages.SETTINGS_LABEL_KEYS.get(locale)) + ")";
}
languageBox.getItems().add(item);
if (Languages.getCurrentLocale().equals(locale)) {
languageBox.getSelectionModel().select(item);
}
}
Collator coll = Collator.getInstance();
languageBox.getItems().sort(coll);
}
private void populateLanguageIcons() {
languageBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> p) {
return new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
// Pass along the locale text
super.updateItem(item, empty);
this.setText(item);
if (item == null || empty) {
this.setGraphic(null);
} else {
// Get the key thar represents the locale in ImageBundle (flag_xx)
String countryCode = this.getText().split(":")[0].trim();
String id = "flag_" + countryCode;
try {
// Get the appropriate flag icon via BundleUtils
Image icon = BundleUtils.getFxImage(id);
ImageView iconImageView = new ImageView(icon);
iconImageView.setFitHeight(11);
iconImageView.setPreserveRatio(true);
this.setGraphic(iconImageView);
} catch (MissingResourceException e) {
logger.warn("ImageBundle key {} not found", id);
} catch (NullPointerException e) {
logger.warn("Flag icon in ImageBundle key {} missing or corrupt", id);
}
}
}
};
}
});
// Make the icon visible in the control area for the selected locale
languageBox.setButtonCell(languageBox.getCellFactory().call(null));
}
private void populateSearchForLauncherUpdates() {
searchForUpdatesBox.setSelected(launcherSettings.isSearchForLauncherUpdates());
}
private void populateCloseLauncherAfterGameStart() {
closeAfterStartBox.setSelected(launcherSettings.isCloseLauncherAfterGameStart());
}
private void populateSaveDownloadedFiles() {
saveDownloadedFilesBox.setSelected(launcherSettings.isKeepDownloadedFiles());
}
private void populateLogLevel() {
logLevelBox.getItems().clear();
for (LogLevel level : LogLevel.values()) {
logLevelBox.getItems().add(level);
}
updateLogLevelSelection();
}
private void updateDirectoryPathLabels() {
gameDirectoryPath.setText(gameDirectory.getPath());
gameDataDirectoryPath.setText(gameDataDirectory.getPath());
launcherDirectoryPath.setText(launcherDirectory.getPath());
downloadDirectoryPath.setText(downloadDirectory.getPath());
}
private void updateHeapSizeSelection() {
maxHeapSizeBox.getSelectionModel().select(launcherSettings.getMaxHeapSize());
initialHeapSizeBox.getSelectionModel().select(launcherSettings.getInitialHeapSize());
}
private void updateLogLevelSelection() {
logLevelBox.getSelectionModel().select(launcherSettings.getLogLevel());
}
private void initUserParameterFields() {
//if the VM parameters are left default do not display, the prompt message will show
if (!launcherSettings.getUserJavaParameters().equals(BaseLauncherSettings.USER_JAVA_PARAMETERS_DEFAULT)) {
userJavaParametersField.setText(launcherSettings.getUserJavaParameters());
}
//if the Game parameters are left default do not display, the prompt message will show
if (!launcherSettings.getUserGameParameters().equals(BaseLauncherSettings.USER_GAME_PARAMETERS_DEFAULT)) {
userGameParametersField.setText(launcherSettings.getUserGameParameters());
}
}
}