/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.impl.gui.main.chat;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.main.chat.menus.*;
import net.java.sip.communicator.impl.gui.main.chat.toolBars.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.util.skin.*;
import org.jitsi.util.*;
import org.osgi.framework.*;
import com.explodingpixels.macwidgets.*;
/**
* The chat window is the place, where users can write and send messages, view
* received messages. The ChatWindow supports two modes of use: "Group all
* messages in one window" and "Open messages in new window". In the first case
* a <tt>JTabbedPane</tt> is added in the window, where each tab contains a
* <tt>ChatPanel</tt>. In the second case the <tt>ChatPanel</tt> is added
* like a "write message area", "send" button, etc. It corresponds to a
* <tt>MetaContact</tt> or to a conference.
* <p>
* Note that the conference case is not yet implemented.
*
* @author Yana Stamcheva
* @author Lyubomir Marinov
* @author Adam Netocny
*/
public class ChatWindow
extends SIPCommFrame
implements ChatContainer,
ExportedWindow,
PluginComponentListener,
WindowFocusListener
{
/**
* The <tt>Logger</tt> used by the <tt>ChatWindow</tt> class and its
* instances for logging output.
*/
private static final Logger logger = Logger.getLogger(ChatWindow.class);
private ConversationTabbedPane chatTabbedPane = null;
private int chatCount = 0;
private final List<ChatChangeListener> chatChangeListeners
= new Vector<ChatChangeListener>();
private final JPanel mainPanel
= new TransparentPanel(new BorderLayout());
private final JPanel statusBarPanel
= new TransparentPanel(new BorderLayout());
private final JPanel pluginPanelSouth = new JPanel();
private final JPanel pluginPanelWest = new JPanel();
private final JPanel pluginPanelEast = new JPanel();
private final ContactPhotoPanel contactPhotoPanel;
private final MessageWindowMenuBar menuBar;
private MainToolBar mainToolBar;
private final Component toolbarPanel;
/**
* A keyboard manager, where we register our own key dispatcher.
*/
private KeyboardFocusManager keyManager;
/**
* A key dispatcher that redirects all key events to call field.
*/
private KeyEventDispatcher keyDispatcher;
/**
* Creates an instance of <tt>ChatWindow</tt> by passing to it an instance
* of the main application window.
*/
public ChatWindow()
{
if (!ConfigurationUtils.isWindowDecorated())
this.setUndecorated(true);
this.addWindowFocusListener(this);
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//If in mode TABBED_CHAT_WINDOW initialize the tabbed pane
if(ConfigurationUtils.isMultiChatWindowEnabled())
chatTabbedPane = new ConversationTabbedPane();
menuBar = new MessageWindowMenuBar(this);
contactPhotoPanel = new ContactPhotoPanel();
this.setJMenuBar(menuBar);
toolbarPanel = createToolBar();
this.getContentPane().add(toolbarPanel, BorderLayout.NORTH);
this.getContentPane().add(mainPanel, BorderLayout.CENTER);
this.getContentPane().add(statusBarPanel, BorderLayout.SOUTH);
this.initPluginComponents();
this.setKeybindingInput(KeybindingSet.Category.CHAT);
this.addKeybindingAction( "chat-nextTab",
new ForwardTabAction());
this.addKeybindingAction( "chat-previousTab",
new BackwordTabAction());
this.addKeybindingAction( "chat-copy",
new CopyAction());
this.addKeybindingAction( "chat-paste",
new PasteAction());
this.addKeybindingAction( "chat-openSmileys",
new OpenSmileyAction());
this.addKeybindingAction( "chat-openHistory",
new OpenHistoryAction());
this.addKeybindingAction( "chat-close",
new CloseAction());
this.addWindowListener(new ChatWindowAdapter());
int width = GuiActivator.getResources()
.getSettingsInt("impl.gui.CHAT_WINDOW_WIDTH");
int height = GuiActivator.getResources()
.getSettingsInt("impl.gui.CHAT_WINDOW_HEIGHT");
this.setSize(width, height);
}
/**
* Shows or hides the Toolbar depending on the value of parameter b.
*
* @param b if true, makes the Toolbar visible, otherwise hides the Toolbar
*/
public void setToolbarVisible(boolean b)
{
// The north panel is the one containing the toolbar and contact photo.
toolbarPanel.setVisible(b);
}
/**
* @see SIPCommFrame#dispose()
*/
@Override
public void dispose()
{
try
{
UIServiceImpl uiService = GuiActivator.getUIService();
/*
* The ChatWindow should seize to exist so we don't want any strong
* references to it i.e. it cannot be exported anymore.
*/
uiService.unregisterExportedWindow(this);
uiService.removePluginComponentListener(this);
mainToolBar.dispose();
menuBar.dispose();
}
finally
{
super.dispose();
}
}
/**
* Returns the main toolbar in this chat window.
* @return the main toolbar in this chat window
*/
public MainToolBar getMainToolBar()
{
return mainToolBar;
}
/**
* Adds a given <tt>ChatPanel</tt> to this chat window.
*
* @param chatPanel The <tt>ChatPanel</tt> to add.
*/
public void addChat(final ChatPanel chatPanel)
{
if (ConfigurationUtils.isMultiChatWindowEnabled())
addChatTab(chatPanel);
else
addSimpleChat(chatPanel);
chatCount ++;
chatPanel.setShown(true);
for (ChatChangeListener l : chatChangeListeners)
l.chatChanged(chatPanel);
}
/**
* Creates the toolbar panel for this chat window, depending on the current
* operating system.
*
* @return the created toolbar
*/
private Component createToolBar()
{
Component toolbarPanel = null;
mainToolBar = new MainToolBar(this);
boolean chatToolbarVisible
= ConfigurationUtils.isChatToolbarVisible();
if (OSUtils.IS_MAC)
{
UnifiedToolBar macToolbarPanel = new UnifiedToolBar();
MacUtils.makeWindowLeopardStyle(getRootPane());
macToolbarPanel.addComponentToLeft(mainToolBar);
macToolbarPanel.addComponentToRight(contactPhotoPanel);
macToolbarPanel.disableBackgroundPainter();
macToolbarPanel.installWindowDraggerOnWindow(this);
macToolbarPanel.getComponent()
.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
macToolbarPanel.getComponent().setVisible(chatToolbarVisible);
toolbarPanel = macToolbarPanel.getComponent();
}
else
{
ToolbarPanel panel = new ToolbarPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
panel.add(mainToolBar, BorderLayout.CENTER);
panel.add(contactPhotoPanel, BorderLayout.EAST);
panel.setVisible(chatToolbarVisible);
toolbarPanel = panel;
}
return toolbarPanel;
}
/**
* Adds a given <tt>ChatPanel</tt> to this chat window.
*
* @param chatPanel The <tt>ChatPanel</tt> to add.
*/
private void addSimpleChat(ChatPanel chatPanel)
{
this.mainPanel.add(chatPanel, BorderLayout.CENTER);
}
/**
* Adds a given <tt>ChatPanel</tt> to the <tt>JTabbedPane</tt> of this
* chat window.
*
* @param chatPanel The <tt>ChatPanel</tt> to add.
*/
private void addChatTab(ChatPanel chatPanel)
{
ChatSession chatSession = chatPanel.getChatSession();
String chatName = chatSession.getChatName();
ChatPanel currentChatPanel = getCurrentChat();
if (currentChatPanel == null)
{
this.mainPanel.add(chatPanel, BorderLayout.CENTER);
}
else if (getChatTabCount() == 0)
{
ChatSession firstChatSession = currentChatPanel.getChatSession();
// Add first two tabs to the tabbed pane.
chatTabbedPane.addTab( firstChatSession.getChatName(),
firstChatSession.getChatStatusIcon(),
currentChatPanel);
chatTabbedPane.addTab( chatName,
chatSession.getChatStatusIcon(),
chatPanel);
// When added to the tabbed pane, the first chat panel should
// rest the selected component.
chatTabbedPane.setSelectedComponent(currentChatPanel);
//add the chatTabbedPane to the window
this.mainPanel.add(chatTabbedPane, BorderLayout.CENTER);
this.mainPanel.validate();
}
else
{
// The tabbed pane already contains tabs.
chatTabbedPane.addTab(
chatName,
chatSession.getChatStatusIcon(),
chatPanel);
chatTabbedPane.getParent().validate();
}
}
/**
* Removes a given <tt>ChatPanel</tt> from this chat window.
*
* @param chatPanel The <tt>ChatPanel</tt> to remove.
*/
public void removeChat(ChatPanel chatPanel)
{
if (logger.isDebugEnabled())
logger.debug("Removes chat for contact: "
+ chatPanel.getChatSession().getChatName());
//if there's no tabs remove the chat panel directly from the content
//pane.
if(getChatTabCount() == 0)
{
this.mainPanel.remove(chatPanel);
chatCount --;
}
else
{
//in the case of a tabbed chat window
int index = chatTabbedPane.indexOfComponent(chatPanel);
if (index != -1)
{
if (chatTabbedPane.getTabCount() > 1)
chatTabbedPane.removeTabAt(index);
if (chatTabbedPane.getTabCount() == 1)
{
ChatPanel currentChatPanel = getCurrentChat();
this.chatTabbedPane.removeAll();
this.mainPanel.remove(chatTabbedPane);
this.mainPanel.add(currentChatPanel, BorderLayout.CENTER);
this.setCurrentChat(currentChatPanel);
// The current chat is now the focused chat, so we remove the
// non read chat state.
GuiActivator.getUIService().getChatWindowManager()
.removeNonReadChatState(currentChatPanel);
}
chatCount --;
}
}
if (getChatCount() == 0)
dispose();
}
/**
* Removes all tabs in the chat tabbed pane. If not in mode
* TABBED_CHAT_WINDOW does nothing.
*/
public void removeAllChats()
{
if (logger.isDebugEnabled())
logger.debug("Remove all tabs from the chat window.");
if(getChatTabCount() > 0)
{
this.chatTabbedPane.removeAll();
this.mainPanel.remove(chatTabbedPane);
chatCount = 0;
}
else
{
this.removeChat(getCurrentChat());
}
dispose();
}
/**
* Selects the chat tab which corresponds to the given <tt>MetaContact</tt>.
*
* @param chatPanel The <tt>ChatPanel</tt> to select.
*/
public void setCurrentChat(final ChatPanel chatPanel)
{
ChatSession chatSession = chatPanel.getChatSession();
if (logger.isDebugEnabled())
logger.debug(
"Set current chat panel to: " + chatSession.getChatName());
if(getChatTabCount() > 0)
this.chatTabbedPane.setSelectedComponent(chatPanel);
this.setTitle(chatSession.getChatName());
this.setChatContactPhoto(chatSession);
this.mainToolBar.getSmileysBox().setChat(chatPanel);
chatPanel.requestFocusInWriteArea();
for (ChatChangeListener l : chatChangeListeners)
l.chatChanged(chatPanel);
}
/**
* Selects the tab given by the index. If there's no tabbed pane, does
* nothing.
*
* @param index the index to select
*/
public void setCurrentChatTab(int index)
{
if(getChatTabCount() > 0)
{
ChatPanel chatPanel
= (ChatPanel) this.chatTabbedPane.getComponentAt(index);
setCurrentChat(chatPanel);
}
}
/**
* Returns the currently selected chat panel.
*
* @return the currently selected chat panel.
*/
public ChatPanel getCurrentChat()
{
if(getChatTabCount() > 0)
return (ChatPanel) chatTabbedPane.getSelectedComponent();
else
{
int componentCount = mainPanel.getComponentCount();
for (int i = 0; i < componentCount; i ++)
{
Component c = mainPanel.getComponent(i);
if(c instanceof ChatPanel)
return (ChatPanel) c;
}
}
return null;
}
/**
* Returns the currently available chat panels.
*
* @return the currently available chat panels.
*/
public List<ChatPanel> getChats()
{
java.awt.Container container
= (getChatTabCount() > 0) ? chatTabbedPane : mainPanel;
int componentCount = container.getComponentCount();
List<ChatPanel> chatPanels
= new ArrayList<ChatPanel>(componentCount);
for (int i = 0; i < componentCount; i++)
{
Component c = container.getComponent(i);
if (c instanceof ChatPanel)
chatPanels.add((ChatPanel) c);
}
return chatPanels;
}
/**
* Returns the tab count of the chat tabbed pane. Meant to be used when in
* "Group chat windows" mode.
*
* @return int The number of opened tabs.
*/
private int getChatTabCount()
{
return (chatTabbedPane == null) ? 0 : chatTabbedPane.getTabCount();
}
/**
* Highlights the corresponding tab for the given chat panel.
*
* @param chatPanel the chat panel which corresponds to the tab to highlight
*/
private void highlightTab(ChatPanel chatPanel)
{
int tabIndex = chatTabbedPane.indexOfComponent(chatPanel);
if (tabIndex > -1)
{
chatPanel.unreadMessageNumber ++;
chatTabbedPane.highlightTab(tabIndex, chatPanel.unreadMessageNumber);
}
}
/**
* Sets the given icon to the tab opened for the given chat panel.
*
* @param chatPanel the chat panel, which corresponds the tab
* @param icon the icon to be set
*/
public void setTabIcon(ChatPanel chatPanel, Icon icon)
{
int index = this.chatTabbedPane.indexOfComponent(chatPanel);
if (index > -1)
this.chatTabbedPane.setIconAt(index, icon);
}
/**
* Sets the given title to the tab opened for the given chat panel.
* @param chatPanel the chat panel
* @param title the new title of the tab
*/
public void setTabTitle(ChatPanel chatPanel, String title)
{
int index = this.chatTabbedPane.indexOfComponent(chatPanel);
if(index > -1)
this.chatTabbedPane.setTitleAt(index, title);
}
/**
* The <tt>ForwardTabAction</tt> is an <tt>AbstractAction</tt> that
* changes the currently selected tab with the next one. Each time when the
* last tab index is reached the first one is selected.
*/
private class ForwardTabAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
if (getChatTabCount() > 0)
{
int selectedIndex = chatTabbedPane.getSelectedIndex();
if (selectedIndex < chatTabbedPane.getTabCount() - 1)
setCurrentChatTab(selectedIndex + 1);
else
setCurrentChatTab(0);
}
}
}
/**
* The <tt>BackwordTabAction</tt> is an <tt>AbstractAction</tt> that
* changes the currently selected tab with the previous one. Each time when
* the first tab index is reached the last one is selected.
*/
private class BackwordTabAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
if (getChatTabCount() > 0)
{
int selectedIndex = chatTabbedPane.getSelectedIndex();
if (selectedIndex != 0)
setCurrentChatTab(selectedIndex - 1);
else
setCurrentChatTab(chatTabbedPane.getTabCount() - 1);
}
}
}
/**
* The <tt>CopyAction</tt> is an <tt>AbstractAction</tt> that copies the
* text currently selected.
*/
private class CopyAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
getCurrentChat().copy();
}
}
/**
* The <tt>PasteAction</tt> is an <tt>AbstractAction</tt> that pastes
* the text contained in the clipboard in the current <tt>ChatPanel</tt>.
*/
private class PasteAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
getCurrentChat().paste();
}
}
/**
* The <tt>OpenSmileyAction</tt> is an <tt>AbstractAction</tt> that
* opens the menu, containing all available smileys' icons.
*/
private class OpenSmileyAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
mainToolBar.getSmileysBox().open();
}
}
/**
* The <tt>OpenHistoryAction</tt> is an <tt>AbstractAction</tt> that
* opens the history window for the currently selected contact.
*/
private class OpenHistoryAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
mainToolBar.getHistoryButton().doClick();
}
}
/**
* The <tt>CloseAction</tt> is an <tt>AbstractAction</tt> that
* closes a tab in the chat window.
*/
private class CloseAction
extends UIAction
{
public void actionPerformed(ActionEvent e)
{
close(true);
}
}
/**
* Before closing the chat window saves the current size and position
* through the <tt>ConfigurationService</tt>.
*/
public class ChatWindowAdapter
extends WindowAdapter
{
@Override
public void windowDeiconified(WindowEvent e)
{
String title = getTitle();
if (title.startsWith("*"))
setTitle(title.substring(1, title.length()));
}
@Override
public void windowOpened(WindowEvent e)
{
if (keyManager == null)
{
keyManager
= KeyboardFocusManager.getCurrentKeyboardFocusManager();
if (keyDispatcher == null)
keyDispatcher = new MainKeyDispatcher(keyManager);
keyManager.addKeyEventDispatcher(keyDispatcher);
}
}
@Override
public void windowClosed(WindowEvent e)
{
if (keyManager != null)
keyManager.removeKeyEventDispatcher(keyDispatcher);
keyManager = null;
keyDispatcher = null;
}
}
/**
* Implements the <tt>SIPCommFrame</tt> close method. We check for an open
* menu and if there's one we close it, otherwise we close the current chat.
* @param isEscaped indicates if this window was closed by pressing the esc
* button
*/
@Override
protected void close(boolean isEscaped)
{
if(isEscaped)
{
ChatPanel chatPanel = getCurrentChat();
if(chatPanel == null
|| chatPanel.getChatConversationPanel() == null)
return;
ChatRightButtonMenu chatRightMenu
= chatPanel.getChatConversationPanel().getRightButtonMenu();
ChatWritePanel chatWritePanel = chatPanel.getChatWritePanel();
WritePanelRightButtonMenu writePanelRightMenu
= chatWritePanel.getRightButtonMenu();
if (chatRightMenu.isVisible())
{
chatRightMenu.setVisible(false);
}
else if (writePanelRightMenu.isVisible())
{
writePanelRightMenu.setVisible(false);
}
else if ((menuBar.getSelectedMenu() != null)
|| mainToolBar.getSmileysBox()
.getPopupMenu().isVisible())
{
MenuSelectionManager.defaultManager().clearSelectedPath();
}
else
{
GuiActivator
.getUIService().getChatWindowManager().closeChat(chatPanel);
}
}
else
{
if(ConfigurationUtils.isMultiChatWindowEnabled())
{
GuiActivator
.getUIService().getChatWindowManager().closeAllChats(this, true);
}
else
{
ChatPanel chatPanel = getCurrentChat();
if(chatPanel == null
|| chatPanel.getChatConversationPanel() == null)
return;
GuiActivator
.getUIService().getChatWindowManager().closeChat(chatPanel);
}
}
}
/**
* Implements the <tt>ExportedWindow.getIdentifier()</tt> method.
* @return the identifier of this window, used as plugin container
* identifier.
*/
public WindowID getIdentifier()
{
return ExportedWindow.CHAT_WINDOW;
}
/**
* Implements the <tt>ExportedWindow.minimize()</tt> method. Minimizes this
* window.
*/
public void minimize()
{
this.setExtendedState(JFrame.ICONIFIED);
}
/**
* Implements the <tt>ExportedWindow.maximize()</tt> method. Maximizes this
* window.
*/
public void maximize()
{
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
/**
* Implements the <tt>ExportedWindow.bringToFront()</tt> method. Brings
* this window to front.
*/
public void bringToFront()
{
if(getExtendedState() == JFrame.ICONIFIED)
setExtendedState(JFrame.NORMAL);
this.toFront();
}
/**
* Initialize plugin components already registered for this container.
*/
private void initPluginComponents()
{
// Make sure that we don't miss any event.
GuiActivator.getUIService().addPluginComponentListener(this);
pluginPanelEast.setLayout(
new BoxLayout(pluginPanelEast, BoxLayout.Y_AXIS));
pluginPanelSouth.setLayout(
new BoxLayout(pluginPanelSouth, BoxLayout.Y_AXIS));
pluginPanelWest.setLayout(
new BoxLayout(pluginPanelWest, BoxLayout.Y_AXIS));
this.getContentPane().add(pluginPanelEast, BorderLayout.EAST);
this.getContentPane().add(pluginPanelWest, BorderLayout.WEST);
this.mainPanel.add(pluginPanelSouth, BorderLayout.SOUTH);
// Search for plugin components registered through the OSGI bundle
// context.
Collection<ServiceReference<PluginComponentFactory>> serRefs;
String osgiFilter
= "(|(" + Container.CONTAINER_ID + "="
+ Container.CONTAINER_CHAT_WINDOW.getID() + ")("
+ Container.CONTAINER_ID + "="
+ Container.CONTAINER_CHAT_STATUS_BAR.getID() + "))";
try
{
serRefs
= GuiActivator.bundleContext.getServiceReferences(
PluginComponentFactory.class,
osgiFilter);
}
catch (InvalidSyntaxException ex)
{
serRefs = null;
logger.error("Could not obtain plugin component reference.", ex);
}
if ((serRefs != null) && !serRefs.isEmpty())
{
for (ServiceReference<PluginComponentFactory> serRef : serRefs)
{
PluginComponentFactory factory
= GuiActivator.bundleContext.getService(serRef);
Component comp
= (Component)
factory
.getPluginComponentInstance(ChatWindow.this)
.getComponent();
// If this component has been already added, we have nothing
// more to do here.
if (comp.getParent() != null)
return;
Object borderLayoutConstraints
= UIServiceImpl.getBorderLayoutConstraintsFromContainer(
factory.getConstraints());
addPluginComponent(
comp,
factory.getContainer(),
borderLayoutConstraints);
}
}
}
/**
* Adds a plugin component to this container.
* @param event the <tt>PluginComponentEvent</tt> that notified us of the
* add
*/
public void pluginComponentAdded(PluginComponentEvent event)
{
PluginComponentFactory factory = event.getPluginComponentFactory();
Component comp = (Component)factory.getPluginComponentInstance(
ChatWindow.this).getComponent();
// If this component has been already added, we have nothing more to do
// here.
if (comp.getParent() != null)
return;
if (factory.getContainer().equals(Container.CONTAINER_CHAT_WINDOW)
|| factory.getContainer().equals(
Container.CONTAINER_CHAT_STATUS_BAR))
{
Object borderLayoutConstraints = UIServiceImpl
.getBorderLayoutConstraintsFromContainer(
factory.getConstraints());
this.addPluginComponent(comp,
factory.getContainer(),
borderLayoutConstraints);
}
}
/**
* Removes a plugin component from this container.
* @param event the <tt>PluginComponentEvent</tt> that notified us of the
* remove
*/
public void pluginComponentRemoved(PluginComponentEvent event)
{
PluginComponentFactory factory = event.getPluginComponentFactory();
if (factory.getContainer().equals(Container.CONTAINER_CHAT_WINDOW)
|| factory.getContainer().equals(
Container.CONTAINER_CHAT_STATUS_BAR))
{
Object borderLayoutConstraint = UIServiceImpl
.getBorderLayoutConstraintsFromContainer(
factory.getConstraints());
this.removePluginComponent(
(Component)factory.getPluginComponentInstance(
ChatWindow.this).getComponent(),
factory.getContainer(),
borderLayoutConstraint);
this.pack();
}
}
/**
* The source of the window
* @return the source of the window
*/
public Object getSource()
{
return this;
}
/**
* Returns the number of all open chats.
*
* @return the number of all open chats
*/
public int getChatCount()
{
return chatCount;
}
/**
* Adds the given <tt>ChatChangeListener</tt>.
* @param listener the listener to add
*/
public void addChatChangeListener(ChatChangeListener listener)
{
synchronized (chatChangeListeners)
{
if (!chatChangeListeners.contains(listener))
chatChangeListeners.add(listener);
}
}
/**
* Removes the given <tt>ChatChangeListener</tt>.
* @param listener the listener to remove
*/
public void removeChatChangeListener(ChatChangeListener listener)
{
synchronized (chatChangeListeners)
{
chatChangeListeners.remove(listener);
}
}
/**
* Adds the given component with to the container corresponding to the
* given constraints.
*
* @param c the component to add
* @param container the plugin container
* @param constraints the constraints determining the container
*/
private void addPluginComponent(Component c,
Container container,
Object constraints)
{
if (container.equals(Container.CONTAINER_CHAT_WINDOW))
{
if (constraints.equals(BorderLayout.SOUTH))
{
pluginPanelSouth.add(c);
pluginPanelSouth.repaint();
}
else if (constraints.equals(BorderLayout.WEST))
{
pluginPanelWest.add(c);
pluginPanelSouth.repaint();
}
else if (constraints.equals(BorderLayout.EAST))
{
pluginPanelEast.add(c);
pluginPanelSouth.repaint();
}
}
else if (container.equals(Container.CONTAINER_CHAT_STATUS_BAR))
{
statusBarPanel.add(c);
}
this.getContentPane().repaint();
}
/**
* Removes the given component from the container corresponding to the given
* constraints.
*
* @param c the component to remove
* @param container the plugin container
* @param constraints the constraints determining the container
*/
private void removePluginComponent( Component c,
Container container,
Object constraints)
{
if (container.equals(Container.CONTAINER_CHAT_WINDOW))
{
if (constraints.equals(BorderLayout.SOUTH))
pluginPanelSouth.remove(c);
else if (constraints.equals(BorderLayout.WEST))
pluginPanelWest.remove(c);
else if (constraints.equals(BorderLayout.EAST))
pluginPanelEast.remove(c);
}
else if (container.equals(Container.CONTAINER_CHAT_STATUS_BAR))
{
this.statusBarPanel.remove(c);
}
}
/**
* Sets the chat panel contact photo to this window.
*
* @param chatSession
*/
private void setChatContactPhoto(ChatSession chatSession)
{
this.contactPhotoPanel.setChatSession(chatSession);
byte[] chatAvatar = chatSession.getChatAvatar();
ImageIcon contactPhotoIcon;
if (chatAvatar != null && chatAvatar.length > 0)
{
contactPhotoIcon = ImageUtils.getScaledRoundedIcon(chatAvatar,
128,
128);
if (contactPhotoIcon != null)
this.setIconImage(contactPhotoIcon.getImage());
}
else
{
this.setIconImage(ImageLoader
.getImage(ImageLoader.SIP_COMMUNICATOR_LOGO));
}
}
/**
* Implementation of {@link ExportedWindow#setParams(Object[])}.
*/
public void setParams(Object[] windowParams) {}
/**
* Handles <tt>WindowEvent</tt>s triggered when the window has gained focus.
* @param evt the <tt>WindowEvent</tt>
*/
public void windowGainedFocus(WindowEvent evt)
{
ChatPanel currentChat = getCurrentChat();
if (currentChat != null)
GuiActivator.getUIService().getChatWindowManager()
.removeNonReadChatState(currentChat);
}
public void windowLostFocus(WindowEvent arg0) {}
private static class ToolbarPanel
extends TransparentPanel
implements Skinnable
{
private Image logoBgImage;
public ToolbarPanel(LayoutManager layoutManager)
{
super(layoutManager);
loadSkin();
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (logoBgImage != null)
{
g.drawImage(
logoBgImage,
(this.getWidth() - logoBgImage.getWidth(null))/2,
0,
null);
}
}
/**
* Reloads bg image.
*/
public void loadSkin()
{
Image logoBgImage
= ImageLoader.getImage(ImageLoader.WINDOW_TITLE_BAR);
if (logoBgImage != null)
logoBgImage
= ImageUtils.scaleImageWithinBounds(logoBgImage, 80, 35);
this.logoBgImage = logoBgImage;
}
}
/**
* Sends all files from the given directory when it's dropped in the chat
* window.
* @param dir the directory to send
* @param point the point, where the directory was dropped
*/
public void directoryDropped(File dir, Point point)
{
// TODO Implement send directory
}
/**
* Sends the given file when dropped to the chat window.
* @param file the file to send
* @param point the point, where the file was dropped
*/
public void fileDropped(File file, Point point)
{
getCurrentChat().sendFile(file);
}
/**
* Opens the specified <tt>ChatPanel</tt> and optionally brings it to the
* front.
*
* @param chatPanel the <tt>ChatPanel</tt> to be opened
* @param setSelected <tt>true</tt> if <tt>chatPanel</tt> (and respectively
* this <tt>ChatContainer</tt>) should be brought to the front; otherwise,
* <tt>false</tt>
*/
public void openChat(ChatPanel chatPanel, boolean setSelected)
{
boolean isWindowVisible = isVisible();
if(getExtendedState() != JFrame.ICONIFIED)
{
if (ConfigurationUtils.isAutoPopupNewMessage() || setSelected)
{
if (!isVisible())
setVisible(true);
toFront();
}
else if (!isWindowVisible)
{
setFocusableWindowState(false);
if (!OSUtils.IS_MAC)
{
setState(Frame.ICONIFIED);
}
setVisible(true);
setFocusableWindowState(true);
}
}
else
{
if (setSelected)
{
setExtendedState(JFrame.NORMAL);
toFront();
}
String chatWindowTitle = getTitle();
if(!chatWindowTitle.startsWith("*"))
setTitle("*" + chatWindowTitle);
}
if (setSelected || !isWindowVisible)
{
setCurrentChat(chatPanel);
}
else if(!getCurrentChat().equals(chatPanel) && getChatTabCount() > 0)
{
highlightTab(chatPanel);
}
}
/**
* Returns the frame to which this container belongs.
*
* @return the frame to which this container belongs
*/
public Frame getFrame()
{
return this;
}
/**
* Sets the title of this chat container.
*
* @param chatPanel the chat, for which we set the title
* @param title the title to set
*/
public void setChatTitle(ChatPanel chatPanel, String title)
{
setTabTitle(chatPanel, title);
}
public void setChatIcon(ChatPanel chatPanel, Icon icon)
{
setTabIcon(chatPanel, icon);
}
/**
* Indicates if the parent frame is currently the active window.
*
* @return <tt>true</tt> if the parent window is currently the active
* window, <tt>false</tt> otherwise
*/
public boolean isFrameActive()
{
return isActive();
}
/**
* Updates history buttons state.
*
* @param chatPanel the chat panel for which we should update button states
*/
public void updateHistoryButtonState(ChatPanel chatPanel)
{
mainToolBar.changeHistoryButtonsState(chatPanel);
}
/**
* The <tt>MainKeyDispatcher</tt> is added to pre-listen KeyEvents before
* they're delivered to the current focus owner in order to introduce a
* specific behavior for the <tt>CallField</tt> on top of the dial pad.
*/
private class MainKeyDispatcher implements KeyEventDispatcher
{
private final KeyboardFocusManager keyManager;
/**
* Creates an instance of <tt>MainKeyDispatcher</tt>.
* @param keyManager the parent <tt>KeyboardFocusManager</tt>
*/
public MainKeyDispatcher(KeyboardFocusManager keyManager)
{
this.keyManager = keyManager;
}
/**
* Dispatches the given <tt>KeyEvent</tt>.
* @param e the <tt>KeyEvent</tt> to dispatch
* @return <tt>true</tt> if the KeyboardFocusManager should take no
* further action with regard to the KeyEvent; <tt>false</tt>
* otherwise
*/
public boolean dispatchKeyEvent(KeyEvent e)
{
// If this window is not the focus window or if the event is not
// of type PRESSED we have nothing more to do here.
if (!isFocused() || (e.getID() != KeyEvent.KEY_TYPED))
return false;
if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED
|| e.getKeyCode() == KeyEvent.VK_ENTER)
{
return false;
}
if(getCurrentChat() == null)
return false;
ChatWritePanel chatWritePanel
= getCurrentChat().getChatWritePanel();
JEditorPane chatWriteEditor = chatWritePanel.getEditorPane();
// Don't re-dispatch any events if the menu is active. Fixes the
// navigation in the menu.
if (menuBar.getSelectedMenu() != null
&& menuBar.getSelectedMenu().isPopupMenuVisible())
{
return false;
}
if (keyManager.getFocusOwner() != null
&& !chatWritePanel.isFocusOwner())
{
// Request the focus in the chat write panel if a letter is
// typed.
chatWriteEditor.requestFocusInWindow();
// We re-dispatch the event to the chat write panel.
keyManager.redispatchEvent(chatWriteEditor, e);
// We don't want to dispatch further this event.
return true;
}
return false;
}
}
}