package magic.ui.dialog.prefs;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.FileNotFoundException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.text.NumberFormatter;
import magic.data.GeneralConfig;
import magic.translate.MText;
import magic.ui.FontsAndBorders;
import magic.ui.MagicFrame;
import magic.ui.MagicImages;
import magic.ui.ScreenController;
import magic.ui.dialog.button.CancelButton;
import magic.ui.dialog.button.SaveButton;
import magic.ui.theme.Theme;
import magic.ui.utility.MagicStyle;
import magic.ui.widget.M.MCheckBox;
import magic.ui.widget.SliderPanel;
import net.miginfocom.swing.MigLayout;
@SuppressWarnings("serial")
public class PreferencesDialog
extends JDialog
implements ActionListener, MouseListener, WindowListener {
// translatable strings.
private static final String _S3 = "General";
private static final String _S4 = "Gameplay";
private static final String _S5 = "Theme";
private static final String _S6 = "Audio";
private static final String _S7 = "Network";
private static final String _S12 = "Proxy:";
private static final String _S13 = "URL:";
private static final String _S14 = "Port:";
private static final String _S15 = "[Experimental] Suppress AI action prompts";
private static final String _S16 = "If enabled, hides AI prompts in the user action panel. Only prompts that require you to make a decision will be shown.";
private static final String _S17 = "Use Mulligan screen";
private static final String _S20 = "Double-click to cast or activate ability (for touchscreen)";
private static final String _S21 = "Automatically pass priority";
private static final String _S22 = "When the only option is to pass don't prompt player, just pass immediately.";
private static final String _S23 = "Always pass during draw and begin of combat step";
private static final String _S24 = "Limit options for human player to those available to the AI";
private static final String _S25 = "Positive effects, such as pump and untap, can only be applied to your own permanents. Negative effects, such as destroy and exile, can only be applied to opponent's permanents.";
private static final String _S26 = "Message:";
private static final String _S27 = "The duration in milliseconds (1000 = 1 second) that the game pauses when an item is added to the stack. This has no effect unless the 'Automatically pass priority' option is enabled.";
private static final String _S37 = "Proxy settings are invalid!";
private static final String _S68 = "Images";
private static final String _S79 = "Preferences";
private static final String _S80 = "There is a problem reading the translation file.";
private static final String _S81 = "Please ensure the file is encoded as 'UTF-8 without BOM'.";
private static final String _S82 = "Animations";
private final static GeneralConfig config = GeneralConfig.getInstance();
private final ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(final ActionEvent actionEvent) {
dispose();
}
};
private final MagicFrame frame;
private final JComboBox<Proxy.Type> proxyComboBox = new JComboBox<>();
private final JTextField proxyAddressTextField = new JTextField();
private final JSpinner proxyPortSpinner = new JSpinner(new SpinnerNumberModel());
private MCheckBox touchscreenCheckBox;
private MCheckBox skipSingleCheckBox;
private MCheckBox alwaysPassCheckBox;
private MCheckBox smartTargetCheckBox;
private SliderPanel messageDelaySlider;
private JButton saveButton;
private JButton cancelButton;
private MCheckBox mulliganScreenCheckbox;
private MCheckBox hideAIPromptCheckBox;
private final GeneralPanel generalPanel;
private final AnimationsPanel animationsPanel;
private final GameplayImagesPanel gameImagesPanel;
private final AudioPanel audioPanel;
private final ThemesPanel themesPanel;
private final JLabel hintLabel = new JLabel();
private boolean isProxyUpdated = false;
private boolean isRestartRequired = false;
private final boolean isGamePlayMode;
public PreferencesDialog(final MagicFrame frame, final boolean isGamePlayMode) {
super(frame, true);
this.isGamePlayMode = isGamePlayMode;
this.setTitle(MText.get(_S79));
this.setSize(460, 530);
this.setLocationRelativeTo(ScreenController.getFrame());
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setUndecorated(true);
((JComponent) getContentPane()).setBorder(BorderFactory.createMatteBorder(0, 8, 8, 8, MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_BACKGROUND)));
this.frame = frame;
generalPanel = new GeneralPanel(this);
animationsPanel = new AnimationsPanel(this);
gameImagesPanel = new GameplayImagesPanel(this);
audioPanel = new AudioPanel(this);
themesPanel = new ThemesPanel(this);
hintLabel.setVerticalAlignment(SwingConstants.TOP);
hintLabel.setFont(new Font("SansSerif", Font.ITALIC, 12));
hintLabel.setVerticalTextPosition(SwingConstants.TOP);
// hint label replaces tooltips.
ToolTipManager.sharedInstance().setEnabled(false);
getContentPane().setLayout(new MigLayout("flowy, insets 0, gapy 0"));
getContentPane().add(getDialogCaptionLabel(), "w 100%, h 26!");
getContentPane().add(getTabbedSettingsPane(), "w 10:100%, h 100%");
getContentPane().add(hintLabel, "w 100%, h 76!, gapx 10 10");
getContentPane().add(getActionButtonsPanel(), "w 100%, aligny bottom, pushy");
setEscapeKeyAsCancelAction();
addWindowListener(this);
setVisible(true);
}
private JLabel getDialogCaptionLabel() {
final JLabel lbl = new JLabel(getTitle());
lbl.setOpaque(true);
lbl.setBackground(MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_BACKGROUND));
lbl.setForeground(MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_FOREGROUND));
lbl.setFont(FontsAndBorders.FONT1.deriveFont(14f));
lbl.setHorizontalAlignment(SwingConstants.CENTER);
return lbl;
}
private void setEscapeKeyAsCancelAction() {
final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
getRootPane().registerKeyboardAction(actionListener, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
}
private JTabbedPane getTabbedSettingsPane() {
final JTabbedPane tabbedPane = new JTabbedPane();
if (isGamePlayMode) {
tabbedPane.addTab(MText.get(_S4), getGameplaySettingsTabbedPane());
tabbedPane.addTab(MText.get(_S5), themesPanel);
tabbedPane.addTab(MText.get(_S6), audioPanel);
} else {
tabbedPane.addTab(MText.get(_S3), generalPanel);
tabbedPane.addTab(MText.get(_S4), getGameplaySettingsTabbedPane());
tabbedPane.addTab(MText.get(_S5), themesPanel);
tabbedPane.addTab(MText.get(_S6), audioPanel);
tabbedPane.addTab(MText.get(_S7), getNetworkSettingsPanel());
}
return tabbedPane;
}
private JPanel getNetworkSettingsPanel() {
final Proxy proxy = config.getProxy();
//
proxyComboBox.setModel(new DefaultComboBoxModel<>(Proxy.Type.values()));
proxyComboBox.setFocusable(false);
proxyComboBox.addActionListener(this);
proxyComboBox.setSelectedItem(proxy.type());
//
// allow only numeric characters to be recognised.
proxyPortSpinner.setEditor(new JSpinner.NumberEditor(proxyPortSpinner, "#"));
final JFormattedTextField txt1 = ((JSpinner.NumberEditor) proxyPortSpinner.getEditor()).getTextField();
((NumberFormatter) txt1.getFormatter()).setAllowsInvalid(false);
//
if (proxy != Proxy.NO_PROXY) {
proxyAddressTextField.setText(proxy.address().toString());
proxyPortSpinner.setValue(((InetSocketAddress) proxy.address()).getPort());
}
// layout components
final JPanel panel = new JPanel(new MigLayout("flowx, wrap 2, insets 16, gapy 4"));
panel.add(getCaptionLabel(MText.get(_S12)));
panel.add(proxyComboBox, "w 140!");
panel.add(getCaptionLabel(MText.get(_S13)));
panel.add(proxyAddressTextField, "w 100%");
panel.add(getCaptionLabel(MText.get(_S14)));
panel.add(proxyPortSpinner, "w 60!");
return panel;
}
private Proxy getNewProxy() {
if (proxyComboBox.getSelectedItem() == Proxy.Type.DIRECT) {
return Proxy.NO_PROXY;
} else {
final Proxy.Type proxyType = (Proxy.Type) proxyComboBox.getSelectedItem();
final String urlAddress = proxyAddressTextField.getText().trim();
final String portString = proxyPortSpinner.getValue().toString();
final int portNumber = portString.isEmpty() ? 0 : Integer.parseInt(portString);
return new Proxy(proxyType, new InetSocketAddress(urlAddress, portNumber));
}
}
private void updateProxy() {
final boolean use = proxyComboBox.getSelectedItem() != Proxy.Type.DIRECT;
proxyAddressTextField.setEnabled(use);
proxyPortSpinner.setEnabled(use);
if (use) {
proxyAddressTextField.requestFocus();
}
isProxyUpdated = true;
}
private String getAsHtml(String text) {
return String.format("<html>%s</html>", text);
}
private void setButtonPropertyDefaults(MCheckBox btn) {
btn.setFocusable(false);
btn.setVerticalTextPosition(SwingConstants.TOP);
btn.addMouseListener(this);
}
private JScrollPane getGameplaySettingsPanel1() {
hideAIPromptCheckBox = new MCheckBox(getAsHtml(MText.get(_S15)), config.getHideAiActionPrompt());
hideAIPromptCheckBox.setToolTipText(MText.get(_S16));
setButtonPropertyDefaults(hideAIPromptCheckBox);
mulliganScreenCheckbox = new MCheckBox(getAsHtml(MText.get(_S17)), config.showMulliganScreen());
setButtonPropertyDefaults(mulliganScreenCheckbox);
touchscreenCheckBox = new MCheckBox(getAsHtml(MText.get(_S20)), config.isTouchscreen());
setButtonPropertyDefaults(touchscreenCheckBox);
skipSingleCheckBox = new MCheckBox(getAsHtml(MText.get(_S21)), config.getSkipSingle());
skipSingleCheckBox.setToolTipText(MText.get(_S22));
setButtonPropertyDefaults(skipSingleCheckBox);
alwaysPassCheckBox = new MCheckBox(getAsHtml(MText.get(_S23)), config.getAlwaysPass());
setButtonPropertyDefaults(alwaysPassCheckBox);
smartTargetCheckBox = new MCheckBox(getAsHtml(MText.get(_S24)), config.getSmartTarget());
smartTargetCheckBox.setToolTipText(MText.get(_S25));
setButtonPropertyDefaults(smartTargetCheckBox);
messageDelaySlider = new SliderPanel(getAsHtml(MText.get(_S26)), 0, 3000, 500, config.getMessageDelay());
messageDelaySlider.setToolTipText(MText.get(_S27));
messageDelaySlider.addMouseListener(this);
final ScrollablePanel panel = new ScrollablePanel(new MigLayout("flowy, insets 16, gapy 10"));
panel.setScrollableWidth(ScrollablePanel.ScrollableSizeHint.FIT);
panel.add(hideAIPromptCheckBox.component());
panel.add(mulliganScreenCheckbox.component());
panel.add(touchscreenCheckBox.component());
panel.add(skipSingleCheckBox.component());
panel.add(alwaysPassCheckBox.component());
panel.add(smartTargetCheckBox.component());
panel.add(messageDelaySlider, "w 100%");
final JScrollPane scroller = new JScrollPane(panel);
scroller.getVerticalScrollBar().setUnitIncrement(18);
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
return scroller;
}
private void saveSettings() {
animationsPanel.saveSettings();
gameImagesPanel.saveSettings();
audioPanel.saveSettings();
themesPanel.saveSettings();
config.setTouchscreen(touchscreenCheckBox.isSelected());
config.setSkipSingle(skipSingleCheckBox.isSelected());
config.setAlwaysPass(alwaysPassCheckBox.isSelected());
config.setSmartTarget(smartTargetCheckBox.isSelected());
config.setMessageDelay(messageDelaySlider.getValue());
config.setShowMulliganScreen(mulliganScreenCheckbox.isSelected());
config.setHideAiActionPrompt(hideAIPromptCheckBox.isSelected());
if (isGamePlayMode == false) {
generalPanel.saveSettings();
// Network
config.setProxy(getNewProxy());
}
config.save();
}
private void doSaveButtonAction() {
final boolean isNewTranslation = !config.getTranslation().equals(generalPanel.getLanguage());
saveSettings();
if (isNewTranslation) {
if (!setNewTranslation()) {
return;
}
}
MagicImages.clearCache();
frame.refreshUI();
dispose();
}
private boolean setNewTranslation() {
try {
MText.loadTranslationFile();
isRestartRequired = true;
return true;
} catch (NumberFormatException ex) {
MText.disableTranslations();
System.err.println(ex);
ScreenController.showWarningMessage(String.format("%s\n%s\n\n%s",
MText.get(_S80), MText.get(_S81), ex)
);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
config.setTranslation(GeneralConfig.DEFAULT_TRANSLATION);
config.save();
generalPanel.refreshLanguageCombo();
}
});
return false;
} catch (FileNotFoundException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void actionPerformed(final ActionEvent event) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
final Object source = event.getSource();
if (source == saveButton) {
if (validateSettings()) {
doSaveButtonAction();
}
} else if (source == cancelButton) {
dispose();
} else if (source == proxyComboBox) {
updateProxy();
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
private boolean validateSettings() {
if (isGamePlayMode == false) {
if (isProxyUpdated && !isProxyValid()) {
ScreenController.showWarningMessage(MText.get(_S37));
return false;
}
}
return true;
}
private boolean isProxyValid() {
try {
getNewProxy();
return true;
} catch (Exception e) {
System.err.println(e);
return false;
}
}
private JButton getCancelButton() {
cancelButton = new CancelButton();
cancelButton.setFocusable(false);
cancelButton.addActionListener(this);
return cancelButton;
}
private JButton getSaveButton() {
saveButton = new SaveButton();
saveButton.setFocusable(false);
saveButton.addActionListener(this);
return saveButton;
}
private JPanel getActionButtonsPanel() {
final JPanel buttonPanel = new JPanel(new MigLayout("insets 5, gapx 5, flowx"));
buttonPanel.add(getCancelButton(), "alignx right, pushx");
buttonPanel.add(getSaveButton());
return buttonPanel;
}
/*
* MouseListener overrides
*/
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
if (e.getSource() instanceof JComponent) {
final JComponent c = (JComponent) e.getSource();
String caption = "";
if (c instanceof AbstractButton) {
caption = ((AbstractButton) c).getText();
}
hintLabel.setText(String.format("<html>%s</html>",
c.getToolTipText() == null ? caption : c.getToolTipText()));
}
}
@Override
public void mouseExited(MouseEvent e) {
if (e.getSource() instanceof JComponent) {
hintLabel.setText("");
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
/*
* WindowListener overrides
*/
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
ToolTipManager.sharedInstance().setEnabled(true);
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowOpened(WindowEvent e) {
}
private JLabel getCaptionLabel(final String text) {
JLabel lbl = new JLabel(text);
lbl.setFont(FontsAndBorders.FONT1);
return lbl;
}
private JTabbedPane getGameplaySettingsTabbedPane() {
final JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab(MText.get(_S3), getGameplaySettingsPanel1());
tabbedPane.addTab(MText.get(_S68), gameImagesPanel);
tabbedPane.addTab(MText.get(_S82), animationsPanel);
return tabbedPane;
}
public boolean isRestartRequired() {
return isRestartRequired;
}
}