package org.suffieldacademy.introcs; import org.suffieldacademy.introcs.jhealy.LineSocket; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.Socket; import java.util.function.Consumer; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; /** *
* A sample client program to connect to a RobotServer. This class * is a skeleton and does the basic setup necessary to connect to another * program via the network and allow the sending and receiving of data. * You will need to implement any protocol between the two programs, and * may wish to add other features (buttons, macros, keyboard listeners, * etc.) that make the program easier to use. *
* *
* Note that this class has a send()
method. When you call
* this method and provide a String object, it sends the message to
* the remotely connected socket, as well as prints it to the screen.
*
* Note that this class has a println()
method. When you call
* this method and provide a String object, it will print it in the GUI
* window (rather than on the console). You can use it for debugging.
*
* Handles a key being pressed while the focus is on the input * text field. You'll probably want to examine the key code * and send a specific message to the remote host based upon * which key is being pressed. The default is simply to send * the value of the keycode. *
* ** See https://docs.oracle.com/javase/8/docs/api/java/awt/event/KeyEvent.html * for a list of key names. You can compare the key code to them by * writing something like: *
* ** if (keyCode == KeyEvent.VK_UP) { * ... * } ** *
* That would check to see if the "up arrow" key was the one that * was pressed. *
* * @param keyCode The code of the key being pressed. */ public void keyPressed(int keyCode) { println("Ignoring key press: " + keyCode); } /** ** Handles a key being released while the focus is on the input * text field. You'll probably want to examine the key code * and send a specific message to the remote host based upon * which key is being pressed. The default is simply to send * the value of the keycode. *
* ** See https://docs.oracle.com/javase/8/docs/api/java/awt/event/KeyEvent.html * for a list of key names. You can compare the key code to them by * writing something like: *
* ** if (keyCode == KeyEvent.VK_SPACE) { * ... * } ** *
* That would check to see if the space bar was the one that * was released. *
* * @param keyCode The code of the key being released. */ public void keyReleased(int keyCode) { println("Ignoring key release: " + keyCode); } /** ** Handles a key being pressed and released in a short time (typed) * while the focus is on the input text field. It produces the * final value resulting from the type (including capitalization). * However, this method does not capture keys like "shift" * and "up arrow" because they don't produce a character. * You'll probably want to examine the key code * and send a specific message to the remote host based upon * which key was pressed. The default is simply to send * the value of the typed character. *
* * @param letter The character resulting from the key being pressed. */ public void keyTyped(String letter) { //println("Ignoring key type: " + letter); } /* * * * You do not need to edit below this comment, though you are * welcome to read and make changes to figure out how this works! * * * */ /** ** A helper method that sends a message to a connected LineSocket. * If any exceptions occur during the sending, this method closes * the connection and prompts the user to re-connect. *
* ** Any messages received from the server are displayed in the GUI window. *
* * @param s The message to send */ public void send(String s) { try { // Send the message (and duplicate it in the window) println("Sent: " + s, oStyle); // Log any response to the window println("Got: " + client.request(s), iStyle); } catch (Exception ex) { // If something goes wrong, close the connection and force // the user to reconnect println("Connection lost: " + ex + "\nYou must connect again", eStyle); host.setEnabled(true); port.setEnabled(true); connect.setEnabled(true); input.setEnabled(false); client.close(); } } /** Name/address of the remote host to connect to */ private JTextField host; /** Port number to connect to */ private JTextField port; /** Button to press when you want to connect */ private JButton connect; /** Data to send to remote host */ private JTextField input; /** Scroll bar for message area (so we can jump to the bottom) */ private JScrollBar messageScroll; // Below is extra stuff just to make pretty colors in the text windows /** Style context to hold computed styles */ private static final StyleContext sc = new StyleContext(); /** Display program status messages */ private DefaultStyledDocument doc = new DefaultStyledDocument(sc); /** Status line (default) from the program */ private static final Style dStyle; /** Error message */ private static final Style eStyle; /** Input from remote program */ private static final Style iStyle; /** Output to remote program */ private static final Style oStyle; /** Debugging info */ private static final Style xStyle; static { dStyle = sc.getStyle(StyleContext.DEFAULT_STYLE); // errors are red eStyle = sc.addStyle("eStyle", dStyle); StyleConstants.setForeground(eStyle, Color.red); // input is blue iStyle = sc.addStyle("iStyle", dStyle); StyleConstants.setForeground(iStyle, new Color(0, 102, 255)); // output is green, and I made a rhyme, too! oStyle = sc.addStyle("oStyle", dStyle); StyleConstants.setForeground(oStyle, new Color(0, 153, 0)); // ... and debug, which doesn't rhyme with anything :-( xStyle = sc.addStyle("xStyle", dStyle); StyleConstants.setForeground(xStyle, new Color(192, 192, 192)); } /** ** Static entry point to the program. *
* * @param args Command-line arguments (not used) */ public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } /** ** Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. *
*/ private static void createAndShowGUI() { //Create and set up the window. JFrame frame = new JFrame("ComputerClient"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. ComputerClient cc = new ComputerClient(); cc.setOpaque(true); //content panes must be opaque frame.setContentPane(cc); //Display the window. frame.pack(); frame.setVisible(true); } /** ** Constructs this component and adds all of the GUI components * that belong to it. *
* */ public ComputerClient() { super(); // Use a box layout (row-by-row) for components setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); // connection parameters (first row) JPanel paramPanel = new JPanel(); JLabel hostLabel = new JLabel("Host:"); host = new JTextField("lego-ev3-sa.suffieldacademy.org", 33); host.setCaretPosition(11); host.addActionListener(this); hostLabel.setLabelFor(host); paramPanel.add(hostLabel); paramPanel.add(host); JLabel portLabel = new JLabel("Port:"); port = new JTextField("12345", 5); port.addActionListener(this); portLabel.setLabelFor(port); paramPanel.add(portLabel); paramPanel.add(port); connect = new JButton("Connect"); connect.addActionListener(this); paramPanel.add(connect); add(paramPanel); // input string panel (second row) JPanel inputPanel = new JPanel(); input = new JTextField(20); input.setEnabled(false); input.addKeyListener(this); inputPanel.add(input); add(inputPanel); // display messages (third row) JPanel messagePanel = new JPanel(new BorderLayout()); JTextPane messages = new JTextPane(doc); messages.setEditable(false); JScrollPane messagePane = new JScrollPane(messages); messagePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); messagePane.setPreferredSize(new Dimension(400, 400)); messagePane.setMinimumSize(new Dimension(300, 200)); messageScroll = messagePane.getVerticalScrollBar(); messagePanel.add(messagePane); add(messagePanel); } /** ** Responds to events from text fields and buttons. *
* * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent evt) { // determine which object generated an event Object src = evt.getSource(); // User pressed "connect" button or hit return in the host or port field if (src == connect || src == host || src == port) { // try to connect Socket sock = null; println("Connecting to host " + host.getText() + "..."); try { sock = new Socket(host.getText(), Integer.parseInt(port.getText())); } catch (Exception e) { println("Unable to connect to host: " + e, eStyle); return; // don't attempt further processing } println("Connected!"); // now that we've established a connection, set up send/receive try { client = LineSocket.getClient(sock, debug); // Once we have a connection, we disable the connect // inputs and enable the features to send data to the // established connection host.setEnabled(false); port.setEnabled(false); connect.setEnabled(false); input.setEnabled(true); } catch (Exception ex) { // if anything goes wrong, just try to clean up // as best we can if (null != client) { client.close(); } client = null; try { sock.close(); } catch (Exception ignore) {} } } } /** ** Responds to key press and release from text fields. *
* * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent) */ @Override public void keyTyped(KeyEvent evt) { if (evt.getSource() == input) { // decode the key and send it to the methods above for processing keyTyped(""+evt.getKeyChar()); // clear the text field so its ready for the user // to type something else input.setText(""); input.setCaretPosition(0); input.requestFocusInWindow(); } } /** ** Responds to key presses from text fields. *
* * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ @Override public void keyPressed(KeyEvent evt) { if (evt.getSource() == input) { // decode the key and send it to the methods above for processing keyPressed(evt.getKeyCode()); // clear the text field so its ready for the user // to type something else input.setText(""); input.setCaretPosition(0); input.requestFocusInWindow(); } } /** ** Responds to key releases from text fields. *
* * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent) */ @Override public void keyReleased(KeyEvent evt) { if (evt.getSource() == input) { // decode the key and send it to the methods above for processing keyReleased(evt.getKeyCode()); // clear the text field so its ready for the user // to type something else input.setText(""); input.setCaretPosition(0); input.requestFocusInWindow(); } } /** ** Helper function: print a line of text to the status text box * using the default style. *
* * @param msg The message to append to the status text box */ public void println(String msg) { println(msg, dStyle); } /** ** Print a message with the given style to the status text box. *
* * @param msg The message to append * @param s The style to use for the text */ public void println(String msg, Style s) { try { // append to the end of the message pane doc.insertString(doc.getLength(), msg+"\n", s); // jump the scrollbar to the bottom messageScroll.setValue(messageScroll.getMaximum()); } catch (BadLocationException ble) { // This shouldn't happen, but we'll log a message if it does System.err.println("Couldn't print message '" + msg + "': " + ble); } } /** Generate a serial ID to prevent compiler warnings */ private static final long serialVersionUID = -6770801668840369177L; }