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