package com.framsticks.net.client3D; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.SwingUtilities; import javax.swing.text.BadLocationException; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.StyledDocument; import foxtrot.Task; import foxtrot.Worker; /** * The main application class. * * Since formerly the main interface of the program was the command line, * all the actions are executed via text commands. Menu options simply execute * the parseCommand method with the command specified. */ // TODO: console scrollLock public class App extends JFrame { static final long serialVersionUID = 1; private Client client; private Viewer viewer; private StyledDocument styledDocument; private ArrayList commandHistory; private JTextPane textPane; private JTextField inputLine; private boolean lockScroll = false; private JMenu viewMenu; private JMenuBar menuBar; private JMenuItem connectItem; private JMenuItem disconnectItem; private JCheckBoxMenuItem loggingItem; private JCheckBoxMenuItem autorefreshItem; private JMenu styleMenu; private final String DEFAULT_HOST = "127.0.0.1"; // "192.168.10.3"; // //192.168.1.102 private final String DEFAULT_PORT = "9009"; private class SwitchCreatureAction implements ActionListener { private App console; private int group; private int index; public SwitchCreatureAction(App console, int group, int index) { this.console = console; this.group = group; this.index = index; } public void actionPerformed(ActionEvent e) { console.parseCommand("viewcreature " + group + " " + index); } } /** * Constructor. */ public App() { super("Framsticks 3D Client"); init(); } private void init() { JFrame.setDefaultLookAndFeelDecorated(true); commandHistory = new ArrayList(); createChildren(); createMenuBar(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); client = new Client(); try { viewer = new Viewer(this); viewer.showInFrame(); } // An exception thrown by Viewer's constructor. catch (IOException e) { Log.getInstance().log("err", "Couldn't initialize the viewer window: " + e.toString()); e.printStackTrace(); } } private void createChildren() { JFrame.setDefaultLookAndFeelDecorated(true); setSize(500, 500); setLocationRelativeTo(null); setLocation(this.getX() + 255, this.getY()); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); getContentPane().setLayout(new BorderLayout()); createTextArea(); // since all options are now available in the menu, // the textual input line for commands has been hidden: // createInputLine(); Log.getInstance().addLoggerListener(new ILogListener() { public void onMesssage(String category, String text) { try { styledDocument.insertString(styledDocument.getLength(), text + "\n", styledDocument.getStyle(category)); if (!lockScroll) textPane.scrollRectToVisible(textPane.getVisibleRect()); } catch (BadLocationException ble) { System.err .println("Couldn't insert initial text into text pane."); } } }); } public void setInputLineText(String text) { inputLine.setText(text); } private void createTextArea() { textPane = new JTextPane(); textPane.setEditable(false); styledDocument = textPane.getStyledDocument(); addStylesToDocument(styledDocument); JScrollPane paneScrollPane = new JScrollPane(textPane); paneScrollPane .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); getContentPane().add(paneScrollPane, BorderLayout.CENTER); } class StyleListener implements ActionListener { String styleName; StyleListener(String name) { styleName = name; } public void actionPerformed(ActionEvent event) { parseCommand("style " + styleName); } } /** * Adds a new style. * * @param name * Name of the new style. */ public void AddStyle(String name) { JMenuItem item = new JMenuItem(name); styleMenu.add(item); item.addActionListener(new StyleListener(name)); } private void createMenuBar() { menuBar = new JMenuBar(); JMenu serverItem = new JMenu("Server"); menuBar.add(serverItem); JMenuItem testModeItem = new JMenuItem("Test mode"); serverItem.add(testModeItem); testModeItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("connect mock"); } }); connectItem = new JMenuItem("Connect"); serverItem.add(connectItem); final App window = this; connectItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { String params = (String) JOptionPane.showInputDialog(window, "Enter server address (IP:port)\n", "Connect", JOptionPane.PLAIN_MESSAGE, null, null, DEFAULT_HOST + ":" + DEFAULT_PORT); if (params != null) { params = params.replaceAll(":", " "); parseCommand("connect " + params); } } }); disconnectItem = new JMenuItem("Disonnect"); serverItem.add(disconnectItem); disconnectItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("disconnect"); } }); JMenu simulationItem = new JMenu("Simulation"); menuBar.add(simulationItem); JMenuItem initItem = new JMenuItem("Init"); simulationItem.add(initItem); initItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("> call /simulator init"); } }); JMenuItem startItem = new JMenuItem("Start"); simulationItem.add(startItem); startItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("> set /simulator running 1"); } }); JMenuItem stopItem = new JMenuItem("Stop"); simulationItem.add(stopItem); stopItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("> set /simulator running 0"); } }); JMenuItem refreshCreaturesItem = new JMenuItem("Refresh creatures"); simulationItem.add(refreshCreaturesItem); refreshCreaturesItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("refreshcreatures"); } }); JMenuItem refreshWorldItem = new JMenuItem("Refresh world"); simulationItem.add(refreshWorldItem); refreshWorldItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { parseCommand("refreshworld"); } }); styleMenu = new JMenu("Style"); menuBar.add(styleMenu); viewMenu = new JMenu("View"); menuBar.add(viewMenu); rebuildViewMenu(null); JMenu optionsMenu = new JMenu("Options"); menuBar.add(optionsMenu); autorefreshItem = new JCheckBoxMenuItem("Autorefresh"); autorefreshItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { switchAutorefresh(); } }); optionsMenu.add(autorefreshItem); loggingItem = new JCheckBoxMenuItem("Logging"); loggingItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { switchLogging(); } }); optionsMenu.add(loggingItem); loggingItem.setSelected(true); setJMenuBar(menuBar); } /** * Turns logging on and off. */ public void switchLogging() { Log.getInstance().setEnabled(!Log.getInstance().isEnabled()); } /** * Turns autorefreshing on and off. */ public void switchAutorefresh() { if (autorefreshItem.isSelected()) { parseCommand("refreshcreatures"); } } /** * Rebuilds the 'View' menu with a specified list of creatures. * * @param creatures * Creatures to include in the 'View' menu. */ public void rebuildViewMenu(Creature[] creatures) { viewMenu.removeAll(); JMenuItem allMenuItem = new JMenuItem("World"); allMenuItem.addActionListener(new SwitchCreatureAction(this, -1, -1)); viewMenu.add(allMenuItem); if (creatures != null) { for (Creature creature : creatures) { JMenuItem item = new JMenuItem("Creature " + creature.toString()); item.addActionListener(new SwitchCreatureAction(this, creature .getGroup(), creature.getIndex())); viewMenu.add(item); } } } /** * Sets font styles in a specified document. * * @param doc * A document object. */ protected void addStylesToDocument(StyledDocument doc) { // Initialize some styles Style def = StyleContext.getDefaultStyleContext().getStyle( StyleContext.DEFAULT_STYLE); Style s = doc.addStyle("dbg", def); StyleConstants.setForeground(s, new Color(150, 150, 150)); s = doc.addStyle("wrn", def); StyleConstants.setForeground(s, new Color(255, 150, 0)); s = doc.addStyle("err", def); StyleConstants.setForeground(s, new Color(255, 0, 0)); s = doc.addStyle("cmd", def); StyleConstants.setForeground(s, new Color(0, 150, 0)); s = doc.addStyle("<<<", def); StyleConstants.setForeground(s, new Color(100, 40, 200)); // StyleConstants.setFontFamily(s, "Courier New"); s = doc.addStyle(">>>", def); StyleConstants.setForeground(s, new Color(30, 100, 200)); // StyleConstants.setFontFamily(s, "Courier New"); } private void parseCommand(String line) { commandHistory.add(line); commandHistory.size(); int endIndex; if (line.contains(" ")) endIndex = line.indexOf(" "); else endIndex = line.length(); String command = line.substring(0, endIndex).trim(); String params = line.substring(endIndex).trim(); handleCommand(command, params); } private void handleCommand(String command, String params) { Log.getInstance().log("cmd", command + " " + params); if (command.equals("connect") || command.equals("c")) { if (params.equals("mock")) { connectMockAction(); } else { String[] paramList = params.split(" "); String host = ""; int port = 9009; if (paramList.length > 0) host = paramList[0]; try { if (paramList.length > 1) port = Integer.parseInt(paramList[1]); } catch (NumberFormatException e) { Log.getInstance().log("err", "Invalid port, setting default value 9009"); } connectHostAction(host, port); } } else if (command.equals("disconnect")) { viewer.setCreatures(null); viewer.setWorld(null); client.closeConnection(); } else if (command.equals("refreshcreatures") || command.equals("rc")) { refreshCreaturesAction(); } else if (command.equals("viewcreature")) { viewCreatureAction(params); } else if (command.equals("refreshworld") || command.equals("rw")) { refreshWorldAction(); } else if (command.equals("style") || command.equals("s")) { styleAction(params); } else if (command.equals(">")) { serverRequestAction(params); } else if (command.equals("quit") || command.equals("q")) { // TODO: quit command } } private void connectMockAction() { client.initConnectionMock(); refreshWorldAction(); refreshCreaturesAction(); } private void connectHostAction(String host, int port) { client.initConnection(host, port); if (client.isConnected()) { refreshWorldAction(); refreshCreaturesAction(); } } private void refreshCreaturesAction() { Log.getInstance().log("dbg", "refreshCreaturesAction"); try { Creature[] creatures = (Creature[]) Worker.post(new Task() { public Object run() throws Exception { return client.readCreatures(); } }); viewer.setCreatures(creatures); rebuildViewMenu(creatures); } catch (Exception e) { Log.getInstance().log("err", e.toString()); e.printStackTrace(); } Log.getInstance().log( "autorefresh " + autorefreshItem.isSelected() + " " + client.isConnected()); if (autorefreshItem.isSelected() && client.isConnected()) { SwingUtilities.invokeLater(new Runnable() { public void run() { refreshCreaturesAction(); } }); } } private void refreshWorldAction() { Log.getInstance().log("dbg", "refreshWorldAction"); try { World world = (World) Worker.post(new Task() { public Object run() throws Exception { return client.readWorld(); } }); viewer.setWorld(world); } catch (Exception e) { Log.getInstance().log("err", e.toString()); e.printStackTrace(); } } private void styleAction(String styleName) { Log.getInstance().log("dbg", "SetStyle " + styleName); viewer.setStyle(styleName); } private void viewCreatureAction(String params) { int group = -1; int index = -1; try { String[] arr = params.split(" "); if (arr.length > 0) { group = Integer.parseInt(arr[0]); } if (arr.length > 1) { index = Integer.parseInt(arr[1]); } } catch (Exception e) { e.printStackTrace(); } viewer.setView(group, index); } private void serverRequestAction(String request) { Log.getInstance().log("dbg", "serverRequestAction \"" + request + "\""); final String finalRequest = request; try { Worker.post(new Task() { public Object run() throws Exception { client.send(finalRequest); return null; } }); } catch (Exception e) { e.printStackTrace(); } Log.getInstance().log("dbg", "serverRequestAction finished \"" + request + "\""); } /** * The application entry-point. * * @param args * Command-line argument. */ public static void main(String[] args) { new App(); } }