1 | package com.framsticks.net.client3D;
|
---|
2 |
|
---|
3 | import java.awt.BorderLayout;
|
---|
4 | import java.awt.Color;
|
---|
5 | import java.awt.event.ActionEvent;
|
---|
6 | import java.awt.event.ActionListener;
|
---|
7 | import java.io.IOException;
|
---|
8 | import java.util.ArrayList;
|
---|
9 |
|
---|
10 | import javax.swing.JCheckBoxMenuItem;
|
---|
11 | import javax.swing.JFrame;
|
---|
12 | import javax.swing.JMenu;
|
---|
13 | import javax.swing.JMenuBar;
|
---|
14 | import javax.swing.JMenuItem;
|
---|
15 | import javax.swing.JOptionPane;
|
---|
16 | import javax.swing.JScrollPane;
|
---|
17 | import javax.swing.JTextField;
|
---|
18 | import javax.swing.JTextPane;
|
---|
19 | import javax.swing.SwingUtilities;
|
---|
20 | import javax.swing.text.BadLocationException;
|
---|
21 | import javax.swing.text.Style;
|
---|
22 | import javax.swing.text.StyleConstants;
|
---|
23 | import javax.swing.text.StyleContext;
|
---|
24 | import javax.swing.text.StyledDocument;
|
---|
25 |
|
---|
26 | import foxtrot.Task;
|
---|
27 | import foxtrot.Worker;
|
---|
28 |
|
---|
29 | /**
|
---|
30 | * The main application class.
|
---|
31 | *
|
---|
32 | * Since formerly the main interface of the program was the command line,
|
---|
33 | * all the actions are executed via text commands. Menu options simply execute
|
---|
34 | * the parseCommand method with the command specified.
|
---|
35 | */
|
---|
36 | // TODO: console scrollLock
|
---|
37 | public class App extends JFrame {
|
---|
38 | static final long serialVersionUID = 1;
|
---|
39 |
|
---|
40 | private Client client;
|
---|
41 | private Viewer viewer;
|
---|
42 | private StyledDocument styledDocument;
|
---|
43 | private ArrayList<String> commandHistory;
|
---|
44 | private JTextPane textPane;
|
---|
45 | private JTextField inputLine;
|
---|
46 | private boolean lockScroll = false;
|
---|
47 | private JMenu viewMenu;
|
---|
48 | private JMenuBar menuBar;
|
---|
49 | private JMenuItem connectItem;
|
---|
50 | private JMenuItem disconnectItem;
|
---|
51 | private JCheckBoxMenuItem loggingItem;
|
---|
52 | private JCheckBoxMenuItem autorefreshItem;
|
---|
53 | private JMenu styleMenu;
|
---|
54 |
|
---|
55 | private final String DEFAULT_HOST = "127.0.0.1"; // "192.168.10.3";
|
---|
56 | // //192.168.1.102
|
---|
57 | private final String DEFAULT_PORT = "9009";
|
---|
58 |
|
---|
59 | private class SwitchCreatureAction implements ActionListener {
|
---|
60 | private App console;
|
---|
61 | private int group;
|
---|
62 | private int index;
|
---|
63 |
|
---|
64 | public SwitchCreatureAction(App console, int group, int index) {
|
---|
65 | this.console = console;
|
---|
66 | this.group = group;
|
---|
67 | this.index = index;
|
---|
68 | }
|
---|
69 |
|
---|
70 | public void actionPerformed(ActionEvent e) {
|
---|
71 | console.parseCommand("viewcreature " + group + " " + index);
|
---|
72 | }
|
---|
73 | }
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * Constructor.
|
---|
77 | */
|
---|
78 | public App() {
|
---|
79 | super("Framsticks 3D Client");
|
---|
80 | init();
|
---|
81 | }
|
---|
82 |
|
---|
83 | private void init() {
|
---|
84 | JFrame.setDefaultLookAndFeelDecorated(true);
|
---|
85 | commandHistory = new ArrayList<String>();
|
---|
86 | createChildren();
|
---|
87 | createMenuBar();
|
---|
88 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
---|
89 | setVisible(true);
|
---|
90 |
|
---|
91 | client = new Client();
|
---|
92 | try
|
---|
93 | {
|
---|
94 | viewer = new Viewer(this);
|
---|
95 | viewer.showInFrame();
|
---|
96 | }
|
---|
97 | // An exception thrown by Viewer's constructor.
|
---|
98 | catch (IOException e)
|
---|
99 | {
|
---|
100 | Log.getInstance().log("err", "Couldn't initialize the viewer window: " + e.toString());
|
---|
101 | e.printStackTrace();
|
---|
102 | }
|
---|
103 | }
|
---|
104 |
|
---|
105 | private void createChildren() {
|
---|
106 | JFrame.setDefaultLookAndFeelDecorated(true);
|
---|
107 | setSize(500, 500);
|
---|
108 | setLocationRelativeTo(null);
|
---|
109 | setLocation(this.getX() + 255, this.getY());
|
---|
110 | setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
---|
111 |
|
---|
112 | getContentPane().setLayout(new BorderLayout());
|
---|
113 | createTextArea();
|
---|
114 | // since all options are now available in the menu,
|
---|
115 | // the textual input line for commands has been hidden:
|
---|
116 | // createInputLine();
|
---|
117 |
|
---|
118 | Log.getInstance().addLoggerListener(new ILogListener() {
|
---|
119 | public void onMesssage(String category, String text) {
|
---|
120 | try {
|
---|
121 | styledDocument.insertString(styledDocument.getLength(),
|
---|
122 | text + "\n", styledDocument.getStyle(category));
|
---|
123 | if (!lockScroll)
|
---|
124 | textPane.scrollRectToVisible(textPane.getVisibleRect());
|
---|
125 | } catch (BadLocationException ble) {
|
---|
126 | System.err
|
---|
127 | .println("Couldn't insert initial text into text pane.");
|
---|
128 | }
|
---|
129 | }
|
---|
130 | });
|
---|
131 |
|
---|
132 | }
|
---|
133 |
|
---|
134 | public void setInputLineText(String text) {
|
---|
135 | inputLine.setText(text);
|
---|
136 | }
|
---|
137 |
|
---|
138 | private void createTextArea() {
|
---|
139 | textPane = new JTextPane();
|
---|
140 | textPane.setEditable(false);
|
---|
141 | styledDocument = textPane.getStyledDocument();
|
---|
142 | addStylesToDocument(styledDocument);
|
---|
143 |
|
---|
144 | JScrollPane paneScrollPane = new JScrollPane(textPane);
|
---|
145 | paneScrollPane
|
---|
146 | .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
|
---|
147 |
|
---|
148 | getContentPane().add(paneScrollPane, BorderLayout.CENTER);
|
---|
149 | }
|
---|
150 |
|
---|
151 | class StyleListener implements ActionListener {
|
---|
152 | String styleName;
|
---|
153 |
|
---|
154 | StyleListener(String name) {
|
---|
155 | styleName = name;
|
---|
156 | }
|
---|
157 |
|
---|
158 | public void actionPerformed(ActionEvent event) {
|
---|
159 | parseCommand("style " + styleName);
|
---|
160 | }
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * Adds a new style.
|
---|
165 | *
|
---|
166 | * @param name
|
---|
167 | * Name of the new style.
|
---|
168 | */
|
---|
169 | public void AddStyle(String name) {
|
---|
170 | JMenuItem item = new JMenuItem(name);
|
---|
171 | styleMenu.add(item);
|
---|
172 | item.addActionListener(new StyleListener(name));
|
---|
173 | }
|
---|
174 |
|
---|
175 | private void createMenuBar() {
|
---|
176 | menuBar = new JMenuBar();
|
---|
177 |
|
---|
178 | JMenu serverItem = new JMenu("Server");
|
---|
179 | menuBar.add(serverItem);
|
---|
180 |
|
---|
181 | JMenuItem testModeItem = new JMenuItem("Test mode");
|
---|
182 | serverItem.add(testModeItem);
|
---|
183 |
|
---|
184 | testModeItem.addActionListener(new ActionListener() {
|
---|
185 | public void actionPerformed(ActionEvent event) {
|
---|
186 | parseCommand("connect mock");
|
---|
187 | }
|
---|
188 | });
|
---|
189 |
|
---|
190 | connectItem = new JMenuItem("Connect");
|
---|
191 | serverItem.add(connectItem);
|
---|
192 |
|
---|
193 | final App window = this;
|
---|
194 | connectItem.addActionListener(new ActionListener() {
|
---|
195 | public void actionPerformed(ActionEvent event) {
|
---|
196 | String params = (String) JOptionPane.showInputDialog(window,
|
---|
197 | "Enter server address (IP:port)\n", "Connect",
|
---|
198 | JOptionPane.PLAIN_MESSAGE, null, null, DEFAULT_HOST
|
---|
199 | + ":" + DEFAULT_PORT);
|
---|
200 | if (params != null) {
|
---|
201 | params = params.replaceAll(":", " ");
|
---|
202 | parseCommand("connect " + params);
|
---|
203 | }
|
---|
204 | }
|
---|
205 | });
|
---|
206 |
|
---|
207 | disconnectItem = new JMenuItem("Disonnect");
|
---|
208 | serverItem.add(disconnectItem);
|
---|
209 | disconnectItem.addActionListener(new ActionListener() {
|
---|
210 | public void actionPerformed(ActionEvent event) {
|
---|
211 | parseCommand("disconnect");
|
---|
212 | }
|
---|
213 | });
|
---|
214 |
|
---|
215 | JMenu simulationItem = new JMenu("Simulation");
|
---|
216 | menuBar.add(simulationItem);
|
---|
217 |
|
---|
218 | JMenuItem initItem = new JMenuItem("Init");
|
---|
219 | simulationItem.add(initItem);
|
---|
220 | initItem.addActionListener(new ActionListener() {
|
---|
221 | public void actionPerformed(ActionEvent event) {
|
---|
222 | parseCommand("> call /simulator init");
|
---|
223 | }
|
---|
224 | });
|
---|
225 |
|
---|
226 | JMenuItem startItem = new JMenuItem("Start");
|
---|
227 | simulationItem.add(startItem);
|
---|
228 | startItem.addActionListener(new ActionListener() {
|
---|
229 | public void actionPerformed(ActionEvent event) {
|
---|
230 | parseCommand("> set /simulator running 1");
|
---|
231 | }
|
---|
232 | });
|
---|
233 |
|
---|
234 | JMenuItem stopItem = new JMenuItem("Stop");
|
---|
235 | simulationItem.add(stopItem);
|
---|
236 | stopItem.addActionListener(new ActionListener() {
|
---|
237 | public void actionPerformed(ActionEvent event) {
|
---|
238 | parseCommand("> set /simulator running 0");
|
---|
239 | }
|
---|
240 | });
|
---|
241 |
|
---|
242 | JMenuItem refreshCreaturesItem = new JMenuItem("Refresh creatures");
|
---|
243 | simulationItem.add(refreshCreaturesItem);
|
---|
244 | refreshCreaturesItem.addActionListener(new ActionListener() {
|
---|
245 | public void actionPerformed(ActionEvent event) {
|
---|
246 | parseCommand("refreshcreatures");
|
---|
247 | }
|
---|
248 | });
|
---|
249 |
|
---|
250 | JMenuItem refreshWorldItem = new JMenuItem("Refresh world");
|
---|
251 | simulationItem.add(refreshWorldItem);
|
---|
252 | refreshWorldItem.addActionListener(new ActionListener() {
|
---|
253 | public void actionPerformed(ActionEvent event) {
|
---|
254 | parseCommand("refreshworld");
|
---|
255 | }
|
---|
256 | });
|
---|
257 |
|
---|
258 | styleMenu = new JMenu("Style");
|
---|
259 | menuBar.add(styleMenu);
|
---|
260 |
|
---|
261 | viewMenu = new JMenu("View");
|
---|
262 | menuBar.add(viewMenu);
|
---|
263 | rebuildViewMenu(null);
|
---|
264 |
|
---|
265 | JMenu optionsMenu = new JMenu("Options");
|
---|
266 | menuBar.add(optionsMenu);
|
---|
267 |
|
---|
268 | autorefreshItem = new JCheckBoxMenuItem("Autorefresh");
|
---|
269 | autorefreshItem.addActionListener(new ActionListener() {
|
---|
270 | public void actionPerformed(ActionEvent e) {
|
---|
271 | switchAutorefresh();
|
---|
272 | }
|
---|
273 | });
|
---|
274 | optionsMenu.add(autorefreshItem);
|
---|
275 |
|
---|
276 | loggingItem = new JCheckBoxMenuItem("Logging");
|
---|
277 | loggingItem.addActionListener(new ActionListener() {
|
---|
278 | public void actionPerformed(ActionEvent e) {
|
---|
279 | switchLogging();
|
---|
280 | }
|
---|
281 | });
|
---|
282 | optionsMenu.add(loggingItem);
|
---|
283 | loggingItem.setSelected(true);
|
---|
284 |
|
---|
285 | setJMenuBar(menuBar);
|
---|
286 | }
|
---|
287 |
|
---|
288 | /**
|
---|
289 | * Turns logging on and off.
|
---|
290 | */
|
---|
291 | public void switchLogging() {
|
---|
292 | Log.getInstance().setEnabled(!Log.getInstance().isEnabled());
|
---|
293 | }
|
---|
294 |
|
---|
295 | /**
|
---|
296 | * Turns autorefreshing on and off.
|
---|
297 | */
|
---|
298 | public void switchAutorefresh() {
|
---|
299 | if (autorefreshItem.isSelected()) {
|
---|
300 | parseCommand("refreshcreatures");
|
---|
301 | }
|
---|
302 | }
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * Rebuilds the 'View' menu with a specified list of creatures.
|
---|
306 | *
|
---|
307 | * @param creatures
|
---|
308 | * Creatures to include in the 'View' menu.
|
---|
309 | */
|
---|
310 | public void rebuildViewMenu(Creature[] creatures) {
|
---|
311 | viewMenu.removeAll();
|
---|
312 | JMenuItem allMenuItem = new JMenuItem("World");
|
---|
313 | allMenuItem.addActionListener(new SwitchCreatureAction(this, -1, -1));
|
---|
314 | viewMenu.add(allMenuItem);
|
---|
315 | if (creatures != null) {
|
---|
316 | for (Creature creature : creatures) {
|
---|
317 | JMenuItem item = new JMenuItem("Creature "
|
---|
318 | + creature.toString());
|
---|
319 | item.addActionListener(new SwitchCreatureAction(this, creature
|
---|
320 | .getGroup(), creature.getIndex()));
|
---|
321 | viewMenu.add(item);
|
---|
322 | }
|
---|
323 | }
|
---|
324 | }
|
---|
325 |
|
---|
326 | /**
|
---|
327 | * Sets font styles in a specified document.
|
---|
328 | *
|
---|
329 | * @param doc
|
---|
330 | * A document object.
|
---|
331 | */
|
---|
332 | protected void addStylesToDocument(StyledDocument doc) {
|
---|
333 | // Initialize some styles
|
---|
334 | Style def = StyleContext.getDefaultStyleContext().getStyle(
|
---|
335 | StyleContext.DEFAULT_STYLE);
|
---|
336 |
|
---|
337 | Style s = doc.addStyle("dbg", def);
|
---|
338 | StyleConstants.setForeground(s, new Color(150, 150, 150));
|
---|
339 |
|
---|
340 | s = doc.addStyle("wrn", def);
|
---|
341 | StyleConstants.setForeground(s, new Color(255, 150, 0));
|
---|
342 |
|
---|
343 | s = doc.addStyle("err", def);
|
---|
344 | StyleConstants.setForeground(s, new Color(255, 0, 0));
|
---|
345 |
|
---|
346 | s = doc.addStyle("cmd", def);
|
---|
347 | StyleConstants.setForeground(s, new Color(0, 150, 0));
|
---|
348 |
|
---|
349 | s = doc.addStyle("<<<", def);
|
---|
350 | StyleConstants.setForeground(s, new Color(100, 40, 200));
|
---|
351 | // StyleConstants.setFontFamily(s, "Courier New");
|
---|
352 |
|
---|
353 | s = doc.addStyle(">>>", def);
|
---|
354 | StyleConstants.setForeground(s, new Color(30, 100, 200));
|
---|
355 | // StyleConstants.setFontFamily(s, "Courier New");
|
---|
356 | }
|
---|
357 |
|
---|
358 | private void parseCommand(String line) {
|
---|
359 | commandHistory.add(line);
|
---|
360 | commandHistory.size();
|
---|
361 |
|
---|
362 | int endIndex;
|
---|
363 | if (line.contains(" "))
|
---|
364 | endIndex = line.indexOf(" ");
|
---|
365 | else
|
---|
366 | endIndex = line.length();
|
---|
367 |
|
---|
368 | String command = line.substring(0, endIndex).trim();
|
---|
369 | String params = line.substring(endIndex).trim();
|
---|
370 |
|
---|
371 | handleCommand(command, params);
|
---|
372 | }
|
---|
373 |
|
---|
374 | private void handleCommand(String command, String params) {
|
---|
375 | Log.getInstance().log("cmd", command + " " + params);
|
---|
376 | if (command.equals("connect") || command.equals("c")) {
|
---|
377 | if (params.equals("mock")) {
|
---|
378 | connectMockAction();
|
---|
379 | } else {
|
---|
380 | String[] paramList = params.split(" ");
|
---|
381 | String host = "";
|
---|
382 | int port = 9009;
|
---|
383 | if (paramList.length > 0)
|
---|
384 | host = paramList[0];
|
---|
385 | try {
|
---|
386 | if (paramList.length > 1)
|
---|
387 | port = Integer.parseInt(paramList[1]);
|
---|
388 | } catch (NumberFormatException e) {
|
---|
389 | Log.getInstance().log("err",
|
---|
390 | "Invalid port, setting default value 9009");
|
---|
391 | }
|
---|
392 | connectHostAction(host, port);
|
---|
393 | }
|
---|
394 | } else if (command.equals("disconnect")) {
|
---|
395 | viewer.setCreatures(null);
|
---|
396 | viewer.setWorld(null);
|
---|
397 | client.closeConnection();
|
---|
398 | } else if (command.equals("refreshcreatures") || command.equals("rc")) {
|
---|
399 | refreshCreaturesAction();
|
---|
400 | } else if (command.equals("viewcreature")) {
|
---|
401 | viewCreatureAction(params);
|
---|
402 | } else if (command.equals("refreshworld") || command.equals("rw")) {
|
---|
403 | refreshWorldAction();
|
---|
404 | } else if (command.equals("style") || command.equals("s")) {
|
---|
405 | styleAction(params);
|
---|
406 | } else if (command.equals(">")) {
|
---|
407 | serverRequestAction(params);
|
---|
408 | } else if (command.equals("quit") || command.equals("q")) {
|
---|
409 | // TODO: quit command
|
---|
410 | }
|
---|
411 | }
|
---|
412 |
|
---|
413 | private void connectMockAction() {
|
---|
414 | client.initConnectionMock();
|
---|
415 | refreshWorldAction();
|
---|
416 | refreshCreaturesAction();
|
---|
417 | }
|
---|
418 |
|
---|
419 | private void connectHostAction(String host, int port) {
|
---|
420 | client.initConnection(host, port);
|
---|
421 | if (client.isConnected()) {
|
---|
422 | refreshWorldAction();
|
---|
423 | refreshCreaturesAction();
|
---|
424 | }
|
---|
425 | }
|
---|
426 |
|
---|
427 | private void refreshCreaturesAction() {
|
---|
428 | Log.getInstance().log("dbg", "refreshCreaturesAction");
|
---|
429 | try {
|
---|
430 | Creature[] creatures = (Creature[]) Worker.post(new Task() {
|
---|
431 | public Object run() throws Exception {
|
---|
432 | return client.readCreatures();
|
---|
433 | }
|
---|
434 | });
|
---|
435 | viewer.setCreatures(creatures);
|
---|
436 | rebuildViewMenu(creatures);
|
---|
437 | } catch (Exception e) {
|
---|
438 | Log.getInstance().log("err", e.toString());
|
---|
439 | e.printStackTrace();
|
---|
440 | }
|
---|
441 | Log.getInstance().log(
|
---|
442 | "autorefresh " + autorefreshItem.isSelected() + " "
|
---|
443 | + client.isConnected());
|
---|
444 | if (autorefreshItem.isSelected() && client.isConnected()) {
|
---|
445 | SwingUtilities.invokeLater(new Runnable() {
|
---|
446 | public void run() {
|
---|
447 | refreshCreaturesAction();
|
---|
448 | }
|
---|
449 | });
|
---|
450 | }
|
---|
451 | }
|
---|
452 |
|
---|
453 | private void refreshWorldAction() {
|
---|
454 | Log.getInstance().log("dbg", "refreshWorldAction");
|
---|
455 | try {
|
---|
456 | World world = (World) Worker.post(new Task() {
|
---|
457 | public Object run() throws Exception {
|
---|
458 | return client.readWorld();
|
---|
459 | }
|
---|
460 | });
|
---|
461 | viewer.setWorld(world);
|
---|
462 | } catch (Exception e) {
|
---|
463 | Log.getInstance().log("err", e.toString());
|
---|
464 | e.printStackTrace();
|
---|
465 | }
|
---|
466 | }
|
---|
467 |
|
---|
468 | private void styleAction(String styleName) {
|
---|
469 | Log.getInstance().log("dbg", "SetStyle " + styleName);
|
---|
470 | viewer.setStyle(styleName);
|
---|
471 | }
|
---|
472 |
|
---|
473 | private void viewCreatureAction(String params) {
|
---|
474 | int group = -1;
|
---|
475 | int index = -1;
|
---|
476 | try {
|
---|
477 | String[] arr = params.split(" ");
|
---|
478 | if (arr.length > 0) {
|
---|
479 | group = Integer.parseInt(arr[0]);
|
---|
480 | }
|
---|
481 | if (arr.length > 1) {
|
---|
482 | index = Integer.parseInt(arr[1]);
|
---|
483 | }
|
---|
484 | } catch (Exception e) {
|
---|
485 | e.printStackTrace();
|
---|
486 | }
|
---|
487 |
|
---|
488 | viewer.setView(group, index);
|
---|
489 | }
|
---|
490 |
|
---|
491 | private void serverRequestAction(String request) {
|
---|
492 | Log.getInstance().log("dbg", "serverRequestAction \"" + request + "\"");
|
---|
493 | final String finalRequest = request;
|
---|
494 | try {
|
---|
495 | Worker.post(new Task() {
|
---|
496 | public Object run() throws Exception {
|
---|
497 | client.send(finalRequest);
|
---|
498 | return null;
|
---|
499 | }
|
---|
500 | });
|
---|
501 | } catch (Exception e) {
|
---|
502 | e.printStackTrace();
|
---|
503 | }
|
---|
504 | Log.getInstance().log("dbg",
|
---|
505 | "serverRequestAction finished \"" + request + "\"");
|
---|
506 | }
|
---|
507 |
|
---|
508 | /**
|
---|
509 | * The application entry-point.
|
---|
510 | *
|
---|
511 | * @param args
|
---|
512 | * Command-line argument.
|
---|
513 | */
|
---|
514 | public static void main(String[] args) {
|
---|
515 | new App();
|
---|
516 | }
|
---|
517 | }
|
---|