package client;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.Reader;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import msg.Message;
import msg.MessageSender;
import network.Host;
import network.NetworkClient;
import network.NetworkServer;
import util.Constants;
import util.Globals;
@SuppressWarnings("serial")
public class ChatFrame extends JFrame implements Observer
{
/** Texto a enviar */
JTextArea textToSend;
JScrollPane scrollTextToSend;
/** Boton de envio */
JButton sendButton;
/** Boton de limpiar chat */
JButton clearButton;
/** Combo de hosts */
JComboBox hostComboBox;
/** Historial de chat */
JEditorPane chatLog;
JScrollPane scrollChatLog;
/** Paneles contenedores */
JPanel writePanel;
JPanel actionPanel;
Isytok owner;
/** Manejadores de envio y recepcion de mensajes */
NetworkServer networkServer;
NetworkClient sender;
/**
* Constructor principal
* @param listener
* @throws Exception
*/
public ChatFrame(NetworkServer listener, Isytok owner) throws Exception
{
this.owner = owner;
networkServer = listener;
networkServer.getMessage().addObserver(this);
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
setTitle("Chitero... " + Globals.localHostName + " - " + (Globals.online?"ONLINE":"OFFLINE"));
Container c = getContentPane();
c.setLayout(new BorderLayout());
writePanel = new JPanel(new BorderLayout());
actionPanel = new JPanel(new BorderLayout());
textToSend = new JTextArea(10, 100);
textToSend.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER)
{
e.consume();
procesarEnviar();
}
}
});
scrollTextToSend = new JScrollPane(textToSend);
scrollTextToSend.setPreferredSize(new Dimension(800, 200));
scrollTextToSend.setMinimumSize(new Dimension(10, 10));
hostComboBox = new JComboBox();
hostComboBox.setPreferredSize(new Dimension(200,20));
hostComboBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
textToSend.requestFocus();
}
}
);
reloadComboOptions();
chatLog = new JEditorPane("text/html", "");
chatLog.setEditable(false);
scrollChatLog = new JScrollPane(chatLog);
scrollChatLog.setPreferredSize(new Dimension(800, 400));
scrollChatLog.setMinimumSize(new Dimension(10, 10));
sendButton = new JButton("Enviar");
sendButton.setPreferredSize(new Dimension(100, 20));
sendButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
procesarEnviar();
}
}
);
clearButton = new JButton("Limpiar Chat");
clearButton.setPreferredSize(new Dimension(100, 20));
clearButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
chatLog.setText(null);
}
}
);
writePanel.add(scrollChatLog, BorderLayout.NORTH);
writePanel.add(scrollTextToSend, BorderLayout.CENTER);
actionPanel.add(clearButton, BorderLayout.WEST);
actionPanel.add(hostComboBox, BorderLayout.CENTER);
actionPanel.add(sendButton, BorderLayout.EAST);
c.add(writePanel, BorderLayout.NORTH);
c.add(actionPanel, BorderLayout.SOUTH);
pack();
setResizable(false);
setVisible(false);
}
/**
* Procesa los eventos de enter y click en enviar
*/
public void procesarEnviar()
{
// si no estoy online, no puedo enviar!
if (!Globals.online)
{
appendText(" ** Estas offline!! ** ", "maroon");
return;
}
// si el destinatario no esta online, no puedo enviarle
Host host = (Host)hostComboBox.getSelectedItem();
if (!host.isConnected())
return;
String target = host.getHostName();
if (target.equalsIgnoreCase(Constants.MASS_MESSAGE_TEXT))
sendMassMessage(textToSend.getText());
else
sendMessage(((Host)hostComboBox.getSelectedItem()).getHostName(), ((Host)hostComboBox.getSelectedItem()).getHostIP(), textToSend.getText(), false);
}
/**
* Visualizar la ventana, recargar el combo de hosts.
*/
public void toggleViewState(String hostName)
{
reloadComboOptions();
Host host = Host.findHost(hostName);
if (host != null)
hostComboBox.setSelectedItem(host);
setVisible(true);
}
/**
* Cargar las entradas del comboBox
*/
private void reloadComboOptions()
{
hostComboBox.removeAllItems();
hostComboBox.addItem(new Host(Constants.MASS_MESSAGE_TEXT, Constants.MASS_MESSAGE_TEXT, true));
Iterator<Host> it = Globals.hosts.iterator();
while (it.hasNext())
hostComboBox.addItem(it.next());
}
/**
* Observer: al momento de llegar un mensaje, se actualiza la información de manera acorde
*/
@Override
public void update(Observable o, Object arg)
{
Message message = (Message)arg;
// si es un mensaje de texto...
if (message.getMessageKind() == Constants.MESSAGE_KIND_TEXT)
{
// incorporar al chatLog, (si no es mio)
if (!message.isToAll() || !message.getSender().equalsIgnoreCase(Globals.localHostName))
appendText( getTimeStr() + " <b>[" + message.getSender().toUpperCase() + " a "+ (message.isToAll()?Constants.MASS_MESSAGE_TEXT:message.getReceiver().toUpperCase()) +"]</b> " + (String)message.getMessageContent(), "green");
// si es mensaje masivo, actualizar el Combobox a masivo
if (message.isToAll())
hostComboBox.setSelectedIndex(0);
// si no es un mensaje de mi propia maquina, actualizar el Combobox a masivo
else if (!message.getSender().equalsIgnoreCase(Globals.localHostName))
hostComboBox.setSelectedItem(Host.findHost(message.getSender()));
// forzar la visualización
setVisible(true);
}
else if (message.getMessageKind() == Constants.MESSAGE_KIND_IMAGE)
{
ImageFrame.showReceivedImage((ImageIcon)message.getMessageContent(), message.getSender());
}
else if (message.getMessageKind() == Constants.MESSAGE_KIND_STATUS)
{
boolean newStatus = (Boolean)message.getMessageContent();
Host host = Host.findHost(message.getSender());
if (host == null)
{
System.out.println("ChatFrame.update() Imposible encontrar el host a actualizar");
return;
}
host.setConnected(newStatus);
setTitle("Chitero... " + Globals.localHostName + " - " + (Globals.online?"ONLINE":"OFFLINE"));
appendText(getTimeStr() + " <b> ** " + message.getSender().toUpperCase() + " ESTA " + (newStatus?"ONLINE!":"OFFLINE") + " </b> ", "purple");
reloadComboOptions();
if (owner.getMenu() != null)
owner.getMenu().updatePopupSubMenu();
}
}
/**
* Envia un mensaje
* @param host el destinatario
* @param content mensajea a enviar
*/
private void sendMessage(String hostName, String host, String content, boolean massive)
{
if (content.trim().length()==0)
return;
System.out.println("Enviando " + content );
if (!massive)
appendText(getTimeStr() + " <b>["+Globals.localHostName.toUpperCase() +" a " + hostName.toUpperCase() + "]</b> " + content, "blue");
// crear el nuevo mensaje a enviar
Message message = new Message();
message.setMessageKind(Constants.MESSAGE_KIND_TEXT);
message.setMessageContent(content);
message.setReceiver(hostName);
message.setReceiverIP(host);
message.setSender(Globals.localHostName);
message.setSenderIP(Globals.localIP);
message.setToAll(massive);
new Thread(new MessageSender(message)).start();
if (!massive)
textToSend.setText(null);
}
/**
* Envio de mensaje masivo a todos los destinatarios del comboBox
* @param message
*/
private void sendMassMessage(String message)
{
appendText(getTimeStr() + " <b>["+Globals.localHostName.toUpperCase() +" a " + Constants.MASS_MESSAGE_TEXT + "]</b> " + message, "blue");
new Thread(new MassMessageSender(message)).start();
}
/**
* Incorporar texto al final del chatLog
* @param text texto a incorporar
* @param color color a incorporar
*/
public void appendText(String text, String color) {
try
{
text = text.replaceAll("\n", "<br>");
text = "<font color="+color+">" + text + "</font>";
Document doc = chatLog.getDocument();
try
{
Reader reader = new StringReader(text);
EditorKit kit = chatLog.getEditorKit();
kit.read(reader, doc, doc.getLength());
}
catch (Exception e)
{
System.out.println("Exception en appendText:" + e.getMessage());
}
chatLog.setCaretPosition(doc.getLength());
scrollPaneToBottom();
}
catch (Exception e)
{}
}
/**
* Fuerza a llevar la scrollbar al final del chatLog
*/
private void scrollPaneToBottom() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollChatLog.getVerticalScrollBar().setValue(
scrollChatLog.getVerticalScrollBar().getMaximum());
}
});
}
/**
* Thread aparte para el envio de mensajes masivos
*/
public class MassMessageSender implements Runnable
{
String massMessageContent;
public MassMessageSender(String content)
{
massMessageContent = content;
}
@Override
public void run()
{
for (int i=0; i < Globals.hosts.size() ; i++)
if (!Globals.hosts.elementAt(i).getHostName().equalsIgnoreCase(Globals.localHostName) && Globals.hosts.elementAt(i).isConnected())
{
sendMessage(Globals.hosts.elementAt(i).getHostName(), Globals.hosts.elementAt(i).getHostIP(), massMessageContent, true);
try {Thread.sleep(Constants.threadSafeDelayMS);} catch (Exception e) { System.out.println("Error en run de ChatFrame: " + e.getMessage()); }
}
textToSend.setText(null);
}
}
/**
* Retorna la fecha y hora para mostrar en pantalla
* @return
*/
public String getTimeStr()
{
Date dateNow = new Date();
SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
StringBuilder now = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );
return now.toString();
}
}