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