// 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;
}
}