/** * $Revision: $ * $Date: $ * * Copyright (C) 2006 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Lesser Public License (LGPL), * a copy of which is included in this distribution. */ package org.jivesoftware.spark.ui; import org.jivesoftware.MainWindow; import org.jivesoftware.Spark; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Roster; 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.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.ui.status.StatusItem; import org.jivesoftware.spark.util.ModelUtil; import org.jivesoftware.spark.util.SwingWorker; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.settings.local.SettingsManager; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Should be subclassed to track all available ChatRooms. */ public class ChatContainer extends SparkTabbedPane implements MessageListener, ChangeListener { /** * List of all ChatRoom Listeners. */ private final List chatRoomListeners = new ArrayList(); private final List chatRoomList = new ArrayList(); private final Map presenceMap = new HashMap(); private static final String WELCOME_TITLE = SparkRes.getString(SparkRes.WELCOME); private ChatFrame chatFrame; /** * Creates the ChatRooms to hold all ChatRooms. */ public ChatContainer() { // Set minimum size setMinimumSize(new Dimension(400, 200)); // Don't allow tabs to shrink and allow scrolling. addSparkTabbedPaneListener(new SparkTabbedPaneListener() { public void tabRemoved(SparkTab tab, Component component, int index) { stateChanged(null); cleanupChatRoom((ChatRoom)component); } public void tabAdded(SparkTab tab, Component component, int index) { stateChanged(null); } public void tabSelected(SparkTab tab, Component component, int index) { stateChanged(null); } public void allTabsRemoved() { chatFrame.setTitle(""); chatFrame.setVisible(false); } }); setCloseButtonEnabled(true); // Add Key Navigation addKeyNavigation(); this.setFocusable(false); } /** * 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_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("alt " + leftStrokeString + ""), "navigateLeft"); this.getActionMap().put("navigateLeft", new AbstractAction("navigateLeft") { public void actionPerformed(ActionEvent evt) { int selectedIndex = getSelectedIndex(); if (selectedIndex > 0) { setSelectedIndex(selectedIndex - 1); } else { setSelectedIndex(getTabCount() - 1); } } }); KeyStroke rightStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0); String rightStrokeString = org.jivesoftware.spark.util.StringUtils.keyStroke2String(rightStroke); // Handle Right Arrow this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("alt " + rightStrokeString + ""), "navigateRight"); this.getActionMap().put("navigateRight", new AbstractAction("navigateRight") { public void actionPerformed(ActionEvent evt) { int selectedIndex = getSelectedIndex(); if (selectedIndex > -1) { int count = getTabCount(); if (selectedIndex == (count - 1)) { setSelectedIndex(0); } else { setSelectedIndex(selectedIndex + 1); } } } }); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ESCAPE"), "escape"); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control W"), "escape"); this.getActionMap().put("escape", new AbstractAction("escape") { public void actionPerformed(ActionEvent evt) { closeActiveRoom(); } }); } /** * Adds a new ChatRoom to Spark. * * @param room the ChatRoom to add. */ public 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); } }); } }; 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 = "
Contact: " + nickname + "ChatRoomListener to register
*/
public void addChatRoomListener(ChatRoomListener listener) {
if (!chatRoomListeners.contains(listener)) {
chatRoomListeners.add(listener);
}
}
/**
* Removes the specified ChatRoomListener.
*
* @param listener the ChatRoomListener to remove
*/
public void removeChatRoomListener(ChatRoomListener listener) {
chatRoomListeners.remove(listener);
}
/**
* Notifies users that a ChatRoom has been opened.
*
* @param room - the ChatRoom that has been opened.
*/
protected void fireChatRoomOpened(ChatRoom room) {
final Iterator iter = new ArrayList(chatRoomListeners).iterator();
while (iter.hasNext()) {
((ChatRoomListener)iter.next()).chatRoomOpened(room);
}
}
/**
* Notifies users that a ChatRoom has been left.
*
* @param room - the ChatRoom that has been left
*/
protected void fireChatRoomLeft(ChatRoom room) {
final Iterator iter = new HashSet(chatRoomListeners).iterator();
while (iter.hasNext()) {
final Object chatRoomListener = iter.next();
((ChatRoomListener)chatRoomListener).chatRoomLeft(room);
}
}
/**
* Notifies users that a ChatRoom has been closed.
*
* @param room - the ChatRoom that has been closed.
*/
protected void fireChatRoomClosed(ChatRoom room) {
final Iterator iter = new HashSet(chatRoomListeners).iterator();
while (iter.hasNext()) {
final Object chatRoomListener = iter.next();
((ChatRoomListener)chatRoomListener).chatRoomClosed(room);
}
}
/**
* Notifies users that a ChatRoom has been activated.
*
* @param room - the ChatRoom that has been activated.
*/
protected void fireChatRoomActivated(ChatRoom room) {
final Iterator iter = new HashSet(chatRoomListeners).iterator();
while (iter.hasNext()) {
((ChatRoomListener)iter.next()).chatRoomActivated(room);
}
}
/**
* Notifies users that a user has joined a ChatRoom.
*
* @param room - the ChatRoom 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() {
final Iterator iter = new HashSet(chatRoomListeners).iterator();
while (iter.hasNext()) {
((ChatRoomListener)iter.next()).userHasJoined(room, userid);
}
}
});
}
/**
* Notifies users that a user has left a ChatRoom.
*
* @param room - the ChatRoom 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() {
final Iterator iter = new HashSet(chatRoomListeners).iterator();
while (iter.hasNext()) {
((ChatRoomListener)iter.next()).userHasLeft(room, userid);
}
}
});
}
/**
* Starts flashing of MainWindow.
*
* @param room the ChatRoom to check if a message has been inserted
* but the room is not the selected room.
*/
public void startFlashing(final ChatRoom room) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
final int index = indexOfComponent(room);
if (index != -1) {
room.increaseUnreadMessageCount();
int unreadMessageCount = room.getUnreadMessageCount();
String appendedMessage = "";
if (unreadMessageCount > 1) {
appendedMessage = " (" + unreadMessageCount + ")";
}
SparkTab tab = getTabAt(index);
tab.getTitleLabel().setText(room.getTabTitle() + appendedMessage);
makeTabRed(room);
}
boolean invokeFlash = SettingsManager.getLocalPreferences().isChatRoomNotificationsOn() || !(room instanceof GroupChatRoom);
if (!chatFrame.isFocused() && invokeFlash) {
SparkManager.getAlertManager().flashWindow(chatFrame);
}
}
catch (Exception ex) {
Log.error("Issue in ChatRooms with tab location.", ex);
}
}
});
}
public void flashWindow(final ChatRoom room) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
boolean invokeFlash = SettingsManager.getLocalPreferences().isChatRoomNotificationsOn() || !(room instanceof GroupChatRoom);
if (!chatFrame.isFocused() && invokeFlash) {
SparkManager.getAlertManager().flashWindow(chatFrame);
}
}
catch (Exception ex) {
Log.error("Issue in ChatRooms with tab location.", ex);
}
}
});
}
public void makeTabRed(final ChatRoom room) {
final int index = indexOfComponent(room);
if (index != -1) {
SparkTab tab = getTabAt(index);
Font font = tab.getTitleLabel().getFont();
final JLabel titleLabel = tab.getTitleLabel();
Font newFont = font.deriveFont(Font.BOLD);
titleLabel.setFont(newFont);
titleLabel.setForeground(Color.red);
}
}
public void useTabDefault(final ChatRoom room) {
final int index = indexOfComponent(room);
if (index != -1) {
SparkTab tab = getTabAt(index);
Font font = tab.getTitleLabel().getFont();
tab.getTitleLabel().setForeground(Color.black);
Font newFont = font.deriveFont(Font.PLAIN);
tab.getTitleLabel().setFont(newFont);
}
}
/**
* Checks to see if the MainWindow should stop flashing.
*
* @param room the ChatRoom to check.
*/
public void stopFlashing(final ChatRoom room) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
int index = indexOfComponent(room);
if (index != -1) {
SparkTab tab = getTabAt(index);
useTabDefault(room);
tab.getTitleLabel().setText(room.getTabTitle());
room.clearUnreadMessageCount();
}
}
catch (Exception ex) {
Log.error("Could not stop flashing for " + room + " because " + ex.getMessage(), ex);
}
SparkManager.getAlertManager().stopFlashing(chatFrame);
}
});
}
public void setChatRoomTitle(ChatRoom room, String title) {
int index = indexOfComponent(room);
if (index != -1) {
SparkTab tab = getTabAt(index);
useTabDefault(room);
tab.getTitleLabel().setText(room.getTabTitle());
}
}
private void createFrameIfNeeded() {
if (chatFrame != null) {
return;
}
chatFrame = new ChatFrame();
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) {
}
}
public void windowDeactivated(WindowEvent windowEvent) {
}
});
}
public void focusChat() {
SwingWorker worker = new SwingWorker() {
public Object construct() {
try {
Thread.sleep(50);
}
catch (InterruptedException e1) {
Log.error(e1);
}
return "ok";
}
public void finished() {
try {
//chatFrame.requestFocus();
ChatRoom chatRoom = getActiveChatRoom();
chatRoom.requestFocusInWindow();
chatRoom.getChatInputEditor().requestFocusInWindow();
}
catch (ChatRoomNotFoundException e1) {
// Ignore. There may legitamtly not be a chat room.
}
}
};
worker.start();
}
public Collection getChatRooms() {
return chatRoomList;
}
public ChatFrame getChatFrame() {
return chatFrame;
}
public void blinkFrameIfNecessary(final JFrame frame) {
final MainWindow mainWindow = SparkManager.getMainWindow();
if (mainWindow.isFocused()) {
frame.setVisible(true);
return;
}
else {
// Set to new tab.
if (Spark.isWindows()) {
frame.setState(Frame.ICONIFIED);
SparkManager.getAlertManager().flashWindow(frame);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent e) {
SparkManager.getAlertManager().stopFlashing(frame);
}
});
}
}
}
}