// GoGui.java package net.sf.gogui.gogui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Frame; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.io.StringReader; import java.net.URL; import static java.text.MessageFormat.format; import java.util.ArrayList; import java.util.Locale; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; import java.util.regex.Pattern; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import net.sf.gogui.game.ConstClock; import net.sf.gogui.game.ConstGame; import net.sf.gogui.game.ConstGameInfo; import net.sf.gogui.game.ConstGameTree; import net.sf.gogui.game.ConstNode; import net.sf.gogui.game.Game; import net.sf.gogui.game.GameTree; import net.sf.gogui.game.GameInfo; import net.sf.gogui.game.MarkType; import net.sf.gogui.game.NodeUtil; import net.sf.gogui.game.StringInfo; import net.sf.gogui.game.StringInfoColor; import net.sf.gogui.game.TimeSettings; import net.sf.gogui.gamefile.GameFile; import net.sf.gogui.gamefile.GameReader; import net.sf.gogui.gamefile.GameWriter; import net.sf.gogui.go.Board; import net.sf.gogui.go.BoardUtil; import net.sf.gogui.go.ConstBoard; import net.sf.gogui.go.ConstPointList; import net.sf.gogui.go.CountScore; 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.InvalidKomiException; import net.sf.gogui.go.Komi; import net.sf.gogui.go.Move; import net.sf.gogui.go.PointList; import net.sf.gogui.go.Score; import net.sf.gogui.go.Score.ScoringMethod; import static net.sf.gogui.gogui.I18n.i18n; import net.sf.gogui.gtp.AnalyzeCommand; import net.sf.gogui.gtp.AnalyzeDefinition; import net.sf.gogui.gtp.AnalyzeType; import net.sf.gogui.gtp.AnalyzeUtil; import net.sf.gogui.gtp.GtpClient; import net.sf.gogui.gtp.GtpClientUtil; import net.sf.gogui.gtp.GtpCommand; import net.sf.gogui.gtp.GtpError; import net.sf.gogui.gtp.GtpResponseFormatError; import net.sf.gogui.gtp.GtpSynchronizer; import net.sf.gogui.gtp.GtpUtil; import net.sf.gogui.gui.AnalyzeDialog; import net.sf.gogui.gui.AnalyzeShow; import net.sf.gogui.gui.BoardSizeDialog; import net.sf.gogui.gui.Bookmark; import net.sf.gogui.gui.BookmarkEditor; import net.sf.gogui.gui.Comment; import net.sf.gogui.gui.ConstGuiBoard; import net.sf.gogui.gui.ContextMenu; import net.sf.gogui.gui.FindDialog; import net.sf.gogui.gui.GameInfoDialog; import net.sf.gogui.gui.GameInfoPanel; import net.sf.gogui.gui.GameTreePanel; import net.sf.gogui.gui.GameTreeViewer; import net.sf.gogui.gui.GtpShell; import net.sf.gogui.gui.GuiAction; import net.sf.gogui.gui.GuiBoard; import net.sf.gogui.gui.GuiBoardUtil; import net.sf.gogui.gui.GuiGtpClient; import net.sf.gogui.gui.GuiUtil; import net.sf.gogui.gui.FileDialogs; import net.sf.gogui.gui.Help; import net.sf.gogui.gui.LiveGfx; import net.sf.gogui.gui.MessageDialogs; import net.sf.gogui.gui.ObjectListEditor; import net.sf.gogui.gui.ParameterDialog; import net.sf.gogui.gui.Program; import net.sf.gogui.gui.ProgramEditor; import net.sf.gogui.gui.RecentFileMenu; import net.sf.gogui.gui.Session; import net.sf.gogui.gui.ScoreDialog; import net.sf.gogui.gui.StatusBar; import net.sf.gogui.gui.TimeLeftDialog; import net.sf.gogui.sgf.SgfError; import net.sf.gogui.sgf.SgfReader; import net.sf.gogui.sgf.SgfWriter; import net.sf.gogui.tex.TexWriter; import net.sf.gogui.text.TextParser; import net.sf.gogui.text.ParseError; import net.sf.gogui.thumbnail.ThumbnailCreator; import net.sf.gogui.thumbnail.ThumbnailPlatform; import net.sf.gogui.util.ErrorMessage; import net.sf.gogui.util.FileUtil; import net.sf.gogui.util.ObjectUtil; import net.sf.gogui.util.LineReader; import net.sf.gogui.util.Platform; import net.sf.gogui.util.ProgressShow; import net.sf.gogui.util.StringUtil; import net.sf.gogui.version.Version; /** Graphical user interface to a Go program. */ public class GoGui extends JFrame implements AnalyzeDialog.Listener, GuiBoard.Listener, GameTreeViewer.Listener, GtpShell.Listener, ScoreDialog.Listener, GoGuiMenuBar.Listener, ContextMenu.Listener, LiveGfx.Listener { public enum ShowVariations { CHILDREN, SIBLINGS, NONE } public GoGui(String program, File file, int move, String time, boolean verbose, boolean initComputerColor, boolean computerBlack, boolean computerWhite, boolean auto, boolean register, String gtpFile, String gtpCommand, File analyzeCommandsFile) throws GtpError, ErrorMessage { int boardSize = m_prefs.getInt("boardsize", GoPoint.DEFAULT_SIZE); m_beepAfterMove = m_prefs.getBoolean("beep-after-move", true); m_initialFile = file; m_gtpFile = gtpFile; m_gtpCommand = gtpCommand; m_analyzeCommandsFile = analyzeCommandsFile; m_move = move; m_register = register; if (initComputerColor) { m_computerBlack = computerBlack; m_computerWhite = computerWhite; } else if (m_prefs.getBoolean("computer-none", false)) { m_computerBlack = false; m_computerWhite = false; } else { m_computerBlack = false; m_computerWhite = true; } m_auto = auto; m_verbose = verbose; m_showInfoPanel = true; m_showToolbar = false; Container contentPane = getContentPane(); m_innerPanel = new JPanel(new BorderLayout()); contentPane.add(m_innerPanel, BorderLayout.CENTER); m_toolBar = new GoGuiToolBar(this); m_infoPanel = new JPanel(new BorderLayout()); m_game = new Game(boardSize); m_gameInfoPanel = new GameInfoPanel(m_game); m_gameInfoPanel.setBorder(GuiUtil.createSmallEmptyBorder()); m_infoPanel.add(m_gameInfoPanel, BorderLayout.NORTH); m_guiBoard = new GuiBoard(boardSize); m_showAnalyzeText = new ShowAnalyzeText(this, m_guiBoard); m_statusBar = new StatusBar(); m_innerPanel.add(m_statusBar, BorderLayout.SOUTH); Comment.Listener commentListener = new Comment.Listener() { public void changed(String comment) { m_game.setComment(comment); // Cannot call updateViews, which calls // Comment.setComment(), in comment callback SwingUtilities.invokeLater(new Runnable() { public void run() { updateViews(false); } }); } public void textSelected(String text) { GoGui.this.textSelected(text); } }; m_comment = new Comment(commentListener); boolean monoFont = m_prefs.getBoolean("comment-font-fixed", false); m_comment.setMonoFont(monoFont); m_infoPanel.add(m_comment, BorderLayout.CENTER); m_splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, m_guiBoard, m_infoPanel); m_splitPane.setResizeWeight(1); m_innerPanel.add(m_splitPane, BorderLayout.CENTER); addWindowListener(new WindowAdapter() { public void windowActivated(WindowEvent e) { m_guiBoard.requestFocusInWindow(); } public void windowClosing(WindowEvent event) { close(); } }); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); GuiUtil.setGoIcon(this); RecentFileMenu.Listener recentListener = new RecentFileMenu.Listener() { public void fileSelected(String label, File file) { actionOpenFile(file); } }; RecentFileMenu.Listener recentGtp = new RecentFileMenu.Listener() { public void fileSelected(String label, File file) { actionSendFile(file); } }; m_menuBar = new GoGuiMenuBar(m_actions, recentListener, recentGtp, this); // enums are stored as int's for compatibility with earlier versions // of GoGui try { m_treeLabels = GameTreePanel.Label.values()[ m_prefs.getInt("gametree-labels", GameTreePanel.Label.NUMBER.ordinal())]; } catch (ArrayIndexOutOfBoundsException e) { m_treeLabels = GameTreePanel.Label.NUMBER; } try { m_treeSize = GameTreePanel.Size.values()[ m_prefs.getInt("gametree-size", GameTreePanel.Size.NORMAL.ordinal())]; } catch (ArrayIndexOutOfBoundsException e) { m_treeSize = GameTreePanel.Size.NORMAL; } try { m_showVariations = ShowVariations.values()[ m_prefs.getInt("show-variations", ShowVariations.CHILDREN.ordinal())]; } catch (ArrayIndexOutOfBoundsException e) { m_showVariations = ShowVariations.CHILDREN; } m_showSubtreeSizes = m_prefs.getBoolean("gametree-show-subtree-sizes", false); m_autoNumber = m_prefs.getBoolean("gtpshell-autonumber", false); m_commandCompletion = ! m_prefs.getBoolean("gtpshell-disable-completions", false); m_timeStamp = m_prefs.getBoolean("gtpshell-timestamp", false); m_showLastMove = m_prefs.getBoolean("show-last-move", true); m_showMoveNumbers = m_prefs.getBoolean("show-move-numbers", false); boolean showCursor = m_prefs.getBoolean("show-cursor", false); boolean showGrid = m_prefs.getBoolean("show-grid", false); m_guiBoard.setShowCursor(showCursor); m_guiBoard.setShowGrid(showGrid); setJMenuBar(m_menuBar); setMinimumSize(); m_programCommand = program; if (m_programCommand != null && m_programCommand.trim().equals("")) m_programCommand = null; if (time != null) m_timeSettings = TimeSettings.parse(time); protectGui(); // Show wait cursor SwingUtilities.invokeLater(new Runnable() { public void run() { initialize(); } }); } public void actionAbout() { String command = null; if (m_gtp != null) command = m_gtp.getProgramCommand(); AboutDialog.show(this, getProgramLabel(), m_version, command, m_messageDialogs); } public void actionAddBookmark() { if (m_gameFile == null) { showError(i18n("MSG_CANNOT_SET_BOOKMARK_NO_FILE"), i18n("MSG_CANNOT_SET_BOOKMARK_NO_FILE_2"), false); return; } if (isModified()) { showError(i18n("MSG_CANNOT_SET_BOOKMARK_MODIFIED"), i18n("MSG_CANNOT_SET_BOOKMARK_MODIFIED_2"), false); return; } if (getCurrentNode().getFatherConst() != null && getCurrentNode().getMove() == null) { showError(i18n("MSG_CANNOT_SET_BOOKMARK_NODE"), i18n("MSG_CANNOT_SET_BOOKMARK_NODE_2"), false); return; } String variation = NodeUtil.getVariationString(getCurrentNode()); int move = NodeUtil.getMoveNumber(getCurrentNode()); Bookmark bookmark = new Bookmark(m_gameFile.m_file, move, variation); BookmarkEditor editor = new BookmarkEditor(); bookmark = editor.editItem(this, i18n("TIT_ADD_BOOKMARK"), bookmark, true, m_messageDialogs); if (bookmark == null) return; m_bookmarks.add(bookmark); m_menuBar.setBookmarks(m_bookmarks); Bookmark.save(m_bookmarks); } public void actionAttachProgram(int index) { m_prefs.putInt("program", index); actionAttachProgram(m_programs.get(index)); } public void actionAttachProgram(final Program program) { if (! checkCommandInProgress()) return; protectGui(); Runnable runnable = new Runnable() { public void run() { try { attachNewProgram(program.m_command, program); } finally { unprotectGui(); } } }; SwingUtilities.invokeLater(runnable); } public void actionBackToMainVariation() { if (! checkStateChangePossible()) return; ConstNode node = NodeUtil.getBackToMainVariation(getCurrentNode()); actionGotoNode(node); } public void actionBackward(int n) { if (! checkStateChangePossible()) return; boolean protectGui = (m_gtp != null && (n > 1 || ! m_gtp.isSupported("undo"))); actionGotoNode(NodeUtil.backward(getCurrentNode(), n), protectGui); } public void actionBeginning() { if (! checkStateChangePossible()) return; actionBackward(NodeUtil.getDepth(getCurrentNode())); } public void actionBoardSize(int size) { if (! checkCommandInProgress()) return; actionNewGame(size); m_prefs.putInt("boardsize", size); } public void actionBoardSizeOther() { if (! checkCommandInProgress()) return; int size = BoardSizeDialog.show(this, getBoardSize(), m_messageDialogs); if (size < 1 || size > GoPoint.MAX_SIZE) return; actionBoardSize(size); } public void actionClearAnalyzeCommand() { if (! checkCommandInProgress()) return; clearAnalyzeCommand(); } public void actionClockHalt() { if (! getClock().isRunning()) return; m_game.haltClock(); updateViews(false); } public void actionClockResume() { if (getClock().isRunning()) return; m_game.resumeClock(); updateViews(false); } public void actionClockStart() { if (getClock().isRunning()) return; m_game.startClock(); updateViews(false); } public void actionComputerColor(boolean isBlack, boolean isWhite) { boolean computerNone = (! isBlack && ! isWhite); m_prefs.putBoolean("computer-none", computerNone); m_computerBlack = isBlack; m_computerWhite = isWhite; if (! isCommandInProgress()) checkComputerMove(); updateViews(false); } public void actionDeleteSideVariations() { if (! checkStateChangePossible()) return; if (! NodeUtil.isInMainVariation(getCurrentNode())) return; String disableKey = "net.sf.gogui.gogui.GoGui.delete-side-variations"; if (! m_messageDialogs.showQuestion(disableKey, this, i18n("MSG_DELETE_VARIATIONS"), i18n("MSG_DELETE_VARIATIONS_2"), i18n("LB_DELETE"), false)) return; m_game.keepOnlyMainVariation(); boardChangedBegin(false, true); } public void actionDetachProgram() { if (m_gtp == null) return; if (isCommandInProgress() && ! showQuestion(format(i18n("MSG_TERMINATE_COMMAND_IN_PROGRESS"), getProgramLabel()), i18n("MSG_TERMINATE_COMMAND_IN_PROGRESS_2"), i18n("LB_TERMINATE"), true)) return; m_prefs.putInt("program", -1); protectGui(); Runnable runnable = new Runnable() { public void run() { try { saveSession(); detachProgram(); updateViews(false); } finally { unprotectGui(); } } }; SwingUtilities.invokeLater(runnable); } public void actionDisposeAnalyzeDialog() { if (m_analyzeDialog != null) { clearAnalyzeCommand(); saveSession(); m_analyzeDialog.dispose(); m_analyzeDialog = null; } } public void actionDisposeTree() { if (m_gameTreeViewer != null) { saveSession(); m_gameTreeViewer.dispose(); m_gameTreeViewer = null; updateViews(false); } } public void actionHelp() { ClassLoader classLoader = getClass().getClassLoader(); URL url = classLoader.getResource("net/sf/gogui/doc/index.html"); if (url == null) { showError(i18n("MSG_HELP_NOT_FOUND"), ""); return; } if (m_help == null) { m_help = new Help(url, m_messageDialogs, i18n("TIT_HELP") + " - " + i18n("LB_GOGUI")); m_session.restoreSize(m_help.getWindow(), "help"); } m_help.getWindow().setVisible(true); m_help.getWindow().toFront(); } public void actionEditBookmarks() { BookmarkEditor editor = new BookmarkEditor(); ObjectListEditor<Bookmark> listEditor = new ObjectListEditor<Bookmark>(); if (! listEditor.edit(this, i18n("TIT_EDIT_BOOKMARKS"), m_bookmarks, editor, m_messageDialogs)) return; m_menuBar.setBookmarks(m_bookmarks); Bookmark.save(m_bookmarks); } public void actionEditLabel(GoPoint point) { String value = getCurrentNode().getLabel(point); Object message = format(i18n("MSG_EDIT_LABEL"), point); value = (String)JOptionPane.showInputDialog(this, message, i18n("TIT_EDIT_LABEL"), JOptionPane.PLAIN_MESSAGE, null, null, value); if (value == null) return; m_game.setLabel(point, value); m_guiBoard.setLabel(point, value); updateViews(false); } public void actionEditPrograms() { ProgramEditor editor = new ProgramEditor(); ObjectListEditor<Program> listEditor = new ObjectListEditor<Program>(); if (! listEditor.edit(this, i18n("TIT_EDIT_PROGRAMS"), m_programs, editor, m_messageDialogs)) return; m_menuBar.setPrograms(m_programs); m_prefs.putInt("program", -1); Program.save(m_programs); } public void actionEnd() { actionForward(NodeUtil.getNodesLeft(getCurrentNode())); } public void actionExportLatexMainVariation() { File file = showSave(i18n("TIT_EXPORT_LATEX")); if (file == null) return; try { OutputStream out = new FileOutputStream(file); String title = FileUtil.removeExtension(new File(file.getName()), "tex"); new TexWriter(title, out, getTree()); } catch (FileNotFoundException e) { showError(i18n("MSG_EXPORT_FAILED"), e); } } public void actionExportLatexPosition() { File file = showSave(i18n("TIT_EXPORT_LATEX_POSITION")); if (file == null) return; try { OutputStream out = new FileOutputStream(file); String title = FileUtil.removeExtension(new File(file.getName()), "tex"); new TexWriter(title, out, getBoard(), GuiBoardUtil.getLabels(m_guiBoard), GuiBoardUtil.getMark(m_guiBoard), GuiBoardUtil.getMarkTriangle(m_guiBoard), GuiBoardUtil.getMarkCircle(m_guiBoard), GuiBoardUtil.getMarkSquare(m_guiBoard), GuiBoardUtil.getSelects(m_guiBoard)); } catch (FileNotFoundException e) { showError(i18n("MSG_EXPORT_FAILED"), e); } } public void actionExportSgfPosition() { File file = FileDialogs.showSaveSgf(this, m_messageDialogs); if (file == null) return; try { savePosition(file); } catch (FileNotFoundException e) { showError(i18n("MSG_EXPORT_FAILED"), e); } } public void actionExportTextPosition() { File file = showSave(i18n("MSG_EXPORT_TEXT")); if (file == null) return; try { String text = BoardUtil.toString(getBoard(), false, false); PrintStream out = new PrintStream(file); out.print(text); out.close(); } catch (FileNotFoundException e) { showError(i18n("MSG_EXPORT_FAILED"), e); } } public void actionExportPng() { ExportPng.run(this, m_guiBoard, m_prefs, m_messageDialogs); } public void actionExportTextPositionToClipboard() { GuiUtil.copyToClipboard(BoardUtil.toString(getBoard(), false, false)); } public void actionFind() { if (! checkStateChangePossible()) return; Pattern pattern = FindDialog.run(this, m_comment.getSelectedText(), m_messageDialogs); if (pattern == null) return; m_pattern = pattern; if (NodeUtil.commentContains(getCurrentNode(), m_pattern)) m_comment.markAll(m_pattern); else actionFindNext(); } public void actionFindNext() { if (! checkStateChangePossible()) return; if (m_pattern == null) return; protectGui(); showStatus(i18n("STAT_FIND_SEARCHING_COMMENTS")); Runnable runnable = new Runnable() { public void run() { try { ConstNode root = getTree().getRootConst(); ConstNode currentNode = getCurrentNode(); ConstNode node = NodeUtil.findInComments(currentNode, m_pattern); boolean cancel = false; if (node == null && getCurrentNode() != root) { unprotectGui(); if (showQuestion(i18n("MSG_FIND_CONTINUE"), i18n("MSG_FIND_CONTINUE_2"), i18n("LB_FIND_CONTINUE"), false)) { protectGui(); node = root; if (! NodeUtil.commentContains(node, m_pattern)) node = NodeUtil.findInComments(node, m_pattern); } else cancel = true; } if (! cancel) { if (node == null) { unprotectGui(); showInfo(i18n("MSG_FIND_NOT_FOUND"), format(i18n("MSG_FIND_NOT_FOUND_2"), m_pattern), false); m_pattern = null; } else { gotoNode(node); boardChangedBegin(false, false); m_comment.markAll(m_pattern); } } } finally { unprotectGui(); clearStatus(); } } }; SwingUtilities.invokeLater(runnable); } public void actionFindNextComment() { if (! checkStateChangePossible()) return; protectGui(); showStatus(i18n("STAT_FIND_SEARCHING_COMMENTS")); Runnable runnable = new Runnable() { public void run() { try { ConstNode root = getTree().getRootConst(); ConstNode currentNode = getCurrentNode(); ConstNode node = NodeUtil.findNextComment(currentNode); boolean cancel = false; if (node == null && getCurrentNode() != root) { unprotectGui(); if (showQuestion(i18n("MSG_FIND_CONTINUE"), i18n("MSG_FIND_CONTINUE_2"), i18n("LB_FIND_CONTINUE"), false)) { protectGui(); node = root; if (! node.hasComment()) node = NodeUtil.findNextComment(node); } else cancel = true; } if (! cancel) { if (node == null) { unprotectGui(); showInfo(i18n("MSG_FIND_NO_COMMENT_FOUND"), null, false); } else { gotoNode(node); boardChangedBegin(false, false); } } } finally { unprotectGui(); clearStatus(); } } }; SwingUtilities.invokeLater(runnable); } public void actionForward(int n) { if (! checkStateChangePossible()) return; boolean protectGui = (m_gtp != null && n > 1); actionGotoNode(NodeUtil.forward(getCurrentNode(), n), protectGui); } public void actionGameInfo() { if (! checkCommandInProgress()) // Changes in game info may send GTP commands return; ConstNode node = m_game.getGameInfoNode(); GameInfo info = new GameInfo(node.getGameInfoConst()); GameInfoDialog.show(this, info, m_messageDialogs); m_game.setGameInfo(info, node); currentNodeChanged(); // updates komi, time settings Komi prefsKomi = getPrefsKomi(); Komi komi = info.getKomi(); if (komi != null && ! komi.equals(prefsKomi) && info.getHandicap() == 0) m_prefs.put("komi", komi.toString()); if (info.getTimeSettings() != null && ! info.getTimeSettings().equals(m_timeSettings)) { TimeSettings timeSettings = info.getTimeSettings(); m_game.setTimeSettings(timeSettings); m_timeSettings = timeSettings; } setTitle(); updateViews(false); } public void actionGotoBookmark(int i) { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; if (i < 0 || i >= m_bookmarks.size()) return; Bookmark bookmark = m_bookmarks.get(i); if (! loadFile(bookmark.m_file, -1)) return; updateViews(true); String variation = bookmark.m_variation; ConstNode node = getTree().getRootConst(); if (! variation.equals("")) { node = NodeUtil.findByVariation(node, variation); if (node == null) { showError(i18n("MSG_BOOKMARK_INVALID_VARIATION"), ""); return; } } node = NodeUtil.findByMoveNumber(node, bookmark.m_move); if (node == null) { showError(i18n("MSG_BOOKMARK_INVALID_MOVE_NUMBER"), ""); return; } actionGotoNode(node); } public void actionGotoMove() { if (! checkStateChangePossible()) return; ConstNode node = MoveNumberDialog.show(this, getCurrentNode(), m_messageDialogs); if (node == null) return; actionGotoNode(node); } public void actionGotoNode(ConstNode node) { boolean protectGui = (m_gtp != null); actionGotoNode(node, protectGui); } private void actionGotoNode(final ConstNode node, final boolean protectGui) { if (! checkStateChangePossible()) return; if (protectGui) protectGui(); Runnable runnable = new Runnable() { public void run() { gotoNode(node); boardChangedBegin(false, false); if (protectGui) unprotectGui(); } }; if (protectGui) SwingUtilities.invokeLater(runnable); else runnable.run(); } public void actionGotoVariation() { if (! checkStateChangePossible()) return; ConstNode node = GotoVariationDialog.show(this, getTree(), getCurrentNode(), m_messageDialogs); if (node == null) return; actionGotoNode(node); } public void actionHandicap(int handicap) { if (! checkCommandInProgress()) return; m_handicap = handicap; if (isModified()) showInfo(i18n("MSG_HANDICAP_NEXT_GAME"), i18n("MSG_HANDICAP_NEXT_GAME_2"), true); else { m_computerBlack = false; m_computerWhite = false; newGame(getBoardSize()); updateViews(true); } } public void actionImportSgfFromClipboard() { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; String text = GuiUtil.getClipboardText(); if (text == null) { showError(i18n("MSG_NO_TEXT_IN_CLIPBOARD"), "", false); return; } ByteArrayInputStream in = new ByteArrayInputStream(text.getBytes()); try { SgfReader reader = new SgfReader(in, null, null, 0); GameTree tree = reader.getTree(); m_game.init(tree); } catch (SgfError e) { showError(i18n("MSG_IMPORT_FAILED"), e); } m_guiBoard.initSize(getBoard().getSize()); initGtp(); m_computerBlack = false; m_computerWhite = false; boardChangedBegin(false, true); } public void actionImportTextPosition() { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; File file = FileDialogs.showOpen(this, i18n("TIT_IMPORT_TEXT")); if (file == null) return; try { importTextPosition(new FileReader(file)); } catch (FileNotFoundException e) { showError(i18n("MSG_FILE_NOT_FOUND"), "", false); } } public void actionImportTextPositionFromClipboard() { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; String text = GuiUtil.getClipboardText(); if (text == null) showError(i18n("MSG_NO_TEXT_IN_CLIPBOARD"), "", false); else importTextPosition(new StringReader(text)); } public void actionInterrupt() { if (m_gtp == null || m_gtp.isProgramDead() || ! isCommandInProgress()) return; if (m_interrupt.run(this, m_gtp, m_messageDialogs)) { showStatus(i18n("STAT_INTERRUPT")); m_interruptComputerBoth = true; } } public void actionKeepOnlyPosition() { if (! checkStateChangePossible()) return; if (! showQuestion(i18n("MSG_KEEP_ONLY_POSITION"), i18n("MSG_KEEP_ONLY_POSITION_2"), i18n("LB_DELETE"), true)) return; m_game.keepOnlyPosition(); initGtp(); boardChangedBegin(false, true); } public void actionMainWindowActivate() { requestFocus(); } public void actionMakeMainVariation() { if (! checkStateChangePossible()) return; String disableKey = "net.sf.gogui.gogui.GoGui.make-main-variation"; if (! m_messageDialogs.showQuestion(disableKey, this, i18n("MSG_MAKE_MAIN_VAR"), i18n("MSG_MAKE_MAIN_VAR_2"), i18n("LB_MAKE_MAIN_VAR"), false)) return; m_game.makeMainVariation(); boardChangedBegin(false, true); } public void actionMark(GoPoint point, MarkType type, boolean mark) { if (mark) m_game.addMarked(point, type); else m_game.removeMarked(point, type); if (type == MarkType.MARK) m_guiBoard.setMark(point, mark); else if (type == MarkType.CIRCLE) m_guiBoard.setMarkCircle(point, mark); else if (type == MarkType.SQUARE) m_guiBoard.setMarkSquare(point, mark); else if (type == MarkType.TRIANGLE) m_guiBoard.setMarkTriangle(point, mark); updateViews(false); } public void actionNewGame() { actionNewGame(getBoardSize()); } public void actionNewGame(int size) { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; setFile(null); newGame(size); if (m_gtp != null && ! m_gtp.isGenmoveSupported()) { m_computerBlack = false; m_computerWhite = false; } else if (m_computerBlack || m_computerWhite) { // Set computer color to the color not to move to avoid automatic // move generation after starting a new game if (m_handicap == 0) { m_computerBlack = false; m_computerWhite = true; } else { m_computerBlack = true; m_computerWhite = false; } } boardChangedBegin(true, true); } public void actionNewProgram() { m_newProgram = new Program("", "", "", "", ""); final ProgramEditor editor = new ProgramEditor(); m_newProgram = editor.editItem(this, i18n("TIT_NEW_PROGRAM"), m_newProgram, true, false, m_messageDialogs); if (m_newProgram == null) return; protectGui(); SwingUtilities.invokeLater(new Runnable() { public void run() { attachNewProgram(m_newProgram.m_command, m_newProgram); unprotectGui(); if (m_gtp == null || m_gtp.isProgramDead()) { m_newProgram = editor.editItem(GoGui.this, i18n("TIT_NEW_PROGRAM"), m_newProgram, true, false, m_messageDialogs); if (m_newProgram == null) return; SwingUtilities.invokeLater(this); return; } m_newProgram.m_name = m_gtp.getLabel(); m_newProgram.m_version = m_version; m_newProgram.setUniqueLabel(m_programs); m_newProgram = editor.editItem(GoGui.this, i18n("TIT_NEW_PROGRAM"), m_newProgram, false, true, m_messageDialogs); if (m_newProgram == null) { actionDetachProgram(); return; } m_programs.add(m_newProgram); m_program = m_newProgram; m_prefs.putInt("program", m_programs.size() - 1); m_menuBar.setPrograms(m_programs); Program.save(m_programs); updateViews(false); } }); } public void actionNextEarlierVariation() { if (! checkStateChangePossible()) return; ConstNode node = NodeUtil.getNextEarlierVariation(getCurrentNode()); if (node != null) actionGotoNode(node); } public void actionNextVariation() { if (! checkStateChangePossible()) return; ConstNode node = NodeUtil.getNextVariation(getCurrentNode()); if (node != null) actionGotoNode(node); } public void actionOpen() { if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; File file = FileDialogs.showOpenSgf(this); if (file == null) return; actionOpenFile(file); } public void actionOpenFile(final File file) { if (file == null) return; if (! checkStateChangePossible()) return; if (! checkSaveGame()) return; final boolean protectGui = (m_gtp != null); if (protectGui) protectGui(); SwingUtilities.invokeLater(new Runnable() { public void run() { loadFile(file, -1); boardChangedBegin(false, true); if (protectGui) unprotectGui(); } }); } public void actionPass() { if (! checkStateChangePossible()) return; if (! showOptionalQuestion("pass", i18n("MSG_PASS"), i18n("MSG_PASS_2"), i18n("LB_PASS"), false)) return; humanMoved(Move.getPass(getToMove())); } public void actionPlay(boolean isSingleMove) { if (! checkStateChangePossible()) return; if (! synchronizeProgram()) return; if (! isSingleMove && ! isComputerBoth()) { m_computerBlack = false; m_computerWhite = false; if (getToMove() == BLACK) m_computerBlack = true; else m_computerWhite = true; } generateMove(isSingleMove); m_game.startClock(); } public void actionPreviousEarlierVariation() { if (! checkStateChangePossible()) return; ConstNode node = NodeUtil.getPreviousEarlierVariation(getCurrentNode()); if (node != null) actionGotoNode(node); } public void actionPreviousVariation() { if (! checkStateChangePossible()) return; ConstNode node = NodeUtil.getPreviousVariation(getCurrentNode()); if (node != null) actionGotoNode(node); } public void actionPrint() { Print.run(this, m_guiBoard, m_messageDialogs); } public void actionReattachProgram() { if (m_gtp == null) return; if (! checkCommandInProgress()) return; protectGui(); Runnable runnable = new Runnable() { public void run() { try { attachNewProgram(m_programCommand, m_program); } finally { unprotectGui(); } } }; SwingUtilities.invokeLater(runnable); } public void actionReattachWithParameters() { if (m_gtp == null) return; if (! checkCommandInProgress()) return; final boolean fromSnapshot = (isProgramDead() && m_parameterSnapshot != null); if (! fromSnapshot) { if (! checkHasParameterCommands()) return; } protectGui(); Runnable runnable = new Runnable() { public void run() { try { File file; if (fromSnapshot) file = m_parameterSnapshot; else { try { file = File.createTempFile("gogui-param", ".gtp"); } catch (IOException e) { showError(i18n("MSG_PARAM_TMP_FILE_ERROR"), e); return; } if (! saveParameters(file)) return; } if (! attachNewProgram(m_programCommand, m_program)) return; sendGtpFile(file); } finally { unprotectGui(); } } }; SwingUtilities.invokeLater(runnable); } public void actionRestoreParameters() { if (m_gtp == null) return; if (! checkCommandInProgress()) return; if (m_parameterSnapshot == null) return; sendGtpFile(m_parameterSnapshot); } public void actionSave() { if (! isModified()) return; if (m_gameFile == null) actionSaveAs(); else { File file = m_gameFile.m_file; if (file.exists()) { String mainMessage = format(i18n("MSG_REPLACE_FILE"), file.getName()); String optionalMessage = i18n("MSG_REPLACE_FILE_2"); String disableKey = "net.sf.gogui.GoGui.overwrite"; if (! m_messageDialogs.showQuestion(disableKey, this, mainMessage, optionalMessage, i18n("LB_REPLACE_FILE"), true)) return; } save(m_gameFile); } updateViews(false); } public void actionSaveAs() { saveDialog(); updateViews(false); } public void actionSaveCommands() { if (m_shell == null) return; m_shell.saveCommands(this); } public void actionSaveLog() { if (m_shell == null) return; m_shell.saveLog(this); } public void actionSaveParameters() { if (m_gtp == null) return; if (! checkHasParameterCommands()) return; File file = showSave(i18n("TIT_SAVE_PARAM")); if (file == null) return; saveParameters(file); } public void actionSnapshotParameters() { if (m_gtp == null) return; if (! checkCommandInProgress()) return; if (! checkHasParameterCommands()) return; if (m_parameterSnapshot == null) try { m_parameterSnapshot = File.createTempFile("gogui-param", ".gtp"); } catch (IOException e) { showError(i18n("MSG_PARAM_TMP_FILE_ERROR"), e); return; } saveParameters(m_parameterSnapshot); updateViews(false); } public void actionScore() { if (m_scoreMode) return; if (! checkStateChangePossible()) return; boolean programReady = (m_gtp != null && synchronizeProgram()); if (m_gtp == null || ! programReady) { String disableKey = "net.sf.gogui.gogui.GoGui.score-no-program"; String optionalMessage; if (m_gtp == null) optionalMessage = "MSG_SCORE_NO_PROGRAM"; else optionalMessage = "MSG_SCORE_CANNOT_USE_PROGRAM"; m_messageDialogs.showInfo(disableKey, this, i18n("MSG_SCORE_MANUAL"), i18n(optionalMessage), true); updateViews(false); initScore(null); return; } if (m_gtp.isSupported("final_status_list")) { Runnable callback = new Runnable() { public void run() { scoreContinue(); } }; runLengthyCommand("final_status_list dead", callback); } else { String disableKey = "net.sf.gogui.gogui.GoGui.score-not-supported"; String optionalMessage; String name = getProgramName(); optionalMessage = format(i18n("MSG_SCORE_NO_SUPPORT"), name); m_messageDialogs.showInfo(disableKey, this, i18n("MSG_SCORE_MANUAL"), optionalMessage, true); updateViews(false); initScore(null); } } public void actionScoreDone(Score score) { if (! m_scoreMode) return; scoreDone(score); updateViews(false); } public void actionSendCommand(String command, final boolean isCritical, final boolean showError) { if (! checkCommandInProgress()) return; if (GtpUtil.isStateChangingCommand(command)) { showError(i18n("MSG_BOARD_CHANGING_COMMAND"), "", false); return; } if (! synchronizeProgram()) return; Runnable callback = new Runnable() { public void run() { endLengthyCommand(isCritical, showError); } }; m_gtp.send(command, callback); beginLengthyCommand(); } public void actionSendFile() { if (! checkStateChangePossible()) return; if (m_shell == null) return; File file = FileDialogs.showOpen(this, i18n("TIT_CHOOSE_GTP_FILE")); if (file == null) return; actionSendFile(file); } public void actionSendFile(File file) { if (file == null) return; if (! checkStateChangePossible()) return; if (m_shell == null) return; if (! synchronizeProgram()) return; sendGtpFile(file); m_menuBar.addRecentGtp(file); updateViews(false); } public void actionSetAnalyzeCommand(AnalyzeCommand command) { actionSetAnalyzeCommand(command, false, true, true, false); } public void actionSetAnalyzeCommand(AnalyzeCommand command, boolean autoRun, boolean clearBoard, boolean oneRunOnly, boolean reuseTextWindow) { if (! synchronizeProgram()) return; if (! checkStateChangePossible()) return; initAnalyzeCommand(command, autoRun, clearBoard, reuseTextWindow); m_analyzeOneRunOnly = oneRunOnly; boolean needsPointArg = m_analyzeCommand.needsPointArg(); if (needsPointArg && ! m_analyzeCommand.isPointArgMissing()) { m_guiBoard.clearAllSelect(); m_guiBoard.setSelect(m_analyzeCommand.getPointArg(), true); } else if (needsPointArg || m_analyzeCommand.needsPointListArg()) { m_guiBoard.clearAllSelect(); if (m_analyzeCommand.getType() == AnalyzeType.EPLIST) GuiBoardUtil.setSelect(m_guiBoard, m_analyzeCommand.getPointListArg(), true); toFront(); return; } analyzeBegin(false); } public void actionSetShowVariations(ShowVariations mode) { m_showVariations = mode; m_prefs.putInt("show-variations", m_showVariations.ordinal()); resetBoard(); updateViews(false); } public void actionSetTimeLeft() { TimeLeftDialog.show(this, m_game, getCurrentNode(), m_messageDialogs); updateViews(false); } public void actionSetup(GoColor color) { assert color.isBlackWhite(); if (! checkCommandInProgress()) return; if (m_scoreMode) scoreDone(null); ConstNode node = getCurrentNode(); if (m_setupMode) { if (color == m_setupColor) { setupDone(); boardChangedBegin(false, true); } else m_setupColor = color; updateViews(false); } else { resetBoard(); m_setupMode = true; m_setupColor = color; m_setupNodeCreated = (node.getMove() != null || node.hasChildren()); if (m_setupNodeCreated) { m_game.createNewChild(); currentNodeChanged(); updateViews(true); } else updateViews(false); } if (m_setupMode) { if (m_setupColor == BLACK) showStatus(i18n("STAT_SETUP_BLACK")); else showStatus(i18n("STAT_SETUP_WHITE")); } } public void actionShowAnalyzeDialog() { if (m_gtp == null) return; if (m_analyzeDialog == null) createAnalyzeDialog(); else m_analyzeDialog.toFront(); } public void actionShowShell() { showShell(); } public void actionShowTree() { if (m_gameTreeViewer == null) { createTree(); updateViews(false); } else m_gameTreeViewer.toFront(); } public void actionToggleBeepAfterMove() { m_beepAfterMove = ! m_beepAfterMove; m_prefs.putBoolean("beep-after-move", m_beepAfterMove); } public void actionToggleAutoNumber() { m_autoNumber = ! m_autoNumber; if (m_gtp != null) m_gtp.setAutoNumber(m_autoNumber); } public void actionToggleCommentMonoFont() { boolean monoFont = ! m_comment.getMonoFont(); m_comment.setMonoFont(monoFont); m_prefs.putBoolean("comment-font-fixed", monoFont); } public void actionToggleCompletion() { m_commandCompletion = ! m_commandCompletion; if (m_shell != null) m_shell.setCommandCompletion(m_commandCompletion); m_prefs.putBoolean("gtpshell-disable-completions", ! m_commandCompletion); } public void actionToggleShowCursor() { boolean showCursor = ! m_guiBoard.getShowCursor(); m_guiBoard.setShowCursor(showCursor); m_prefs.putBoolean("show-cursor", showCursor); } public void actionToggleShowGrid() { boolean showGrid = ! m_guiBoard.getShowGrid(); m_guiBoard.setShowGrid(showGrid); m_prefs.putBoolean("show-grid", showGrid); } public void actionToggleShowInfoPanel() { if (GuiUtil.isNormalSizeMode(this)) { if (m_showInfoPanel) m_comment.setPreferredSize(m_comment.getSize()); m_guiBoard.setPreferredFieldSize(m_guiBoard.getFieldSize()); } showInfoPanel(! m_showInfoPanel); updateViews(false); } public void actionToggleShowLastMove() { m_showLastMove = ! m_showLastMove; m_prefs.putBoolean("show-last-move", m_showLastMove); updateFromGoBoard(); updateViews(false); } public void actionToggleShowMoveNumbers() { if (m_showMoveNumbers) m_guiBoard.clearAllLabels(); m_showMoveNumbers = ! m_showMoveNumbers; m_prefs.putBoolean("show-move-numbers", m_showMoveNumbers); updateFromGoBoard(); updateViews(false); } public void actionToggleShowSubtreeSizes() { m_showSubtreeSizes = ! m_showSubtreeSizes; m_prefs.putBoolean("gametree-show-subtree-sizes", m_showSubtreeSizes); if (m_gameTreeViewer == null) updateViews(false); else { m_gameTreeViewer.setShowSubtreeSizes(m_showSubtreeSizes); updateViews(true); } } public void actionToggleShowToolbar() { if (GuiUtil.isNormalSizeMode(this)) { if (m_showInfoPanel) m_comment.setPreferredSize(m_comment.getSize()); m_guiBoard.setPreferredFieldSize(m_guiBoard.getFieldSize()); } showToolbar(! m_showToolbar); updateViews(false); } public void actionToggleTimeStamp() { m_timeStamp = ! m_timeStamp; if (m_shell != null) m_shell.setTimeStamp(m_timeStamp); m_prefs.putBoolean("gtpshell-timestamp", m_timeStamp); updateViews(false); } public void actionTreeLabels(GameTreePanel.Label mode) { m_treeLabels = mode; m_prefs.putInt("gametree-labels", mode.ordinal()); if (m_gameTreeViewer == null) updateViews(false); else { m_gameTreeViewer.setLabelMode(mode); updateViews(true); } } public void actionTreeSize(GameTreePanel.Size mode) { m_treeSize = mode; m_prefs.putInt("gametree-size", mode.ordinal()); if (m_gameTreeViewer == null) updateViews(false); else { m_gameTreeViewer.setSizeMode(mode); updateViews(true); } } public void actionTruncate() { if (! checkStateChangePossible()) return; if (! getCurrentNode().hasFather()) return; String disableKey = "net.sf.gogui.gogui.GoGui.truncate"; if (! m_messageDialogs.showQuestion(disableKey, this, i18n("MSG_TRUNCATE"), i18n("MSG_TRUNCATE_2"), i18n("LB_TRUNCATE"), false)) return; m_game.truncate(); actionGotoNode(getCurrentNode()); boardChangedBegin(false, true); } public void actionTruncateChildren() { if (! checkStateChangePossible()) return; int numberChildren = getCurrentNode().getNumberChildren(); if (numberChildren == 0) return; String disableKey = "net.sf.gogui.gogui.GoGui.truncate-children"; if (! m_messageDialogs.showQuestion(disableKey, this, i18n("MSG_TRUNCATE_CHILDREN"), i18n("MSG_TRUNCATE_CHILDREN_2"), i18n("LB_TRUNCATE"), false)) return; m_game.truncateChildren(); boardChangedBegin(false, true); } public void actionQuit() { close(); } public boolean getAutoNumber() { return m_autoNumber; } public boolean getBeepAfterMove() { return m_beepAfterMove; } public boolean getCommentMonoFont() { return m_comment.getMonoFont(); } public boolean getCompletion() { return m_commandCompletion; } /** Get name of currently attached program. @return Name or null, if no program is attached or name is not known. */ public String getProgramName() { String name = null; if (m_gtp != null) name = m_gtp.getName(); if (name == null) name = i18n("MSG_UNKNOWN_PROGRAM_NAME"); return name; } public int getNumberPrograms() { return m_programs.size(); } /** Get label for currently attached program. @return Label from Program instance, if program was created with a Program instance, otherwise label as in GtpClientBase#getLabel; null if no program is attached. */ public String getProgramLabel() { if (m_gtp == null) return null; else if (m_program != null && ! StringUtil.isEmpty(m_program.m_label)) return m_program.m_label; else return m_gtp.getLabel(); } public GoColor getSetupColor() { return m_setupColor; } public boolean getShowLastMove() { return m_showLastMove; } public boolean getShowMoveNumbers() { return m_showMoveNumbers; } public boolean getShowSubtreeSizes() { return m_showSubtreeSizes; } public ShowVariations getShowVariations() { return m_showVariations; } public boolean getTimeStamp() { return m_timeStamp; } public GameTreePanel.Label getTreeLabels() { return m_treeLabels; } public GameTreePanel.Size getTreeSize() { return m_treeSize; } /** Return whether the currently attached program has analyze commands of type "param". */ public boolean hasParameterCommands() { if (m_analyzeCommands == null) return false; return AnalyzeUtil.hasParameterCommands(m_analyzeCommands); } public boolean hasParameterSnapshot() { return m_parameterSnapshot != null; } public boolean isAnalyzeDialogShown() { return (m_analyzeDialog != null); } public boolean isCommandInProgress() { return (m_gtp != null && m_gtp.isCommandInProgress()); } /** Check if computer plays a color (or both). */ public boolean isComputerColor(GoColor color) { if (color == BLACK) return m_computerBlack; assert color == WHITE; return m_computerWhite; } public boolean isInfoPanelShown() { return m_showInfoPanel; } public boolean isShellShown() { return (m_shell != null && m_shell.isVisible()); } public boolean isToolbarShown() { return m_showToolbar; } public boolean isTreeShown() { return (m_gameTreeViewer != null); } public void contextMenu(GoPoint point, Component invoker, int x, int y) { if (m_setupMode || (m_analyzeCommand != null && m_analyzeCommand.needsPointListArg())) { fieldClicked(point, true); return; } ContextMenu contextMenu = createContextMenu(point); contextMenu.show(invoker, x, y); } public void fieldClicked(GoPoint p, boolean modifiedSelect) { if (m_setupMode) { if (! checkCommandInProgress()) return; GoColor color; if (modifiedSelect) color = m_setupColor.otherColor(); else color = m_setupColor; if (getBoard().getColor(p) == color) color = EMPTY; setup(p, color); updateViews(false); } else if (m_analyzeCommand != null && m_analyzeCommand.needsPointArg() && ! modifiedSelect) { if (! checkCommandInProgress()) return; m_analyzeCommand.setPointArg(p); m_guiBoard.clearAllSelect(); m_guiBoard.setSelect(p, true); analyzeBegin(false); } else if (m_analyzeCommand != null && m_analyzeCommand.needsPointListArg()) { if (! checkCommandInProgress()) return; PointList pointListArg = m_analyzeCommand.getPointListArg(); if (pointListArg.contains(p)) { pointListArg.remove(p); if (modifiedSelect) pointListArg.add(p); } else pointListArg.add(p); m_guiBoard.clearAllSelect(); GuiBoardUtil.setSelect(m_guiBoard, pointListArg, true); if (modifiedSelect && pointListArg.size() > 0) analyzeBegin(false); } else if (m_scoreMode && ! modifiedSelect) { if (! checkCommandInProgress()) return; GuiBoardUtil.scoreSetDead(m_guiBoard, m_countScore, getBoard(), p); Komi komi = getGameInfo().getKomi(); m_scoreDialog.showScore(m_countScore, komi); } else if (modifiedSelect) m_guiBoard.contextMenu(p); else { if (getBoard().getColor(p) != EMPTY) return; if (! checkCommandInProgress()) return; if (getBoard().isSuicide(getToMove(), p) && ! showQuestion(i18n("MSG_SUICIDE"), i18n("MSG_SUICIDE_2"), i18n("LB_SUICIDE"),false)) return; else if (getBoard().isKo(p) && ! showQuestion(i18n("MSG_ILLEGAL_KO"), i18n("MSG_ILLEGAL_KO_2"), i18n("LB_ILLEGAL_KO"), false)) return; Move move = Move.get(getToMove(), p); humanMoved(move); } } public GoGuiActions getActions() { return m_actions; } public File getFile() { if (m_gameFile == null) return null; return m_gameFile.m_file; } public ConstGame getGame() { return m_game; } public ConstGuiBoard getGuiBoard() { return m_guiBoard; } public int getHandicapDefault() { return m_handicap; } public boolean getMonoFont() { return m_comment.getMonoFont(); } /** Get currently used pattern for search in comments. */ public Pattern getPattern() { return m_pattern; } public boolean isInSetupMode() { return m_setupMode; } /** Callback for selected text. This is a callback for text selection exents in different components. It parses the text for valid points and marks them on the board. */ public void textSelected(String text) { if (text == null) text = ""; PointList points = GtpUtil.parsePointString(text, getBoardSize()); GuiBoardUtil.showPointList(m_guiBoard, points); } public void initAnalyzeCommand(AnalyzeCommand command, boolean autoRun, boolean clearBoard, boolean reuseTextWindow) { if (! synchronizeProgram()) return; m_analyzeCommand = command; m_analyzeAutoRun = autoRun; m_analyzeClearBoard = clearBoard; m_analyzeReuseTextWindow = reuseTextWindow; if (command.needsPointArg()) { setBoardCursor(Cursor.HAND_CURSOR); showStatusSelectTarget(); } else if (command.needsPointListArg()) { setBoardCursor(Cursor.HAND_CURSOR); showStatusSelectPointList(); } } public boolean isInterruptSupported() { return (m_gtp != null && m_gtp.isInterruptSupported()); } public boolean isModified() { return m_game.isModified(); } /** Return if a program is currently attached. Also returns true, if a program is attached but dead, which can be checked with isProgramDead() */ public boolean isProgramAttached() { return (m_gtp != null); } public boolean isProgramDead() { return (m_gtp != null && m_gtp.isProgramDead()); } public void showLiveGfx(String text) { assert SwingUtilities.isEventDispatchThread(); // The live gfx events can arrive delayed, we don't want to allow // them to paint on the board, if no command is currently running if (! isCommandInProgress()) return; m_guiBoard.clearAll(); GuiBoardUtil.updateFromGoBoard(m_guiBoard, getBoard(), false, false); AnalyzeShow.showGfx(text, m_guiBoard, m_statusBar, null); } private class AnalyzeContinue implements Runnable { public AnalyzeContinue(boolean checkComputerMove) { m_checkComputerMove = checkComputerMove; } public void run() { analyzeContinue(m_checkComputerMove); } private final boolean m_checkComputerMove; } private class ShowInvalidResponse implements Runnable { public ShowInvalidResponse(String line) { m_line = line; } public void run() { String name = getProgramName(); String mainMessage = format(i18n("MSG_INVALID_RESPONSE"), name); String disableKey = "net.sf.gogui.gogui.GoGui.invalid-response"; String optionalMessage = format(i18n("MSG_INVALID_NOSTATUS_RESPONSE"), name); m_messageDialogs.showWarning(disableKey, GoGui.this, mainMessage, optionalMessage, true); } private final String m_line; } private static class LoadFileRunnable implements GuiUtil.ProgressRunnable { public LoadFileRunnable(File file) { m_file = file; } public GameTree getTree() { return m_reader.getTree(); } public String getWarnings() { return m_reader.getWarnings(); } public GameFile getGameFile() { return m_reader.getFile(); } public void run(ProgressShow progressShow) throws Throwable { m_reader = new GameReader(m_file, progressShow); } private final File m_file; private GameReader m_reader; } private boolean m_analyzeAutoRun; private boolean m_analyzeClearBoard; private boolean m_analyzeOneRunOnly; private boolean m_analyzeReuseTextWindow; private boolean m_autoNumber; private boolean m_commandCompletion; /** Automatically register program in Program menu if GoGui was invoked with the option -program */ private final boolean m_register; private boolean m_timeStamp; private final boolean m_auto; private boolean m_beepAfterMove; private boolean m_computerBlack; private boolean m_computerWhite; /** State variable used between generateMove and computerMoved. Flag is set in actionInterrupt. */ private boolean m_interruptComputerBoth; /** State variable used between generateMove and computerMoved. */ private boolean m_isSingleMove; private boolean m_lostOnTimeShown; private boolean m_resigned; private boolean m_scoreMode; private boolean m_setupMode; private boolean m_showInfoPanel; private boolean m_showLastMove; private boolean m_showMoveNumbers; private boolean m_showSubtreeSizes; private boolean m_showToolbar; private ShowVariations m_showVariations; private boolean m_setupNodeCreated; private final boolean m_verbose; private int m_handicap; private final int m_move; private GameTreePanel.Label m_treeLabels; private GameTreePanel.Size m_treeSize; private final GuiBoard m_guiBoard; private GuiGtpClient m_gtp; private final Comment m_comment; private final Interrupt m_interrupt = new Interrupt(); /** File corresponding to the current game. */ private GameFile m_gameFile; private File m_initialFile; private final GameInfoPanel m_gameInfoPanel; private GtpShell m_shell; private GameTreeViewer m_gameTreeViewer; private Help m_help; private final JPanel m_infoPanel; private final JPanel m_innerPanel; private final JSplitPane m_splitPane; private final GoGuiMenuBar m_menuBar; private final Game m_game; private GoColor m_setupColor; private final MessageDialogs m_messageDialogs = new MessageDialogs(); private Pattern m_pattern; private final File m_analyzeCommandsFile; private AnalyzeCommand m_analyzeCommand; private final Session m_session = new Session("net/sf/gogui/gogui/session"); private final CountScore m_countScore = new CountScore(); private final StatusBar m_statusBar; private final String m_gtpCommand; private final String m_gtpFile; private String m_lastAnalyzeCommand; private String m_programCommand; private String m_titleFromProgram; private String m_version = ""; private AnalyzeDialog m_analyzeDialog; private final Preferences m_prefs = Preferences.userNodeForPackage(getClass()); private ScoreDialog m_scoreDialog; private ArrayList<AnalyzeDefinition> m_analyzeCommands; /** Program information. Can be null even if a program is attached, if only m_programName is known. */ private Program m_program; /** Program currently being edited in actionNewProgram() */ private Program m_newProgram; private final ThumbnailCreator m_thumbnailCreator = new ThumbnailCreator(false); private TimeSettings m_timeSettings; private final GoGuiActions m_actions = new GoGuiActions(this); private final GoGuiToolBar m_toolBar; private ArrayList<Bookmark> m_bookmarks; private ArrayList<Program> m_programs; private ShowAnalyzeText m_showAnalyzeText; /** Snapshot used in actionSnapshotParameters and actionRestoreParameters. */ private File m_parameterSnapshot; private void analyzeBegin(boolean checkComputerMove) { if (m_gtp == null || m_analyzeCommand == null || m_analyzeCommand.isPointArgMissing() || ! synchronizeProgram()) return; GoColor toMove = getToMove(); m_lastAnalyzeCommand = m_analyzeCommand.replaceWildCards(toMove); runLengthyCommand(m_lastAnalyzeCommand, new AnalyzeContinue(checkComputerMove)); showStatus(format(i18n("STAT_RUNNING"), m_analyzeCommand.getResultTitle())); } private void analyzeContinue(boolean checkComputerMove) { if (m_analyzeClearBoard) resetBoard(); boolean isCritical = (m_gtp != null && m_gtp.isProgramDead()); if (! endLengthyCommand(isCritical)) return; clearStatus(); if (m_analyzeCommand == null) { // Program was detached while running the analyze command resetBoard(); return; } String title = m_analyzeCommand.getResultTitle(); try { String response = m_gtp.getResponse(); StringBuilder showTextBuffer = new StringBuilder(256); AnalyzeShow.show(m_analyzeCommand, m_guiBoard, m_statusBar, getBoard(), response, showTextBuffer); AnalyzeType type = m_analyzeCommand.getType(); GoPoint pointArg = null; if (m_analyzeCommand.needsPointArg()) pointArg = m_analyzeCommand.getPointArg(); else if (m_analyzeCommand.needsPointListArg()) { ConstPointList list = m_analyzeCommand.getPointListArg(); if (list.size() > 0) pointArg = list.get(list.size() - 1); } if (type == AnalyzeType.PARAM) ParameterDialog.editParameters(m_lastAnalyzeCommand, this, title, response, m_gtp, m_messageDialogs); boolean isTextType = m_analyzeCommand.isTextType(); String showText = null; if (showTextBuffer.length() > 0) showText = showTextBuffer.toString(); else if (isTextType) showText = response; if (showText != null) { if (showText.indexOf("\n") < 0) { if (isTextType && showText.trim().equals("")) showText = i18n("STAT_ANALYZE_TEXT_EMPTY_RESPONSE"); showStatus(format(i18n("STAT_ANALYZE_TEXT_RESPONSE"), title, showText)); } else { m_showAnalyzeText.show(type, pointArg, title, showText, m_analyzeReuseTextWindow); } } if ("".equals(m_statusBar.getText()) && type != AnalyzeType.PARAM) showStatus(title); if (checkComputerMove) checkComputerMove(); } catch (GtpResponseFormatError e) { showStatus(title); showError(e); } finally { if (m_analyzeOneRunOnly) clearAnalyzeCommand(false); } } private boolean attachNewProgram(String command, Program program) { if (m_gtp != null) { saveSession(); detachProgram(); } if (! attachProgram(command, program, false)) { m_prefs.putInt("program", -1); if (m_gtp == null || m_gtp.isProgramDead()) if (! m_shell.isVisible() && m_shell.isLastTextNonGTP()) showShell(); updateViews(false); return false; } if (m_shell != null && m_session.isVisible("shell")) m_shell.setVisible(true); if (m_session.isVisible("analyze")) createAnalyzeDialog(); toFrontLater(); updateViews(false); return true; } /** Attach program. @param programCommand Command line for running program. @param program Program information (may be null) @param register Create an entry for this program in the Program menu. @return true if program was successfully attached. */ private boolean attachProgram(String programCommand, Program program, boolean register) { programCommand = programCommand.trim(); if (programCommand.equals("")) return false; m_program = program; m_programCommand = programCommand; if (m_shell != null) { m_shell.dispose(); m_shell = null; } m_shell = new GtpShell(this, this, m_messageDialogs); GuiAction.registerAll(m_shell.getLayeredPane()); m_shell.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { saveSession(); hideShell(); } }); restoreSize(m_shell, "shell"); m_shell.setProgramCommand(programCommand); m_shell.setTimeStamp(m_timeStamp); m_shell.setCommandCompletion(m_commandCompletion); GtpClient.InvalidResponseCallback invalidResponseCallback = new GtpClient.InvalidResponseCallback() { public void show(String line) { Runnable runnable = new ShowInvalidResponse(line); if (SwingUtilities.isEventDispatchThread()) runnable.run(); else GuiUtil.invokeAndWait(runnable); } }; GtpClient.IOCallback ioCallback = new GtpClient.IOCallback() { public void receivedInvalidResponse(String s) { if (m_shell == null) return; boolean invokeLater = true; m_shell.receivedInvalidResponse(s, invokeLater); } public void receivedResponse(boolean error, String s) { if (m_shell == null) return; boolean invokeLater = true; m_shell.receivedResponse(error, s, invokeLater); } public void receivedStdErr(String s) { if (m_shell == null) return; m_lineReader.add(s); while (m_lineReader.hasLines()) { String line = m_lineReader.getLine(); boolean isLiveGfx = m_liveGfx.handleLine(line); boolean isWarning = line.startsWith("warning:") || line.startsWith("Warning:") || line.startsWith("WARNING:"); boolean invokeLater = true; m_shell.receivedStdErr(line, invokeLater, isLiveGfx, isWarning); } } public void sentCommand(String s) { if (m_shell != null) m_shell.sentCommand(s); } private final LineReader m_lineReader = new LineReader(); private LiveGfx m_liveGfx = new LiveGfx(GoGui.this); }; GtpSynchronizer.Listener synchronizerCallback = new GtpSynchronizer.Listener() { public void moveNumberChanged(int moveNumber) { String text = "[" + moveNumber + "]"; m_statusBar.immediatelyPaintMoveText(text); } }; try { showStatusImmediately(i18n("STAT_ATTACHING_PROGRAM")); File workingDirectory = null; if (program != null && ! StringUtil.isEmpty(program.m_workingDirectory)) workingDirectory = new File(program.m_workingDirectory); GtpClient gtp = new GtpClient(m_programCommand, workingDirectory, m_verbose, ioCallback); gtp.setInvalidResponseCallback(invalidResponseCallback); gtp.setAutoNumber(m_autoNumber); m_gtp = new GuiGtpClient(gtp, this, synchronizerCallback, m_messageDialogs); m_gtp.queryName(); m_gtp.queryProtocolVersion(); try { m_version = m_gtp.queryVersion(); m_shell.setProgramVersion(m_version); m_gtp.querySupportedCommands(); m_gtp.queryInterruptSupport(); if (m_program == null) { m_program = Program.findProgram(m_programs, programCommand); if (m_program == null && m_register) { m_program = new Program("", m_gtp.getName(), m_version, programCommand, ""); m_program.setUniqueLabel(m_programs); m_programs.add(m_program); m_menuBar.setPrograms(m_programs); Program.save(m_programs); } } } catch (GtpError e) { } if (m_program != null && m_program.updateInfo(getProgramName(), m_version)) { Program.save(m_programs); m_menuBar.setPrograms(m_programs); } try { String programAnalyzeCommands = GtpClientUtil.getAnalyzeCommands(m_gtp); m_analyzeCommands = AnalyzeDefinition.read(m_gtp.getSupportedCommands(), m_analyzeCommandsFile, programAnalyzeCommands); } catch (ErrorMessage e) { showError(i18n("MSG_COULD_NOT_READ_ANALYZE_CONFIGURATION"), e); } restoreSize(m_shell, "shell"); m_shell.setProgramName(getProgramLabel()); ArrayList<String> supportedCommands = m_gtp.getSupportedCommands(); m_shell.setInitialCompletions(supportedCommands); if (! m_gtp.isGenmoveSupported()) { m_computerBlack = false; m_computerWhite = false; } initGtp(); if (! m_gtpFile.equals("")) sendGtpFile(new File(m_gtpFile)); if (! m_gtpCommand.equals("")) sendGtpString(m_gtpCommand); setTitle(); } catch (GtpError e) { showError(e); return false; } finally { clearStatus(); } currentNodeChanged(); return true; } private void beginLengthyCommand() { setBoardCursor(Cursor.WAIT_CURSOR); m_shell.setCommandInProgess(true); showStatus(format(i18n("STAT_THINKING"), getProgramName())); updateViews(false); } private void boardChangedBegin(boolean doCheckComputerMove, boolean gameTreeChanged) { updateFromGoBoard(); updateViews(gameTreeChanged); if (m_analyzeDialog != null) m_analyzeDialog.setSelectedColor(getToMove()); if (m_gtp != null && ! isOutOfSync() && m_analyzeCommand != null && m_analyzeAutoRun && ! m_analyzeCommand.isPointArgMissing()) analyzeBegin(doCheckComputerMove); else { resetBoard(); clearStatus(); if (doCheckComputerMove) checkComputerMove(); } } private boolean checkCommandInProgress() { if (isCommandInProgress()) { showError(i18n("MSG_CANNOT_EXECUTE_WHILE_THINKING"), i18n("MSG_CANNOT_EXECUTE_WHILE_THINKING_2"), false); return false; } return true; } private void checkComputerMove() { if (m_gtp == null || isOutOfSync() || m_gtp.isProgramDead()) return; int moveNumber = NodeUtil.getMoveNumber(getCurrentNode()); boolean bothPassed = (moveNumber >= 2 && getBoard().bothPassed()); boolean gameFinished = (bothPassed || m_resigned); if (isComputerBoth()) { if (gameFinished) { if (m_auto) { newGame(getBoardSize()); updateViews(true, true); checkComputerMove(); return; } m_game.haltClock(); showGameFinished(); return; } generateMove(false); } else { if (gameFinished) { m_game.haltClock(); showGameFinished(); return; } else if (computerToMove()) generateMove(false); } } private boolean checkHasParameterCommands() { if (! AnalyzeUtil.hasParameterCommands(m_analyzeCommands)) { String optionalMessage = format(i18n("MSG_NO_PARAM_COMMANDS_2"), getProgramName()); showError(i18n("MSG_NO_PARAM_COMMANDS"), optionalMessage); return false; } return true; } private void checkLostOnTime(GoColor color) { if (getClock().lostOnTime(color) && ! getClock().lostOnTime(color.otherColor()) && ! m_lostOnTimeShown) { String result = color.otherColor().getUppercaseLetter() + "+Time"; String mainMessage; String optionalMessage; if (color == BLACK) { mainMessage = i18n("MSG_LOST_ON_TIME_BLACK"); optionalMessage = format(i18n("MSG_LOST_ON_TIME_BLACK_2"), result); } else { mainMessage = i18n("MSG_LOST_ON_TIME_WHITE"); optionalMessage = format(i18n("MSG_LOST_ON_TIME_WHITE_2"), result); } showInfo(mainMessage, optionalMessage, false); setResult(result); m_lostOnTimeShown = true; } } private boolean checkSaveGame() { return checkSaveGame(false); } /** Ask for saving file if it was modified. @return true If file was not modified, user chose not to save it or file was saved successfully */ private boolean checkSaveGame(boolean isProgramTerminating) { if (! isModified()) return true; String mainMessage = i18n("MSG_SAVE_CURRENT"); String optionalMessage = i18n("MSG_SAVE_CURRENT_2"); int result; String disableKey = null; if (! isProgramTerminating) disableKey = "net.sf.gogui.gogui.GoGui.save"; result = m_messageDialogs.showYesNoCancelQuestion(disableKey, this, mainMessage, optionalMessage, i18n("LB_DONT_SAVE"), i18n("LB_SAVE")); switch (result) { case 0: m_game.clearModified(); return true; case 1: if (m_gameFile == null) return saveDialog(); else return save(m_gameFile); case 2: return false; default: assert false; return true; } } /** Check if command is in progress or setup or score mode. */ private boolean checkStateChangePossible() { if (! checkCommandInProgress()) return false; if (m_setupMode) setupDone(); if (m_scoreMode) scoreDone(null); return true; } private void clearAnalyzeCommand() { clearAnalyzeCommand(true); } private void clearAnalyzeCommand(boolean resetBoard) { if (m_analyzeCommand != null) { m_analyzeCommand = null; setBoardCursorDefault(); } if (resetBoard) { resetBoard(); clearStatus(); } } private void clearStatus() { m_statusBar.clear(); } private void close() { if (! checkSaveGame(true)) return; saveSession(); setVisible(false); SwingUtilities.invokeLater(new Runnable() { public void run() { if (m_gtp != null) { m_analyzeCommand = null; detachProgram(); } dispose(); System.exit(0); } }); } private void computerMoved() { if (! endLengthyCommand()) return; if (m_beepAfterMove) Toolkit.getDefaultToolkit().beep(); GoColor toMove = getToMove(); try { String response = m_gtp.getResponse(); checkLostOnTime(toMove); boolean gameTreeChanged = false; String name = getProgramName(); if (response.equalsIgnoreCase("resign")) { String result = toMove.otherColor().getUppercaseLetter() + "+Resign"; if (! m_auto) { String mainMessage = format(i18n("MSG_RESIGN"), name); String optionalMessage = format(i18n("MSG_RESIGN_2"), result); showInfo(mainMessage, optionalMessage, false); } m_resigned = true; setResult(result); } else { GoPoint point = GtpUtil.parsePoint(response, getBoardSize()); ConstBoard board = getBoard(); if (point != null) { if (board.getColor(point) != EMPTY) { String mainMessage = format(i18n("MSG_NONEMPTY"), name); String optionalMessage = format(i18n("MSG_NONEMPTY_2"), name); showWarning(mainMessage, optionalMessage, true); m_computerBlack = false; m_computerWhite = false; } else if (board.isKo(point)) { String mainMessage = format(i18n("MSG_VIOLATE_KO"), name); showWarning(mainMessage, i18n("MSG_VIOLATE_KO_2"), true); m_computerBlack = false; m_computerWhite = false; } } Move move = Move.get(toMove, point); m_game.play(move); m_gtp.updateAfterGenmove(getBoard()); if (point == null && ! isComputerBoth()) { String disableKey = "net.sf.gogui.gogui.GoGui.computer-passed"; String mainMessage = format(i18n("MSG_PROGRAM_PASS"), name); String optionalMessage = format(i18n("MSG_PROGRAM_PASS_2"), name); m_messageDialogs.showInfo(disableKey, this, mainMessage, optionalMessage, false); } m_resigned = false; gameTreeChanged = true; ConstNode currentNode = getCurrentNode(); if (currentNode.getFatherConst().getNumberChildren() == 1) { if (m_gameTreeViewer != null) m_gameTreeViewer.addNewSingleChild(currentNode); gameTreeChanged = false; } } boolean doCheckComputerMove = (! m_isSingleMove && ! (isComputerBoth() && m_interruptComputerBoth)); boardChangedBegin(doCheckComputerMove, gameTreeChanged); } catch (GtpResponseFormatError e) { showError(e); clearStatus(); } } private boolean computerToMove() { if (getToMove() == BLACK) return m_computerBlack; else return m_computerWhite; } private void createAnalyzeDialog() { m_analyzeDialog = new AnalyzeDialog(this, this, m_analyzeCommands, m_gtp, m_messageDialogs); m_analyzeDialog.setReuseTextWindow( m_prefs.getBoolean("analyze-reuse-text-window", false)); GuiAction.registerAll(m_analyzeDialog.getLayeredPane()); m_analyzeDialog.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { actionDisposeAnalyzeDialog(); } }); m_analyzeDialog.setBoardSize(getBoardSize()); m_analyzeDialog.setSelectedColor(getToMove()); restoreSize(m_analyzeDialog, "analyze"); m_analyzeDialog.setVisible(true); } private ContextMenu createContextMenu(GoPoint point) { boolean noProgram = (m_gtp == null); return new ContextMenu(point, m_guiBoard.getMark(point), m_guiBoard.getMarkCircle(point), m_guiBoard.getMarkSquare(point), m_guiBoard.getMarkTriangle(point), this); } private void createTree() { m_gameTreeViewer = new GameTreeViewer(this, this, m_messageDialogs); m_gameTreeViewer.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { actionDisposeTree(); } }); GuiAction.registerAll(m_gameTreeViewer.getLayeredPane()); m_gameTreeViewer.setLabelMode(m_treeLabels); m_gameTreeViewer.setSizeMode(m_treeSize); m_gameTreeViewer.setShowSubtreeSizes(m_showSubtreeSizes); restoreSize(m_gameTreeViewer, "tree"); m_gameTreeViewer.update(getTree(), getCurrentNode()); m_gameTreeViewer.setVisible(true); } private void createThumbnail(File file) { if (! ThumbnailPlatform.checkThumbnailSupport()) return; String path = file.getAbsolutePath(); if (! path.startsWith("/tmp") && ! path.startsWith("/var/tmp")) { try { m_thumbnailCreator.create(file); } catch (ErrorMessage e) { } } } private void currentNodeChanged() { updateFromGoBoard(); } private void detachProgram() { if (m_gtp != null) showStatusImmediately(i18n("STAT_DETACHING")); if (isCommandInProgress()) { m_gtp.destroyGtp(); m_gtp.close(); } else { if (m_gtp != null && ! m_gtp.isProgramDead()) { // Some programs do not handle closing the GTP stream // correctly, so we send a quit before try { if (m_gtp.isSupported("quit")) m_gtp.send("quit"); } catch (GtpError e) { } m_gtp.close(); } } m_gtp = null; if (m_analyzeCommand != null) clearAnalyzeCommand(); m_version = null; m_shell.dispose(); m_shell = null; if (m_analyzeDialog != null) { m_analyzeDialog.saveRecent(); m_analyzeDialog.dispose(); m_analyzeDialog = null; } resetBoard(); clearStatus(); setTitle(); } private boolean endLengthyCommand() { return endLengthyCommand(true, true); } private boolean endLengthyCommand(boolean isCritical) { return endLengthyCommand(isCritical, true); } private boolean endLengthyCommand(boolean isCritical, boolean showError) { restoreBoardCursor(); clearStatus(); if (m_shell != null) m_shell.setCommandInProgess(false); // Program could have been killed in actionDetachProgram() if (m_gtp == null) return false; GtpError error = m_gtp.getException(); updateViews(false); if (error != null && showError) { showError(error, isCritical); return false; } return true; } private String formatCommand(String command) { if (command.length() < 20) return command; GtpCommand cmd = new GtpCommand(command); return cmd.getCommand() + " [...]"; } private void generateMove(boolean isSingleMove) { if (! synchronizeProgram()) return; GoColor toMove = getToMove(); ConstNode node = getCurrentNode(); ConstNode father = node.getFatherConst(); ConstGameInfo info = getGameInfo(); String playerToMove = info.get(StringInfoColor.NAME, toMove); String playerOther = info.get(StringInfoColor.NAME, toMove.otherColor()); String name = getProgramLabel(); if (! isSingleMove && m_gameFile == null && playerToMove == null && (father == null || (! father.hasFather() && (playerOther == null || playerOther.equals(name))))) { m_game.setPlayer(toMove, name); updateViews(false); } String command; if (NodeUtil.isInCleanup(getCurrentNode()) && m_gtp.isSupported("kgs-genmove_cleanup")) { command = "kgs-genmove_cleanup"; if (toMove == BLACK) command += " b"; else if (toMove == WHITE) command += " w"; else assert false; } else { command = m_gtp.getCommandGenmove(toMove); } m_isSingleMove = isSingleMove; m_interruptComputerBoth = false; Runnable callback = new Runnable() { public void run() { computerMoved(); } }; if (getClock().isInitialized() && NodeUtil.isTimeLeftKnown(getCurrentNode(), toMove)) GtpUtil.sendTimeLeft(m_gtp, getClock(), toMove); m_game.startClock(); runLengthyCommand(command, callback); } private ConstBoard getBoard() { return m_game.getBoard(); } private int getBoardSize() { return m_game.getSize(); } private ConstClock getClock() { return m_game.getClock(); } private ConstNode getCurrentNode() { return m_game.getCurrentNode(); } private ConstGameInfo getGameInfo() { return m_game.getGameInfo(getCurrentNode()); } private Komi getPrefsKomi() { try { String s = m_prefs.get("komi", "6.5"); return Komi.parseKomi(s); } catch (InvalidKomiException e) { return null; } } private GoColor getToMove() { return m_game.getToMove(); } private ConstGameTree getTree() { return m_game.getTree(); } /** Change current node. Automatically restores the clock, or halts it, if no time settings are known. */ private void gotoNode(ConstNode node) { // GameTreeViewer is not disabled in score mode if (m_scoreMode) return; m_game.gotoNode(node); if (getClock().isInitialized()) m_game.restoreClock(); else m_game.haltClock(); currentNodeChanged(); } private void hideShell() { if (m_shell == null) return; saveSession(); m_shell.setVisible(false); } private void humanMoved(Move move) { GoPoint p = move.getPoint(); if (p != null) paintImmediately(p, move.getColor(), true); if (m_gtp != null && ! isComputerNone() && ! isOutOfSync() && ! m_gtp.isProgramDead()) { synchronizeProgram(); try { m_gtp.updateHumanMove(getBoard(), move); } catch (GtpError e) { showError(e); boardChangedBegin(false, false); return; } } boolean newNodeCreated = false; ConstNode node = NodeUtil.getChildWithMove(getCurrentNode(), move); if (node == null) { newNodeCreated = true; m_game.play(move); } else { m_game.haltClock(); m_game.gotoNode(node); } checkLostOnTime(move.getColor()); m_resigned = false; boolean gameTreeChanged = newNodeCreated; ConstNode currentNode = getCurrentNode(); if (newNodeCreated && currentNode.getFatherConst().getNumberChildren() == 1) { if (m_gameTreeViewer != null) m_gameTreeViewer.addNewSingleChild(currentNode); gameTreeChanged = false; } boardChangedBegin(true, gameTreeChanged); } private void importTextPosition(Reader reader) { try { TextParser parser = new TextParser(); parser.parse(reader); GameTree tree = NodeUtil.makeTreeFromPosition(null, parser.getBoard()); m_game.init(tree); } catch (ParseError e) { showError(i18n("MSG_IMPORT_FAILED"), e); } m_guiBoard.initSize(getBoard().getSize()); initGtp(); m_computerBlack = false; m_computerWhite = false; boardChangedBegin(false, true); } private void initGame(int size) { int oldSize = getBoardSize(); if (size != oldSize) { // Clear analyze command when board size changes, because eplist // could contain points out of board) clearAnalyzeCommand(); saveSession(); m_guiBoard.initSize(size); restoreMainWindow(size); JLayeredPane layeredPane = getLayeredPane(); if (layeredPane.isVisible()) { // Loading a file with program attached can take long GuiUtil.paintImmediately(layeredPane); } } Komi komi = (m_handicap == 0 ? getPrefsKomi() : new Komi(0)); ConstPointList handicap = Board.getHandicapStones(size, m_handicap); if (handicap == null) showWarning(i18n("MSG_HANDICAP_UNDEFINED"), format(i18n("MSG_HANDICAP_UNDEFINED_2"), m_handicap, size), false); m_game.init(size, komi, handicap, m_prefs.get("rules", ""), m_timeSettings); if (size != oldSize) { if (m_shell != null) restoreSize(m_shell, "shell"); if (m_analyzeDialog != null) { restoreSize(m_analyzeDialog, "analyze"); m_analyzeDialog.setBoardSize(size); } if (m_gameTreeViewer != null) restoreSize(m_gameTreeViewer, "tree"); } updateFromGoBoard(); resetBoard(); m_game.resetClock(); m_lostOnTimeShown = false; m_resigned = false; m_pattern = null; } private boolean initGtp() { if (m_gtp != null) { try { ConstGameInfo info = getGameInfo(); m_gtp.initSynchronize(getBoard(), info.getKomi(), info.getTimeSettings()); } catch (GtpError error) { showError(error); return false; } } currentNodeChanged(); return ! isOutOfSync(); } private void initialize() { m_guiBoard.setListener(this); m_guiBoard.addMouseWheelListener(new MouseWheelListener() { public void mouseWheelMoved(MouseWheelEvent e) { // Silently ignore mouse wheel events if command in // progress because it is easy to generate multiple events // while using the wheel and if an analyze command is // enabled to automatically run after each board change, // actionForward() and actionBackward() would pop up an // error dialog if the analyze command is still in progress if (isCommandInProgress()) return; int n = e.getWheelRotation(); int mod = e.getModifiers(); int scale = (mod == ActionEvent.SHIFT_MASK ? 10 : 1); if (n > 0) actionForward(scale * n); else actionBackward(-scale * n); } }); GuiUtil.removeKeyBinding(m_splitPane, "F8"); GuiAction.registerAll(getLayeredPane()); m_bookmarks = Bookmark.load(); m_menuBar.setBookmarks(m_bookmarks); m_programs = Program.load(); m_menuBar.setPrograms(m_programs); if (m_programCommand == null) { int index = m_prefs.getInt("program", -1); if (index >= 0 && index < m_programs.size()) { m_program = m_programs.get(index); m_programCommand = m_program.m_command; } } if (m_initialFile == null) newGame(getBoardSize()); else newGameFile(getBoardSize(), m_move); if (! m_prefs.getBoolean("show-info-panel", true)) showInfoPanel(false); if (m_prefs.getBoolean("show-toolbar", true)) showToolbar(true); restoreMainWindow(getBoardSize()); // Attaching a program can take some time, so we want to make // the window visible, but not draw the window content yet getLayeredPane().setVisible(false); setVisible(true); if (m_programCommand != null) { attachProgram(m_programCommand, m_program, m_register); if (m_gtp == null || m_gtp.isProgramDead()) m_prefs.putInt("program", -1); } setTitle(); registerSpecialMacHandler(); // Children dialogs should be set visible after main window, otherwise // they get minimize window buttons and a taskbar entry (KDE 3.4) if (m_shell != null && m_session.isVisible("shell")) m_shell.setVisible(true); if (m_session.isVisible("tree")) createTree(); if (m_gtp != null && m_session.isVisible("analyze")) createAnalyzeDialog(); setTitleFromProgram(); updateViews(true); getLayeredPane().setVisible(true); unprotectGui(); toFrontLater(); checkComputerMove(); } private void initScore(ConstPointList deadStones) { resetBoard(); GuiBoardUtil.scoreBegin(m_guiBoard, m_countScore, getBoard(), deadStones); m_scoreMode = true; if (m_scoreDialog == null) { ScoringMethod scoringMethod = getGameInfo().parseRules(); m_scoreDialog = new ScoreDialog(this, this, scoringMethod); } restoreLocation(m_scoreDialog, "score"); Komi komi = getGameInfo().getKomi(); m_scoreDialog.showScore(m_countScore, komi); m_scoreDialog.setVisible(true); showStatus(i18n("STAT_SCORE")); } private boolean isComputerBoth() { return (m_computerBlack && m_computerWhite); } private boolean isComputerNone() { return ! (m_computerBlack || m_computerWhite); } private boolean isOutOfSync() { return (m_gtp != null && m_gtp.isOutOfSync()); } private boolean loadFile(File file, int move) { try { LoadFileRunnable runnable = new LoadFileRunnable(file); if (file.length() > 500000) { newGame(getBoardSize()); // Frees space if already large tree GuiUtil.runProgress(this, i18n("LB_LOADING"), runnable); } else runnable.run(null); GameTree tree = runnable.getTree(); initGame(tree.getBoardSize()); m_menuBar.addRecent(file); m_game.init(tree); initGtp(); if (move > 0) { ConstNode node = NodeUtil.findByMoveNumber(getCurrentNode(), move); if (node != null) m_game.gotoNode(node); } setFile(runnable.getGameFile()); FileDialogs.setLastFile(file); String warnings = runnable.getWarnings(); if (warnings != null) { String optionalMessage = i18n("MSG_FILE_FORMAT_WARNING_2") + "\n(" + warnings.replaceAll("\n\\z", "").replaceAll("\n", ")\n(") + ")"; showWarning(i18n("MSG_FILE_FORMAT_WARNING"), optionalMessage, true); } m_computerBlack = false; m_computerWhite = false; createThumbnail(file); } catch (FileNotFoundException e) { showError(i18n("MSG_FILE_NOT_FOUND"), e); return false; } catch (SgfError e) { showError(i18n("MSG_COULD_NOT_READ_FILE"), e); return false; } catch (ErrorMessage e) { showError(i18n("MSG_COULD_NOT_READ_FILE"), e); return false; } catch (Throwable t) { t.printStackTrace(); assert false; return false; } return true; } private void newGame(int size) { initGame(size); initGtp(); updateFromGoBoard(); setTitle(); setTitleFromProgram(); clearStatus(); } private void newGameFile(int size, int move) { initGame(size); if (! loadFile(m_initialFile, move)) m_gameFile = null; } /** Paint point immediately to pretend better responsiveness. Necessary because waiting for a repaint of the Go board can be slow due to the updating game tree or response to GTP commands. */ private void paintImmediately(GoPoint point, GoColor color, boolean isMove) { m_guiBoard.setColor(point, color); if (isMove && m_showLastMove) m_guiBoard.markLastMove(point); m_guiBoard.paintImmediately(point); } private void protectGui() { getGlassPane().setVisible(true); setCursor(getGlassPane(), Cursor.WAIT_CURSOR); } private void registerSpecialMacHandler() { if (! Platform.isMac()) return; Platform.SpecialMacHandler handler = new Platform.SpecialMacHandler() { public boolean handleAbout() { assert SwingUtilities.isEventDispatchThread(); actionAbout(); return true; } public boolean handleOpenFile(String filename) { assert SwingUtilities.isEventDispatchThread(); if (! checkSaveGame()) return true; loadFile(new File(filename), -1); boardChangedBegin(false, true); return true; } public boolean handleQuit() { assert SwingUtilities.isEventDispatchThread(); close(); // close() calls System.exit() if not cancelled return false; } }; Platform.registerSpecialMacHandler(handler); } private void resetBoard() { clearStatus(); m_guiBoard.clearAll(); updateFromGoBoard(); updateGuiBoard(); } private void restoreBoardCursor() { if (m_analyzeCommand != null && (m_analyzeCommand.needsPointArg() || m_analyzeCommand.needsPointListArg())) setBoardCursor(Cursor.HAND_CURSOR); else setBoardCursorDefault(); } private void restoreLocation(JDialog dialog, String name) { m_session.restoreLocation(dialog, this, name + "-" + getBoardSize()); } private void restoreMainWindow(int size) { setState(Frame.NORMAL); m_session.restoreLocation(this, "main-" + size); String path = "windows/main/size-" + size + "/fieldsize"; int fieldSize = m_prefs.getInt(path, -1); if (fieldSize > 0) m_guiBoard.setPreferredFieldSize(new Dimension(fieldSize, fieldSize)); path = "windows/main/size-" + size + "/comment"; int width = m_prefs.getInt(path + "/width", -1); int height = m_prefs.getInt(path + "/height", -1); Dimension preferredCommentSize = null; if (width > 0 && height > 0) { preferredCommentSize = new Dimension(width, height); m_comment.setPreferredSize(preferredCommentSize); } else m_comment.setPreferredSize(); m_splitPane.resetToPreferredSizes(); pack(); // To avoid smallish empty borders (less than one field size) on top // and bottom borders of the board we adjust the comment size slightly // if necessary if (m_infoPanel.getHeight() - m_guiBoard.getHeight() < 2 * fieldSize && preferredCommentSize != null && fieldSize > 0) { preferredCommentSize.height -= 2 * fieldSize; m_comment.setPreferredSize(preferredCommentSize); m_splitPane.resetToPreferredSizes(); pack(); } } private void restoreSize(JDialog dialog, String name) { m_session.restoreSize(dialog, this, name + "-" + getBoardSize()); } private void runLengthyCommand(String cmd, Runnable callback) { assert m_gtp != null; m_gtp.send(cmd, callback); beginLengthyCommand(); } /** Save game to file. @return true If successfully saved. */ private boolean save(GameFile gameFile) { try { new GameWriter(gameFile, getTree(), i18n("LB_GOGUI"), Version.get()); } catch (ErrorMessage e) { showError(i18n("MSG_SAVING_FAILED"), e); return false; } m_menuBar.addRecent(gameFile.m_file); createThumbnail(gameFile.m_file); setFile(gameFile); m_game.clearModified(); updateViews(false); return true; } private boolean saveDialog() { File file = FileDialogs.showSaveSgf(this, m_messageDialogs); if (file == null) return false; GameFile gameFile = new GameFile(); gameFile.m_file = file; if (FileUtil.hasExtension(file, "xml")) gameFile.m_format = GameFile.Format.XML; else gameFile.m_format = GameFile.Format.SGF; return save(gameFile); } private boolean saveParameters(File file) { try { GtpClientUtil.saveParameters(m_gtp, m_analyzeCommands, file); } catch (ErrorMessage e) { showError(i18n("MSG_COULD_NOT_SAVE_PARAMETERS"), e); return false; } return true; } private void savePosition(File file) throws FileNotFoundException { OutputStream out = new FileOutputStream(file); new SgfWriter(out, getBoard(), i18n("LB_GOGUI"), Version.get()); m_menuBar.addRecent(file); updateViews(false); } private void saveSession() { if (m_shell != null) m_shell.saveHistory(); if (m_analyzeDialog != null) { m_analyzeDialog.saveRecent(); m_prefs.putBoolean("analyze-reuse-text-window", m_analyzeDialog.getReuseTextWindow()); } if (! isVisible()) // can that happen? return; if (m_help != null) m_session.saveSize(m_help.getWindow(), "help"); saveSizeAndVisible(m_gameTreeViewer, "tree"); if (m_gtp != null) { saveSizeAndVisible(m_shell, "shell"); saveSizeAndVisible(m_analyzeDialog, "analyze"); } m_session.saveLocation(this, "main-" + getBoardSize()); if (GuiUtil.isNormalSizeMode(this)) { String name = "windows/main/size-" + getBoardSize() + "/fieldsize"; int fieldSize = m_guiBoard.getFieldSize().width; if (fieldSize == 0) // BoardPainter was never invoked return; m_prefs.putInt(name, fieldSize); name = "windows/main/size-" + getBoardSize() + "/comment/width"; m_prefs.putInt(name, m_comment.getWidth()); name = "windows/main/size-" + getBoardSize() + "/comment/height"; m_prefs.putInt(name, m_comment.getHeight()); } // GoGui's program logic does currently not depend on syncing the // preferences to disk immediately, but we do it anyway to work around // a bug in OpenJDK 1.6.0_20 on Linux (Ubuntu 10.10), which fails to // perform the automatic syncing of class Preferences on shutdown of // the VM (probably because of a BadWindow X Error on window closing) try { m_prefs.sync(); } catch (BackingStoreException e) { System.err.println(e.getMessage()); } } private void saveLocation(JDialog dialog, String name) { m_session.saveLocation(dialog, this, name + "-" + getBoardSize()); } private void saveSizeAndVisible(JDialog dialog, String name) { int size = getBoardSize(); if (dialog != null) m_session.saveSize(dialog, this, name + "-" + size); m_session.saveVisible(dialog, name); } private void scoreContinue() { boolean success = endLengthyCommand(); clearStatus(); PointList isDeadStone = null; if (success) { String response = m_gtp.getResponse(); try { isDeadStone = GtpUtil.parsePointList(response, getBoardSize()); } catch (GtpResponseFormatError e) { showError(e); } } updateViews(false); initScore(isDeadStone); } private void scoreDone(Score score) { if (! m_scoreMode) return; m_scoreMode = false; saveLocation(m_scoreDialog, "score"); m_scoreDialog.setVisible(false); clearStatus(); m_guiBoard.clearAll(); if (score != null) setResult(score.formatResult()); } private void sendGtp(Reader reader) { if (m_gtp == null) return; java.io.BufferedReader in; in = new BufferedReader(reader); try { while (true) { try { String line = in.readLine(); if (line == null) break; if (! GtpUtil.isCommand(line)) continue; if (GtpUtil.isStateChangingCommand(line)) { showError(i18n("MSG_BOARD_CHANGING_COMMAND"), ""); break; } try { m_gtp.send(line); } catch (GtpError e) { showError(e); if (m_gtp.isProgramDead() || ! showQuestion(i18n("MSG_CONTINUE_SEND"), "", i18n("LB_CONTINUE_SEND"), false)) break; } } catch (IOException e) { showError(i18n("MSG_COULD_NOT_READ_FILE"), e); break; } } } finally { try { in.close(); } catch (IOException e) { } } } private void sendGtpFile(File file) { try { sendGtp(new FileReader(file)); } catch (FileNotFoundException e) { showError(i18n("MSG_FILE_NOT_FOUND"), e); } } private void sendGtpString(String commands) { commands = commands.replaceAll("\\\\n", "\n"); sendGtp(new StringReader(commands)); } private void setBoardCursor(int type) { setCursor(m_guiBoard, type); } private void setBoardCursorDefault() { setCursorDefault(m_guiBoard); } private void setCursor(Component component, int type) { Cursor cursor = Cursor.getPredefinedCursor(type); component.setCursor(cursor); } private void setCursorDefault(Component component) { component.setCursor(Cursor.getDefaultCursor()); } private void setFile(GameFile gameFile) { m_gameFile = gameFile; setTitle(); } private void setMinimumSize() { int width = 128; int height = 32; Insets rootInsets = getRootPane().getInsets(); int rootInsetsWidth = rootInsets.left + rootInsets.right; Dimension menuBarSize = getJMenuBar().getPreferredSize(); width = Math.max(width, (int)menuBarSize.getWidth() + rootInsetsWidth); height = Math.max(height, (int)menuBarSize.getHeight()); if (m_showToolbar) { Insets contentInsets = getContentPane().getInsets(); int contentInsetsWidth = contentInsets.left + contentInsets.right; Dimension toolBarSize = m_toolBar.getPreferredSize(); width = Math.max(width, (int)toolBarSize.getWidth() + rootInsetsWidth + contentInsetsWidth + GuiUtil.PAD); height += (int)toolBarSize.getHeight(); } height += 224; setMinimumSize(new Dimension(width, height)); } private void setResult(String result) { String oldResult = getGameInfo().get(StringInfo.RESULT); if (! (oldResult == null || oldResult.equals("") || oldResult.equals(result)) && ! showQuestion(format(i18n("MSG_REPLACE_RESULT"), oldResult, result), i18n("MSG_REPLACE_RESULT_2"), i18n("LB_REPLACE_RESULT"), false)) return; m_game.setResult(result); } private void setTitle() { if (m_titleFromProgram != null) { setTitle(m_titleFromProgram); return; } String appName = i18n("LB_GOGUI"); if (m_gtp != null) appName = getProgramLabel(); String filename = null; if (m_gameFile != null) { filename = m_gameFile.m_file.getName(); // On the Mac, a modified document is indicated by setting the // windowModified property in updateViews() if (isModified() && ! Platform.isMac()) filename = filename + "*"; } ConstGameInfo info = getGameInfo(); String gameName = info.suggestGameName(); if (gameName != null) { if (filename != null) gameName = filename + " " + gameName; } else if (filename != null) gameName = filename; if (gameName == null) setTitle(appName); else { String name = getProgramLabel(); String nameBlack = info.get(StringInfoColor.NAME, BLACK); String nameWhite = info.get(StringInfoColor.NAME, WHITE); if (! appName.equals(i18n("LB_GOGUI")) && (ObjectUtil.equals(nameBlack, name) || ObjectUtil.equals(nameWhite, name))) setTitle(gameName); else setTitle(gameName + " - " + appName); } } private void setTitleFromProgram() { if (m_gtp == null) m_titleFromProgram = null; else m_titleFromProgram = GtpClientUtil.getTitle(m_gtp); if (m_titleFromProgram != null) setTitle(m_titleFromProgram); } private void setup(GoPoint point, GoColor color) { assert point != null; m_game.setup(point, color); } private void setupDone() { if (! m_setupMode) return; m_setupMode = false; ConstNode currentNode = getCurrentNode(); if (currentNode.hasSetup() || m_setupColor != getToMove()) m_game.setToMove(m_setupColor); else if (m_setupNodeCreated && currentNode.isEmpty() && currentNode.hasFather()) m_game.truncate(); currentNodeChanged(); } private void showError(String message, Exception e) { showError(message, e, true); } private void showError(String message, Exception e, boolean isCritical) { m_messageDialogs.showError(this, message, e, isCritical); } private void showError(GtpError error) { showError(error, true); } private void showError(GtpResponseFormatError e) { String name = getProgramName(); String mainMessage = format(i18n("MSG_INVALID_RESPONSE"), name); String optionalMessage = format(i18n("MSG_INVALID_RESPONSE_2"), name, e.getMessage()); showError(mainMessage, optionalMessage, true); } private void showError(GtpError e, boolean isCritical) { String name = getProgramName(); String mainMessage; String optionalMessage; if (m_gtp != null && m_gtp.isProgramDead()) { if (m_gtp.wasKilled()) mainMessage = format(i18n("MSG_PROGRAM_TERMINATED"), name); else mainMessage = i18n("MSG_PROGRAM_TERMINATED_UNEXPECTEDLY"); boolean hasErrorOutput = m_shell.isLastTextNonGTP(); boolean anyResponses = m_gtp.getAnyCommandsResponded(); if (hasErrorOutput && ! anyResponses) optionalMessage = format(i18n("MSG_PROGRAM_TERMINATED_2"), name); else if (hasErrorOutput && anyResponses) optionalMessage = format(i18n("MSG_PROGRAM_TERMINATED_3"), name); else optionalMessage = i18n("MSG_PROGRAM_TERMINATED_4"); } else if (e instanceof GtpClient.ExecFailed) { mainMessage = i18n("MSG_COULD_NOT_EXECUTE"); if (StringUtil.isEmpty(e.getMessage())) optionalMessage = i18n("MSG_COULD_NOT_EXECUTE_2"); else optionalMessage = format(i18n("MSG_COULD_NOT_EXECUTE_3"), e.getMessage()); } else { mainMessage = i18n("MSG_COMMAND_FAILED"); if (e.getMessage().trim().equals("")) optionalMessage = format(i18n("MSG_COMMAND_FAILED_2"), e.getCommand()); else optionalMessage = format(i18n("MSG_COMMAND_FAILED_3"), e.getCommand(), e.getMessage()); } showError(mainMessage, optionalMessage, isCritical); updateViews(false); // If program died, menu items need to be updated } private void showError(String mainMessage, String optionalMessage) { showError(mainMessage, optionalMessage, true); } private void showError(String mainMessage, String optionalMessage, boolean isCritical) { m_messageDialogs.showError(this, mainMessage, optionalMessage, isCritical); } private void showGameFinished() { if (m_resigned) return; String disableKey = "net.sf.gogui.gogui.GoGui.game-finished"; m_messageDialogs.showInfo(disableKey, this, i18n("MSG_GAME_FINISHED"), i18n("MSG_GAME_FINISHED_2"), false); } private void showInfo(String mainMessage, String optionalMessage, boolean isCritical) { m_messageDialogs.showInfo(this, mainMessage, optionalMessage, isCritical); } private void showInfoPanel(boolean enable) { if (enable == m_showInfoPanel) return; m_prefs.putBoolean("show-info-panel", enable); m_showInfoPanel = enable; if (enable) { m_innerPanel.remove(m_guiBoard); m_splitPane.add(m_guiBoard); m_innerPanel.add(m_splitPane); } else { m_splitPane.remove(m_guiBoard); m_innerPanel.remove(m_splitPane); m_innerPanel.add(m_guiBoard); } m_splitPane.resetToPreferredSizes(); pack(); } private boolean showOptionalQuestion(String id, String mainMessage, String optionalMessage, String destructiveOption, boolean isCritical) { String disableKey = "net.sf.gogui.gogui.GoGui" + id; return m_messageDialogs.showQuestion(disableKey, this, mainMessage, optionalMessage, destructiveOption, isCritical); } private boolean showQuestion(String mainMessage, String optionalMessage, String destructiveOption, boolean isCritical) { return m_messageDialogs.showQuestion(this, mainMessage, optionalMessage, destructiveOption, isCritical); } private File showSave(String title) { return FileDialogs.showSave(this, title, m_messageDialogs); } private void showShell() { if (m_gtp == null) return; if (m_shell.isVisible()) m_shell.toFront(); else { restoreSize(m_shell, "shell"); m_shell.setVisible(true); } } private void showStatus(String text) { m_statusBar.setText(text); } private void showStatusImmediately(String text) { m_statusBar.immediatelyPaintText(text); } private void showStatusSelectPointList() { showStatus(format(i18n("STAT_SELECT_POINTLIST"), m_analyzeCommand.getLabel())); } private void showStatusSelectTarget() { showStatus(format(i18n("STAT_SELECT_TARGET"), m_analyzeCommand.getResultTitle())); } private void showToolbar(boolean enable) { if (enable == m_showToolbar) return; m_prefs.putBoolean("show-toolbar", enable); m_showToolbar = enable; if (enable) getContentPane().add(m_toolBar, BorderLayout.NORTH); else getContentPane().remove(m_toolBar); m_splitPane.resetToPreferredSizes(); setMinimumSize(); pack(); } private void showWarning(String mainMessage, String optionalMessage, boolean isCritical) { m_messageDialogs.showWarning(this, mainMessage, optionalMessage, isCritical); } private boolean synchronizeProgram() { if (m_gtp == null) { showError(i18n("MSG_NO_PROGRAM_ATTACHED"), "", false); return false; } if (! checkCommandInProgress()) return false; String name = getProgramName(); if (m_gtp.isProgramDead()) { String mainMessage = format(i18n("MSG_PROGRAM_TERMINATED"), name); String optionalMessage = ""; if (m_shell.isLastTextNonGTP()) { showShell(); optionalMessage = format(i18n("MSG_PROGRAM_TERMINATED_CHECK_GTP"), name); } else { showShell(); optionalMessage = format(i18n("MSG_PROGRAM_TERMINATED_REATTACH"), name); } showError(mainMessage, optionalMessage, false); // If program died, menu items need to be updated updateViews(false); return false; } boolean wasOutOfSync = isOutOfSync(); try { ConstGameInfo info = getGameInfo(); m_gtp.synchronize(getBoard(), info.getKomi(), info.getTimeSettings()); } catch (GtpError e) { if (wasOutOfSync) { String mainMessage = format(i18n("MSG_OUT_OF_SYNC"), name); String optionalMessage = format(i18n("MSG_OUT_OF_SYNC_2"), name); showError(mainMessage, optionalMessage, false); } else { String mainMessage = format(i18n("MSG_NOSYNC"), name); String command = null; if (e.getCommand() != null) command = formatCommand(e.getCommand()); String message = e.getMessage(); String response = null; if (! message.trim().equals("")) response = message; String optionalMessage; if (command == null) optionalMessage = format(i18n("MSG_NOSYNC_ERROR"), name, message); else if (response == null) optionalMessage = format(i18n("MSG_NOSYNC_FAILURE"), command, name); else optionalMessage = format(i18n("MSG_NOSYNC_FAILURE_RESPONSE"), command, name, response); showWarning(mainMessage, optionalMessage, true); // If the program died, menu items need to be updated updateViews(false); } return false; } return true; } private void toFrontLater() { // Calling toFront() directly does not give the focus to this // frame, if dialogs are open SwingUtilities.invokeLater(new Runnable() { public void run() { requestFocus(); toFront(); } }); } private void unprotectGui() { getGlassPane().setVisible(false); setCursor(getGlassPane(), Cursor.DEFAULT_CURSOR); } private void updateViews(boolean gameTreeChanged) { updateViews(gameTreeChanged, false); } /** Update all views. @param gameTreeChanged If nodes were added to or removed from the game tree, which will trigger a full and potentially slow game tree update @param sync Update game tree within the event handler if the gameTree has changed. */ private void updateViews(boolean gameTreeChanged, boolean sync) { m_actions.update(); m_menuBar.update(isProgramAttached(), isTreeShown(), isShellShown()); m_gameInfoPanel.update(); m_comment.setComment(getCurrentNode().getComment()); updateFromGoBoard(); updateGuiBoard(); getRootPane().putClientProperty("windowModified", Boolean.valueOf(isModified())); setTitle(); GoGuiUtil.updateMoveText(m_statusBar, getGame()); m_statusBar.setSetupMode(m_setupMode); if (m_setupMode) m_statusBar.setToPlay(m_setupColor); if (m_gameTreeViewer != null) { if (gameTreeChanged) { if (sync) m_gameTreeViewer.update(getTree(), getCurrentNode()); else { protectGui(); showStatus(i18n("STAT_UPDATING_TREE")); Runnable runnable = new Runnable() { public void run() { try { m_gameTreeViewer.update(getTree(), getCurrentNode()); } finally { unprotectGui(); clearStatus(); } } }; SwingUtilities.invokeLater(runnable); } } else m_gameTreeViewer.update(getCurrentNode()); } } private void updateFromGoBoard() { boolean showLastMove = (m_showLastMove && ! (m_showVariations == ShowVariations.SIBLINGS && NodeUtil.hasSiblingMoves(getCurrentNode()))); GuiBoardUtil.updateFromGoBoard(m_guiBoard, getBoard(), m_showLastMove, m_showMoveNumbers); if (! showLastMove || getCurrentNode().getMove() == null) m_guiBoard.markLastMove(null); } private void updateGuiBoard() { if (m_showVariations == ShowVariations.CHILDREN) { ConstPointList moves = NodeUtil.getChildrenMoves(getCurrentNode()); GuiBoardUtil.showMoves(m_guiBoard, moves); } else if (m_showVariations == ShowVariations.SIBLINGS && NodeUtil.hasSiblingMoves(getCurrentNode())) { ConstNode father = getCurrentNode().getFatherConst(); if (father != null) { ConstPointList moves = NodeUtil.getChildrenMoves(father); if (moves.size() > 1) GuiBoardUtil.showMoves(m_guiBoard, moves); } } GuiBoardUtil.showMarkup(m_guiBoard, getCurrentNode()); } }