// AnalyzeDialog.java package net.sf.gogui.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusAdapter; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.text.MessageFormat; import java.util.ArrayList; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import net.sf.gogui.go.ConstPointList; import net.sf.gogui.go.GoColor; import static net.sf.gogui.go.GoColor.BLACK; import static net.sf.gogui.go.GoColor.WHITE; import static net.sf.gogui.go.GoColor.EMPTY; import net.sf.gogui.go.GoPoint; import net.sf.gogui.go.PointList; import net.sf.gogui.gtp.AnalyzeCommand; import net.sf.gogui.gtp.AnalyzeDefinition; import net.sf.gogui.gtp.AnalyzeType; import net.sf.gogui.gtp.GtpError; import net.sf.gogui.gtp.GtpResponseFormatError; import net.sf.gogui.gtp.GtpUtil; import static net.sf.gogui.gui.I18n.i18n; import net.sf.gogui.util.Platform; import net.sf.gogui.util.PrefUtil; /** Dialog for selecting an AnalyzeCommand. */ public final class AnalyzeDialog extends JDialog implements ActionListener, ListSelectionListener { /** Callback for actions generated by AnalyzeDialog. */ public interface Listener { void actionClearAnalyzeCommand(); void actionSetAnalyzeCommand(AnalyzeCommand command, boolean autoRun, boolean clearBoard, boolean oneRunOnly, boolean reuseTextWindow); } public AnalyzeDialog(Frame owner, Listener listener, ArrayList<AnalyzeDefinition> commands, GuiGtpClient gtp, MessageDialogs messageDialogs) { super(owner, i18n("TIT_ANALYZE")); m_messageDialogs = messageDialogs; m_gtp = gtp; m_commands = commands; m_listener = listener; Container contentPane = getContentPane(); JPanel commandPanel = createCommandPanel(); contentPane.add(commandPanel, BorderLayout.CENTER); comboBoxChanged(); setSelectedColor(BLACK); int minWidth = commandPanel.getPreferredSize().width; setMinimumSize(new Dimension(minWidth, 192)); pack(); addWindowListener(new WindowAdapter() { public void windowActivated(WindowEvent e) { m_comboBoxHistory.requestFocusInWindow(); } }); } public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("clear")) clearCommand(); else if (command.equals("comboBoxChanged")) comboBoxChanged(); else if (command.equals("run")) runCommand(); else assert false; } public void dispose() { if (! m_autoRun.isSelected()) clearCommand(); saveRecent(); super.dispose(); } public boolean getReuseTextWindow() { return m_reuseWindow.isSelected(); } public GoColor getSelectedColor() { if (m_black.isSelected()) return BLACK; else return WHITE; } public void saveRecent() { ArrayList<String> recent = new ArrayList<String>(MAX_SAVE_RECENT); int start = (m_firstIsTemp ? 1 : 0); for (int i = start; i < getComboBoxItemCount(); ++i) { String name = getComboBoxItem(i); if (recent.indexOf(name) < 0) recent.add(name); } for (int i = 0; i < m_fullRecentList.size(); ++i) { if (recent.size() == MAX_SAVE_RECENT) break; String name = m_fullRecentList.get(i); if (recent.indexOf(name) < 0) recent.add(name); } PrefUtil.putList("net/sf/gogui/gui/analyzedialog/recentcommands", recent); } /** Set board size. Need for verifying responses to initial value for EPLIST commands. Default is 19. */ public void setBoardSize(int boardSize) { m_boardSize = boardSize; } public void setReuseTextWindow(boolean enable) { m_reuseWindow.setSelected(enable); } public void setSelectedColor(GoColor color) { m_selectedColor = color; selectColor(); } public void valueChanged(ListSelectionEvent e) { int index = m_list.getSelectedIndex(); if (index >= 0) selectCommand(index); } private static final int MAX_SAVE_RECENT = 100; /** Is the first item in the history combo box a temporary item? Avoids that the first item in the history combo box is treated as a real history command, if it was not run. */ private boolean m_firstIsTemp; private int m_boardSize = GoPoint.DEFAULT_SIZE; private ArrayList<String> m_fullRecentList; private GoColor m_selectedColor = EMPTY; private final MessageDialogs m_messageDialogs; private final GuiGtpClient m_gtp; private JButton m_clearButton; private JButton m_runButton; private JCheckBox m_autoRun; private JCheckBox m_clearBoard; private JCheckBox m_reuseWindow; private JComboBox m_comboBoxHistory; private JList m_list; private Box m_colorBox; private JRadioButton m_black; private JRadioButton m_white; private final ArrayList<AnalyzeDefinition> m_commands; private final Listener m_listener; private String m_lastUpdateOptionsCommand; private void clearCommand() { m_listener.actionClearAnalyzeCommand(); m_autoRun.setSelected(false); } private void comboBoxChanged() { Object item = m_comboBoxHistory.getSelectedItem(); if (item == null) { m_list.clearSelection(); return; } String label = item.toString(); updateOptions(label); String selectedValue = (String)m_list.getSelectedValue(); if (selectedValue != null && ! selectedValue.equals(label)) m_list.clearSelection(); } private JPanel createButtons() { JPanel innerPanel = new JPanel(new GridLayout(1, 0, GuiUtil.PAD, 0)); m_runButton = new JButton(i18n("LB_RUN")); m_runButton.setToolTipText(i18n("TT_ANALYZE_RUN")); m_runButton.setActionCommand("run"); m_runButton.addActionListener(this); m_runButton.setMnemonic(KeyEvent.VK_R); m_runButton.setEnabled(false); GuiUtil.setMacBevelButton(m_runButton); innerPanel.add(m_runButton); m_clearButton = new JButton(i18n("LB_ANALYZE_CLEAR")); m_clearButton.setToolTipText(i18n("TT_ANALYZE_CLEAR")); m_clearButton.setActionCommand("clear"); m_clearButton.addActionListener(this); m_clearButton.setMnemonic(KeyEvent.VK_C); GuiUtil.setMacBevelButton(m_clearButton); innerPanel.add(m_clearButton); JPanel outerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); outerPanel.add(innerPanel); return outerPanel; } private JComponent createColorPanel() { m_colorBox = Box.createVerticalBox(); ButtonGroup group = new ButtonGroup(); m_black = new JRadioButton(i18n("LB_BLACK")); m_black.setToolTipText(i18n("TT_ANALYZE_BLACK")); m_black.setEnabled(false); group.add(m_black); m_colorBox.add(m_black); m_white = new JRadioButton(i18n("LB_WHITE")); m_white.setToolTipText(i18n("TT_ANALYZE_WHITE")); m_white.setEnabled(false); group.add(m_white); m_colorBox.add(m_white); return m_colorBox; } private JPanel createCommandPanel() { JPanel panel = new JPanel(new BorderLayout()); m_list = new JList(); m_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); m_list.setVisibleRowCount(25); m_list.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { int modifiers = e.getModifiers(); int mask = ActionEvent.ALT_MASK; if (e.getClickCount() == 2 || ((modifiers & mask) != 0)) { //int index = // m_list.locationToIndex(event.getPoint()); runCommand(); } } }); m_list.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { int index = getSelectedCommand(); if (index >= 0) m_list.setSelectedIndex(index); } }); m_list.addListSelectionListener(this); JScrollPane scrollPane = new JScrollPane(m_list); if (Platform.isMac()) // Default Apple L&F uses no border, but Quaqua 3.7.4 does scrollPane.setBorder(null); panel.add(scrollPane, BorderLayout.CENTER); panel.add(createLowerPanel(), BorderLayout.SOUTH); String[] labels = new String[m_commands.size()]; for (int i = 0; i < m_commands.size(); ++i) labels[i] = m_commands.get(i).getLabel(); m_list.setListData(labels); comboBoxChanged(); loadRecent(); return panel; } private JComponent createLowerPanel() { Box panel = Box.createVerticalBox(); panel.add(GuiUtil.createFiller()); m_comboBoxHistory = new JComboBox(); panel.add(m_comboBoxHistory); Box lowerPanel = Box.createVerticalBox(); lowerPanel.setBorder(GuiUtil.createEmptyBorder()); panel.add(lowerPanel); Box optionsPanel = Box.createHorizontalBox(); lowerPanel.add(optionsPanel); JPanel leftPanel = new JPanel(); optionsPanel.add(leftPanel); Box leftBox = Box.createVerticalBox(); leftPanel.add(leftBox); m_autoRun = new JCheckBox(i18n("LB_ANALYZE_AUTORUN")); m_autoRun.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (! m_autoRun.isSelected()) m_listener.actionClearAnalyzeCommand(); } }); m_autoRun.setToolTipText(i18n("TT_ANALYZE_AUTORUN")); m_autoRun.setEnabled(false); leftBox.add(m_autoRun); m_clearBoard = new JCheckBox(i18n("LB_ANALYZE_CLEARBOARD")); m_clearBoard.setToolTipText(i18n("TT_ANALYZE_CLEARBOARD")); m_clearBoard.setEnabled(false); leftBox.add(m_clearBoard); m_clearBoard.setSelected(true); m_reuseWindow = new JCheckBox(i18n("LB_ANALYZE_REUSE_TEXT_WINDOW")); m_reuseWindow.setToolTipText(i18n("TT_ANALYZE_REUSE_TEXT_WINDOW")); leftBox.add(m_reuseWindow); JPanel rightPanel = new JPanel(); rightPanel.add(createColorPanel()); optionsPanel.add(rightPanel); // TODO: The following horizontal glue does not really work as expected // (tested on Linux/Sun Java 1.6.0_14) and the left two components in // the box are not aligned to the left. optionsPanel.add(Box.createHorizontalGlue()); // TODO: If GTK Looks L&F is used on Linux/Sun Java 1.6.0_14 or OpenJDK // 6b14-1.4.1-0ubuntu11, then the text of the checkbox items can be // truncated a bit on the left (wrong minimum size calculation?). The // two fillers are a workaround for this. optionsPanel.add(GuiUtil.createFiller()); optionsPanel.add(GuiUtil.createFiller()); lowerPanel.add(createButtons()); m_comboBoxHistory.addActionListener(this); return panel; } private String getComboBoxItem(int i) { return m_comboBoxHistory.getItemAt(i).toString(); } private int getComboBoxItemCount() { return m_comboBoxHistory.getItemCount(); } private int getCommandIndex(String label) { for (int i = 0; i < m_commands.size(); ++i) if (m_commands.get(i).getLabel().equals(label)) return i; return -1; } private int getSelectedCommand() { Object item = m_comboBoxHistory.getSelectedItem(); if (item == null) return -1; return getCommandIndex(item.toString()); } private void insertComboBoxItem(String label, int index) { m_comboBoxHistory.insertItemAt(GuiUtil.createComboBoxItem(label), index); } private void loadRecent() { m_comboBoxHistory.removeAllItems(); m_fullRecentList = PrefUtil.getList("net/sf/gogui/gui/analyzedialog/recentcommands"); for (int i = 0; i < m_fullRecentList.size(); ++i) { String name = m_fullRecentList.get(i); if (getCommandIndex(name) >= 0) m_comboBoxHistory.addItem(GuiUtil.createComboBoxItem(name)); if (m_comboBoxHistory.getItemCount() > 20) break; } int index = getSelectedCommand(); if (index >= 0) selectCommand(index); m_firstIsTemp = false; } private void runCommand() { if (m_gtp.isCommandInProgress()) { showError("MSG_ANALYZE_CANNOT_EXECUTE", "MSG_ANALYZE_CANNOT_EXECUTE_2", false); return; } int index = getSelectedCommand(); if (index < 0) { String name = m_gtp.getName(); if (name == null) showError("MSG_ANALYZE_NOT_SUPPORTED", "MSG_ANALYZE_NOT_SUPPORTED_2", false); else showError("MSG_ANALYZE_NOT_SUPPORTED", "MSG_ANALYZE_NOT_SUPPORTED_3", false, name); return; } updateRecent(index); AnalyzeCommand command = new AnalyzeCommand(m_commands.get(index)); if (command.needsColorArg()) command.setColorArg(getSelectedColor()); String label = command.getResultTitle(); if (command.needsStringArg()) { String stringArg = JOptionPane.showInputDialog(this, label, i18n("TIT_INPUT"), JOptionPane.PLAIN_MESSAGE); if (stringArg == null) return; command.setStringArg(stringArg); } if (command.needsOptStringArg()) { command.setOptStringArg(""); String commandWithoutArg = command.replaceWildCards(m_selectedColor); try { String value = m_gtp.send(commandWithoutArg); Object optStringArg = JOptionPane.showInputDialog(this, label, i18n("TIT_INPUT"), JOptionPane.PLAIN_MESSAGE, null, null, value); if (optStringArg == null || optStringArg.equals(value)) return; command.setOptStringArg((String)optStringArg); } catch (GtpError e) { showError("MSG_ANALYZE_COMMAND_FAILED", e.getMessage(), false, commandWithoutArg); return; } } if (command.getType() == AnalyzeType.EPLIST) { command.setPointListArg(new PointList()); String commandWithoutArg = command.replaceWildCards(m_selectedColor) + " show"; try { String response = m_gtp.send(commandWithoutArg); ConstPointList pointList = GtpUtil.parsePointList(response, m_boardSize); command.setPointListArg(pointList); } catch (GtpError e) { showError("MSG_ANALYZE_COMMAND_FAILED", e.getMessage(), false, commandWithoutArg); return; } catch (GtpResponseFormatError e) { showError("MSG_ANALYZE_INVALID_RESPONSE", "MSG_ANALYZE_INVALID_RESPONSE_2", true, commandWithoutArg, e.getMessage()); return; } } if (command.needsFileArg()) { File fileArg = FileDialogs.showSelectFile(this, label); if (fileArg == null) return; command.setFileArg(fileArg); } if (command.needsFileOpenArg()) { File fileArg = FileDialogs.showOpen(this, label); if (fileArg == null) return; command.setFileOpenArg(fileArg); } if (command.needsFileSaveArg()) { File fileArg = FileDialogs.showSave(this, label, m_messageDialogs); if (fileArg == null) return; command.setFileSaveArg(fileArg); } if (command.needsColorArg()) command.setColorArg(getSelectedColor()); boolean autoRun = m_autoRun.isEnabled() && m_autoRun.isSelected(); boolean clearBoard = ! m_clearBoard.isEnabled() || m_clearBoard.isSelected(); boolean reuseWindow = m_reuseWindow.isEnabled() && m_reuseWindow.isSelected(); m_listener.actionSetAnalyzeCommand(command, autoRun, clearBoard, false, reuseWindow); } private void selectCommand(int index) { String label = m_commands.get(index).getLabel(); updateOptions(label); m_comboBoxHistory.removeActionListener(this); if (m_firstIsTemp && getComboBoxItemCount() > 0) m_comboBoxHistory.removeItemAt(0); if (getComboBoxItemCount() == 0 || ! getComboBoxItem(0).equals(label)) { insertComboBoxItem(label, 0); m_firstIsTemp = true; m_comboBoxHistory.setSelectedIndex(0); } m_comboBoxHistory.addActionListener(this); } private void selectColor() { if (m_selectedColor == BLACK) m_black.setSelected(true); else if (m_selectedColor == WHITE) m_white.setSelected(true); } private void showError(String mainMessage, String optionalMessage, boolean isCritical) { m_messageDialogs.showError(this, i18n(mainMessage), i18n(optionalMessage), isCritical); } private void showError(String mainMessage, String optionalMessage, boolean isCritical, Object... args) { optionalMessage = MessageFormat.format(i18n(optionalMessage), args); m_messageDialogs.showError(this, i18n(mainMessage), optionalMessage, isCritical); } private void updateOptions(String label) { if (label.equals(m_lastUpdateOptionsCommand)) return; m_lastUpdateOptionsCommand = label; int index = getCommandIndex(label); if (index < 0) return; AnalyzeCommand command = new AnalyzeCommand(m_commands.get(index)); boolean needsColorArg = command.needsColorArg(); m_black.setEnabled(needsColorArg); m_white.setEnabled(needsColorArg); m_autoRun.setEnabled(command.getType() != AnalyzeType.PARAM); m_autoRun.setSelected(false); m_clearBoard.setEnabled(command.getType() != AnalyzeType.PARAM); m_runButton.setEnabled(true); } private void updateRecent(int index) { String label = m_commands.get(index).getLabel(); insertComboBoxItem(label, 0); m_comboBoxHistory.setSelectedIndex(0); for (int i = 1; i < getComboBoxItemCount(); ++i) if (getComboBoxItem(i).equals(label)) m_comboBoxHistory.removeItemAt(i); m_firstIsTemp = false; } }