/*
* XNap
*
* A peer-to-peer framework and client
*
* See AUTHORS for copyright information.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package xnap.gui;
import xnap.XNap;
import xnap.util.Preferences;
import xnap.cmdl.Executer;
import xnap.gui.action.MenuAction;
import xnap.gui.event.PopupListener;
import xnap.gui.event.UserSupport;
import xnap.gui.menu.UserMenu;
import xnap.gui.table.ChatUserTableModel;
import xnap.gui.table.UserTableModel;
import xnap.gui.table.SortableTableModel;
import xnap.gui.table.TableHeaderListener;
import xnap.net.IChannel;
import xnap.net.IUser;
import xnap.net.event.ChannelEvent;
import xnap.net.event.ChannelListener;
import xnap.util.EventVector;
import xnap.util.Formatter;
import org.apache.log4j.Logger;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class ChatSubPanel extends AbstractPanel
implements ChannelListener, PropertyChangeListener, TableModelListener,
UserSupport {
// --- Data Field(s) ---
protected ChatPane cpChat;
protected JTable jta;
protected JTextField jteInput;
protected Preferences prefs = Preferences.getInstance();
protected UserTableModel utm;
protected JSplitPane jspH;
protected JLabel jlTopic;
protected JLabel jlServer;
protected JCheckBox jcbShowChatMsgTime;
private JCheckBox jcbBeepOnChatMessage;
private JCheckBox jcbBlinkOnChatMessage;
private JCheckBox jcbServerIsSticky;
private JComboBox jcbServers;
private ChangeTopicAction acChangeTopic = new ChangeTopicAction();
private SaveChatLogAction saveChatLogAction;
private SendAction acSend = new SendAction();
private MenuAction acMenu = new MenuAction(new UserMenu(this));
protected IChannel channel;
protected LinkedList channels = new LinkedList();
/**
* Used as a cache for the ChatUserTableModel.
*/
protected ChatUserTableModel.IUserVector userData
= new ChatUserTableModel.IUserVector();
private static Logger logger = Logger.getLogger(ChatSubPanel.class);
//--- Constructor(s) ---
public ChatSubPanel(IChannel channel)
{
this.channel = channel;
initialize();
jspH.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,
this);
addChannel(channel);
}
// --- Method(s) ---
private void initialize()
{
cpChat = new ChatPane();
Box boxTop = new Box(BoxLayout.X_AXIS);
// topic
boxTop.add(Box.createHorizontalStrut(5));
jlTopic = new JLabel(channel.getTopic());
boxTop.add(jlTopic);
boxTop.add(Box.createGlue());
jcbShowChatMsgTime = new JCheckBox
(XNap.tr("Timestamp Messages"), prefs.getShowChatMsgTime());
boxTop.add(jcbShowChatMsgTime);
jcbBeepOnChatMessage = new JCheckBox
(XNap.tr("Beep"), prefs.getBeepOnChatMessage());
boxTop.add(jcbBeepOnChatMessage);
jcbBlinkOnChatMessage = new JCheckBox(XNap.tr("Blink"), false);
boxTop.add(jcbBlinkOnChatMessage);
// users
JPanel jpUsers = new JPanel(new BorderLayout());
UserMenu jm = new UserMenu(this, true);
utm = new ChatUserTableModel("user", userData);
jta = utm.createJTable();
utm.addTableModelListener(this);
MouseListener ml = new PopupListener(jm);
jta.addMouseListener(ml);
jta.setShowGrid(false);
JScrollPane jsp = new JScrollPane(jta);
jpUsers.add(jsp, BorderLayout.CENTER);
//jta.setPreferredScrollableViewportSize(new Dimension(250, 200));
// server label
JPanel jpServer = new JPanel(new BorderLayout());
jpServer.setBorder(new EmptyBorder(5, 5, 5, 5));
jlServer = new JLabel(" ");
jpServer.add(jlServer, BorderLayout.CENTER);
jpUsers.add(jpServer, BorderLayout.SOUTH);
// input
Box boxBottom = new Box(BoxLayout.X_AXIS);
jteInput = new HistoryTextField("", 20);
jteInput.getInputMap().put
(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), acSend);
boxBottom.add(jteInput);
boxBottom.add(Box.createHorizontalStrut(5));
jcbServerIsSticky = new JCheckBox(XNap.tr("Sticky"));
boxBottom.add(jcbServerIsSticky);
jcbServers = new JComboBox();
jcbServers.setRenderer(new ChatServerCellRenderer());
boxBottom.add(jcbServers);
boxBottom.add(new JButton(acSend));
// popup menu
JPopupMenu popupMenu = new JPopupMenu();
saveChatLogAction = new SaveChatLogAction();
popupMenu.add(new JMenuItem(saveChatLogAction));
cpChat.getTextPane().addMouseListener(new PopupListener(popupMenu));
// split pane
jspH = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
jspH.add(cpChat, JSplitPane.LEFT);
jspH.add(jpUsers, JSplitPane.RIGHT);
jspH.setDividerLocation(prefs.getChatVerticalDividerLocation());
jspH.setResizeWeight(1);
jspH.setOneTouchExpandable(true);
// content
setLayout(new BorderLayout());
add(boxTop, BorderLayout.NORTH);
add(jspH, BorderLayout.CENTER);
add(boxBottom, BorderLayout.SOUTH);
}
/**
* This call is not synchronized with the swing event thread.
*/
public void addChannel(final IChannel channel)
{
IUser[] users = channel.getUsers();
for (int i = 0; i < users.length; i++) {
userData.add(users[i]);
}
channel.addChannelListener(this);
channels.add(channel);
if (prefs.getPrintServerNotificationsInChatWindow()) {
println(MessageFormat.format(XNap.tr("connected to {0}"),
new Object[] {
channel.getServer().getName()
}),
"chatInfo");
}
Runnable runner = new Runnable()
{
public void run()
{
jcbServers.addItem(channel);
acChangeTopic.setEnabled
(acChangeTopic.isEnabled() | channel.canChangeTopic());
updateStatus();
}
};
SwingUtilities.invokeLater(runner);
}
public void channelClosed(ChannelEvent e)
{
removeChannel(channel);
}
/**
* This call is not synchronized with the swing event thread.
*/
public void removeChannel(final IChannel channel)
{
IUser[] users = channel.getUsers();
for (int i = 0; i < users.length; i++) {
userData.remove(users[i]);
}
channels.remove(channel);
if (prefs.getPrintServerNotificationsInChatWindow()) {
println(MessageFormat.format
(XNap.tr("disconnected from {0}"),
new Object[] { channel.getServer().getName() }),
"chatInfo");
}
Runnable runner = new Runnable()
{
public void run()
{
jcbServers.removeItem(channel);
updateStatus();
}
};
SwingUtilities.invokeLater(runner);
}
public IChannel getChannel()
{
return channel;
}
public int getChannelCount()
{
return channels.size();
}
public IChannel[] getChannels()
{
IChannel[] array = new IChannel[channels.size()];
System.arraycopy(channels.toArray(), 0, array, 0, array.length);
return array;
}
public AbstractAction[] getActions()
{
return (new AbstractAction[] { acChangeTopic, null, acMenu , null,
saveChatLogAction});
}
public IUser[] getUsers()
{
int[] rows = jta.getSelectedRows();
IUser[] users = new IUser[rows.length];
for (int i = 0; i < rows.length; i++) {
users[i] = utm.get(rows[i]);
}
return users;
}
public Iterator iterator()
{
return channels.iterator();
}
public void messageReceived(ChannelEvent e)
{
IUser sender = e.getUser();
if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_MESSAGE
&& (sender == null || !sender.isChatIgnored())) {
printTimeStamp();
print(sender.getName(), "chatUser");
printServer(e);
print("> " + e.getMessage(), "chatMessage");
println("", "chatMessage", !itsMe(sender));
saveChatLogAction.setEnabled(true);
if (!jcbServerIsSticky.isSelected()) {
jcbServers.setSelectedItem(e.getSource());
}
}
else if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_ERROR) {
println(e.getMessage(), "chatError");
}
else if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_INFO) {
println(e.getMessage(), "chatInfo");
}
if (jcbBlinkOnChatMessage.isSelected()) {
Runnable runner = new Runnable()
{
public void run()
{
XNapFrame.getInstance().chatBlink();
}
};
SwingUtilities.invokeLater(runner);
}
}
public void print(String s, String style)
{
cpChat.appendLater(s, style);
}
public void println(String s, String style, boolean beep)
{
if (s.length() > 0) {
printTimeStamp();
}
print(s + "\n", style);
if (beep && jcbBeepOnChatMessage.isSelected()) {
Toolkit.getDefaultToolkit().beep();
}
}
public void println(String s, String style)
{
println(s, style, true);
}
public void printTimeStamp()
{
if (jcbShowChatMsgTime.isSelected()) {
cpChat.appendLater("[" + Formatter.shortDate() + "] ",
"chatMessage");
}
}
public void printServer(ChannelEvent e)
{
if (prefs.getAppendServerNameToChatUser()) {
IChannel channel = (IChannel)e.getSource();
print("@" + channel.getServer().getName(), "chatUser");
}
}
public void propertyChange(PropertyChangeEvent e)
{
prefs.setChatVerticalDividerLocation(jspH.getDividerLocation());
}
public void tableChanged(TableModelEvent e)
{
updateStatus();
}
public void updateStatus()
{
StringBuffer sb = new StringBuffer();
if (channels.size() == 0) {
sb.append("(");
sb.append(XNap.tr("disconnected"));
sb.append(")");
}
else {
sb.append(channels.size());
sb.append(" ");
sb.append(XNap.tr("Servers"));
}
sb.append(" / ");
sb.append(utm.getRowCount());
sb.append(" ");
sb.append(XNap.tr("users"));
final String s = sb.toString();
Runnable runner = new Runnable()
{
public void run()
{
jlServer.setText(s);
}
};
SwingUtilities.invokeLater(runner);
}
public void userAdded(ChannelEvent e)
{
userData.add(e.getUser());
}
public void userRemoved(ChannelEvent e)
{
userData.remove(e.getUser());
}
public void topicChanged(final ChannelEvent e)
{
Runnable runner = new Runnable()
{
public void run()
{
jlTopic.setText(e.getTopic());
}
};
SwingUtilities.invokeLater(runner);
}
protected boolean itsMe(IUser u)
{
return channel.getServer().getUser().equals(u);
}
/**
* Changes the topic of the channel.
*/
private class ChangeTopicAction extends AbstractAction {
public ChangeTopicAction()
{
putValue(Action.NAME, "");
putValue(Action.SMALL_ICON, XNapFrame.getIcon("edit.png"));
putValue(Action.SHORT_DESCRIPTION, XNap.tr("Change topic of channel."));
putValue(Action.MNEMONIC_KEY, new Integer('C'));
this.setEnabled(false);
}
public void actionPerformed(ActionEvent event)
{
String newTopic = JOptionPane.showInputDialog
(ChatSubPanel.this, "Topic", XNap.tr("Change Topic"),
JOptionPane.QUESTION_MESSAGE);
if (newTopic != null) {
try {
channel.changeTopic(newTopic);
}
catch (IOException e) {
println(XNap.tr("Could not change topic:") + " "
+ e.getMessage(), "chatError");
}
}
}
}
private class SaveChatLogAction extends AbstractAction
{
public SaveChatLogAction()
{
putValue(Action.NAME, XNap.tr("Save Text"));
putValue(Action.SHORT_DESCRIPTION, XNap.tr("Saves a file containing the text of this chat session"));
putValue(Action.SMALL_ICON, XNapFrame.getIcon("filesave.png"));
this.setEnabled(false);
}
public void actionPerformed(ActionEvent event)
{
JFileChooser chooser = new JFileChooser();
if (chooser.showSaveDialog(ChatSubPanel.this)
== JFileChooser.APPROVE_OPTION) {
// chooser.setSelectedFile
// (new File(channel.getServer().getName()));
try {
File file = chooser.getSelectedFile();
FileWriter out = new FileWriter(file);
String text = cpChat.getText();
out.write(text);
out.close();
}
catch (IOException e) {
}
}
}
}
/**
* Sends a message.
*/
private class SendAction extends AbstractAction {
public SendAction()
{
putValue(Action.NAME, XNap.tr("Send"));
putValue(Action.SHORT_DESCRIPTION, XNap.tr("Send message"));
putValue(Action.MNEMONIC_KEY, new Integer('S'));
}
public void actionPerformed(ActionEvent event)
{
String s = jteInput.getText();
if (s.length() > 0) {
// send message to all channels
// for (Iterator i = channels.iterator(); i.hasNext();) {
// try {
// ((IChannel)i.next()).sendMessage(s);
// }
// catch (IOException e) {
// println(e.getMessage(), "chatError");
// }
// }
IChannel c = (IChannel)jcbServers.getSelectedItem();
if (c != null) {
prefs.setSendChatAwayMessage(false);
try {
c.sendMessage(s);
}
catch (IOException e) {
println(e.getMessage(), "chatError");
}
jteInput.setText("");
}
}
}
}
}