Package net.java.sip.communicator.impl.gui.main.chat

Source Code of net.java.sip.communicator.impl.gui.main.chat.ChatPanel$ChangeTransportAction

/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.chat;

import java.awt.*;
import java.awt.Container;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.conference.*;
import net.java.sip.communicator.impl.gui.main.chat.filetransfer.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.filehistory.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.event.*;
import net.java.sip.communicator.service.metahistory.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
import net.java.sip.communicator.util.swing.*;
import net.java.sip.communicator.util.swing.SwingWorker;

/**
* The <tt>ChatPanel</tt> is the panel, where users can write and send messages,
* view received messages. A ChatPanel is created for a contact or for a group
* of contacts in case of a chat conference. There is always one default contact
* for the chat, which is the first contact which was added to the chat.
* When chat is in mode "open all messages in new window", each ChatPanel
* corresponds to a ChatWindow. When chat is in mode "group all messages in
* one chat window", each ChatPanel corresponds to a tab in the ChatWindow.
*
* @author Yana Stamcheva
* @author Lubomir Marinov
* @author Adam Netocny
*/
@SuppressWarnings("serial")
public class ChatPanel
    extends TransparentPanel
    implements  ChatSessionRenderer,
                Chat,
                ChatConversationContainer,
                ChatRoomMemberRoleListener,
                ChatRoomLocalUserRoleListener,
                ChatRoomMemberPropertyChangeListener,
                FileTransferStatusListener,
                Skinnable
{
    private static final Logger logger = Logger.getLogger(ChatPanel.class);

    private final JSplitPane messagePane
        = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

    private JSplitPane topSplitPane;

    private final JPanel topPanel = new JPanel(new BorderLayout());

    private final ChatConversationPanel conversationPanel;

    private final ChatWritePanel writeMessagePanel;

    private ChatRoomMemberListPanel chatContactListPanel;

    private final ChatContainer chatContainer;

    private ChatRoomSubjectPanel subjectPanel;

    public int unreadMessageNumber = 0;

    /**
     * The label showing current typing notification.
     */
    private JLabel typingNotificationLabel;

    /**
     * The typing notification icon.
     */
    private final Icon typingIcon = GuiActivator.getResources()
        .getImage("service.gui.icons.TYPING");

    /**
     * Indicates that a typing notification event is successfully sent.
     */
    public static final int TYPING_NOTIFICATION_SUCCESSFULLY_SENT = 1;

    /**
     * Indicates that sending a typing notification event has failed.
     */
    public static final int TYPING_NOTIFICATION_SEND_FAILED = 0;

    /**
     * The number of messages shown per page.
     */
    protected static final int MESSAGES_PER_PAGE = 20;

    private boolean isShown = false;

    public ChatSession chatSession;

    private long firstHistoryMsgTimestamp;

    private long lastHistoryMsgTimestamp;

    private final java.util.List<ChatFocusListener> focusListeners
        = new Vector<ChatFocusListener>();

    private final java.util.List<ChatHistoryListener> historyListeners
        = new Vector<ChatHistoryListener>();

    private final Vector<Object> incomingEventBuffer = new Vector<Object>();

    private boolean isHistoryLoaded;

    private int autoDividerLocation = 0;

    /**
     * Stores all active  file transfer requests and effective transfers with
     * the identifier of the transfer.
     */
    private final Hashtable<String, Object> activeFileTransfers
        = new Hashtable<String, Object>();

    /**
     * Creates a <tt>ChatPanel</tt> which is added to the given chat window.
     *
     * @param chatContainer The parent window of this chat panel.
     */
    public ChatPanel(ChatContainer chatContainer)
    {
        super(new BorderLayout());

        this.chatContainer = chatContainer;

        this.conversationPanel = new ChatConversationPanel(this);
        this.conversationPanel.setPreferredSize(new Dimension(400, 200));
        this.conversationPanel.getChatTextPane()
            .setTransferHandler(new ChatTransferHandler(this));

        topPanel.setBackground(Color.WHITE);
        topPanel.setBorder(
            BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
        initTypingNotificationLabel();

        this.writeMessagePanel = new ChatWritePanel(this);

        this.messagePane.setBorder(null);
        this.messagePane.setOpaque(false);
        this.messagePane.addPropertyChangeListener(
            new DividerLocationListener());

        this.messagePane.setDividerSize(3);
        this.messagePane.setResizeWeight(1.0D);
        this.messagePane.setBottomComponent(writeMessagePanel);

        this.messagePane.setTopComponent(topPanel);

        this.add(messagePane, BorderLayout.CENTER);

        if (OSUtils.IS_MAC)
        {
            setOpaque(true);
            setBackground(
                new Color(GuiActivator.getResources()
                    .getColor("service.gui.MAC_PANEL_BACKGROUND")));
        }

        this.addComponentListener(new TabSelectionComponentListener());
    }

    /**
     * Sets the chat session to associate to this chat panel.
     * @param chatSession the chat session to associate to this chat panel
     */
    public void setChatSession(ChatSession chatSession)
    {
        this.chatSession = chatSession;

        if ((this.chatSession != null)
                && this.chatSession.isContactListSupported())
        {
            topPanel.remove(conversationPanel);

            TransparentPanel rightPanel
                = new TransparentPanel(new BorderLayout(5, 5));
            Dimension chatContactPanelSize = new Dimension(150, 100);
            rightPanel.setMinimumSize(chatContactPanelSize);
            rightPanel.setPreferredSize(chatContactPanelSize);

            this.chatContactListPanel = new ChatRoomMemberListPanel(this);
            this.chatContactListPanel.setOpaque(false);

            topSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
            topSplitPane.setBorder(null); // remove default borders
            topSplitPane.setOneTouchExpandable(true);
            topSplitPane.setOpaque(false);
            topSplitPane.setResizeWeight(1.0D);

            ChatTransport chatTransport = chatSession.getCurrentChatTransport();

            JLabel localUserLabel = new JLabel(
                chatTransport.getProtocolProvider()
                    .getAccountID().getDisplayName());

            localUserLabel.setFont(
                localUserLabel.getFont().deriveFont(Font.BOLD));

            rightPanel.add(localUserLabel, BorderLayout.NORTH);
            rightPanel.add(chatContactListPanel, BorderLayout.CENTER);

            topSplitPane.setLeftComponent(conversationPanel);
            topSplitPane.setRightComponent(rightPanel);

            topPanel.add(topSplitPane);
        }
        else
        {
            if (topSplitPane != null)
            {
                if (chatContactListPanel != null)
                {
                    topSplitPane.remove(chatContactListPanel);
                    chatContactListPanel = null;
                }

                this.messagePane.remove(topSplitPane);
                topSplitPane = null;
            }

            topPanel.add(conversationPanel);
        }

        if (chatSession instanceof MetaContactChatSession)
        {
            // The subject panel is added here, because it's specific for the
            // multi user chat and is not contained in the single chat chat panel.
            if (subjectPanel != null)
            {
                this.remove(subjectPanel);
                subjectPanel = null;

                this.revalidate();
                this.repaint();
            }

            writeMessagePanel.setTransportSelectorBoxVisible(true);

            //Enables to change the protocol provider by simply pressing the
            // CTRL-P key combination
            ActionMap amap = this.getActionMap();

            amap.put("ChangeProtocol", new ChangeTransportAction());

            InputMap imap = this.getInputMap(
                JComponent.WHEN_IN_FOCUSED_WINDOW);

            imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_P,
                KeyEvent.CTRL_DOWN_MASK), "ChangeProtocol");
        }
        else if (chatSession instanceof ConferenceChatSession)
        {
            ConferenceChatSession confSession
                = (ConferenceChatSession) chatSession;

            writeMessagePanel.setTransportSelectorBoxVisible(false);

            confSession.addLocalUserRoleListener(this);
            confSession.addMemberRoleListener(this);

            ((ChatRoomWrapper) chatSession.getDescriptor())
                .getChatRoom().addMemberPropertyChangeListener(this);

            subjectPanel
                = new ChatRoomSubjectPanel((ConferenceChatSession) chatSession);

            // The subject panel is added here, because it's specific for the
            // multi user chat and is not contained in the single chat chat panel.
            this.add(subjectPanel, BorderLayout.NORTH);
        }

        if (chatContactListPanel != null)
        {
            // Initialize chat participants' panel.
            Iterator<ChatContact<?>> chatParticipants
                = chatSession.getParticipants();

            while (chatParticipants.hasNext())
                chatContactListPanel.addContact(chatParticipants.next());
        }
    }

    /**
     * Returns the chat session associated with this chat panel.
     * @return the chat session associated with this chat panel
     */
    public ChatSession getChatSession()
    {
        return chatSession;
    }

    /**
     * Runs clean-up for associated resources which need explicit disposal (e.g.
     * listeners keeping this instance alive because they were added to the
     * model which operationally outlives this instance).
     */
    public void dispose()
    {
        writeMessagePanel.dispose();
        chatSession.dispose();
    }

    /**
     * Returns the chat window, where this chat panel is added.
     *
     * @return the chat window, where this chat panel is added
     */
    public ChatContainer getChatContainer()
    {
        return chatContainer;
    }

    /**
     * Returns the chat window, where this chat panel
     * is located. Implements the
     * <tt>ChatConversationContainer.getConversationContainerWindow()</tt>
     * method.
     *
     * @return ChatWindow The chat window, where this
     * chat panel is located.
     */
    public Window getConversationContainerWindow()
    {
        return chatContainer.getFrame();
    }

    /**
     * Adds a typing notification message to the conversation panel.
     *
     * @param typingNotification the typing notification to show
     */
    public void addTypingNotification(String typingNotification)
    {
        typingNotificationLabel.setText(typingNotification);

        if (typingNotification != null && !typingNotification.equals(" "))
            typingNotificationLabel.setIcon(typingIcon);
        else
            typingNotificationLabel.setIcon(null);

        revalidate();
        repaint();
    }

    /**
     * Removes the typing notification message from the conversation panel.
     */
    public void removeTypingNotification()
    {
        addTypingNotification(" ");
    }

    /**
     * Initializes the typing notification label.
     */
    private void initTypingNotificationLabel()
    {
        typingNotificationLabel
            = new JLabel(" ", SwingConstants.CENTER);

        typingNotificationLabel.setPreferredSize(new Dimension(500, 20));
        typingNotificationLabel.setForeground(Color.GRAY);
        typingNotificationLabel.setFont(
            typingNotificationLabel.getFont().deriveFont(11f));
        typingNotificationLabel.setVerticalTextPosition(JLabel.BOTTOM);
        typingNotificationLabel.setHorizontalTextPosition(JLabel.LEFT);
        typingNotificationLabel.setIconTextGap(0);
        topPanel.add(typingNotificationLabel, BorderLayout.SOUTH);
    }

    /**
     * Returns the conversation panel, contained in this chat panel.
     *
     * @return the conversation panel, contained in this chat panel
     */
    public ChatConversationPanel getChatConversationPanel()
    {
        return this.conversationPanel;
    }

    /**
     * Returns the write area panel, contained in this chat panel.
     *
     * @return the write area panel, contained in this chat panel
     */
    public ChatWritePanel getChatWritePanel()
    {
        return this.writeMessagePanel;
    }

    /**
     * Returns the corresponding role description to the given role index.
     *
     * @param role to role index to analyse
     * @return String the corresponding role description
     */
    public String getRoleDescription(ChatRoomMemberRole role)
    {
        String roleDescription = null;

        switch(role)
        {
            case OWNER: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.OWNER");
            break;
            case ADMINISTRATOR: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.ADMINISTRATOR");
            break;
            case MODERATOR: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.MODERATOR");
            break;
            case MEMBER: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.MEMBER");
            break;
            case GUEST: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.GUEST");
            break;
            case SILENT_MEMBER: roleDescription
                = GuiActivator.getResources().getI18NString(
                    "service.gui.SILENT_MEMBER");
            break;
            default:;
        }

        return roleDescription;
    }

    /**
     * Implements the <tt>memberRoleChanged()</tt> method.
     *
     * @param evt
     */
    public void memberRoleChanged(ChatRoomMemberRoleChangeEvent evt)
    {
        this.conversationPanel.appendMessageToEnd(
            "<DIV identifier=\"message\" style=\"color:#707070;\">"
            + GuiActivator.getResources().getI18NString("service.gui.IS_NOW",
                new String[]{evt.getSourceMember().getName(),
                getRoleDescription(evt.getNewRole())})
                +"</DIV>");
    }

    /**
     * Implements the <tt>localUserRoleChanged()</tt> method.
     *
     * @param evt
     */
    public void localUserRoleChanged(ChatRoomLocalUserRoleChangeEvent evt)
    {
        this.conversationPanel.appendMessageToEnd(
            "<DIV identifier=\"message\" style=\"color:#707070;\">"
            +GuiActivator.getResources().getI18NString("service.gui.ARE_NOW",
                new String[]{
                getRoleDescription(evt.getNewRole())}) +"</DIV>");
    }

    /**
     * Every time the chat panel is shown we set it as a current chat panel.
     * This is done here and not in the Tab selection listener, because the tab
     * change event is not fired when the user clicks on the close tab button
     * for example.
     */
    private class TabSelectionComponentListener
        extends ComponentAdapter
    {
        public void componentShown(ComponentEvent evt)
        {
            Component component = evt.getComponent();
            Container parent = component.getParent();

            if (!(parent instanceof JTabbedPane))
                return;

            JTabbedPane tabbedPane = (JTabbedPane) parent;

            if (tabbedPane.getSelectedComponent() != component)
                return;

            chatContainer.setCurrentChat(ChatPanel.this);
        }
    }

    /**
     * Requests the focus in the write message area.
     */
    public void requestFocusInWriteArea()
    {
        getChatWritePanel().getEditorPane().requestFocus();
    }

    /**
     * Checks if the editor contains text.
     *
     * @return TRUE if editor contains text, FALSE otherwise.
     */
    public boolean isWriteAreaEmpty()
    {
        JEditorPane editorPane = getChatWritePanel().getEditorPane();

        Document doc = editorPane.getDocument();

        try
        {
            String text = doc.getText(0, doc.getLength());

            if (text == null || text.equals(""))
                return true;
        }
        catch (BadLocationException e)
        {
            logger.error("Failed to obtain document text.", e);
        }

        return false;
    }

    /**
     * Process history messages.
     *
     * @param historyList The collection of messages coming from history.
     * @param escapedMessageID The incoming message needed to be ignored if
     * contained in history.
     */
    private void processHistory( Collection<Object> historyList,
                                String escapedMessageID)
    {
        Iterator<Object> iterator = historyList.iterator();

        String messageType;

        while (iterator.hasNext())
        {
            Object o = iterator.next();
            String historyString = "";

            if(o instanceof MessageDeliveredEvent)
            {
                MessageDeliveredEvent evt
                    = (MessageDeliveredEvent)o;

                ProtocolProviderService protocolProvider = evt
                    .getDestinationContact().getProtocolProvider();

                if (isGreyHistoryStyleDisabled(protocolProvider))
                    messageType = Chat.OUTGOING_MESSAGE;
                else
                    messageType = Chat.HISTORY_OUTGOING_MESSAGE;

                historyString = processHistoryMessage(
                            GuiActivator.getUIService().getMainFrame()
                                .getAccount(protocolProvider),
                            evt.getTimestamp(),
                            messageType,
                            evt.getSourceMessage().getContent(),
                            evt.getSourceMessage().getContentType());
            }
            else if(o instanceof MessageReceivedEvent)
            {
                MessageReceivedEvent evt = (MessageReceivedEvent)o;

                ProtocolProviderService protocolProvider
                    = evt.getSourceContact().getProtocolProvider();

                if(!evt.getSourceMessage().getMessageUID()
                        .equals(escapedMessageID))
                {
                    if (isGreyHistoryStyleDisabled(protocolProvider))
                        messageType = Chat.INCOMING_MESSAGE;
                    else
                        messageType = Chat.HISTORY_INCOMING_MESSAGE;

                    historyString = processHistoryMessage(
                                evt.getSourceContact().getDisplayName(),
                                evt.getTimestamp(),
                                messageType,
                                evt.getSourceMessage().getContent(),
                                evt.getSourceMessage().getContentType());
                }
            }
            else if(o instanceof ChatRoomMessageDeliveredEvent)
            {
                ChatRoomMessageDeliveredEvent evt
                    = (ChatRoomMessageDeliveredEvent)o;

                ProtocolProviderService protocolProvider = evt
                    .getSourceChatRoom().getParentProvider();

                historyString = processHistoryMessage(
                            GuiActivator.getUIService().getMainFrame()
                                .getAccount(protocolProvider),
                            evt.getTimestamp(),
                            Chat.HISTORY_OUTGOING_MESSAGE,
                            evt.getMessage().getContent(),
                            evt.getMessage().getContentType());
            }
            else if(o instanceof ChatRoomMessageReceivedEvent)
            {
                ChatRoomMessageReceivedEvent evt
                    = (ChatRoomMessageReceivedEvent) o;

                if(!evt.getMessage().getMessageUID()
                        .equals(escapedMessageID))
                {
                    historyString = processHistoryMessage(
                            evt.getSourceChatRoomMember().getName(),
                            evt.getTimestamp(),
                            Chat.HISTORY_INCOMING_MESSAGE,
                            evt.getMessage().getContent(),
                            evt.getMessage().getContentType());
                }
            }
            else if (o instanceof FileRecord)
            {
                FileRecord fileRecord = (FileRecord) o;

                FileHistoryConversationComponent component
                    = new FileHistoryConversationComponent(fileRecord);

                conversationPanel.addComponent(component);
            }

            if (historyString != null)
                conversationPanel.appendMessageToEnd(historyString);
        }

        fireChatHistoryChange();
    }

    /**
     * Passes the message to the contained <code>ChatConversationPanel</code>
     * for processing and appends it at the end of the conversationPanel
     * document.
     *
     * @param contactName the name of the contact sending the message
     * @param date the time at which the message is sent or received
     * @param messageType the type of the message. One of OUTGOING_MESSAGE
     * or INCOMING_MESSAGE
     * @param message the message text
     * @param contentType the content type
     */
    public void addMessage(String contactName, long date,
            String messageType, String message, String contentType)
    {
        ChatMessage chatMessage = new ChatMessage(contactName, date,
            messageType, message, contentType);

        this.addChatMessage(chatMessage);

        // A bug Fix for Previous/Next buttons .
        // Must update buttons state after message is processed
        // otherwise states are not proper
        fireChatHistoryChange();
    }

    /**
     * Passes the message to the contained <code>ChatConversationPanel</code>
     * for processing and appends it at the end of the conversationPanel
     * document.
     *
     * @param contactName the name of the contact sending the message
     * @param date the time at which the message is sent or received
     * @param messageType the type of the message. One of OUTGOING_MESSAGE
     * or INCOMING_MESSAGE
     * @param title the title of the message
     * @param message the message text
     * @param contentType the content type
     */
    public void addMessage(String contactName, long date,
            String messageType, String title, String message, String contentType)
    {
        ChatMessage chatMessage = new ChatMessage(contactName, date,
            messageType, title, message, contentType);

        this.addChatMessage(chatMessage);
    }

    /**
     * Passes the message to the contained <code>ChatConversationPanel</code>
     * for processing and appends it at the end of the conversationPanel
     * document.
     *
     * @param chatMessage the chat message to add
     */
    private void addChatMessage(ChatMessage chatMessage)
    {
        if (ConfigurationManager.isHistoryShown() && !isHistoryLoaded)
        {
            synchronized (incomingEventBuffer)
            {
                incomingEventBuffer.add(chatMessage);
            }
        }
        else
        {
            appendChatMessage(chatMessage);
        }

        // change the last history message timestamp after we add one.
        this.lastHistoryMsgTimestamp = chatMessage.getDate();
    }

    /**
     * Adds the given error message to the chat window conversation area.
     *
     * @param contactName the name of the contact, for which the error occured
     * @param message the error message
     */
    public void addErrorMessage(String contactName,
                                String message)
    {
        this.addMessage(contactName,  System.currentTimeMillis(),
                Chat.ERROR_MESSAGE,
                GuiActivator.getResources()
                    .getI18NString("service.gui.MSG_DELIVERY_FAILURE"),
                message, "text");
    }

    /**
     * Adds the given error message to the chat window conversation area.
     *
     * @param contactName the name of the contact, for which the error occurred
     * @param title the title of the error
     * @param message the error message
     */
    public void addErrorMessage(String contactName,
                                String title,
                                String message)
    {
        this.addMessage(contactName,  System.currentTimeMillis(),
                Chat.ERROR_MESSAGE,
                title,
                message, "text");
    }

    /**
     * Passes the message to the contained <code>ChatConversationPanel</code>
     * for processing and appends it at the end of the conversationPanel
     * document.
     *
     * @param chatMessage the message to append
     */
    private void appendChatMessage(ChatMessage chatMessage)
    {
        String processedMessage
            = this.conversationPanel.processMessage(chatMessage);

        if (chatSession instanceof ConferenceChatSession)
        {
            if (chatMessage.getMessageType().equals(Chat.INCOMING_MESSAGE))
            {
                String keyWord =
                    ((ChatRoomWrapper) chatSession.getDescriptor())
                        .getChatRoom().getUserNickname();

                processedMessage =
                    this.conversationPanel
                        .processChatRoomHighlight(processedMessage,
                            chatMessage.getContentType(), keyWord);
            }

            String meCommandMsg
                = this.conversationPanel.processMeCommand(chatMessage);

            if (meCommandMsg.length() > 0)
            {
                processedMessage = meCommandMsg;
            }
        }

        this.conversationPanel.appendMessageToEnd(processedMessage);
    }

    /**
     * Passes the message to the contained <code>ChatConversationPanel</code>
     * for processing.
     *
     * @param contactName The name of the contact sending the message.
     * @param date The time at which the message is sent or received.
     * @param messageType The type of the message. One of OUTGOING_MESSAGE
     * or INCOMING_MESSAGE.
     * @param message The message text.
     * @param contentType the content type of the message (html or plain text)
     *
     * @return a string containing the processed message.
     */
    private String processHistoryMessage(String contactName,
                                        long date,
                                        String messageType,
                                        String message,
                                        String contentType)
    {
        ChatMessage chatMessage = new ChatMessage(contactName, date,
            messageType, message, contentType);

        String processedMessage =
            this.conversationPanel.processMessage(chatMessage);
        if (chatSession instanceof ConferenceChatSession)
        {
            String tempMessage =
                conversationPanel.processMeCommand(chatMessage);
            if (tempMessage.length() > 0)
            {
                processedMessage = tempMessage;
            }
        }

        return processedMessage;
    }

    /**
     * Refreshes write area editor pane. Deletes all existing text
     * content.
     */
    public void refreshWriteArea()
    {
        this.writeMessagePanel.clearWriteArea();
    }

    /**
     * Adds text to the write area editor.
     *
     * @param text The text to add.
     */
    public void addTextInWriteArea(String text){
        JEditorPane editorPane = this.writeMessagePanel.getEditorPane();

        editorPane.setText(editorPane.getText() + text);
    }

    /**
     * Returns the text contained in the write area editor.
     * @param mimeType the mime type
     * @return The text contained in the write area editor.
     */
    public String getTextFromWriteArea(String mimeType)
    {
        if (mimeType.equals(
            OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE))
        {
            return writeMessagePanel.getText();
        }
        else
        {
            return writeMessagePanel.getTextAsHtml();
        }
    }

    /**
     * Cuts the write area selected content to the clipboard.
     */
    public void cut(){
        this.writeMessagePanel.getEditorPane().cut();
    }

    /**
     * Copies either the selected write area content or the selected
     * conversation panel content to the clipboard.
     */
    public void copy(){
        JTextComponent textPane = this.conversationPanel.getChatTextPane();

        if (textPane.getSelectedText() == null)
        {
            textPane = this.writeMessagePanel.getEditorPane();
        }

        textPane.copy();
    }

    /**
     * Copies the selected write panel content to the clipboard.
     */
    public void copyWriteArea(){
        JEditorPane editorPane = this.writeMessagePanel.getEditorPane();

        editorPane.copy();
    }

    /**
     * Pastes the content of the clipboard to the write area.
     */
    public void paste()
    {
        JEditorPane editorPane = this.writeMessagePanel.getEditorPane();

        editorPane.paste();

        editorPane.requestFocus();
    }

    /**
     * Sends current write area content.
     */
    public void sendButtonDoClick()
    {
        if (!isWriteAreaEmpty())
        {
            new Thread()
            {
                public void run()
                {
                    sendMessage();
                }
            }.start();
        }

        //make sure the focus goes back to the write area
        requestFocusInWriteArea();
    }

    /**
     * Returns TRUE if this chat panel is added to a container (window or
     * tabbed pane), which is shown on the screen, FALSE - otherwise.
     *
     * @return TRUE if this chat panel is added to a container (window or
     * tabbed pane), which is shown on the screen, FALSE - otherwise
     */
    public boolean isShown()
    {
        return isShown;
    }

    /**
     * Marks this chat panel as shown or hidden.
     *
     * @param isShown TRUE to mark this chat panel as shown, FALSE - otherwise
     */
    public void setShown(boolean isShown)
    {
        this.isShown = isShown;
    }

    /**
     * Brings the <tt>ChatWindow</tt> containing this <tt>ChatPanel</tt> to the
     * front if <tt>isVisble</tt> is <tt>true</tt>; hides it, otherwise.
     *
     * @param isVisible <tt>true</tt> to bring the <tt>ChatWindow</tt> of this
     * <tt>ChatPanel</tt> to the front; <tt>false</tt> to close this
     * <tt>ChatPanel</tt>
     */
    public void setChatVisible(boolean isVisible)
    {
        ChatWindowManager chatWindowManager
            = GuiActivator.getUIService().getChatWindowManager();

        if (isVisible)
            chatWindowManager.openChat(this, isVisible);
        else
            chatWindowManager.closeChat(this);
    }

    /**
     * Implements the <tt>Chat.isChatFocused</tt> method. Returns TRUE if this
     * chat panel is the currently selected panel and if the chat window, where
     * it's contained is active.
     *
     * @return true if this chat panel has the focus and false otherwise.
     */
    public boolean isChatFocused()
    {
        ChatPanel currentChatPanel = chatContainer.getCurrentChat();

        return (currentChatPanel != null
                && currentChatPanel.equals(this)
                && chatContainer.getFrame().isActive());
    }

    /**
     * Adds the given {@link KeyListener} to this <tt>Chat</tt>.
     * The <tt>KeyListener</tt> is used to inform other bundles when a user has
     * typed in the chat editor area.
     *
     * @param l the <tt>KeyListener</tt> to add
     */
    public void addChatEditorKeyListener(KeyListener l)
    {
        this.getChatWritePanel().getEditorPane().addKeyListener(l);
    }

    /**
     * Removes the given {@link KeyListener} from this <tt>Chat</tt>.
     * The <tt>KeyListener</tt> is used to inform other bundles when a user has
     * typed in the chat editor area.
     *
     * @param l the <tt>ChatFocusListener</tt> to remove
     */
    public void removeChatEditorKeyListener(KeyListener l)
    {
        this.getChatWritePanel().getEditorPane().removeKeyListener(l);
    }

    /**
     * Returns the message written by user in the chat write area.
     *
     * @return the message written by user in the chat write area
     */
    public String getMessage()
    {
        Document writeEditorDoc
            = writeMessagePanel.getEditorPane().getDocument();

        try
        {
            return writeEditorDoc.getText(0, writeEditorDoc.getLength());
        }
        catch (BadLocationException e)
        {
            return writeMessagePanel.getEditorPane().getText();
        }
    }

    /**
     * Sets the given message as a message in the chat write area.
     *
     * @param message the text that would be set to the chat write area
     */
    public void setMessage(String message)
    {
        writeMessagePanel.getEditorPane().setText(message);
    }

    /**
     * Indicates if the history of a hidden protocol should be shown to the
     * user in the default <b>grey</b> history style or it should be shown as
     * a normal message.
     *
     * @param protocolProvider the protocol provider to check
     * @return <code>true</code> if the given protocol is a hidden one and the
     * "hiddenProtocolGreyHistoryDisabled" property is set to true.
     */
    private boolean isGreyHistoryStyleDisabled(
        ProtocolProviderService protocolProvider)
    {
        boolean isProtocolHidden =
            protocolProvider.getAccountID().getAccountPropertyBoolean(
                ProtocolProviderFactory.IS_PROTOCOL_HIDDEN, false);
        boolean isGreyHistoryDisabled = false;

        String greyHistoryProperty
            = GuiActivator.getResources()
                .getSettingsString("impl.gui.GREY_HISTORY_ENABLED");

        if (greyHistoryProperty != null)
            isGreyHistoryDisabled
                = Boolean.parseBoolean(greyHistoryProperty);

        return isProtocolHidden && isGreyHistoryDisabled;
    }

    /**
     * Sends the given file through the currently selected chat transport by
     * using the given fileComponent to visualize the transfer process in the
     * chat conversation panel.
     *
     * @param file the file to send
     * @param fileComponent the file component to use for visualization
     */
    public void sendFile(   final File file,
                            final SendFileConversationComponent fileComponent)
    {
        final ChatTransport sendFileTransport
            = this.findFileTransferChatTransport();

        this.setSelectedChatTransport(sendFileTransport);

        SwingWorker worker = new SwingWorker()
        {
            public Object construct()
                throws Exception
            {
                if(file.length() > sendFileTransport.getMaximumFileLength())
                {
                    addMessage(
                        chatSession.getCurrentChatTransport().getName(),
                        System.currentTimeMillis(),
                        Chat.ERROR_MESSAGE,
                        GuiActivator.getResources()
                            .getI18NString("service.gui.FILE_TOO_BIG",
                            new String[]{
                                sendFileTransport.getMaximumFileLength()/1024/1024
                                + " MB"}),
                        "",
                        "text");
                    fileComponent.setFailed();

                    return "";
                }

                final FileTransfer fileTransfer
                    = sendFileTransport.sendFile(file);

                addActiveFileTransfer(fileTransfer.getID(), fileTransfer);

                // Add the status listener that would notify us when the file
                // transfer has been completed and should be removed from
                // active components.
                fileTransfer.addStatusListener(ChatPanel.this);

                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        fileComponent.setProtocolFileTransfer(fileTransfer);
                    }
                });

                return "";
            }

            public void catchException(Throwable ex)
            {
                logger.error("Failed to send file.", ex);

                if (ex instanceof IllegalStateException)
                {
                    addErrorMessage(
                        chatSession.getCurrentChatTransport().getName(),
                        GuiActivator.getResources().getI18NString(
                            "service.gui.MSG_SEND_CONNECTION_PROBLEM"));
                }
                else
                {
                    addErrorMessage(
                        chatSession.getCurrentChatTransport().getName(),
                        GuiActivator.getResources().getI18NString(
                            "service.gui.MSG_DELIVERY_ERROR",
                            new String[]{ex.getMessage()}));
                }
            }
        };

        worker.start();
    }

    /**
     * Sends the given file through the currently selected chat transport.
     *
     * @param file the file to send
     */
    public void sendFile(final File file)
    {
        final ChatTransport fileTransferTransport
            = findFileTransferChatTransport();

        // If there's no operation set we show some "not supported" messages
        // and we return.
        if (fileTransferTransport == null)
        {
            logger.error("Failed to send file.");

            this.addErrorMessage(
                chatSession.getChatName(),
                GuiActivator.getResources().getI18NString(
                    "service.gui.FILE_SEND_FAILED",
                    new String[]{file.getName()}),
                GuiActivator.getResources().getI18NString(
                    "service.gui.FILE_TRANSFER_NOT_SUPPORTED"));

            return;
        }

        final SendFileConversationComponent fileComponent
            = new SendFileConversationComponent(
                this,
                fileTransferTransport.getDisplayName(),
                file);

        if (ConfigurationManager.isHistoryShown() && !isHistoryLoaded)
        {
            synchronized (incomingEventBuffer)
            {
                incomingEventBuffer.add(fileComponent);
            }
        }
        else
            getChatConversationPanel().addComponent(fileComponent);

        this.sendFile(file, fileComponent);
    }

    /**
     * Sends the text contained in the write area as an SMS message or an
     * instance message depending on the "send SMS" check box.
     */
    protected void sendMessage()
    {
        if (writeMessagePanel.isSmsSelected())
        {
            this.sendSmsMessage();
        }
        else
        {
            this.sendInstantMessage();
        }
    }

    /**
     * Sends the text contained in the write area as an SMS message.
     */
    public void sendSmsMessage()
    {
        String messageText = getTextFromWriteArea(
            OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE);

        ChatTransport smsChatTransport = chatSession.getCurrentChatTransport();
        if (!smsChatTransport.allowsSmsMessage())
        {
            Iterator<ChatTransport> chatTransports
                = chatSession.getChatTransports();

            while(chatTransports.hasNext())
            {
                ChatTransport transport = chatTransports.next();

                if (transport.allowsSmsMessage())
                {
                    smsChatTransport = transport;
                    break;
                }
            }
        }

        // If there's no operation set we show some "not supported" messages
        // and we return.
        if (!smsChatTransport.allowsSmsMessage())
        {
            logger.error("Failed to send SMS.");

            this.refreshWriteArea();

            this.addMessage(
                smsChatTransport.getName(),
                System.currentTimeMillis(),
                Chat.OUTGOING_MESSAGE,
                messageText,
                "plain/text");

            this.addErrorMessage(
                smsChatTransport.getName(),
                GuiActivator.getResources().getI18NString(
                    "service.gui.SEND_SMS_NOT_SUPPORTED"));

            return;
        }

        smsChatTransport.addSmsMessageListener(
                new SmsMessageListener(smsChatTransport));

        // We open the send SMS dialog.
        SendSmsDialog smsDialog
            = new SendSmsDialog(this, smsChatTransport, messageText);

        smsDialog.setPreferredSize(new Dimension(400, 200));
        smsDialog.setVisible(true);
    }

    /**
     * Implements the <tt>ChatPanel.sendMessage</tt> method. Obtains the
     * appropriate operation set and sends the message, contained in the write
     * area, through it.
     */
    protected void sendInstantMessage()
    {
        String htmlText = getTextFromWriteArea(
            OperationSetBasicInstantMessaging.HTML_MIME_TYPE);
        String plainText = getTextFromWriteArea(
            OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE);

        String messageText;
        String mimeType;
        if (chatSession.getCurrentChatTransport().isContentTypeSupported(
                    OperationSetBasicInstantMessaging.HTML_MIME_TYPE)
             && (htmlText.indexOf("<b") > -1
                || htmlText.indexOf("<i") > -1
                || htmlText.indexOf("<u") > -1
                || htmlText.indexOf("<font") > -1))
        {
            messageText = htmlText;
            mimeType = OperationSetBasicInstantMessaging.HTML_MIME_TYPE;
        }
        else
        {
            messageText = plainText;
            mimeType = OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE;
        }

        try
        {
            chatSession.getCurrentChatTransport()
                .sendInstantMessage(messageText, mimeType);
        }
        catch (IllegalStateException ex)
        {
            logger.error("Failed to send message.", ex);

            this.addMessage(
                chatSession.getCurrentChatTransport().getName(),
                System.currentTimeMillis(),
                Chat.OUTGOING_MESSAGE,
                messageText,
                mimeType);

            String protocolError = "";
            if (ex.getMessage() != null)
                protocolError = " " + GuiActivator.getResources().getI18NString(
                    "service.gui.ERROR_WAS",
                    new String[]{ex.getMessage()});

            this.addErrorMessage(
                chatSession.getCurrentChatTransport().getName(),
                GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_SEND_CONNECTION_PROBLEM")
                    + protocolError);
        }
        catch (Exception ex)
        {
            logger.error("Failed to send message.", ex);

            this.refreshWriteArea();

            this.addMessage(
                chatSession.getCurrentChatTransport().getName(),
                System.currentTimeMillis(),
                Chat.OUTGOING_MESSAGE,
                messageText,
                mimeType);

            String protocolError = "";
            if (ex.getMessage() != null)
                protocolError = " " + GuiActivator.getResources().getI18NString(
                    "service.gui.ERROR_WAS",
                    new String[]{ex.getMessage()});

            this.addErrorMessage(
                chatSession.getCurrentChatTransport().getName(),
                GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_DELIVERY_ERROR",
                    new String[]{protocolError}));
        }

        if (chatSession.getCurrentChatTransport().allowsTypingNotifications())
        {
            // Send TYPING STOPPED event before sending the message
            getChatWritePanel().stopTypingTimer();
        }

        this.refreshWriteArea();
    }

    /**
     * Listens for SMS messages and shows them in the chat.
     */
    private class SmsMessageListener implements MessageListener
    {
        /**
         * @param chatTransport Currently unused
         */
        public SmsMessageListener(ChatTransport chatTransport)
        {
        }

        public void messageDelivered(MessageDeliveredEvent evt)
        {
            Message msg = evt.getSourceMessage();

            Contact contact = evt.getDestinationContact();

            addMessage(
                contact.getDisplayName(),
                System.currentTimeMillis(),
                Chat.OUTGOING_MESSAGE,
                msg.getContent(), msg.getContentType());

            addMessage(
                    contact.getDisplayName(),
                    System.currentTimeMillis(),
                    Chat.ACTION_MESSAGE,
                    GuiActivator.getResources().getI18NString(
                        "service.gui.SMS_SUCCESSFULLY_SENT"),
                    "text");
        }

        public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
        {
            logger.error(evt.getReason());

            String errorMsg = null;

            Message sourceMessage = (Message) evt.getSource();

            Contact sourceContact = evt.getDestinationContact();

            MetaContact metaContact = GuiActivator
                .getContactListService().findMetaContactByContact(sourceContact);

            if (evt.getErrorCode()
                    == MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED)
            {
                errorMsg = GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_DELIVERY_NOT_SUPPORTED");
            }
            else if (evt.getErrorCode()
                    == MessageDeliveryFailedEvent.NETWORK_FAILURE)
            {
                errorMsg = GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_NOT_DELIVERED");
            }
            else if (evt.getErrorCode()
                    == MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED)
            {
                errorMsg = GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_SEND_CONNECTION_PROBLEM");
            }
            else if (evt.getErrorCode()
                    == MessageDeliveryFailedEvent.INTERNAL_ERROR)
            {
                errorMsg = GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_DELIVERY_INTERNAL_ERROR");
            }
            else {
                errorMsg = GuiActivator.getResources().getI18NString(
                    "service.gui.MSG_DELIVERY_UNKNOWN_ERROR");
            }

            String reason = evt.getReason();
            if (reason != null)
                errorMsg += " " + GuiActivator.getResources().getI18NString(
                    "service.gui.ERROR_WAS",
                    new String[]{reason});

            addMessage(
                    metaContact.getDisplayName(),
                    System.currentTimeMillis(),
                    Chat.OUTGOING_MESSAGE,
                    sourceMessage.getContent(),
                    sourceMessage.getContentType());

            addErrorMessage(
                    metaContact.getDisplayName(),
                    errorMsg);
        }

        public void messageReceived(MessageReceivedEvent evt)
        {}
    }

    /**
     * Returns the date of the first message in history for this chat.
     *
     * @return the date of the first message in history for this chat.
     */
    public long getFirstHistoryMsgTimestamp()
    {
        return firstHistoryMsgTimestamp;
    }

    /**
     * Returns the date of the last message in history for this chat.
     *
     * @return the date of the last message in history for this chat.
     */
    public long getLastHistoryMsgTimestamp()
    {
        return lastHistoryMsgTimestamp;
    }

    /**
     * Loads history messages ignoring the message with the specified id.
     *
     * @param escapedMessageID the id of the message to be ignored;
     * <tt>null</tt> if no message is to be ignored
     */
    public void loadHistory(final String escapedMessageID)
    {
        SwingWorker historyWorker = new SwingWorker()
        {
            private Collection<Object> historyList;

            public Object construct() throws Exception
            {
                // Load the history period, which initializes the
                // firstMessageTimestamp and the lastMessageTimeStamp variables.
                // Used to disable/enable history flash buttons in the chat
                // window tool bar.
                loadHistoryPeriod();

                // Load the last N=CHAT_HISTORY_SIZE messages from history.
                historyList = chatSession.getHistory(
                    ConfigurationManager.getChatHistorySize());

                return historyList;
            }

            /**
             * Called on the event dispatching thread (not on the worker thread)
             * after the <code>construct</code> method has returned.
             */
            public void finished()
            {
                if(historyList != null && historyList.size() > 0)
                {
                    processHistory(historyList, escapedMessageID);
                }
                isHistoryLoaded = true;

                // Add incoming events accumulated while the history was loading
                // at the end of the chat.
                addIncomingEvents();
            }
        };

        historyWorker.start();
    }

    /**
     * Loads history for the chat meta contact in a separate thread. Equivalent
     * to calling {@link #loadHistory(String)} with <tt>null</tt> for
     * <tt>escapedMessageID</tt>.
     */
    public void loadHistory()
    {
        this.loadHistory(null);
    }

    /**
     * Loads history period dates for the current chat.
     */
    private void loadHistoryPeriod()
    {
        this.firstHistoryMsgTimestamp = chatSession.getHistoryStartDate();

        this.lastHistoryMsgTimestamp = chatSession.getHistoryEndDate();
    }

    /**
     * Changes the "Send as SMS" check box state.
     *
     * @param isSmsSelected <code>true</code> to set the "Send as SMS" check box
     * selected, <code>false</code> - otherwise.
     */
    public void setSmsSelected(boolean isSmsSelected)
    {
        writeMessagePanel.setSmsSelected(isSmsSelected);
    }

    /**
     * The <tt>ChangeProtocolAction</tt> is an <tt>AbstractAction</tt> that
     * opens the menu, containing all available protocol contacts.
     */
    private class ChangeTransportAction
        extends AbstractAction
    {
        public void actionPerformed(ActionEvent e)
        {
            writeMessagePanel.openChatTransportSelectorBox();
        }
    }

    /**
     * Renames all occurrences of the given <tt>chatContact</tt> in this chat
     * panel.
     *
     * @param chatContact the contact to rename
     * @param name the new name
     */
    public void setContactName(ChatContact<?> chatContact, String name)
    {
        if (chatContactListPanel != null)
        {
            chatContactListPanel.renameContact(chatContact);
        }

        ChatContainer chatContainer = getChatContainer();
        chatContainer.setChatTitle(this, name);

        if (chatContainer.getCurrentChat() == this)
        {
            chatContainer.setTitle(name);
        }
    }

    /**
     * Adds the given chatTransport to the given send via selector box.
     *
     * @param chatTransport the transport to add
     */
    public void addChatTransport(ChatTransport chatTransport)
    {
        writeMessagePanel.addChatTransport(chatTransport);
    }

    /**
     * Removes the given chat status state from the send via selector box.
     *
     * @param chatTransport the transport to remove
     */
    public void removeChatTransport(ChatTransport chatTransport)
    {
        writeMessagePanel.removeChatTransport(chatTransport);
    }

    /**
     * Selects the given chat transport in the send via box.
     *
     * @param chatTransport the chat transport to be selected
     */
    public void setSelectedChatTransport(ChatTransport chatTransport)
    {
        writeMessagePanel.setSelectedChatTransport(chatTransport);
    }

    /**
     * Updates the status of the given chat transport in the send via selector
     * box and notifies the user for the status change.
     * @param chatTransport the <tt>chatTransport</tt> to update
     */
    public void updateChatTransportStatus(ChatTransport chatTransport)
    {
        writeMessagePanel.updateChatTransportStatus(chatTransport);

        // Show a status message to the user.
        this.addMessage(
            chatTransport.getName(),
            System.currentTimeMillis(),
            Chat.STATUS_MESSAGE,
            GuiActivator.getResources().getI18NString(
                "service.gui.STATUS_CHANGED_CHAT_MESSAGE",
                new String[]{chatTransport.getStatus().getStatusName()}),
                "text/plain");

        if(ConfigurationManager.isMultiChatWindowEnabled())
        {
            if (getChatContainer().getChatCount() > 0)
            {
                getChatContainer().setChatIcon(this,
                    new ImageIcon(
                        Constants.getStatusIcon(chatTransport.getStatus())));
            }
        }
    }

    /**
     * Implements <tt>ChatPanel.loadPreviousFromHistory</tt>.
     * Loads previous page from history.
     */
    public void loadPreviousPageFromHistory()
    {
        final MetaHistoryService chatHistory
            = GuiActivator.getMetaHistoryService();

        // If the MetaHistoryService is not registered we have nothing to do
        // here. The history service could be "disabled" from the user
        // through one of the configuration forms.
        if (chatHistory == null)
            return;

        new Thread()
        {
            public void run()
            {
                ChatConversationPanel conversationPanel
                    = getChatConversationPanel();

                Date firstMsgDate
                    = conversationPanel.getPageFirstMsgTimestamp();

                Collection<Object> c = null;

                if(firstMsgDate != null)
                {
                    c = chatSession.getHistoryBeforeDate(
                        firstMsgDate,
                        MESSAGES_PER_PAGE);
                }

                if(c !=null && c.size() > 0)
                {
                    SwingUtilities.invokeLater(
                            new HistoryMessagesLoader(c));
                }
            }
        }.start();
    }

    /**
     * Implements <tt>ChatPanel.loadNextFromHistory</tt>.
     * Loads next page from history.
     */
    public void loadNextPageFromHistory()
    {
        final MetaHistoryService chatHistory
            = GuiActivator.getMetaHistoryService();

        // If the MetaHistoryService is not registered we have nothing to do
        // here. The history could be "disabled" from the user
        // through one of the configuration forms.
        if (chatHistory == null)
            return;

        new Thread()
        {
            public void run()
            {
                Date lastMsgDate
                    = getChatConversationPanel().getPageLastMsgTimestamp();

                Collection<Object> c = null;
                if(lastMsgDate != null)
                {
                    c = chatSession.getHistoryAfterDate(
                        lastMsgDate,
                        MESSAGES_PER_PAGE);
                }

                if(c != null && c.size() > 0)
                    SwingUtilities.invokeLater(
                            new HistoryMessagesLoader(c));
            }
        }.start();
    }

    /**
     * From a given collection of messages shows the history in the chat window.
     */
    private class HistoryMessagesLoader implements Runnable
    {
        private final Collection<Object> chatHistory;

        public HistoryMessagesLoader(Collection<Object> history)
        {
            this.chatHistory = history;
        }

        public void run()
        {
            getChatConversationPanel().clear();

            processHistory(chatHistory, "");

            getChatConversationPanel().setDefaultContent();
        }
    }

    /**
     * Adds the given <tt>chatContact</tt> to the list of chat contacts
     * participating in the corresponding to this chat panel chat.
     * @param chatContact the contact to add
     */
    public void addChatContact(ChatContact<?> chatContact)
    {
        if (chatContactListPanel != null)
            chatContactListPanel.addContact(chatContact);
    }

    /**
     * Removes the given <tt>chatContact</tt> from the list of chat contacts
     * participating in the corresponding to this chat panel chat.
     * @param chatContact the contact to remove
     */
    public void removeChatContact(ChatContact<?> chatContact)
    {
        if (chatContactListPanel != null)
            chatContactListPanel.removeContact(chatContact);
    }

    /**
     * Updates the contact status.
     * @param chatContact the chat contact to update
     * @param statusMessage the status message to show
     */
    public void updateChatContactStatus(ChatContact<?> chatContact,
                                        String statusMessage)
    {
        this.addMessage(
            chatContact.getName(),
            System.currentTimeMillis(),
            Chat.STATUS_MESSAGE,
            statusMessage,
            ChatConversationPanel.TEXT_CONTENT_TYPE);
    }

    /**
     * Sets the given <tt>subject</tt> to this chat.
     * @param subject the subject to set
     */
    public void setChatSubject(String subject)
    {
        if (subjectPanel != null)
        {
            // Don't do anything if the subject doesn't really change.
            String oldSubject = subjectPanel.getSubject();

            if ((subject == null ) || (subject.length() == 0))
            {
                if ((oldSubject == null) || (oldSubject.length() == 0))
                    return;
            }
            else if (subject.equals(oldSubject))
                return;

            subjectPanel.setSubject(subject);

            this.addMessage(
                chatSession.getChatName(),
                System.currentTimeMillis(),
                Chat.STATUS_MESSAGE,
                GuiActivator.getResources().getI18NString(
                    "service.gui.CHAT_ROOM_SUBJECT_CHANGED",
                    new String []{  chatSession.getChatName(),
                                    subject}),
                ChatConversationPanel.TEXT_CONTENT_TYPE);
        }
    }

    /**
     * Adds the given <tt>IncomingFileTransferRequest</tt> to the conversation
     * panel in order to notify the user of the incoming file.
     *
     * @param fileTransferOpSet the file transfer operation set
     * @param request the request to display in the conversation panel
     * @param date the date on which the request has been received
     */
    public void addIncomingFileTransferRequest(
        OperationSetFileTransfer fileTransferOpSet,
        IncomingFileTransferRequest request,
        Date date)
    {
        this.addActiveFileTransfer(request.getID(), request);

        ReceiveFileConversationComponent component
            = new ReceiveFileConversationComponent(
                this, fileTransferOpSet, request, date);

        if (ConfigurationManager.isHistoryShown() && !isHistoryLoaded)
        {
            synchronized (incomingEventBuffer)
            {
                incomingEventBuffer.add(component);
            }
        }
        else
            this.getChatConversationPanel().addComponent(component);
    }

    /**
     * Implements <tt>Chat.addChatFocusListener</tt> method. Adds the given
     * <tt>ChatFocusListener</tt> to the list of listeners.
     *
     * @param listener the listener that we'll be adding.
     */
    public void addChatFocusListener(ChatFocusListener listener)
    {
        synchronized (focusListeners)
        {
            if (!focusListeners.contains(listener))
                focusListeners.add(listener);
        }
    }

    /**
     * Implements <tt>Chat.removeChatFocusListener</tt> method. Removes the given
     * <tt>ChatFocusListener</tt> from the list of listeners.
     *
     * @param listener the listener to remove.
     */
    public void removeChatFocusListener(ChatFocusListener listener)
    {
        synchronized (focusListeners)
        {
            focusListeners.remove(listener);
        }
    }

    /**
     * Returns the first chat transport for the current chat session that
     * supports file transfer.
     *
     * @return the first chat transport for the current chat session that
     * supports file transfer.
     */
    public ChatTransport findFileTransferChatTransport()
    {
        ChatTransport currentChatTransport
            = chatSession.getCurrentChatTransport();

        if (currentChatTransport.getProtocolProvider()
                .getOperationSet(OperationSetFileTransfer.class) != null)
        {
            return currentChatTransport;
        }
        else
        {
            Iterator<ChatTransport> chatTransportsIter
                = chatSession.getChatTransports();

            while (chatTransportsIter.hasNext())
            {
                ChatTransport chatTransport = chatTransportsIter.next();

                Object fileTransferOpSet
                    = chatTransport.getProtocolProvider()
                        .getOperationSet(OperationSetFileTransfer.class);

                if (fileTransferOpSet != null)
                    return chatTransport;
            }
        }

        return null;
    }

    /**
     * Returns the first chat transport for the current chat session that
     * supports group chat.
     *
     * @return the first chat transport for the current chat session that
     * supports group chat.
     */
    public ChatTransport findInviteChatTransport()
    {
        ChatTransport currentChatTransport
            = chatSession.getCurrentChatTransport();

        ProtocolProviderService protocolProvider
            = currentChatTransport.getProtocolProvider();

        // We choose between OpSets for multi user chat...
        if (protocolProvider.getOperationSet(
            OperationSetMultiUserChat.class) != null
            || protocolProvider.getOperationSet(
                OperationSetAdHocMultiUserChat.class) != null)
        {
            return chatSession.getCurrentChatTransport();
        }

        else
        {
            Iterator<ChatTransport> chatTransportsIter
                = chatSession.getChatTransports();

            while (chatTransportsIter.hasNext())
            {
                ChatTransport chatTransport = chatTransportsIter.next();

                Object groupChatOpSet
                    = chatTransport.getProtocolProvider()
                        .getOperationSet(OperationSetMultiUserChat.class);

                if (groupChatOpSet != null)
                    return chatTransport;
            }
        }

        return null;
    }

    /**
     * Invites the given <tt>chatContacts</tt> to this chat.
     * @param inviteChatTransport the chat transport to use to send the invite
     * @param chatContacts the contacts to invite
     * @param reason the reason of the invitation
     */
    public void inviteContacts( ChatTransport inviteChatTransport,
                                Collection<String> chatContacts,
                                String reason)
    {
        ChatSession conferenceChatSession = null;

        if (chatSession instanceof MetaContactChatSession)
        {
            chatContacts.add(inviteChatTransport.getName());

            ConferenceChatManager conferenceChatManager
                = GuiActivator.getUIService().getConferenceChatManager();

            // the chat session is set regarding to which OpSet is used for MUC
            if(inviteChatTransport.getProtocolProvider().
                    getOperationSet(OperationSetMultiUserChat.class) != null)
            {
                ChatRoomWrapper chatRoomWrapper
                    = conferenceChatManager.createChatRoom(
                        inviteChatTransport.getProtocolProvider(),
                        chatContacts,
                        reason);

                conferenceChatSession
                    = new ConferenceChatSession(this, chatRoomWrapper);
            }
            else if (inviteChatTransport.getProtocolProvider().
                getOperationSet(OperationSetAdHocMultiUserChat.class) != null)
            {
                AdHocChatRoomWrapper chatRoomWrapper
                    = conferenceChatManager.createAdHocChatRoom(
                            inviteChatTransport.getProtocolProvider(),
                            chatContacts,
                            reason);

                conferenceChatSession
                    = new AdHocConferenceChatSession(this, chatRoomWrapper);
            }

            if (conferenceChatSession != null)
                this.setChatSession(conferenceChatSession);
        }
        // We're already in a conference chat.
        else
        {
            conferenceChatSession = chatSession;

            for (String contactAddress : chatContacts)
            {
                conferenceChatSession.getCurrentChatTransport()
                    .inviteChatContact(contactAddress, reason);
            }
        }
    }

    /**
     * Informs all <tt>ChatFocusListener</tt>s that a <tt>ChatFocusEvent</tt>
     * has been triggered.
     *
     * @param eventID the type of the <tt>ChatFocusEvent</tt>
     */
    public void fireChatFocusEvent(int eventID)
    {
        ChatFocusEvent evt = new ChatFocusEvent(this, eventID);

        if (logger.isTraceEnabled())
            logger.trace("Will dispatch the following chat event: " + evt);

        Iterable<ChatFocusListener> listeners;
        synchronized (focusListeners)
        {
            listeners = new ArrayList<ChatFocusListener>(focusListeners);
        }

        for (ChatFocusListener listener : listeners)
        {
            switch (evt.getEventID())
            {
            case ChatFocusEvent.FOCUS_GAINED:
                listener.chatFocusGained(evt);
                break;
            case ChatFocusEvent.FOCUS_LOST:
                listener.chatFocusLost(evt);
                break;
            default:
                logger.error("Unknown event type " + evt.getEventID());
            }
        }
    }

    /**
     * Handles file transfer status changed in order to remove completed file
     * transfers from the list of active transfers.
     * @param event the file transfer status change event the notified us for
     * the change
     */
    public void statusChanged(FileTransferStatusChangeEvent event)
    {
        FileTransfer fileTransfer = event.getFileTransfer();

        int newStatus = event.getNewStatus();

        if (newStatus == FileTransferStatusChangeEvent.COMPLETED
            || newStatus == FileTransferStatusChangeEvent.CANCELED
            || newStatus == FileTransferStatusChangeEvent.FAILED
            || newStatus == FileTransferStatusChangeEvent.REFUSED)
        {
            removeActiveFileTransfer(fileTransfer.getID());
            fileTransfer.removeStatusListener(this);
        }
    }

    /**
     * Returns <code>true</code> if there are active file transfers, otherwise
     * returns <code>false</code>.
     * @return <code>true</code> if there are active file transfers, otherwise
     * returns <code>false</code>
     */
    public boolean containsActiveFileTransfers()
    {
        return !activeFileTransfers.isEmpty();
    }

    /**
     * Cancels all active file transfers.
     */
    public void cancelActiveFileTransfers()
    {
        Enumeration<String> activeKeys = activeFileTransfers.keys();

        while (activeKeys.hasMoreElements())
        {
            // catchall so if anything happens we still
            // will close the chat/window
            try
            {
                String key = activeKeys.nextElement();
                Object descriptor = activeFileTransfers.get(key);

                if (descriptor instanceof IncomingFileTransferRequest)
                {
                    ((IncomingFileTransferRequest) descriptor).rejectFile();
                }
                else if (descriptor instanceof FileTransfer)
                {
                    ((FileTransfer) descriptor).cancel();
                }
            }
            catch(Throwable t)
            {
                logger.error("Cannot cancel file transfer.", t);
            }
        }
    }

    /**
     * Stores the current divider position.
     */
    private class DividerLocationListener implements PropertyChangeListener
    {
        public void propertyChange(PropertyChangeEvent evt)
        {
            if (evt.getPropertyName()
                    .equals(JSplitPane.DIVIDER_LOCATION_PROPERTY))
            {
                int dividerLocation = (Integer) evt.getNewValue();

                // We store the divider location only when the user drags the
                // divider and not when we've set it programatically.
//                if (dividerLocation != autoDividerLocation)
//                {
                    int writeAreaSize
                        = messagePane.getHeight() - dividerLocation
                                        - messagePane.getDividerSize();

                    ConfigurationManager
                        .setChatWriteAreaSize(writeAreaSize);

//                    writeMessagePanel.setPreferredSize(
//                        new Dimension(
//                            (int) writeMessagePanel.getPreferredSize()
//                                    .getWidth(),
//                            writeAreaSize));
//                }
            }
        }
    }

    /**
     * Sets the location of the split pane divider.
     *
     * @param location the location of the divider given by the pixel count
     * between the left bottom corner and the left bottom divider location
     */
    public void setDividerLocation(int location)
    {
        int dividerLocation = messagePane.getHeight() - location;

        autoDividerLocation = dividerLocation;

        messagePane.setDividerLocation(dividerLocation);
        messagePane.revalidate();
        messagePane.repaint();
    }

    /**
     * Returns the contained divider location.
     *
     * @return the contained divider location
     */
    public int getDividerLocation()
    {
        return messagePane.getHeight() - messagePane.getDividerLocation();
    }

    /**
     * Returns the contained divider size.
     *
     * @return the contained divider size
     */
    public int getDividerSize()
    {
        return messagePane.getDividerSize();
    }

    /**
     * Adds all events accumulated in the incoming event buffer to the
     * chat conversation panel.
     */
    private void addIncomingEvents()
    {
        synchronized (incomingEventBuffer)
        {
            Iterator<Object> eventBufferIter = incomingEventBuffer.iterator();

            while(eventBufferIter.hasNext())
            {
                Object incomingEvent = eventBufferIter.next();

                if (incomingEvent instanceof ChatMessage)
                {
                    this.appendChatMessage((ChatMessage) incomingEvent);
                }
                else if (incomingEvent instanceof ChatConversationComponent)
                {
                    this.getChatConversationPanel()
                        .addComponent((ChatConversationComponent)incomingEvent);
                }
            }
        }
    }

    /**
     * Adds the given file transfer <tt>id</tt> to the list of active file
     * transfers.
     *
     * @param id the identifier of the file transfer to add
     * @param descriptor the descriptor of the file transfer
     */
    public void addActiveFileTransfer(String id, Object descriptor)
    {
        synchronized (activeFileTransfers)
        {
            activeFileTransfers.put(id, descriptor);
        }
    }

    /**
     * Removes the given file transfer <tt>id</tt> from the list of active
     * file transfers.
     * @param id the identifier of the file transfer to remove
     */
    public void removeActiveFileTransfer(String id)
    {
        synchronized (activeFileTransfers)
        {
            activeFileTransfers.remove(id);
        }
    }

    /**
     * Adds the given {@link ChatMenuListener} to this <tt>Chat</tt>.
     * The <tt>ChatMenuListener</tt> is used to determine menu elements
     * that should be added on right clicks.
     *
     * @param l the <tt>ChatMenuListener</tt> to add
     */
    public void addChatEditorMenuListener(ChatMenuListener l)
    {
        this.getChatWritePanel().addChatEditorMenuListener(l);
    }
   
    /**
     * Adds the given {@link CaretListener} to this <tt>Chat</tt>.
     * The <tt>CaretListener</tt> is used to inform other bundles when a user has
     * moved the caret in the chat editor area.
     *
     * @param l the <tt>CaretListener</tt> to add
     */
    public void addChatEditorCaretListener(CaretListener l)
    {
        this.getChatWritePanel().getEditorPane().addCaretListener(l);
    }

    /**
     * Adds the given {@link DocumentListener} to this <tt>Chat</tt>.
     * The <tt>DocumentListener</tt> is used to inform other bundles when a user has
     * modified the document in the chat editor area.
     *
     * @param l the <tt>DocumentListener</tt> to add
     */
    public void addChatEditorDocumentListener(DocumentListener l)
    {
        this.getChatWritePanel().getEditorPane()
            .getDocument().addDocumentListener(l);
    }

      /**
     * Removes the given {@link CaretListener} from this <tt>Chat</tt>.
     * The <tt>CaretListener</tt> is used to inform other bundles when a user has
     * moved the caret in the chat editor area.
     *
     * @param l the <tt>CaretListener</tt> to remove
     */
    public void removeChatEditorCaretListener(CaretListener l)
    {
        this.getChatWritePanel().getEditorPane().removeCaretListener(l);
    }

     /**
     * Removes the given {@link ChatMenuListener} to this <tt>Chat</tt>.
     * The <tt>ChatMenuListener</tt> is used to determine menu elements
     * that should be added on right clicks.
     *
     * @param l the <tt>ChatMenuListener</tt> to add
     */
    public void removeChatEditorMenuListener(ChatMenuListener l)
    {
        this.getChatWritePanel().removeChatEditorMenuListener(l);
    }

    /**
     * Removes the given {@link DocumentListener} from this <tt>Chat</tt>.
     * The <tt>DocumentListener</tt> is used to inform other bundles when a user has
     * modified the document in the chat editor area.
     *
     * @param l the <tt>DocumentListener</tt> to remove
     */
    public void removeChatEditorDocumentListener(DocumentListener l)
    {
        this.getChatWritePanel().getEditorPane()
            .getDocument().removeDocumentListener(l);
    }

    /**
     * Adds the given <tt>ChatHistoryListener</tt> to the list of listeners
     * notified when a change occurs in the history shown in this chat panel.
     *
     * @param l the <tt>ChatHistoryListener</tt> to add
     */
    public void addChatHistoryListener(ChatHistoryListener l)
    {
        synchronized (historyListeners)
        {
            historyListeners.add(l);
        }
    }

    /**
     * Removes the given <tt>ChatHistoryListener</tt> from the list of listeners
     * notified when a change occurs in the history shown in this chat panel.
     *
     * @param l the <tt>ChatHistoryListener</tt> to remove
     */
    public void removeChatHistoryListener(ChatHistoryListener l)
    {
        synchronized (historyListeners)
        {
            historyListeners.remove(l);
        }
    }

    /**
     * Notifies all registered <tt>ChatHistoryListener</tt>s that a change has
     * occurred in the history of this chat.
     */
    private void fireChatHistoryChange()
    {
        Iterator<ChatHistoryListener> listeners = historyListeners.iterator();

        while (listeners.hasNext())
        {
            listeners.next().chatHistoryChanged(this);
        }
    }

    /**
     * Provides the {@link Highlighter} used in rendering the chat editor.
     *
     * @return highlighter used to render message being composed
     */
    public Highlighter getHighlighter()
    {
        return this.getChatWritePanel().getEditorPane().getHighlighter();
    }

    /**
     * Gets the caret position in the chat editor.
     * @return index of caret in message being composed
     */
    public int getCaretPosition()
    {
        return this.getChatWritePanel().getEditorPane().getCaretPosition();
    }

    /**
     * Causes the chat to validate its appearance (suggests a repaint operation
     * may be necessary).
     */
    public void promptRepaint()
    {
        this.getChatWritePanel().getEditorPane().repaint();
    }


    /**
     * Shows the font chooser dialog
     */
    public void showFontChooserDialog()
    {
        JEditorPane editorPane = writeMessagePanel.getEditorPane();
        FontChooser fontChooser = new FontChooser();

        fontChooser.setFontFamily(
            editorPane.getFont().getFontName());
        fontChooser.setFontSize(
            editorPane.getFont().getSize());

        fontChooser.setBoldStyle(editorPane.getFont().isBold());
        fontChooser.setItalicStyle(editorPane.getFont().isItalic());

        int result = fontChooser.showDialog(this);

        if (result == FontChooser.OK_OPTION)
        {
            // Font family and size
            writeMessagePanel.setFontFamilyAndSize(
                                    fontChooser.getFontFamily(),
                                    fontChooser.getFontSize());

            // Font style
            writeMessagePanel.setBoldStyleEnable(
                fontChooser.isBoldStyleSelected());
            writeMessagePanel.setItalicStyleEnable(
                fontChooser.isItalicStyleSelected());
            writeMessagePanel.setUnderlineStyleEnable(
                fontChooser.isUnderlineStyleSelected());

            // Font color
            writeMessagePanel.setFontColor(fontChooser.getFontColor());
        }

        editorPane.requestFocus();
    }

    /**
     * Reloads chat messages.
     */
    public void loadSkin()
    {
        try
        {
            Document doc
                = this.conversationPanel.getChatTextPane().getDocument();
            doc.remove(0, doc.getLength());
        }
        catch (BadLocationException ex)
        {
            logger.error(ex);
        }
        loadHistory();
    }

    /**
     *  Notifies the user if any member of the chatroom changes nickname
     */
    public void chatRoomPropertyChanged(ChatRoomMemberPropertyChangeEvent event)
    {
        this.conversationPanel
            .appendMessageToEnd(
                "<DIV identifier=\"message\" style=\"color:#707070;\">"
                + event.getOldValue()
                + " is now known as "
                + event.getNewValue() + "</DIV>");
       
    }
}
TOP

Related Classes of net.java.sip.communicator.impl.gui.main.chat.ChatPanel$ChangeTransportAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.