import java.awt.*; import java.awt.event.*; import java.rmi.RemoteException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import javax.swing.*; /** * Java Chat - A simple chatroom application - GUI Class. * * This is the GUI class. It waits for user input and relays * method calls to the ChatClient object passed in upon * instanciation. * * @author Sean Handley * @version November, 2006 */ public class ClientGUI extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = -3830449240459968936L; private ChatClient cc; private boolean requiresCleanExit = true; //swing components private Container frameMessageBuffer, frameMessageEnter; private JLabel lblMessage; private JTextArea txtMessageBuffer; private JTextArea txtUserList; private JTextField txtMessageEnter; private JButton btnSay, btnOptions, btnLogoutOrIn; /** * A welcome message displayed when the GUI is activated. */ public final String welcomeMessage = "*** Welcome to SeanChat 1.0\n*** Enjoy your stay :-)\n\n"; /** * A title for the GUI window. */ public final String windowTitle = "SeanChat 1.0"; /** * Get the chat client reference. * * @return the chat client referred to by the gui */ public ChatClient getChatClient() { return cc; } /** * Constructor for the client GUI. Takes a chat client as a * parameter and puts a reference in a class variable. This * allows client methods to be called by GUI events. * * @param cc */ public ClientGUI(ChatClient cc) { super(); this.cc = cc; //add listener for window close event addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { dispose(); System.exit(0); } } ); /* * Add a hook into the runtime to ensure clean shutdown * in case the client is forcefully terminated */ Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { try { exitProcedure(); } catch(Throwable t) { //At this point, just die... } } } ); //causes problems with the JVM } /* * Cleans up gracefully by announcing a user's departure, * removing them from the collection and disposing the window. */ public void exitProcedure() { if(requiresCleanExit) { try { cc.removeClient(); if(cc.isLoggedIn()) { cc.leave(); cc.setLoggedIn(false); } } catch(RemoteException e) { //At this point, just die... } catch(Exception e) { //At this point, just die... } } } /** * Builds the GUI in a new thread. */ public void run() { buildGUI(); } /** * Add a new message to the message buffer. * * @param msg */ public void addMessage(String msg) { Date date = new Date(); String timestamp = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date); //append the new message and add a newline txtMessageBuffer.append("[" + timestamp + "] " + msg + "\n"); //auto-scroll to the bottom txtMessageBuffer.setCaretPosition(txtMessageBuffer.getDocument().getLength()); } /** * Update the current user list with the supplied arraylist. * * @param users */ public void updateUserList(ArrayList<String> users) { txtUserList.setText("Current users:\n"); Iterator it = users.iterator(); while(it.hasNext()) { txtUserList.append("[" + (String)it.next() + "]\n"); } } /** * Respond to action events accordingly. Implements the ActionListener * interface. * * @param event */ public void actionPerformed(ActionEvent event) { if (event.getSource() == btnSay) { if(cc.isLoggedIn()) //check if the user is logged in { if(txtMessageEnter.getText().length() > 0) //check the message is not null { cc.sendMessage(cc.getUserName() + "> " + txtMessageEnter.getText()); txtMessageEnter.setText(""); //clear the message typing area } } } else if(event.getSource() == btnOptions) { //open an options dialog OptionsFrame of = new OptionsFrame(cc); of.buildGUI(); } else if(event.getSource() == btnLogoutOrIn) { if(btnLogoutOrIn.getText().equalsIgnoreCase("Logout")) { logout(); } else { login(); } pack(); } } /** * Log the user out. */ public void logout() { try { cc.removeClient(); cc.leave(); } catch(RemoteException e) { System.err.println(e.toString()); } cc.setLoggedIn(false); btnLogoutOrIn.setText("Login"); btnSay.setEnabled(false); txtMessageEnter.setEnabled(false); updateUserList(new ArrayList<String>()); addMessage("*** You are now logged out."); } /** * Log the user in. */ public void login() { try { boolean success = cc.addClient(); if(success) { cc.setLoggedIn(true); btnLogoutOrIn.setText("Logout"); btnSay.setEnabled(true); txtMessageEnter.setEnabled(true); addMessage("*** You are now logged in."); cc.announce(); } else { JOptionPane.showMessageDialog(this,"Username is in use. Please select another."); //show options frame to allow new username selection OptionsFrame of = new OptionsFrame(cc); of.buildGUI(); } } catch(RemoteException e) { JOptionPane.showMessageDialog(this,"Cannot connect to server."); } } /* * Build the GUI using swing components. */ private void buildGUI() { //put the GUI together using swing Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); frameMessageBuffer = new Container(); frameMessageBuffer.setLayout(new FlowLayout()); txtMessageBuffer = new JTextArea(""); txtMessageBuffer.setEditable(false); txtMessageBuffer.setText(welcomeMessage); txtMessageBuffer.setLineWrap(true); txtMessageBuffer.setBorder(BorderFactory.createLineBorder(Color.black)); JScrollPane scrollingArea = new JScrollPane(txtMessageBuffer); scrollingArea.setPreferredSize(new Dimension(400,300)); frameMessageBuffer.add(scrollingArea); txtUserList = new JTextArea(""); txtUserList.setEditable(false); txtUserList.setText("Current users:\n"); txtUserList.setLineWrap(true); txtUserList.setBorder(BorderFactory.createLineBorder(Color.black)); txtUserList.setFocusable(false); JScrollPane scrollingArea2 = new JScrollPane(txtUserList); scrollingArea2.setPreferredSize(new Dimension(100,300)); frameMessageBuffer.add(scrollingArea2); frameMessageEnter = new Container(); frameMessageEnter.setLayout(new FlowLayout()); lblMessage = new JLabel("Message:"); txtMessageEnter = new JTextField(""); txtMessageEnter.setPreferredSize(new Dimension(200,25)); btnSay = new JButton("Say"); btnSay.addActionListener(this); btnOptions = new JButton("Options"); btnOptions.addActionListener(this); btnLogoutOrIn = new JButton("Login"); btnLogoutOrIn.addActionListener(this); frameMessageEnter.add(lblMessage); frameMessageEnter.add(txtMessageEnter); frameMessageEnter.add(btnSay); frameMessageEnter.add(btnOptions); frameMessageEnter.add(btnLogoutOrIn); contentPane.add(frameMessageBuffer,BorderLayout.NORTH); contentPane.add(frameMessageEnter,BorderLayout.SOUTH); getRootPane().setDefaultButton(btnSay); pack(); setLocationRelativeTo(null); setTitle(windowTitle + " [" + cc.getUserName() + "]"); setResizable(false); txtMessageEnter.requestFocus(); btnSay.setEnabled(false); txtMessageEnter.setEnabled(false); //wait forever java.lang.Object sync = new java.lang.Object(); synchronized(sync) { while(true) { try { sync.wait(); } catch (InterruptedException e) { System.err.println(e.toString()); } } } } /** * Make the GUI visible. */ public void showGUI() { setVisible(true); } public boolean getRequiresCleanExit() { return requiresCleanExit; } public void setRequiresCleanExit(boolean requiresCleanExit) { this.requiresCleanExit = requiresCleanExit; } }