/**
* $RCSfile: ,v $
* $Revision: $
* $Date: $
*
* Copyright (C) 2004-2011 Jive Software. All rights reserved.
*
* 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 org.jivesoftware.spark.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jivesoftware.MainWindow;
import org.jivesoftware.Spark;
import org.jivesoftware.resource.Res;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromContainsFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.spark.ChatManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.component.tabbedPane.SparkTab;
import org.jivesoftware.spark.component.tabbedPane.SparkTabbedPane;
import org.jivesoftware.spark.component.tabbedPane.SparkTabbedPaneListener;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
import org.jivesoftware.spark.ui.rooms.GroupChatRoom;
import org.jivesoftware.spark.util.SwingTimerTask;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.TaskEngine;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.alerts.SparkToaster;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
/**
* Contains all <code>ChatRoom</code> objects within Spark.
*
* @author Derek DeMoro
*/
public class ChatContainer extends SparkTabbedPane implements MessageListener, ChangeListener, KeyListener {
private static final long serialVersionUID = 3725711237490056136L;
/**
* List of all ChatRoom Listeners.
*/
private final List<ChatRoomListener> chatRoomListeners = new ArrayList<ChatRoomListener>();
private final List<ChatRoom> chatRoomList = new ArrayList<ChatRoom>();
private final Map<String, PacketListener> presenceMap = new HashMap<String, PacketListener>();
private static final String WELCOME_TITLE = SparkRes.getString(SparkRes.WELCOME);
private ChatFrame chatFrame;
private LocalPreferences localPref;
private final TimerTask focusTask;
/**
* Creates the ChatContainer to hold all ChatRooms.
*/
public ChatContainer() {
// Assign location
super(SettingsManager.getLocalPreferences().isTabTopPosition() ? JTabbedPane.TOP : JTabbedPane.BOTTOM);
// Set minimum size
setMinimumSize(new Dimension(400, 200));
// Don't allow tabs to shrink and allow scrolling.
enableDragAndDrop();
addSparkTabbedPaneListener(new SparkTabbedPaneListener() {
public void tabRemoved(SparkTab tab, Component component, int index) {
stateChanged(null);
if (component instanceof ChatRoom) {
cleanupChatRoom((ChatRoom)component);
}
else if (component instanceof ContainerComponent) {
((ContainerComponent)component).closing();
}
}
public void tabAdded(SparkTab tab, Component component, int index) {
stateChanged(null);
}
public void tabSelected(SparkTab tab, Component component, int index) {
stateChanged(null);
// Notify ChatRoomListeners that the tab has been activated.
if (component instanceof ChatRoom) {
fireChatRoomActivated((ChatRoom)component);
}
}
public void allTabsRemoved() {
if (chatFrame != null) {
chatFrame.setTitle("");
chatFrame.setVisible(false);
chatFrame = null;
}
}
public boolean canTabClose(SparkTab tab, Component component) {
return true;
}
});
setCloseButtonEnabled(true);
// Add Key Navigation
addKeyNavigation();
this.setFocusable(false);
setOpaque(true);
setBackground(Color.white);
// Create task for focusing chat.
focusTask = new SwingTimerTask() {
public void doRun() {
try {
//chatFrame.requestFocus();
ChatRoom chatRoom = getActiveChatRoom();
chatRoom.getChatInputEditor().requestFocusInWindow();
updateActiveTab();
}
catch (ChatRoomNotFoundException e1) {
// Ignore. There may legitamtly not be a chat room.
}
}
};
}
/**
* Adds navigation capability to chat rooms. Users can navigate using the alt-left or right arrow keys.
*/
private void addKeyNavigation() {
KeyStroke leftStroke = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
String leftStrokeString = org.jivesoftware.spark.util.StringUtils.keyStroke2String(leftStroke);
// Handle Left Arrow
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("alt " + leftStrokeString + ""), "navigateLeft");
this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("alt " + leftStrokeString + ""), "navigateLeft");
this.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("alt " + leftStrokeString + ""), "navigateLeft");
this.getActionMap().put("navigateLeft", new AbstractAction("navigateLeft") {
private static final long serialVersionUID = -8677467560602512074L;
public void actionPerformed(ActionEvent evt) {
navigateLeft();
}
});
KeyStroke rightStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
String rightStrokeString = org.jivesoftware.spark.util.StringUtils.keyStroke2String(rightStroke);
// Handle Right Arrow
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("alt " + rightStrokeString + ""), "navigateRight");
this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("alt " + rightStrokeString + ""), "navigateRight");
this.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("alt " + rightStrokeString + ""), "navigateRight");
this.getActionMap().put("navigateRight", new AbstractAction("navigateRight") {
private static final long serialVersionUID = -7676330627598261416L;
public void actionPerformed(ActionEvent evt) {
navigateRight();
}
});
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
this.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("Ctrl W"), "escape");
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "escape");
this.getActionMap().put("escape", new AbstractAction("escape") {
private static final long serialVersionUID = 5165074248488666495L;
public void actionPerformed(ActionEvent evt) {
closeActiveRoom();
}
});
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | InputEvent.SHIFT_MASK), "shiftCmdW");
this.getActionMap().put("shiftCmdW", new AbstractAction("shiftCmdW") {
private static final long serialVersionUID = -1179625099164632251L;
public void actionPerformed(ActionEvent evt) {
closeAllChatRooms();
}
});
// Add KeyMappings
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "searchContacts");
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("control F"), "searchContacts");
getActionMap().put("searchContacts", new AbstractAction("searchContacts") {
private static final long serialVersionUID = -6904085783599775675L;
public void actionPerformed(ActionEvent evt) {
SwingWorker worker = new SwingWorker() {
@Override
public Object construct() {
return 42;
}
@Override
public void finished() {
SparkManager.getUserManager()
.searchContacts("", SparkManager.getChatManager()
.getChatContainer().getChatFrame());
}
};
worker.start();
}
});
}
/**
* Adds a new ChatRoom to Spark.
*
* @param room the ChatRoom to add.
*/
public synchronized void addChatRoom(final ChatRoom room) {
createFrameIfNeeded();
room.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.LIGHT_GRAY));
AndFilter presenceFilter = new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(room.getRoomname()));
// Next, create a packet listener. We use an anonymous inner class for brevity.
PacketListener myListener = new PacketListener() {
public void processPacket(final Packet packet) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handleRoomPresence((Presence)packet);
}
});
}
};
room.registeredToFrame(chatFrame);
SparkManager.getConnection().addPacketListener(myListener, presenceFilter);
// Add to PresenceMap
presenceMap.put(room.getRoomname(), myListener);
String tooltip;
if (room instanceof ChatRoomImpl) {
tooltip = ((ChatRoomImpl)room).getParticipantJID();
String nickname = SparkManager.getUserManager().getUserNicknameFromJID(((ChatRoomImpl)room).getParticipantJID());
tooltip = "<html><body><b>Contact: </b>" + nickname + "<br><b>JID: </b>" + tooltip;
}
else {
tooltip = room.getRoomname();
}
// Create ChatRoom UI and dock
SparkTab tab = addTab(room.getTabTitle(), room.getTabIcon(), room, tooltip);
tab.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
checkTabPopup(e);
}
public void mousePressed(MouseEvent e) {
checkTabPopup(e);
}
});
room.addMessageListener(this);
// Remove brand panel
final String title = getTabAt(0).getActualText();
if (title.equals(WELCOME_TITLE)) {
chatFrame.setTitle(room.getRoomTitle());
}
final TimerTask visibleTask = new SwingTimerTask() {
public void doRun() {
checkVisibility(room);
}
};
TaskEngine.getInstance().schedule(visibleTask, 100);
// Add to ChatRoomList
chatRoomList.add(room);
// Notify users that the chat room has been opened.
fireChatRoomOpened(room);
// Focus Chat
focusChat();
// Add Room listeners to override issue with input maps and keybinding on the mac.
if (Spark.isMac()) {
room.getChatInputEditor().addKeyListener(this);
}
}
public void addContainerComponent(ContainerComponent comp) {
createFrameIfNeeded();
addTab(comp.getTabTitle(), comp.getTabIcon(), comp.getGUI(), comp.getToolTipDescription());
chatFrame.setTitle(comp.getFrameTitle());
checkVisibility(comp.getGUI());
if (getSelectedComponent() != comp) {
// Notify Decorators
SparkManager.getChatManager().notifySparkTabHandlers(comp.getGUI());
}
}
/**
* Handles the presence of a one to one chat room.
*
* @param p the presence to handle.
*/
private void handleRoomPresence(final Presence p) {
final String roomname = StringUtils.parseBareAddress(p.getFrom());
ChatRoom chatRoom;
try {
chatRoom = getChatRoom(roomname);
}
catch (ChatRoomNotFoundException e1) {
Log.debug("Could not locate chat room " + roomname);
return;
}
final String userid = StringUtils.parseResource(p.getFrom());
if (p.getType() == Presence.Type.unavailable) {
fireUserHasLeft(chatRoom, userid);
}
else if (p.getType() == Presence.Type.available) {
fireUserHasJoined(chatRoom, userid);
}
// Change tab icon
if (chatRoom instanceof ChatRoomImpl) {
// Notify state change.
int tabLoc = indexOfComponent(chatRoom);
if (tabLoc != -1) {
SparkManager.getChatManager().notifySparkTabHandlers(chatRoom);
}
}
}
private void checkVisibility(Component component) {
if (!chatFrame.isVisible() && SparkManager.getMainWindow().isFocusOwner()) {
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
else if (chatFrame.isVisible() && !chatFrame.isInFocus()) {
startFlashing(component, false, null, null);
}
else if (chatFrame.isVisible() && chatFrame.getState() == Frame.ICONIFIED) {
// Set to new tab.
int tabLocation = indexOfComponent(component);
setSelectedIndex(tabLocation);
// If the ContactList is in the tray, we need better notification by flashing
// the chatframe.
startFlashing(component, false, null, null);
}
// Handle when chat frame is visible but the Contact List is not.
else if (chatFrame.isVisible() && !SparkManager.getMainWindow().isVisible()) {
startFlashing(component, false, null, null);
}
else if (!chatFrame.isVisible()) {
// Set to new tab.
int tabLocation = indexOfComponent(component);
setSelectedIndex(tabLocation);
if (Spark.isWindows()) {
chatFrame.setFocusableWindowState(false);
chatFrame.setState(Frame.ICONIFIED);
}
chatFrame.setFocusableWindowState(true);
// If the ContactList is in the tray, we need better notification by flashing
// the chatframe.
if (!SparkManager.getMainWindow().isVisible()) {
startFlashing(component, false, null, null);
}
else if (chatFrame.getState() == Frame.ICONIFIED) {
startFlashing(component, false, null, null);
}
if (component instanceof ChatRoom) {
chatFrame.setTitle(((ChatRoom)component).getRoomTitle());
}
}
}
private void handleMessageNotification(final ChatRoom chatRoom, boolean customMsg, String customMsgText, String customMsgTitle) {
ChatRoom activeChatRoom = null;
boolean groupMessageChecked = false;
try {
activeChatRoom = getActiveChatRoom();
}
catch (ChatRoomNotFoundException e1) {
Log.error(e1);
}
if (chatFrame.isVisible() && (chatFrame.getState() == Frame.ICONIFIED || chatFrame.getInactiveTime() > 20000)) {
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
return;
}
if(chatFrame.isVisible() && chatFrame.getState()== Frame.NORMAL)
{
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
if (!chatFrame.isVisible() && SparkManager.getMainWindow().isFocusOwner()) {
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
else if (chatFrame.isVisible() && !chatFrame.isInFocus()) {
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
else if (chatFrame.isVisible() && chatFrame.getState() == Frame.ICONIFIED) {
// Set to new tab.
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
// If the ContactList is in the tray, we need better notification by flashing
// the chatframe.
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
// Handle when chat frame is visible but the Contact List is not.
else if (chatFrame.isVisible() && !SparkManager.getMainWindow().isVisible() && !chatFrame.isInFocus()) {
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
else if (!chatFrame.isVisible()) {
// Set to new tab.
int tabLocation = indexOfComponent(chatRoom);
setSelectedIndex(tabLocation);
if (Spark.isWindows()) {
chatFrame.setExtendedState(Frame.ICONIFIED);
}
chatFrame.setVisible(true);
// If the ContactList is in the tray, we need better notification by flashing
// the chatframe.
if (!SparkManager.getMainWindow().isVisible()) {
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
else if (chatFrame.getState() == Frame.ICONIFIED) {
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
chatFrame.setTitle(chatRoom.getRoomTitle());
}
else if (chatRoom != activeChatRoom) {
if (!groupMessageChecked) {
groupChatMessageCheck(chatRoom, customMsg, customMsgText, customMsgTitle);
groupMessageChecked = true;
}
}
}
/**
* Removes the ChatRoom resources.
*
* @param room the room to remove.
*/
private void cleanupChatRoom(ChatRoom room) {
if (room.isActive()) {
room.leaveChatRoom();
room.closeChatRoom();
}
final PacketListener listener = presenceMap.get(room.getRoomname());
if (listener != null) {
SparkManager.getConnection().removePacketListener(listener);
}
fireChatRoomClosed(room);
room.removeMessageListener(this);
// Remove mappings
presenceMap.remove(room.getRoomname());
chatRoomList.remove(room);
room.getChatInputEditor().removeKeyListener(this);
// Clear all Text :)
room.getTranscriptWindow().cleanup();
}
/**
* Close all chat rooms.
*/
public void closeAllChatRooms() {
LocalPreferences pref = SettingsManager.getLocalPreferences();
if (MainWindow.getInstance().isDocked() || !pref.isAutoCloseChatRoomsEnabled()) {
return;
}
for (ChatRoom chatRoom : new ArrayList<ChatRoom>(chatRoomList)) {
closeTab(chatRoom);
chatRoom.closeChatRoom();
}
for (int i = 0; i < getTabCount(); i++) {
Component comp = getComponentAt(i);
if (comp instanceof ContainerComponent) {
((ContainerComponent)comp).closing();
}
closeTab(comp);
}
}
/**
* Leaves a ChatRoom. Leaving a chat room does everything but close the room itself.
*
* @param room the room to leave.
*/
public void leaveChatRoom(ChatRoom room) {
// Notify that the chatroom has been left.
fireChatRoomLeft(room);
room.leaveChatRoom();
final PacketListener listener = presenceMap.get(room.getRoomname());
if (listener != null && SparkManager.getConnection().isConnected()) {
SparkManager.getConnection().removePacketListener(listener);
}
}
/**
* Returns a ChatRoom by name.
*
* @param roomName the name of the ChatRoom.
* @return the ChatRoom
* @throws ChatRoomNotFoundException if the room was not found.
*/
public ChatRoom getChatRoom(String roomName) throws ChatRoomNotFoundException {
for (int i = 0; i < getTabCount(); i++) {
ChatRoom room = null;
try {
room = getChatRoom(i);
}
catch (ChatRoomNotFoundException e1) {
// Ignore
}
if (room != null && room.getRoomname().equalsIgnoreCase(roomName) && room.isActive()) {
return room;
}
}
throw new ChatRoomNotFoundException(roomName + " not found.");
}
/**
* Returns a ChatRoom in the specified tab location.
*
* @param location the tab location.
* @return the ChatRoom found.
* @throws ChatRoomNotFoundException thrown if the room is not found.
*/
public ChatRoom getChatRoom(int location) throws ChatRoomNotFoundException {
if (getTabCount() < location) {
throw new ChatRoomNotFoundException();
}
try {
Component comp = getComponentAt(location);
if (comp instanceof ChatRoom) {
return (ChatRoom)comp;
}
}
catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
Log.error("Error getting Chat Room", outOfBoundsEx);
}
throw new ChatRoomNotFoundException();
}
/**
* Returns the Active ChatRoom.
*
* @return the ChatRoom active in the tabbed pane.
* @throws ChatRoomNotFoundException is thrown if no chat room is found.
*/
public ChatRoom getActiveChatRoom() throws ChatRoomNotFoundException {
int location = getSelectedIndex();
if (location != -1) {
return getChatRoom(location);
}
throw new ChatRoomNotFoundException();
}
/**
* Returns the Active Component.
*
* @return the Component active in the tabbed pane.
*/
public Component getActiveRoom() {
int location = getSelectedIndex();
if (location != -1) {
return getComponentAt(location);
}
return null;
}
/**
* Activates the specified ChatRoom.
*
* @param room the ChatRoom to activate.
*/
public void activateChatRoom(ChatRoom room) {
int tabLocation = indexOfComponent(room);
setSelectedIndex(tabLocation);
chatFrame.bringFrameIntoFocus();
focusChat();
}
/**
* Activates the component in tabbed pane.
*
* @param component the component contained within the tab to activate.
*/
public void activateComponent(Component component) {
int tabLocation = indexOfComponent(component);
if (tabLocation != -1) {
setSelectedIndex(tabLocation);
}
chatFrame.bringFrameIntoFocus();
focusChat();
}
/**
* Used for Tray Notifications.
*
* @param room the ChatRoom where the message was received.
* @param message the message received.
*/
public void messageReceived(ChatRoom room, Message message) {
room.increaseUnreadMessageCount();
SparkManager.getWorkspace().getTranscriptPlugin().persistChatRoom(room);
fireNotifyOnMessage(room, false, null, null);
ChatManager.getInstance().fireMessageReceived(message);
}
/**
* Used for Tray Notifications.
*
* @param chatRoom the ChatRoom where the message was received.
* @param customMsg
* @param customMsgText
* @param customMsgTitle
*/
public void fireNotifyOnMessage(final ChatRoom chatRoom, final boolean customMsg, final String customMsgText, final String customMsgTitle) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handleMessageNotification(chatRoom, customMsg, customMsgText, customMsgTitle);
}
});
}
/***
*
* @param room
* @param message
*/
public void messageSent(ChatRoom room, Message message) {
fireChatRoomStateUpdated(room);
}
/**
* Notification that the tab pane has been modified. Generally by changing of the tabs.
*
* @param e the ChangeEvent.
*/
public void stateChanged(ChangeEvent e) {
// Stop the flashing only if the chat frame is in focus.
if (chatFrame.isInFocus()) {
stopFlashing();
}
final Object o = getSelectedComponent();
if (o instanceof ChatRoom) {
final ChatRoom room = (ChatRoom)o;
focusChat();
// Set the title of the room.
chatFrame.setTitle(room.getRoomTitle());
chatFrame.setIconImage(SparkManager.getMainWindow().getIconImage());
}
else if (o instanceof ContainerComponent) {
final ContainerComponent comp = (ContainerComponent)o;
chatFrame.setTitle(comp.getFrameTitle());
chatFrame.setIconImage(comp.getTabIcon().getImage());
SparkManager.getChatManager().notifySparkTabHandlers(comp.getGUI());
}
}
private void stopFlashing() {
try {
// Get current tab
int selectedIndex = getSelectedIndex();
if (selectedIndex != -1) {
Component comp = getComponentAt(selectedIndex);
if (comp != null) {
stopFlashing(comp);
}
}
}
catch (Exception e) {
Log.error(e);
}
}
/**
* Closes a tab of a room.
*
* @param component the component inside of the tab to close.
*/
public void closeTab(Component component) {
int location = indexOfComponent(component);
if (location == -1) {
return;
}
if (getTabCount() == 0) {
chatFrame.setTitle("");
}
this.close(this.getTabAt(location));
}
public void closeActiveRoom() {
ChatRoom room;
try {
room = getActiveChatRoom();
}
catch (ChatRoomNotFoundException e1) {
Component comp = getActiveRoom();
if (comp != null) {
boolean canClose = ((ContainerComponent)comp).closing();
if (canClose) {
closeTab(comp);
}
}
return;
}
// Confirm end session
boolean isGroupChat = room.getChatType() == Message.Type.groupchat;
if (isGroupChat) {
final int ok = JOptionPane.showConfirmDialog(chatFrame, Res.getString("message.end.conversation"),
Res.getString("title.confirmation"), JOptionPane.YES_NO_OPTION);
if (ok == JOptionPane.OK_OPTION) {
room.closeChatRoom();
}
}
else {
room.closeChatRoom();
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
for (ChatRoom room : chatRoomList) {
buf.append("Roomname=").append(room.getRoomname()).append("\n");
}
return buf.toString();
}
/**
* Returns true if there are any Rooms present.
*
* @return true if Rooms are present, otherwise false.
*/
public boolean hasRooms() {
int count = getSelectedIndex();
return count != -1;
}
/**
* Adds a ChatRoom listener to ChatRooms. The
* listener will be called when either a ChatRoom has been
* added, removed, or activated.
*
* @param listener the <code>ChatRoomListener</code> to register
*/
public void addChatRoomListener(ChatRoomListener listener) {
if (!chatRoomListeners.contains(listener)) {
chatRoomListeners.add(listener);
}
}
/**
* Removes the specified <code>ChatRoomListener</code>.
*
* @param listener the <code>ChatRoomListener</code> to remove
*/
public void removeChatRoomListener(ChatRoomListener listener) {
chatRoomListeners.remove(listener);
}
/**
* Notifies users that a <code>ChatRoom</code> has been opened.
*
* @param room - the <code>ChatRoom</code> that has been opened.
*/
protected void fireChatRoomOpened(ChatRoom room) {
for (ChatRoomListener chatRoomListener : new ArrayList<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.chatRoomOpened(room);
}
}
/**
* Notifies users that a <code>ChatRoom</code> has been left.
*
* @param room - the <code>ChatRoom</code> that has been left
*/
protected void fireChatRoomLeft(ChatRoom room) {
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.chatRoomLeft(room);
}
}
/**
* Notifies users that a <code>ChatRoom</code> has been closed.
*
* @param room - the <code>ChatRoom</code> that has been closed.
*/
protected void fireChatRoomClosed(ChatRoom room) {
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.chatRoomClosed(room);
}
}
/**
* Notifies users that a <code>ChatRoom</code> has been activated.
*
* @param room - the <code>ChatRoom</code> that has been activated.
*/
protected void fireChatRoomActivated(ChatRoom room) {
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.chatRoomActivated(room);
}
}
/**
* Notifies users that a user has joined a <code>ChatRoom</code>.
*
* @param room - the <code>ChatRoom</code> that a user has joined.
* @param userid - the userid of the person.
*/
protected void fireUserHasJoined(final ChatRoom room, final String userid) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.userHasJoined(room, userid);
}
}
});
}
/**
* Notifies users that a user has left a <code>ChatRoom</code>.
*
* @param room - the <code>ChatRoom</code> that a user has left.
* @param userid - the userid of the person.
*/
protected void fireUserHasLeft(final ChatRoom room, final String userid) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (ChatRoomListener chatRoomListener : new HashSet<ChatRoomListener>(chatRoomListeners)) {
chatRoomListener.userHasLeft(room, userid);
}
}
});
}
/**
* Starts flashing of MainWindow.
*
* @param comp the Component to check if a message has been inserted
* but the room is not the selected room.
*/
public void startFlashing(final Component comp, final boolean customMsg,
final String customMsgText, final String customMsgTitle) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
final int index = indexOfComponent(comp);
if (index != -1) {
// Check notifications.
if (SettingsManager.getLocalPreferences().isChatRoomNotificationsOn()|| !(comp instanceof GroupChatRoom)) {
if (comp instanceof ChatRoom) {
if (comp instanceof GroupChatRoom) {
if (((GroupChatRoom) comp).getLastMessage() != null)
if (!((GroupChatRoom) comp).isBlocked(((GroupChatRoom) comp).getLastMessage().getFrom()))
checkNotificationPreferences((ChatRoom) comp,customMsg,customMsgText,customMsgTitle);
} else {
checkNotificationPreferences((ChatRoom) comp, customMsg,customMsgText, customMsgTitle);
}
}
}
// Notify decorators
SparkManager.getChatManager().notifySparkTabHandlers(comp);
}
boolean flashAllowed = SettingsManager.getLocalPreferences().isChatRoomNotificationsOn()
|| !(comp instanceof GroupChatRoom);
if (!chatFrame.isInFocus() && flashAllowed) {
SparkManager.getNativeManager().flashWindow(chatFrame);
}
} catch (Exception ex) {
Log.error("Issue in ChatRooms with tab location.", ex);
}
}
});
}
public void fireChatRoomStateUpdated(final ChatRoom room) {
final int index = indexOfComponent(room);
if (index != -1) {
SparkManager.getChatManager().notifySparkTabHandlers(room);
}
}
/**
* Checks to see if the <code>ChatFrame</code> should stop flashing.
*
* @param component the component that should be notified.
*/
public void stopFlashing(final Component component) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Stop the flashing
SparkManager.getNativeManager().stopFlashing(chatFrame);
// Notify decorators
SparkManager.getChatManager().notifySparkTabHandlers(component);
}
catch (Exception ex) {
Log.error("Could not stop flashing because " + ex.getMessage(), ex);
}
}
});
}
/**
* Handles Notification preferences for incoming messages and rooms.
*
* @param room the chat room.
* @param fileTransfer if it is a file transfer then true
* @param fileTName the file name being transfered (if fileTransfer applies)
*
*/
private void checkNotificationPreferences(final ChatRoom room, boolean customMsg, String customMsgText, String customMsgTitle) {
LocalPreferences pref = SettingsManager.getLocalPreferences();
if (pref.getWindowTakesFocus()) {
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
}
if (pref.getShowToasterPopup()) {
SparkToaster toaster = new SparkToaster();
toaster.setCustomAction(new AbstractAction() {
private static final long serialVersionUID = -2759404307378067515L;
public void actionPerformed(ActionEvent actionEvent) {
chatFrame.setState(Frame.NORMAL);
chatFrame.setVisible(true);
int tabLocation = indexOfComponent(room);
if (tabLocation != -1) {
setSelectedIndex(tabLocation);
}
}
});
toaster.setDisplayTime(5000);
toaster.setBorder(BorderFactory.createBevelBorder(0));
String nickname = room.getRoomTitle();
toaster.setToasterHeight(150);
toaster.setToasterWidth(200);
int size = room.getTranscripts().size();
if(customMsg) {
toaster.setTitle(customMsgTitle);
toaster.showToaster(room.getTabIcon(), customMsgText);
} else {
toaster.setTitle(nickname);
if (size > 0) {
Message message = room.getTranscripts().get(size - 1);
toaster.showToaster(room.getTabIcon(), message.getBody());
}
}
}
}
/**
* Performs several group chat checks
*
* chatRoom the chat room that needs to be passed
* customMsg whether or not this is a custom message
* customMsgText if any custom message should appear in the popup
* customMsgTitle whether or not the toaster should have any popup
*
**/
private void groupChatMessageCheck(ChatRoom chatRoom, boolean customMsg, String customMsgText, String customMsgTitle) {
// predefine if this is a group chat message or not
localPref = SettingsManager.getLocalPreferences();
boolean isGroupChat = chatRoom.getChatType() == Message.Type.groupchat;
int size = chatRoom.getTranscripts().size();
// is this a group chat message and is my name in it?
if (isGroupChat) {
// is a group chat, perform some functions
String fromNickName="";
Message lastChatMessage= new Message();
String mucNickNameT="";
String finalRoomName ="";
if(size>0)
{
lastChatMessage = chatRoom.getTranscripts().get(size - 1);
mucNickNameT = lastChatMessage.getFrom();
String[] mucNickName = mucNickNameT.split("/");
finalRoomName = chatRoom.getRoomTitle();
if (mucNickName.length < 2) { // We have no name after "/" in mucNickNameT (must be like: test@conference.jabber.kg/kos)
fromNickName = finalRoomName; //Res.getString("label.message");
} else {
fromNickName = mucNickName[1];
}
}
if (localPref.isMucHighToastEnabled()) {
// allowed to check for new messages containing name
String myNickName = chatRoom.getNickname();
String myUserName = SparkManager.getSessionManager().getUsername();
Pattern usernameMatch = Pattern.compile(myUserName, Pattern.CASE_INSENSITIVE);
Pattern nicknameMatch = Pattern.compile(myNickName, Pattern.CASE_INSENSITIVE);
if (usernameMatch.matcher(lastChatMessage.getBody()).find() || nicknameMatch.matcher(lastChatMessage.getBody()).find()) {
// match, send new message
boolean customMsgS = true;
String customMsgTextS = Res.getString("group.chat.name.match") + " " + finalRoomName + " by " + fromNickName + " (" + lastChatMessage.getBody() + ")";
String customMsgTitleS = Res.getString("group.chat.name.notification");
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
} else {
// regular group message
boolean customMsgS = true;
String customMsgTextS = fromNickName + " says: " + lastChatMessage.getBody();
String customMsgTitleS = finalRoomName;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
} else {
// regular group message
boolean customMsgS = true;
String customMsgTextS = fromNickName + " says: " + lastChatMessage.getBody();
String customMsgTitleS = finalRoomName;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
} else if (customMsg) {
// probablt a file transfer request
boolean customMsgS = customMsg;
String customMsgTextS = customMsgText;
String customMsgTitleS = customMsgTitle;
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
} else {
// normal personal chat
Message lastChatMessage = null;
if(size > 0)
lastChatMessage = chatRoom.getTranscripts().get(size - 1);
String finalRoomName = chatRoom.getRoomTitle();
String customMsgTextS = "";
boolean customMsgS = true;
String customMsgTitleS = finalRoomName;
if(lastChatMessage != null) {
customMsgTextS = lastChatMessage.getBody();
}
startFlashing(chatRoom, customMsgS, customMsgTextS, customMsgTitleS);
return;
}
}
public void setChatRoomTitle(ChatRoom room, String title) {
int index = indexOfComponent(room);
if (index != -1) {
SparkTab tab = getTabAt(index);
fireChatRoomStateUpdated(room);
tab.setTabTitle(room.getTabTitle());
}
}
private void createFrameIfNeeded() {
if (chatFrame != null) {
return;
}
LocalPreferences pref = SettingsManager.getLocalPreferences();
if (pref.isDockingEnabled()) {
chatFrame = MainWindow.getInstance();
} else {
chatFrame = new ChatFrame();
}
if (SparkManager.getMainWindow().isActive() || pref.getWindowTakesFocus()) {
chatFrame.setState(Frame.NORMAL);
} else {
if (System.getProperty("java.version").startsWith("1.7.")) {
try {
// TODO UPDATE ON JAVA 1.7.0 release
// chatFrame.setAutoRequestFocus(false);
// This can be removed once java 1.7 is mainstream
Class<?> c = ClassLoader.getSystemClassLoader().loadClass(
JFrame.class.getCanonicalName());
Method m = c
.getMethod("setAutoRequestFocus", boolean.class);
m.invoke(chatFrame, false);
} catch (Exception e) {
Log.error(e);
}
}
chatFrame.setState(Frame.ICONIFIED);
}
chatFrame.setVisible(true);
chatFrame.addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent windowEvent) {
stopFlashing();
int sel = getSelectedIndex();
if (sel == -1) {
return;
}
final ChatRoom room;
try {
room = getChatRoom(sel);
focusChat();
// Set the title of the room.
chatFrame.setTitle(room.getRoomTitle());
}
catch (ChatRoomNotFoundException e1) {
// Nothing to do
}
}
public void windowDeactivated(WindowEvent windowEvent) {
}
public void windowClosing(WindowEvent windowEvent) {
// Save layout
chatFrame.saveLayout();
SparkManager.getChatManager().getChatContainer()
.closeAllChatRooms();
}
});
// Start timer
handleStaleChats();
}
/**
* Brings the chat into focus.
*/
public void focusChat() {
TaskEngine.getInstance().schedule(focusTask, 50);
}
public Collection<ChatRoom> getChatRooms() {
return new ArrayList<ChatRoom>(chatRoomList);
}
public ChatFrame getChatFrame() {
return chatFrame;
}
public void blinkFrameIfNecessary(final JFrame frame) {
final MainWindow mainWindow = SparkManager.getMainWindow();
if (mainWindow.isFocusOwner()) {
frame.setVisible(true);
}
else {
// Set to new tab.
if (Spark.isWindows()) {
frame.setState(Frame.ICONIFIED);
SparkManager.getNativeManager().flashWindow(frame);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent e) {
SparkManager.getNativeManager().stopFlashing(frame);
}
});
}
}
}
private void checkTabPopup(MouseEvent e) {
final SparkTab tab = (SparkTab)e.getSource();
if (!e.isPopupTrigger()) {
return;
}
final JPopupMenu popup = new JPopupMenu();
// Handle closing this room.
Action closeThisAction = new AbstractAction() {
private static final long serialVersionUID = 5002889397735856123L;
public void actionPerformed(ActionEvent e) {
ChatRoom chatRoom = (ChatRoom)getComponentInTab(tab);
if (chatRoom != null) {
closeTab(chatRoom);
}
}
};
closeThisAction.putValue(Action.NAME, Res.getString("message.close.this.chat"));
popup.add(closeThisAction);
if (getChatRooms().size() > 1) {
// Handle closing other rooms.
Action closeOthersAction = new AbstractAction() {
private static final long serialVersionUID = 1869236917427431585L;
public void actionPerformed(ActionEvent e) {
ChatRoom chatRoom = (ChatRoom)getComponentInTab(tab);
if (chatRoom != null) {
for (ChatRoom cRoom : getChatRooms()) {
if (chatRoom != cRoom) {
closeTab(cRoom);
}
}
}
}
};
closeOthersAction.putValue(Action.NAME, Res.getString("message.close.other.chats"));
popup.add(closeOthersAction);
Action closeOldAction = new AbstractAction() {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
for (ChatRoom rooms : getStaleChatRooms()) {
closeTab(rooms);
}
}
};
closeOldAction.putValue(Action.NAME, Res.getString("message.close.stale.chats"));
popup.add(closeOldAction);
}
popup.show(tab, e.getX(), e.getY());
}
/**
* Returns a Collection of stale chat rooms.
*
* @return a collection of stale chat rooms.
*/
public Collection<ChatRoom> getStaleChatRooms() {
final List<ChatRoom> staleRooms = new ArrayList<ChatRoom>();
for (ChatRoom chatRoom : getChatRooms()) {
long lastActivity = chatRoom.getLastActivity();
long currentTime = System.currentTimeMillis();
long diff = currentTime - lastActivity;
int minutes = (int)(diff / (60 * 1000F));
LocalPreferences pref = SettingsManager.getLocalPreferences();
int timeoutMinutes = pref.getChatLengthDefaultTimeout();
int unreadCount = chatRoom.getUnreadMessageCount();
if (timeoutMinutes <= minutes && unreadCount == 0) {
staleRooms.add(chatRoom);
}
}
return staleRooms;
}
/**
* Checks every room every 30 seconds to see if it's timed out.
*/
private void handleStaleChats() {
int delay = 1000; // delay for 1 second.
int period = 60000; // repeat every minute.
final TimerTask task = new SwingTimerTask() {
public void doRun() {
for (ChatRoom chatRoom : getStaleChatRooms()) {
// Notify decorators
SparkManager.getChatManager().notifySparkTabHandlers(chatRoom);
}
}
};
TaskEngine.getInstance().scheduleAtFixedRate(task, delay, period);
}
private void navigateRight() {
int selectedIndex = getSelectedIndex();
if (selectedIndex > -1) {
int count = getTabCount();
if (selectedIndex == (count - 1)) {
setSelectedIndex(0);
}
else {
setSelectedIndex(selectedIndex + 1);
}
}
}
private void navigateLeft() {
int selectedIndex = getSelectedIndex();
if (selectedIndex > 0) {
setSelectedIndex(selectedIndex - 1);
}
else {
setSelectedIndex(getTabCount() - 1);
}
}
// Handle key listener events for mac only :)
public void keyTyped(KeyEvent keyEvent) {
// Nothing to do.
}
public void keyPressed(KeyEvent keyEvent) {
if (keyEvent.isMetaDown()) {
if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
navigateRight();
}
else if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
navigateLeft();
}
}
}
public void keyReleased(KeyEvent keyEvent) {
// Nothing to do.
}
/**
* Returns the total number of unread messages in Spark.
*
* @return the total number of unread messages in Spark.
*/
public int getTotalNumberOfUnreadMessages() {
int messageCount = 0;
for (ChatRoom chatRoom : chatRoomList) {
messageCount += chatRoom.getUnreadMessageCount();
}
return messageCount;
}
}