package org.chartsy.chatsy.chat.ui; import java.awt.Color; import java.awt.Component; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.KeyStroke; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.chartsy.chatsy.chat.ChatAreaSendField; import org.chartsy.chatsy.chat.ChatsyManager; import org.chartsy.chatsy.chat.component.RolloverButton; import org.chartsy.chatsy.chat.plugin.ContextMenuListener; import org.chartsy.chatsy.chat.util.GraphicUtils; import org.openide.util.NbPreferences; public abstract class ChatRoom extends JPanel implements ActionListener, PacketListener, DocumentListener, ConnectionListener, FocusListener, ContextMenuListener { private final JPanel chatPanel; private final JSplitPane splitPane; private JSplitPane verticalSplit; private final JLabel notificationLabel; private final TranscriptWindow transcriptWindow; private final ChatAreaSendField chatAreaButton; private final ChatToolBar toolbar; private final JScrollPane textScroller; private final JPanel bottomPanel; private final JPanel editorBar; private JPanel chatWindowPanel; private int unreadMessageCount; private boolean mousePressed; private List<ChatRoomClosingListener> closingListeners = new CopyOnWriteArrayList<ChatRoomClosingListener>(); private final List<String> packetIDList; private final List<MessageListener> messageListeners; private List<Message> transcript; private MouseAdapter transcriptWindowMouseListener; private KeyAdapter chatEditorKeyListener; protected ChatRoom() { chatPanel = new JPanel(new GridBagLayout()); transcriptWindow = new TranscriptWindow(); splitPane = new JSplitPane(); packetIDList = new ArrayList<String>(); notificationLabel = new JLabel(); toolbar = new ChatToolBar(); bottomPanel = new JPanel(); messageListeners = new ArrayList<MessageListener>(); transcript = new ArrayList<Message>(); editorBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 1)); transcriptWindowMouseListener = new MouseAdapter() { public void mouseClicked(MouseEvent e) { getChatInputEditor().requestFocus(); } public void mouseReleased(MouseEvent e) { mousePressed = false; if (transcriptWindow.getSelectedText() == null) getChatInputEditor().requestFocus(); } public void mousePressed(MouseEvent e) { mousePressed = true; } }; transcriptWindow.addMouseListener(transcriptWindowMouseListener); transcriptWindow.setBackground(Color.white); chatAreaButton = new ChatAreaSendField("Send"); textScroller = new JScrollPane(transcriptWindow); textScroller.setBackground(transcriptWindow.getBackground()); textScroller.getViewport().setBackground(Color.white); textScroller.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.lightGray)); getChatInputEditor().setSelectedTextColor(Color.decode("0xffffff")); getChatInputEditor().setSelectionColor(Color.decode("0x336699")); init(); getSplitPane().setRightComponent(null); getTranscriptWindow().addContextMenuListener(this); ChatsyManager.getConnection().addConnectionListener(this); addFocusListener(this); } private void init() { setLayout(new GridBagLayout()); setBackground(Color.white); splitPane.setBorder(BorderFactory.createEmptyBorder()); splitPane.setOneTouchExpandable(false); verticalSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); add(verticalSplit, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); verticalSplit.setBorder(BorderFactory.createEmptyBorder()); verticalSplit.setOneTouchExpandable(false); verticalSplit.setTopComponent(splitPane); textScroller.setAutoscrolls(true); textScroller.getVerticalScrollBar().setBlockIncrement(50); textScroller.getVerticalScrollBar().setUnitIncrement(20); chatWindowPanel = new JPanel(); chatWindowPanel.setLayout(new GridBagLayout()); chatWindowPanel.add(textScroller, new GridBagConstraints(0, 10, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); chatWindowPanel.setOpaque(false); chatPanel.add(chatWindowPanel, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0)); splitPane.setLeftComponent(chatPanel); editorBar.setOpaque(false); chatPanel.setOpaque(false); bottomPanel.setOpaque(false); splitPane.setOpaque(false); bottomPanel.setLayout(new GridBagLayout()); bottomPanel.add(chatAreaButton, new GridBagConstraints(0, 1, 5, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 5, 5, 5), 0, 15)); bottomPanel.add(editorBar, new GridBagConstraints(0, 0, 5, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); bottomPanel.setBorder(BorderFactory.createEmptyBorder()); verticalSplit.setOpaque(false); verticalSplit.setBottomComponent(bottomPanel); verticalSplit.setResizeWeight(1.0); verticalSplit.setDividerSize(2); chatAreaButton.getButton().addActionListener(this); getChatInputEditor().getDocument().addDocumentListener(this); chatEditorKeyListener = new KeyAdapter() { public void keyPressed(KeyEvent e) { checkForEnter(e); } }; getChatInputEditor().addKeyListener(chatEditorKeyListener); getChatInputEditor().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ctrl F4"), "closeTheRoom"); getChatInputEditor().getActionMap().put("closeTheRoom", new AbstractAction("closeTheRoom") { public void actionPerformed(ActionEvent evt) { closeChatRoom(); } }); } public void actionPerformed(ActionEvent e) { sendMessage(); getChatInputEditor().clear(); chatAreaButton.getButton().setEnabled(false); } protected abstract void sendMessage(); protected abstract void sendMessage(String text); public abstract void sendMessage(Message message); public String getNickname() { return NbPreferences.root().node("/org/chartsy/chat").get("nickname", ""); } public void insertMessage(Message message) { ChatsyManager.getChatManager().filterIncomingMessage(this, message); ChatsyManager.getChatManager().fireGlobalMessageReceievedListeners(this, message); addToTranscript(message, true); fireMessageReceived(message); } public void addToTranscript(Message message, boolean updateDate) { final Message newMessage = new Message(); newMessage.setTo(message.getTo()); newMessage.setFrom(message.getFrom()); newMessage.setBody(message.getBody()); newMessage.setProperty("date", new Date()); transcript.add(newMessage); if (updateDate && transcriptWindow.getLastUpdated() != null) notificationLabel.setText("las msg received at " + ChatsyManager.DATE_SECOND_FORMATTER.format(transcriptWindow.getLastUpdated())); scrollToBottom(); } public void addToTranscript(String to, String from, String body, Date date) { final Message newMessage = new Message(); newMessage.setTo(to); newMessage.setFrom(from); newMessage.setBody(body); newMessage.setProperty("date", date); transcript.add(newMessage); } public void scrollToBottom() { if (mousePressed) return; int lengthOfChat = transcriptWindow.getDocument().getLength(); transcriptWindow.setCaretPosition(lengthOfChat); try { JScrollBar scrollBar = textScroller.getVerticalScrollBar(); scrollBar.setValue(scrollBar.getMaximum()); } catch (Exception e) { } } protected void checkForText(DocumentEvent e) { final int length = e.getDocument().getLength(); if (length > 0) chatAreaButton.getButton().setEnabled(true); else chatAreaButton.getButton().setEnabled(false); } public void positionCursor() { getChatInputEditor().setCaretPosition(getChatInputEditor().getCaretPosition()); chatAreaButton.getChatInputArea().requestFocusInWindow(); } public abstract void leaveChatRoom(); public void processPacket(Packet packet) { } public ChatInputEditor getChatInputEditor() { return chatAreaButton.getChatInputArea(); } public TranscriptWindow getTranscriptWindow() { return transcriptWindow; } private void checkForEnter(KeyEvent e) { final KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers()); if (!keyStroke.equals(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK)) && e.getKeyChar() == KeyEvent.VK_ENTER) { e.consume(); sendMessage(); getChatInputEditor().setText(""); getChatInputEditor().setCaretPosition(0); } else if (keyStroke.equals(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK))) { final Document document = getChatInputEditor().getDocument(); try { document.insertString(getChatInputEditor().getCaretPosition(), "\n", null); getChatInputEditor().requestFocusInWindow(); chatAreaButton.getButton().setEnabled(true); } catch (BadLocationException badLoc) { } } } public void addMessageListener(MessageListener listener) { messageListeners.add(listener); } public void removeMessageListener(MessageListener listener) { messageListeners.remove(listener); } private void fireMessageReceived(Message message) { for (MessageListener messageListener : messageListeners) messageListener.messageReceived(this, message); } protected void fireMessageSent(Message message) { for (MessageListener messageListener : messageListeners) messageListener.messageSent(this, message); } public List<Message> getTranscripts() { return transcript; } public void disableToolbar() { final int count = editorBar.getComponentCount(); for (int i = 0; i < count; i++) { final Object o = editorBar.getComponent(i); if (o instanceof RolloverButton) { final RolloverButton rb = (RolloverButton)o; rb.setEnabled(false); } } } public void enableToolbar() { final int count = editorBar.getComponentCount(); for (int i = 0; i < count; i++) { final Object o = editorBar.getComponent(i); if (o instanceof RolloverButton) { final RolloverButton rb = (RolloverButton)o; rb.setEnabled(true); } } } public void removeUpdate(DocumentEvent event) { checkForText(event); } public void changedUpdate(DocumentEvent docEvent) { } public JSplitPane getSplitPane() { return splitPane; } public JPanel getChatPanel() { return chatPanel; } public void closeChatRoom() { fireClosingListeners(); getTranscriptWindow().removeContextMenuListener(this); getTranscriptWindow().removeMouseListener(transcriptWindowMouseListener); getChatInputEditor().removeKeyListener(chatEditorKeyListener); textScroller.getViewport().remove(transcriptWindow); ChatsyManager.getConnection().removeConnectionListener(this); packetIDList.clear(); messageListeners.clear(); getChatInputEditor().close(); getChatInputEditor().getActionMap().remove("closeTheRoom"); chatAreaButton.getButton().removeActionListener(this); bottomPanel.remove(chatAreaButton); } public abstract Icon getTabIcon(); public abstract String getRoomname(); public abstract String getTabTitle(); public abstract String getRoomTitle(); public abstract Message.Type getChatType(); public abstract boolean isActive(); public JLabel getNotificationLabel() { return notificationLabel; } public void addPacketID(String packetID) { packetIDList.add(packetID); } public boolean packetIDExists(String packetID) { return packetIDList.contains(packetID); } public ChatRoom getChatRoom() { return this; } public ChatToolBar getToolBar() { return toolbar; } public void insertUpdate(DocumentEvent e) { checkForText(e); } public void saveTranscript() { } public JPanel getSendFieldToolbar() { return editorBar; } public class ChatToolBar extends JPanel { private JPanel buttonPanel; public ChatToolBar() { buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); setLayout(new GridBagLayout()); buttonPanel.setOpaque(false); add(buttonPanel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); setOpaque(false); } public void addChatRoomButton(ChatRoomButton button) { buttonPanel.add(button); Component[] comps = buttonPanel.getComponents(); final int no = comps != null ? comps.length : 0; final List<Component> buttons = new ArrayList<Component>(); for (int i = 0; i < no; i++) { try { Component component = comps[i]; if (component instanceof JButton) buttons.add(component); } catch (NullPointerException e) { } } GraphicUtils.makeSameSize((JComponent[])buttons.toArray(new JComponent[buttons.size()])); } public void removeChatRoomButton(ChatRoomButton button) { buttonPanel.remove(button); } } public int getUnreadMessageCount() { return unreadMessageCount; } public void increaseUnreadMessageCount() { unreadMessageCount++; } public void clearUnreadMessageCount() { unreadMessageCount = 0; } public JPanel getBottomPanel() { return bottomPanel; } public JPanel getChatWindowPanel() { return chatWindowPanel; } public JPanel getEditorBar() { return editorBar; } public void addClosingListener(ChatRoomClosingListener listener) { closingListeners.add(listener); } public void removeClosingListener(ChatRoomClosingListener listener) { closingListeners.remove(listener); } private void fireClosingListeners() { for (ChatRoomClosingListener chatRoomClosingListener : closingListeners) { chatRoomClosingListener.closing(); removeClosingListener(chatRoomClosingListener); } } public JScrollPane getScrollPaneForTranscriptWindow() { return textScroller; } public JButton getSendButton() { return chatAreaButton.getButton(); } public JSplitPane getVerticalSlipPane() { return verticalSplit; } public void focusGained(FocusEvent focusEvent) { validate(); invalidate(); repaint(); } public void poppingUp(Object component, JPopupMenu popup) { } public void poppingDown(JPopupMenu popup) { } public boolean handleDefaultAction(MouseEvent e) { return false; } public void focusLost(FocusEvent focusEvent) { } public abstract long getLastActivity(); public void connectionClosed() { } public void connectionClosedOnError(Exception e) { } public void reconnectingIn(int seconds) { } public void reconnectionSuccessful() { } public void reconnectionFailed(Exception e) { } }