package net.sf.colossus.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.SpinnerNumberModel; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.sf.colossus.common.Options; import net.sf.colossus.game.PlayerColor; import net.sf.colossus.guiutil.KFrame; import net.sf.colossus.util.HTMLColor; import net.sf.colossus.util.Split; @SuppressWarnings("serial") class PreferencesWindow extends KFrame implements ItemListener, ActionListener { /** * Gridbag constraints for a vertical filling (use with empty JPanel). */ private static final GridBagConstraints FILL_CONSTRAINTS = new GridBagConstraints(); static { FILL_CONSTRAINTS.gridx = 1; // first in new line FILL_CONSTRAINTS.weighty = 1; // expand vertically (others should have weighty set to 0) } /** * Gridbag constraints for a vertical spacer (use with empty JPanel). */ private static final GridBagConstraints SPACER_CONSTRAINTS = new GridBagConstraints(); static { SPACER_CONSTRAINTS.gridx = 1; // first in new line SPACER_CONSTRAINTS.ipady = 10; // expand vsize by this many pixels } /** * Gridbag constraints for the controls itself. */ private static final GridBagConstraints CONTROL_CONSTRAINTS = new GridBagConstraints(); static { CONTROL_CONSTRAINTS.gridx = 1; // first in new line CONTROL_CONSTRAINTS.weightx = 1; // expand cell horizontally to use width of pane CONTROL_CONSTRAINTS.anchor = GridBagConstraints.NORTHWEST; // align top left CONTROL_CONSTRAINTS.insets = new Insets(0, 5, 0, 5); // add a bit extra space around it } /** * Gridbag constraints for nested panels. */ private static final GridBagConstraints SUBPANEL_CONSTRAINTS = new GridBagConstraints(); static { SUBPANEL_CONSTRAINTS.gridx = 1; // first in new line SUBPANEL_CONSTRAINTS.weightx = 1; // expand cell horizontally to use width of pane SUBPANEL_CONSTRAINTS.fill = GridBagConstraints.BOTH; // panel should use all of cell } private Options options; private final ClientGUI gui; private final Map<String, JCheckBox> prefCheckboxes = new HashMap<String, JCheckBox>(); private final Map<String, JRadioButton> prefRadioButtons = new HashMap<String, JRadioButton>(); private JButton closeButton; private Box lfBox; // Look & Feel private Box rcModes; // Recruit Chit modes private Box mcModes; // Move confirmation modes private Box nextSplitModes; // Modes for next key during split phase private JPanel favColorPane; private int activePaneIndex; private List<PlayerColor> favoriteColors; private List<PlayerColor> colorsLeft; PreferencesWindow(Options options, ClientGUI clientGui) { super("Preferences"); this.options = options; this.gui = clientGui; getContentPane().add(new JLabel("Dummy")); setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); setupGUI(); pack(); useSaveWindow(options, "Preferences", null); } private void addCheckBox(Container pane, String name) { addCheckBox(pane, name, true, false); } private void addCheckBox(Container pane, String name, boolean enabled, boolean defVal) { JCheckBox cb = new JCheckBox(name); cb.setSelected(options.getOption(name, defVal)); cb.setEnabled(enabled); cb.addItemListener(this); pane.add(cb, CONTROL_CONSTRAINTS); prefCheckboxes.put(name, cb); } private void addRadioButton(Container cont, ButtonGroup group, String text, String cmd, String current) { JRadioButton rb = new JRadioButton(text); if (cmd != null && !cmd.equals("")) { rb.setActionCommand(cmd); } rb.addItemListener(this); group.add(rb); cont.add(rb, CONTROL_CONSTRAINTS); boolean selected = (text.equals(current)); rb.setSelected(selected); prefRadioButtons.put(text, rb); } private void addButton(Container cont, String name, ActionListener al) { JButton button = new JButton(name); button.addActionListener(al); cont.add(button); } private void setupGUI() { JTabbedPane tabbedPane = new JTabbedPane(); getContentPane().setLayout(new BorderLayout()); getContentPane().add(tabbedPane, BorderLayout.CENTER); // Battle Map tab: JPanel battlePane = new JPanel(new GridBagLayout()); tabbedPane.addTab("Battle", battlePane); addCheckBox(battlePane, Options.useColoredBorders); addCheckBox(battlePane, Options.doNotInvertDefender); battlePane.add(new JPanel(), SPACER_CONSTRAINTS); addCheckBox(battlePane, Options.showHitThreshold, true, true); addCheckBox(battlePane, Options.showDiceAjustmentsTerrain, true, true); addCheckBox(battlePane, Options.showDiceAjustmentsRange, true, true); battlePane.add(new JPanel(), FILL_CONSTRAINTS); // Autoplay tab: /* In menu it was like that (saved here for the used key shortcuts) JMenu playerMenu = new JMenu("Autoplay"); playerMenu.setMnemonic(KeyEvent.VK_A); menuBar.add(playerMenu); addCheckBox(playerMenu, Options.autoPickColor, KeyEvent.VK_C); addCheckBox(playerMenu, Options.autoPickMarker, KeyEvent.VK_I); addCheckBox(playerMenu, Options.autoPickEntrySide, KeyEvent.VK_E); addCheckBox(playerMenu, Options.autoForcedStrike, KeyEvent.VK_K); addCheckBox(playerMenu, Options.autoCarrySingle, KeyEvent.VK_Y); addCheckBox(playerMenu, Options.autoRangeSingle, KeyEvent.VK_G); addCheckBox(playerMenu, Options.autoSummonAngels, KeyEvent.VK_O); addCheckBox(playerMenu, Options.autoAcquireAngels, KeyEvent.VK_A); addCheckBox(playerMenu, Options.autoRecruit, KeyEvent.VK_R); addCheckBox(playerMenu, Options.autoPickRecruiter, KeyEvent.VK_U); addCheckBox(playerMenu, Options.autoReinforce, KeyEvent.VK_N); addCheckBox(playerMenu, Options.autoPlay, KeyEvent.VK_P); */ JPanel apPane = new JPanel(new GridBagLayout()); if (!gui.getClient().isSpectator()) { tabbedPane.addTab("Autoplay", apPane); } addCheckBox(apPane, Options.autoPickColor); addCheckBox(apPane, Options.autoPickMarker); addCheckBox(apPane, Options.autoPickEntrySide); addCheckBox(apPane, Options.autoForcedStrike); addCheckBox(apPane, Options.autoCarrySingle); addCheckBox(apPane, Options.autoRangeSingle); addCheckBox(apPane, Options.autoSummonAngels); addCheckBox(apPane, Options.autoAcquireAngels); addCheckBox(apPane, Options.autoRecruit); addCheckBox(apPane, Options.autoPickRecruiter); addCheckBox(apPane, Options.autoReinforce); addCheckBox(apPane, Options.autoPlay); apPane.add(new JPanel(), FILL_CONSTRAINTS); // Color tab JPanel colorPane = getColorPane(); tabbedPane.addTab("Color", colorPane); // Graphics/View tab JPanel viewPane = new JPanel(new GridBagLayout()); tabbedPane.addTab("Graphics/View", viewPane); // - graphics features panel in Graphics tab JPanel graphicsPane = new JPanel(new GridBagLayout()); graphicsPane.setBorder(new TitledBorder("Graphics features")); graphicsPane.setAlignmentX(LEFT_ALIGNMENT); addCheckBox(graphicsPane, Options.antialias); addCheckBox(graphicsPane, Options.useOverlay); addCheckBox(graphicsPane, Options.noBaseColor); viewPane.add(graphicsPane, SUBPANEL_CONSTRAINTS); // - "Show recruit preview chits ..." panel in Graphics tab ButtonGroup group = new ButtonGroup(); rcModes = new Box(BoxLayout.Y_AXIS); rcModes.setAlignmentX(LEFT_ALIGNMENT); rcModes.setBorder(new TitledBorder(Options.showRecruitChitsSubmenu)); viewPane.add(rcModes, SUBPANEL_CONSTRAINTS); String current = options .getStringOption(Options.showRecruitChitsSubmenu); // NOTE! : event handling is based on that the RB is partof this rcModes Box! addRadioButton(rcModes, group, Options.showRecruitChitsNone, "", current); addRadioButton(rcModes, group, Options.showRecruitChitsStrongest, "", current); addRadioButton(rcModes, group, Options.showRecruitChitsRecruitHint, "", current); addRadioButton(rcModes, group, Options.showRecruitChitsAll, "", current); // -- misc. Panel in Graphics tab: // so far only dubious as blanks Box miscPane = new Box(BoxLayout.Y_AXIS); viewPane.add(miscPane, SUBPANEL_CONSTRAINTS); miscPane.setBorder(new TitledBorder("Misc.")); miscPane.setAlignmentX(LEFT_ALIGNMENT); // The "dubious as blanks" option makes only sense with the // "view what SplitPrediction tells us" mode => otherwise inactive. boolean avail2 = (gui.getRawViewMode() != Options.viewableOwnNum); addCheckBox(miscPane, Options.localOnlyOwn, avail2, false); // , KeyEvent.VK_D); boolean avail = (gui.getRawViewMode() == Options.viewableEverNum); addCheckBox(miscPane, Options.dubiousAsBlanks, avail, false); // , KeyEvent.VK_D); addCheckBox(miscPane, Options.showMarker); viewPane.add(new JPanel(), FILL_CONSTRAINTS); // Window tab JPanel windowPane = new JPanel(new GridBagLayout()); tabbedPane.addTab("Windows", windowPane); addCheckBox(windowPane, Options.stealFocus); // KeyEvent.VK_F Box notifBox = new Box(BoxLayout.Y_AXIS); notifBox.setBorder(new TitledBorder("Notifications")); addCheckBox(notifBox, Options.turnStartToFront); addCheckBox(notifBox, Options.turnStartBeep); addCheckBox(notifBox, Options.turnStartBottomBarYellow, true, true); addCheckBox(notifBox, Options.turnStartChatYellow, true, true); windowPane.add(notifBox, SUBPANEL_CONSTRAINTS); // Look & Feel panel in Window tab ButtonGroup lfGroup = new ButtonGroup(); lfBox = new Box(BoxLayout.Y_AXIS); windowPane.add(lfBox, SUBPANEL_CONSTRAINTS); lfBox.setAlignmentX(LEFT_ALIGNMENT); lfBox.setBorder(new TitledBorder("Look & Feel")); lfBox.add(new JLabel("Choose your preferred \"Look & Feel\":")); String currentLF = UIManager.getLookAndFeel().getName(); UIManager.LookAndFeelInfo[] lnfInfos = UIManager .getInstalledLookAndFeels(); for (LookAndFeelInfo lnfInfo : lnfInfos) { // NOTE! : event handling is based on that the RB is part of this lnfBox Box! addRadioButton(lfBox, lfGroup, lnfInfo.getName(), lnfInfo.getClassName(), currentLF); } int oldValue = Scale.get(); int min = 5; int max = 25; int step = 1; ScaleValue scalePane = new ScaleValue(oldValue, min, max, step); scalePane.setAlignmentX(LEFT_ALIGNMENT); windowPane.add(scalePane, SUBPANEL_CONSTRAINTS); windowPane.add(new JPanel(), FILL_CONSTRAINTS); JPanel nextPane = new JPanel(new GridBagLayout()); if (!gui.getClient().isSpectator()) { tabbedPane.addTab("Next key", nextPane); } addCheckBox(nextPane, Options.nextMove, true, true); addCheckBox(nextPane, Options.nextMuster, true, true); addCheckBox(nextPane, Options.nextSplitAllSplitable, true, true); ButtonGroup nextSplitButtonGroup = new ButtonGroup(); nextSplitModes = new Box(BoxLayout.Y_AXIS); nextSplitModes.setBorder(new TitledBorder(Options.nextSplitSubMenu)); nextSplitModes.setAlignmentX(LEFT_ALIGNMENT); nextPane.add(nextSplitModes, SUBPANEL_CONSTRAINTS); String nextSplitCur = options .getStringOption(Options.nextSplitSubMenu); addRadioButton(nextSplitModes, nextSplitButtonGroup, Options.nextSplitNoClick, "", nextSplitCur); addRadioButton(nextSplitModes, nextSplitButtonGroup, Options.nextSplitLeftClick, "", nextSplitCur); addRadioButton(nextSplitModes, nextSplitButtonGroup, Options.nextSplitRightClick, "", nextSplitCur); JPanel confirmationPane = new JPanel(new GridBagLayout()); if (!gui.getClient().isSpectator()) { tabbedPane.addTab("Confirmations", confirmationPane); } addCheckBox(confirmationPane, Options.confirmNoRecruit, true, true); addCheckBox(confirmationPane, Options.confirmNoSplit, true, true); addCheckBox(confirmationPane, Options.confirmConcedeWithTitan, true, true); ButtonGroup moveButtonGroup = new ButtonGroup(); mcModes = new Box(BoxLayout.Y_AXIS); mcModes.setBorder(new TitledBorder( Options.legionMoveConfirmationSubMenu)); mcModes.setAlignmentX(LEFT_ALIGNMENT); confirmationPane.add(mcModes, SUBPANEL_CONSTRAINTS); String legionMoveCur = options .getStringOption(Options.legionMoveConfirmationSubMenu); addRadioButton(mcModes, moveButtonGroup, Options.legionMoveConfirmationNoMove, "", legionMoveCur); addRadioButton(mcModes, moveButtonGroup, Options.legionMoveConfirmationNoUnvisitedMove, "", legionMoveCur); addRadioButton(mcModes, moveButtonGroup, Options.legionMoveConfirmationNoConfirm, "", legionMoveCur); closeButton = new JButton("Close"); closeButton.addActionListener(this); getContentPane().add(closeButton, BorderLayout.SOUTH); restoreWhichTabActive(tabbedPane); } private void restoreWhichTabActive(JTabbedPane tabbedPane) { String activeTab = options .getStringOption(Options.activePreferencesTab); int index = -1; if (activeTab != null) { index = tabbedPane.indexOfTab(activeTab); } if (index != -1) { tabbedPane.setSelectedIndex(index); } this.activePaneIndex = tabbedPane.getSelectedIndex(); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { try { JTabbedPane theTabbedPane = (JTabbedPane)e.getSource(); int newIndex = theTabbedPane.getSelectedIndex(); if (newIndex != activePaneIndex) { activePaneIndex = newIndex; String tabName = theTabbedPane.getTitleAt(newIndex); if (tabName != null) { options.setOption(Options.activePreferencesTab, tabName); } } } catch (NullPointerException nullEx) { // } } }); } private JPanel getColorPane() { JPanel colorPane = new JPanel(new BorderLayout()); favColorPane = new JPanel(); favColorPane .setLayout(new BoxLayout(favColorPane, BoxLayout.PAGE_AXIS)); favColorPane.setBorder(BorderFactory .createTitledBorder("Favorite Colors")); String favorites = options.getStringOption(Options.favoriteColors); favoriteColors = null; colorsLeft = new ArrayList<PlayerColor>(); for (PlayerColor playerColor : PlayerColor.values()) { colorsLeft.add(playerColor); } if (favorites != null) { favoriteColors = PlayerColor .getByName(Split.split(',', favorites)); } else { favoriteColors = new ArrayList<PlayerColor>(); } ActionListener selectColorAL = new ActionListener() { public void actionPerformed(ActionEvent e) { selectColor(); } }; ActionListener clearColorAL = new ActionListener() { public void actionPerformed(ActionEvent e) { clearColor(); } }; JPanel colorButtonPane = new JPanel(); addButton(colorButtonPane, "Select", selectColorAL); addButton(colorButtonPane, "Clear All", clearColorAL); colorPane.add(colorButtonPane, BorderLayout.NORTH); colorPane.add(favColorPane, BorderLayout.CENTER); for (int i = 0; i < favoriteColors.size(); i++) { PlayerColor color = favoriteColors.get(i); addColor(color); colorsLeft.remove(color); } return colorPane; } @Override public void dispose() { super.dispose(); options = null; } @Override public void setVisible(boolean val) { // We use hideOnClose, so it never actually gets disposed while // client stays alive. Thus, to plug in the the saveOptions when // user closes the PreferencesWindow, we have to couple it to the // hide/unhide: if (!val) { options.saveOptions(); } else { if ((this.getExtendedState() & JFrame.ICONIFIED) != 0) { this.setExtendedState(JFrame.NORMAL); } } super.setVisible(val); } public void actionPerformed(ActionEvent e) { setVisible(false); } public void itemStateChanged(ItemEvent e) { Object source = e.getSource(); JComponent sourceJC = (JComponent)source; if (source instanceof JCheckBox) { String text = ((JCheckBox)source).getText(); boolean selected = (e.getStateChange() == ItemEvent.SELECTED); options.setOption(text, selected); if (text.equals(Options.turnStartBottomBarYellow) && !selected) { gui.getBoard().myTurnBottomBarActions(selected); } if (text.equals(Options.turnStartChatYellow) && !selected) { gui.notifyWebClientItsPlayersTurn(selected); } } else if (source instanceof JRadioButton) { String text = ((JRadioButton)source).getActionCommand(); if (!(e.getStateChange() == ItemEvent.SELECTED)) { // Ignore, because was set to FALSE. // In RadioButtons we care only about the new state } else if (sourceJC.getParent() == rcModes) { if (text != null) { options.setOption(Options.showRecruitChitsSubmenu, text); } } else if (sourceJC.getParent() == lfBox) { gui.setLookAndFeel(text); } else if (sourceJC.getParent() == mcModes) { if (text != null) { options.setOption(Options.legionMoveConfirmationSubMenu, text); } } else if (sourceJC.getParent() == nextSplitModes) { if (text != null) { options.setOption(Options.nextSplitSubMenu, text); } } } } private void saveFavColor() { if (favoriteColors.size() > 0) { StringBuilder favorites = new StringBuilder(); for (PlayerColor color : favoriteColors) { if (favorites.length() > 0) { favorites.append(','); } favorites.append(color.getName()); } options.setOption(Options.favoriteColors, favorites.toString()); } else { options.removeOption(Options.favoriteColors); } } private void clearColor() { favoriteColors = new ArrayList<PlayerColor>(); favColorPane.removeAll(); colorsLeft = new ArrayList<PlayerColor>(); for (PlayerColor color : PlayerColor.values()) { colorsLeft.add(color); } this.repaint(); saveFavColor(); } private void unselectColor(JButton button) { String colorName = button.getText(); favColorPane.remove(button); final PlayerColor color = PlayerColor.getByName(colorName); colorsLeft.add(color); this.repaint(); favoriteColors.remove(color); saveFavColor(); } private void addColor(PlayerColor color) { Color realColor = HTMLColor .stringToColor(color.getName() + "Colossus"); JButton button = new JButton(color.getName()); button.setBackground(realColor); int sum = realColor.getRed() + realColor.getGreen() + realColor.getBlue(); button.setForeground(sum > 200 ? Color.black : Color.white); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { unselectColor((JButton)e.getSource()); } }; button.addActionListener(al); favColorPane.add(button); } private void selectColor() { PickColor.PickColorCallback callback = new PickColor.PickColorCallback() { @Override public void tellPickedColor(PlayerColor color) { doSomethingWithPickedColor(color); } }; // Allow null for cancel new PickColor(this, "You", colorsLeft, options, callback, true); } public void doSomethingWithPickedColor(PlayerColor color) { if (color != null) { addColor(color); favoriteColors.add(color); colorsLeft.remove(color); saveFavColor(); this.repaint(); } } class ScaleValue extends JPanel implements ChangeListener, ActionListener { private final SpinnerNumberModel model; private final JSpinner spinner; private int newValue; private int oldValue; ScaleValue(int oldValue, int min, int max, int step) { super(); this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); this.oldValue = oldValue; this.newValue = oldValue; Box scalePane = new Box(BoxLayout.X_AXIS); add(scalePane); add(new JLabel("Window scale: ")); this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); model = new SpinnerNumberModel(oldValue, min, max, step); spinner = new JSpinner(model); add(spinner); spinner.addChangeListener(this); JButton applyButton = new JButton("Apply"); applyButton.addActionListener(this); add(applyButton); } public void stateChanged(ChangeEvent e) { newValue = ((Integer)spinner.getValue()).intValue(); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Apply")) { if (newValue != oldValue) { net.sf.colossus.util.StaticResourceLoader .purgeImageCache(); options.setOption(Options.scale, newValue); } oldValue = newValue; } } } public void setCheckBoxValue(String name, boolean value) { JCheckBox cb; cb = prefCheckboxes.get(name); if (cb != null) { cb.setSelected(value); } } public void setRadioButtonValue(String name, boolean value) { JRadioButton rb; rb = prefRadioButtons.get(name); if (rb != null) { rb.setSelected(value); } } }