/**
* $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.rooms;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import org.jivesoftware.resource.Res;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromContainsFilter;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.PacketFilter;
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.smackx.Form;
import org.jivesoftware.smackx.MessageEventManager;
import org.jivesoftware.smackx.MessageEventNotificationListener;
import org.jivesoftware.smackx.muc.DefaultParticipantStatusListener;
import org.jivesoftware.smackx.muc.DefaultUserStatusListener;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.muc.SubjectUpdatedListener;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.smackx.packet.MUCUser;
import org.jivesoftware.smackx.packet.MUCUser.Destroy;
import org.jivesoftware.spark.ChatManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.component.RolloverButton;
import org.jivesoftware.spark.plugin.ContextMenuListener;
import org.jivesoftware.spark.ui.ChatContainer;
import org.jivesoftware.spark.ui.ChatFrame;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.ui.ChatRoomNotFoundException;
import org.jivesoftware.spark.ui.GroupChatRoomTransferHandler;
import org.jivesoftware.spark.ui.conferences.AnswerFormDialog;
import org.jivesoftware.spark.ui.conferences.ConferenceUtils;
import org.jivesoftware.spark.ui.conferences.DataFormDialog;
import org.jivesoftware.spark.ui.conferences.GroupChatParticipantList;
import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.spark.util.UIComponentRegistry;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
/**
* GroupChatRoom is the conference chat room UI used to have Multi-User Chats.
*/
public class GroupChatRoom extends ChatRoom {
private static final long serialVersionUID = 4469579438292227006L;
private MultiUserChat chat;
private String password = null;
private String roomname;
private Icon tabIcon = SparkRes
.getImageIcon(SparkRes.CONFERENCE_IMAGE_16x16);
private String tabTitle;
private boolean isActive = true;
private SubjectPanel subjectPanel;
private List<String> currentUserList = new ArrayList<String>();
private String conferenceService;
private List<String> blockedUsers = new ArrayList<String>();
private ChatRoomMessageManager messageManager;
private Timer typingTimer;
private int typedChars;
private GroupChatParticipantList roomInfo;
private long lastActivity;
private Message lastMessage;
LocalPreferences pref = SettingsManager.getLocalPreferences();
private boolean showPresenceMessages = pref
.isShowJoinLeaveMessagesEnabled();
private boolean isMucHighlightingNameEnabled = pref.isMucHighNameEnabled();
private boolean isMucHighlightingTextEnabled = pref.isMucHighTextEnabled();
/**
* Creates a GroupChatRoom from a <code>MultiUserChat</code>.
*
* @param chat
* the MultiUserChat to create a GroupChatRoom from.
*/
public GroupChatRoom(final MultiUserChat chat) {
this.chat = chat;
// Create the filter and register with the current connection
// making sure to filter by room
PacketFilter fromFilter = new FromContainsFilter(chat.getRoom());
PacketFilter orFilter = new OrFilter(new PacketTypeFilter(
Presence.class), new PacketTypeFilter(Message.class));
PacketFilter andFilter = new AndFilter(orFilter, fromFilter);
// Add packet Listener.
SparkManager.getConnection().addPacketListener(this, andFilter);
// The Room Name is the same as the ChatRoom name
roomname = chat.getRoom();
// We are just using a generic Group Chat.
tabTitle = StringUtils.parseName(StringUtils.unescapeNode(roomname));
// Room Information
roomInfo = UIComponentRegistry.createGroupChatParticipantList();
getSplitPane().setRightComponent(roomInfo.getGUI());
roomInfo.setChatRoom(this);
getSplitPane().setResizeWeight(.60);
getSplitPane().setDividerLocation(.80);
setupListeners();
conferenceService = StringUtils.parseServer(chat.getRoom());
subjectPanel = new SubjectPanel();
// Do not show top toolbar
getToolBar().add(
subjectPanel,
new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0,
GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 2),
0, 0));
// Add ContextMenuListener
getTranscriptWindow().addContextMenuListener(new ContextMenuListener() {
public void poppingUp(Object component, JPopupMenu popup) {
popup.addSeparator();
Action inviteAction = new AbstractAction() {
private static final long serialVersionUID = -2493782261839364071L;
public void actionPerformed(ActionEvent actionEvent) {
ConferenceUtils.inviteUsersToRoom(conferenceService,
getRoomname(), null, false);
}
};
inviteAction.putValue(Action.NAME,
Res.getString("menuitem.invite.users"));
inviteAction.putValue(Action.SMALL_ICON,
SparkRes.getImageIcon(SparkRes.SMALL_MESSAGE_IMAGE));
popup.add(inviteAction);
Action configureAction = new AbstractAction() {
private static final long serialVersionUID = -7463450970328104297L;
public void actionPerformed(ActionEvent actionEvent) {
try {
ChatFrame chatFrame = SparkManager.getChatManager()
.getChatContainer().getChatFrame();
Form form = chat.getConfigurationForm()
.createAnswerForm();
new DataFormDialog(chatFrame, chat, form);
} catch (XMPPException e) {
Log.error("Error configuring room.", e);
}
}
};
configureAction.putValue(Action.NAME,
Res.getString("title.configure.room"));
configureAction.putValue(Action.SMALL_ICON,
SparkRes.getImageIcon(SparkRes.SETTINGS_IMAGE_16x16));
if (SparkManager.getUserManager().isOwner(
(GroupChatRoom) getChatRoom(), chat.getNickname())) {
popup.add(configureAction);
}
Action subjectChangeAction = new AbstractAction() {
private static final long serialVersionUID = 6730534406025965089L;
public void actionPerformed(ActionEvent actionEvent) {
String newSubject = JOptionPane.showInputDialog(
getChatRoom(),
Res.getString("message.enter.new.subject")
+ ":",
Res.getString("title.change.subject"),
JOptionPane.QUESTION_MESSAGE);
if (ModelUtil.hasLength(newSubject)) {
try {
chat.changeSubject(newSubject);
} catch (XMPPException e) {
Log.error(e);
}
}
}
};
subjectChangeAction.putValue(Action.NAME,
Res.getString("menuitem.change.subject"));
subjectChangeAction.putValue(Action.SMALL_ICON, SparkRes
.getImageIcon(SparkRes.SMALL_MESSAGE_EDIT_IMAGE));
popup.add(subjectChangeAction);
// Define actions to modify/view room information
Action destroyRoomAction = new AbstractAction() {
private static final long serialVersionUID = 6494204166819377882L;
public void actionPerformed(ActionEvent e) {
int ok = JOptionPane.showConfirmDialog(
getChatRoom(),
Res.getString("message.confirm.destruction.of.room"),
Res.getString("title.confirmation"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (ok == JOptionPane.NO_OPTION) {
return;
}
String reason = JOptionPane.showInputDialog(
getChatRoom(),
Res.getString("message.room.destruction.reason"),
Res.getString("title.enter.reason"),
JOptionPane.QUESTION_MESSAGE);
if (ModelUtil.hasLength(reason)) {
try {
chat.destroy(reason, null);
getChatRoom().leaveChatRoom();
} catch (XMPPException e1) {
Log.warning("Unable to destroy room", e1);
}
}
}
};
destroyRoomAction.putValue(Action.NAME,
Res.getString("menuitem.destroy.room"));
destroyRoomAction.putValue(Action.SMALL_ICON,
SparkRes.getImageIcon(SparkRes.SMALL_DELETE));
if (SparkManager.getUserManager().isOwner(
(GroupChatRoom) getChatRoom(), getNickname())) {
popup.add(destroyRoomAction);
}
}
public void poppingDown(JPopupMenu popup) {
}
public boolean handleDefaultAction(MouseEvent e) {
return false;
}
});
messageManager = new ChatRoomMessageManager();
// set last activity to be right now
lastActivity = System.currentTimeMillis();
GroupChatRoomTransferHandler transferHandler = new GroupChatRoomTransferHandler(
this);
getTranscriptWindow().setTransferHandler(transferHandler);
addRoomConfigureButtons(this);
}
/**
* Sets the title to use on the tab describing the Conference room.
*
* @param tabTitle
* the title to use on the tab.
*/
public void setTabTitle(String tabTitle) {
this.tabTitle = tabTitle;
}
/**
* Call this method if you wish to hide the participant list.
*/
public void hideParticipantList() {
getSplitPane().setRightComponent(null);
}
public Message getLastMessage() {
return lastMessage;
}
/**
* Have the user leave this chat room and then close it.
*/
public void closeChatRoom() {
// Specify the end time.
super.closeChatRoom();
// Remove Listener
SparkManager.getConnection().removePacketListener(this);
ChatContainer container = SparkManager.getChatManager()
.getChatContainer();
container.leaveChatRoom(this);
container.closeTab(this);
}
/**
* Determines the background color to use for messages.
*
* @param nickname
* Nickname associated with message.
* @param body
* Body of message to scan for reasons to highlight.
* @return Color of message background.
*/
private Color getMessageBackground(String nickname, String body) {
// define some vcard information
String myNickName = chat.getNickname();
String myUserName = SparkManager.getSessionManager().getUsername();
Pattern usernameMatch = Pattern.compile(myUserName,
Pattern.CASE_INSENSITIVE);
Pattern nicknameMatch = Pattern.compile(myNickName,
Pattern.CASE_INSENSITIVE);
// Should we even highlight this packet?
if (isMucHighlightingNameEnabled
&& myNickName.equalsIgnoreCase(nickname)) {
return new Color(244, 248, 255);
} else if (isMucHighlightingTextEnabled
&& (usernameMatch.matcher(body).find() || nicknameMatch
.matcher(body).find())) {
return new Color(255, 255, 153);
} else {
return new Color(0,0,0,0);
}
}
/**
* Sends a message.
*
* @param message
* - the message to send.
*/
public void sendMessage(Message message) {
try {
message.setTo(chat.getRoom());
message.setType(Message.Type.groupchat);
MessageEventManager.addNotificationsRequests(message, true, true,
true, true);
// Add packetID to list
addPacketID(message.getPacketID());
// Fire Message Filters
SparkManager.getChatManager().filterOutgoingMessage(this, message);
// Fire Global Listeners
SparkManager.getChatManager().fireGlobalMessageSentListeners(this,
message);
chat.sendMessage(message);
} catch (XMPPException ex) {
Log.error("Unable to send message in conference chat.", ex);
}
try {
getTranscriptWindow().insertMessage(getNickname(), message,
getColor(getNickname()),
getMessageBackground(getNickname(), message.getBody()));
getChatInputEditor().selectAll();
getTranscriptWindow().validate();
getTranscriptWindow().repaint();
getChatInputEditor().clear();
} catch (Exception ex) {
Log.error("Error sending message", ex);
}
// Notify users that message has been sent
fireMessageSent(message);
addToTranscript(message, false);
getChatInputEditor().setCaretPosition(0);
getChatInputEditor().requestFocusInWindow();
scrollToBottom();
lastActivity = System.currentTimeMillis();
}
/**
* Sends a message.
*
* @param message
* - the message to send.
*/
public void sendMessageWithoutNotification(Message message) {
try {
message.setTo(chat.getRoom());
message.setType(Message.Type.groupchat);
MessageEventManager.addNotificationsRequests(message, true, true,
true, true);
// Add packetID to list
addPacketID(message.getPacketID());
chat.sendMessage(message);
} catch (XMPPException ex) {
Log.error("Unable to send message in conference chat.", ex);
}
try {
getTranscriptWindow().insertMessage(getNickname(), message,
getColor(getNickname()),
getMessageBackground(getNickname(), message.getBody()));
getChatInputEditor().selectAll();
getTranscriptWindow().validate();
getTranscriptWindow().repaint();
getChatInputEditor().clear();
} catch (Exception ex) {
Log.error("Error sending message", ex);
}
addToTranscript(message, false);
getChatInputEditor().setCaretPosition(0);
getChatInputEditor().requestFocusInWindow();
scrollToBottom();
lastActivity = System.currentTimeMillis();
}
/**
* Return name of the room specified when the room was created.
*
* @return the roomname.
*/
public String getRoomname() {
return roomname;
}
/**
* Retrieve the nickname of the user in this groupchat.
*
* @return the nickname of the agent in this groupchat
*/
public String getNickname() {
return chat.getNickname();
}
/**
* Sets the icon to use on the tab.
*
* @param tabIcon
* the icon to use on the tab.
*/
public void setTabIcon(Icon tabIcon) {
this.tabIcon = tabIcon;
}
/**
* Return the Icon that should be used in the tab of this GroupChat Pane.
*
* @return the Icon to use in tab.
*/
public Icon getTabIcon() {
return tabIcon;
}
/**
* Return the title that should be used in the tab.
*
* @return the title to be used on the tab.
*/
public String getTabTitle() {
return tabTitle;
}
/**
* Return the title of this room.
*
* @return the title of this room.
*/
public String getRoomTitle() {
return getTabTitle();
}
/**
* Return the type of chat we are in.
*
* @return the type of chat we are in.
*/
public Message.Type getChatType() {
return Message.Type.groupchat;
}
/**
* Implementation of leaveChatRoom.
*/
public void leaveChatRoom() {
if (!isActive) {
return;
}
// Remove Packet Listener
SparkManager.getConnection().removePacketListener(this);
// Disable Send Field
getChatInputEditor().showAsDisabled();
// Do not allow other to try and invite or transfer chat
disableToolbar();
getToolBar().setVisible(false);
// Update Room Notice To Inform Agent that he has left the chat.
getTranscriptWindow().insertNotificationMessage(
Res.getString("message.user.left.room", getNickname()),
ChatManager.NOTIFICATION_COLOR);
// Leave the Chat.
try {
chat.leave();
} catch (Exception e) {
Log.error("Closing Group Chat Room error.", e);
}
// Set window as greyed out.
getTranscriptWindow().showWindowDisabled();
// Update Notification Label
getNotificationLabel().setText(
Res.getString("message.chat.session.ended",
SparkManager.DATE_SECOND_FORMATTER
.format(new java.util.Date())));
getNotificationLabel().setIcon(null);
getNotificationLabel().setEnabled(false);
getSplitPane().setRightComponent(null);
getSplitPane().setDividerSize(0);
isActive = false;
}
/**
* If true, will display all presence messages. Set to false to turn off
* presence notifications.
*
* @param showMessages
* true to display presence messages, otherwise false.
*/
public void showPresenceMessages(boolean showMessages) {
showPresenceMessages = showMessages;
}
/**
* Returns whether or not this ChatRoom is active. To be active means to
* have the agent still engaged in a conversation with a customer.
*
* @return true if the ChatRoom is active.
*/
public boolean isActive() {
return isActive;
}
/**
* Returns the number of participants in this room.
*
* @return the number of participants in this room.
*/
public int getParticipantCount() {
if (!isActive) {
return 0;
}
return chat.getOccupantsCount();
}
/**
* Implementation of processPacket to handle muc related packets.
*
* @param packet
* the packet.
*/
public void processPacket(final Packet packet) {
super.processPacket(packet);
if (packet instanceof Presence) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handlePresencePacket(packet);
}
});
}
if (packet instanceof Message) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handleMessagePacket(packet);
// Set last activity
lastActivity = System.currentTimeMillis();
}
});
}
}
/**
* Handle all MUC related packets.
*
* @param packet
* the packet.
*/
private void handleMessagePacket(Packet packet) {
// Do something with the incoming packet here.
final Message message = (Message) packet;
lastMessage = message;
if (message.getType() == Message.Type.groupchat) {
DelayInformation inf = (DelayInformation) message.getExtension("x",
"jabber:x:delay");
Date sentDate;
if (inf != null) {
sentDate = inf.getStamp();
} else {
sentDate = new Date();
}
// Do not accept Administrative messages.
String host = SparkManager.getSessionManager().getServerAddress();
if (host.equals(message.getFrom())) {
return;
}
String messageNickname = StringUtils.parseResource(message
.getFrom());
boolean isFromMe = messageNickname.equals(getNickname())
&& inf == null;
// If the message is not from the current user. Append to chat.
if (ModelUtil.hasLength(message.getBody()) && !isFromMe) {
// Update transcript
super.insertMessage(message);
String from = StringUtils.parseResource(message.getFrom());
if (inf != null) {
getTranscriptWindow().insertHistoryMessage(from,
message.getBody(), sentDate);
} else {
if (isBlocked(message.getFrom())) {
return;
}
boolean isFromRoom = message.getFrom().indexOf("/") == -1;
if (!SparkManager.getUserManager().hasVoice(this,
StringUtils.parseResource(message.getFrom()))
&& !isFromRoom) {
return;
}
getTranscriptWindow().insertMessage(from, message,
getColor(from),
getMessageBackground(from, message.getBody()));
}
if (typingTimer != null) {
showDefaultTabIcon();
}
}
} else if (message.getType() == Message.Type.chat) {
ChatRoom chatRoom;
try {
chatRoom = SparkManager.getChatManager().getChatContainer()
.getChatRoom(message.getFrom());
// TODO: Why was this not here before? Is it supposed to be
// ignored??
// --> because the messages otherwise comes twice [bug
// SPARK-1099]
// if (message.getBody() != null) {
// chatRoom.insertMessage(message);
// }
} catch (ChatRoomNotFoundException e) {
String userNickname = StringUtils.parseResource(message
.getFrom());
String roomTitle = userNickname + " - "
+ StringUtils.parseName(getRoomname());
// Check to see if this is a message notification.
if (message.getBody() != null) {
// Create new room
chatRoom = new ChatRoomImpl(message.getFrom(), userNickname,
roomTitle);
SparkManager.getChatManager().getChatContainer()
.addChatRoom(chatRoom);
SparkManager.getChatManager().getChatContainer()
.activateChatRoom(chatRoom);
chatRoom.insertMessage(message);
}
}
} else if (message.getError() != null) {
String errorMessage = "";
if (message.getError().getCode() == 403
&& message.getSubject() != null) {
errorMessage = Res.getString("message.subject.change.error");
}
else if (message.getError().getCode() == 403) {
errorMessage = Res.getString("message.forbidden.error");
}
if (ModelUtil.hasLength(errorMessage)) {
getTranscriptWindow().insertNotificationMessage(errorMessage,
ChatManager.ERROR_COLOR);
}
}
//Scroll To bottom every time a message is received
scrollToBottom();
}
/**
* Handle all presence packets being sent to this Group Chat Room.
*
* @param packet
* the presence packet.
*/
private void handlePresencePacket(Packet packet) {
Presence presence = (Presence) packet;
if (presence.getError() != null) {
return;
}
final String from = presence.getFrom();
final String nickname = StringUtils.parseResource(from);
MUCUser mucUser = (MUCUser) packet.getExtension("x",
"http://jabber.org/protocol/muc#user");
String code = "";
if (mucUser != null) {
code = mucUser.getStatus() != null ? mucUser.getStatus().getCode()
: "";
Destroy destroy = mucUser.getDestroy();
if (destroy != null) {
String reason = destroy.getReason();
JOptionPane.showMessageDialog(this,
Res.getString("message.room.destroyed", reason),
Res.getString("title.room.destroyed"),
JOptionPane.INFORMATION_MESSAGE);
leaveChatRoom();
return;
}
}
if (presence.getType() == Presence.Type.unavailable
&& !"303".equals(code)) {
if (currentUserList.contains(from)) {
if (showPresenceMessages) {
getTranscriptWindow().insertNotificationMessage(
Res.getString("message.user.left.room", nickname),
ChatManager.NOTIFICATION_COLOR);
scrollToBottom();
}
currentUserList.remove(from);
}
} else {
if (!currentUserList.contains(from)) {
currentUserList.add(from);
getChatInputEditor().setEnabled(true);
if (showPresenceMessages) {
getTranscriptWindow()
.insertNotificationMessage(
Res.getString("message.user.joined.room",
nickname),
ChatManager.NOTIFICATION_COLOR);
scrollToBottom();
}
}
}
}
/**
* Set up the participant listeners and status change listeners.
*/
private void setupListeners() {
chat.addParticipantStatusListener(new DefaultParticipantStatusListener() {
public void kicked(String participant, String actor, String reason) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.kicked.from.room",
nickname,actor,reason));
}
public void voiceGranted(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.given.voice", nickname));
}
public void voiceRevoked(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res
.getString("message.user.voice.revoked", nickname));
}
public void banned(String participant, String actor, String reason) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.banned", nickname, reason));
}
public void membershipGranted(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.granted.membership",
nickname));
}
public void membershipRevoked(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.revoked.membership",
nickname));
}
public void moderatorGranted(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.granted.moderator",
nickname));
}
public void moderatorRevoked(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res.getString("message.user.revoked.moderator",
nickname));
}
public void ownershipGranted(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res
.getString("message.user.granted.owner", nickname));
}
public void ownershipRevoked(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res
.getString("message.user.revoked.owner", nickname));
}
public void adminGranted(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res
.getString("message.user.granted.admin", nickname));
}
public void adminRevoked(String participant) {
String nickname = StringUtils.parseResource(participant);
insertText(Res
.getString("message.user.revoked.admin", nickname));
}
public void nicknameChanged(String participant, String nickname) {
insertText(Res.getString("message.user.nickname.changed",
StringUtils.parseResource(participant), nickname));
}
});
chat.addUserStatusListener(new DefaultUserStatusListener() {
public void kicked(String s, String reason) {
if (ModelUtil.hasLength(reason)) {
insertText(reason);
} else {
insertText(Res.getString("message.your.kicked", s));
}
getChatInputEditor().setEnabled(false);
getSplitPane().setRightComponent(null);
leaveChatRoom();
}
public void voiceGranted() {
insertText(Res.getString("message.your.voice.granted"));
getChatInputEditor().setEnabled(true);
}
public void voiceRevoked() {
insertText(Res.getString("message.your.voice.revoked"));
getChatInputEditor().setEnabled(false);
}
public void banned(String s, String reason) {
insertText(Res.getString("message.your.banned"));
}
public void membershipGranted() {
insertText(Res.getString("message.your.membership.granted"));
}
public void membershipRevoked() {
insertText(Res.getString("message.your.membership.revoked"));
}
public void moderatorGranted() {
insertText(Res.getString("message.your.moderator.granted"));
}
public void moderatorRevoked() {
insertText(Res.getString("message.your.moderator.revoked"));
}
public void ownershipGranted() {
insertText(Res.getString("message.your.ownership.granted"));
}
public void ownershipRevoked() {
insertText(Res.getString("message.your.ownership.revoked"));
}
public void adminGranted() {
insertText(Res.getString("message.your.admin.granted"));
}
public void adminRevoked() {
insertText(Res.getString("message.your.revoked.granted"));
}
});
chat.addSubjectUpdatedListener(new SubjectListener());
}
/**
* Inserts a notification message within the TranscriptWindow.
*
* @param text
* the text to insert.
*/
public void insertText(String text) {
getTranscriptWindow().insertNotificationMessage(text,
ChatManager.NOTIFICATION_COLOR);
}
/**
* Listens for a change in subject and notified containing class.
*/
private class SubjectListener implements SubjectUpdatedListener {
public void subjectUpdated(String subject, String by) {
subjectPanel.setSubject(subject);
subjectPanel.setToolTipText(subject);
String nickname = StringUtils.parseResource(by);
String insertMessage = Res.getString(
"message.subject.has.been.changed.to", subject, nickname);
getTranscriptWindow().insertNotificationMessage(insertMessage,
ChatManager.NOTIFICATION_COLOR);
}
}
/**
* Returns the user format (e.g.
* darkcave@macbeth.shakespeare.lit/thirdwitch) of each user in the room.
*
* @return the user format (e.g.
* darkcave@macbeth.shakespeare.lit/thirdwitch) of each user in the
* room.
*/
public Collection<String> getParticipants() {
return currentUserList;
}
/**
* Sends the message that is currently in the send field. The message is
* automatically added to the transcript for later retrieval.
*/
public void sendMessage() {
final String text = getChatInputEditor().getText();
sendMessage(text);
}
public void sendMessage(String text) {
// IF there is no body, just return and do nothing
if (!ModelUtil.hasLength(text)) {
return;
}
// Create message object
Message message = new Message();
// Set the body of the message using typedMessage and remove control
// characters
text = text.replaceAll("[\\u0001-\\u0008\\u000B-\\u001F]", "");
message.setBody(text);
sendMessage(message);
}
/**
* Returns a MultiUserChat object associated with this room.
*
* @return the <code>MultiUserChat</code> object associated with this room.
*/
public MultiUserChat getMultiUserChat() {
return chat;
}
/**
* Returns the conference service associated with this Conference Chat.
*
* @return the conference service associated with this Conference Chat.
*/
public String getConferenceService() {
return conferenceService;
}
/**
* Adds a user to the blocked user list. Blocked users is NOT a MUC related
* item, but rather used by the client to not display messages from certain
* people.
*
* @param usersJID
* the room jid of the user
* (ex.spark@conference.jivesoftware.com/Dan)
*/
public void addBlockedUser(String usersJID) {
blockedUsers.add(usersJID);
}
/**
* Removes a user from the blocked user list.
*
* @param usersJID
* the jid of the user (ex.
* spark@conference.jivesoftware.com/Dan)
*/
public void removeBlockedUser(String usersJID) {
blockedUsers.remove(usersJID);
}
/**
* Returns true if the user is in the blocked user list.
*
* @param usersJID
* the jid of the user (ex.
* spark@conference.jivesoftware.com/Dan)
* @return true if the user is blocked, otherwise false.
*/
public boolean isBlocked(String usersJID) {
return blockedUsers.contains(usersJID);
}
/**
* Specifies whether to use typing notifications or not. By default, group
* chat rooms will NOT use typing notifications.
*
* @param sendAndReceiveTypingNotifications
* true to use typing notifications.
*/
public void setSendAndReceiveTypingNotifications(
boolean sendAndReceiveTypingNotifications) {
if (sendAndReceiveTypingNotifications) {
typingTimer = new Timer(10000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
showDefaultTabIcon();
}
});
SparkManager.getMessageEventManager()
.addMessageEventNotificationListener(messageManager);
} else {
if (typingTimer != null) {
typingTimer.stop();
}
SparkManager.getMessageEventManager()
.removeMessageEventNotificationListener(messageManager);
}
}
/**
* Invite a user to this conference room.
*
* @param jid
* the jid of the user to invite.
* @param message
* the message to send with the invitation.
*/
public void inviteUser(String jid, String message) {
message = message != null ? message : Res
.getString("message.please.join.in.conference");
// Invite User
getMultiUserChat().invite(jid, message);
// Add Invite
roomInfo.addInvitee(jid, message);
}
/**
* Private implementation of the MessageEventNotificationListener.
*/
private class ChatRoomMessageManager implements
MessageEventNotificationListener {
ChatRoomMessageManager() {
}
public void deliveredNotification(String from, String packetID) {
}
public void displayedNotification(String from, String packetID) {
}
public void composingNotification(final String from, String packetID) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
String bareAddress = StringUtils.parseBareAddress(from);
if (bareAddress.equals(getRoomname())) {
showUserIsTyping();
}
}
});
}
public void offlineNotification(String from, String packetID) {
}
public void cancelledNotification(String from, String packetID) {
}
}
/**
* Displays a typing icon in the tab.
*/
private void showUserIsTyping() {
SparkManager.getChatManager().addTypingNotification(this);
typingTimer.restart();
// Notify handlers
SparkManager.getChatManager().notifySparkTabHandlers(this);
}
/**
* Displays the default icon for the chat room.
*/
private void showDefaultTabIcon() {
SparkManager.getChatManager().removeTypingNotification(this);
// Notify handlers
SparkManager.getChatManager().notifySparkTabHandlers(this);
}
/**
* The current SendField has been updated somehow.
*
* @param e
* - the DocumentEvent to respond to.
*/
public void insertUpdate(DocumentEvent e) {
checkForText(e);
typedChars++;
// If the user pauses for more than two seconds, send out a new notice.
if (typedChars >= 10) {
try {
if (typingTimer != null) {
final Iterator<String> iter = chat.getOccupants();
while (iter.hasNext()) {
String from = iter.next();
String tFrom = StringUtils.parseResource(from);
String nickname = chat.getNickname();
if (tFrom != null && !tFrom.equals(nickname)) {
SparkManager.getMessageEventManager()
.sendComposingNotification(from, "djn");
}
}
}
typedChars = 0;
} catch (Exception exception) {
Log.error("Error updating", exception);
}
}
}
/**
* Returns the GroupChatParticpantList which displays all users within a
* conference room.
*
* @return the GroupChatParticipantList.
*/
public GroupChatParticipantList getConferenceRoomInfo() {
return roomInfo;
}
public long getLastActivity() {
return lastActivity;
}
public void connectionClosed() {
handleDisconnect();
}
public void connectionClosedOnError(Exception ex) {
handleDisconnect();
getTranscriptWindow().showWindowDisabled();
getSplitPane().setRightComponent(null);
String message = Res.getString("message.disconnected.group.chat.error");
getTranscriptWindow().insertNotificationMessage(message,
ChatManager.ERROR_COLOR);
}
/**
* Sets the Password for this GroupChat if available, to rejoin the chat
* after a reconnection without prompting the user
*
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Part of Connectionlistener. Gets triggered when successfully reconnected.
*/
public void reconnectionSuccessful() {
final String roomJID = chat.getRoom();
final String roomDesc = tabTitle;
isActive = false;
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ConferenceUtils.joinConferenceOnSeperateThread(roomDesc, roomJID, password);
closeChatRoom();
}
});
}
/**
* Is called whenever Spark was unexpectadly disconnected.
*/
private void handleDisconnect() {
getChatInputEditor().setEnabled(false);
getSendButton().setEnabled(false);
SparkManager.getChatManager().getChatContainer()
.fireChatRoomStateUpdated(this);
}
/**
* An internal UI implementation for display subjects within the conference
* room.
*/
public class SubjectPanel extends JPanel {
private static final long serialVersionUID = -1459165526561181321L;
private JLabel roomJIDLabel;
private JLabel subjectLabel;
public SubjectPanel() {
setLayout(new GridBagLayout());
// iconLabel = new
// JLabel(SparkRes.getImageIcon(SparkRes.CONFERENCE_IMAGE_24x24));
roomJIDLabel = new JLabel("<" + getMultiUserChat().getRoom() + ">");
subjectLabel = new JLabel(getMultiUserChat().getSubject());
// add(iconLabel, new GridBagConstraints(0, 0, 1, 2, 0.0, 0.0,
// GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new
// Insets(2, 2, 2, 2), 0, 0));
add(roomJIDLabel,
new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0,
GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(2, 2, 0,
2), 0, 0));
add(subjectLabel,
new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0,
2), 0, 0));
setOpaque(false);
}
public void setSubject(String subject) {
subjectLabel.setText(subject);
}
public void setRoomLabel(String label) {
roomJIDLabel.setText(label);
}
}
/**
* Returns the descriptive panel at the top of the room.
*
* @return the SubjectPanel.
*/
public SubjectPanel getSubjectPanel() {
return subjectPanel;
}
/**
* Returns the Color to use
* Use Color.blue for yourself
* @param nickname
* @return
*/
public Color getColor(String nickname) {
if (nickname.equals(this.getNickname())) {
return ChatManager.TO_COLOR;
} else {
if (pref.isMucRandomColors()) {
int index = 0;
for (int i = 0; i < nickname.length(); i++) {
index += nickname.charAt(i) * i;
}
return ChatManager.COLORS[index % ChatManager.COLORS.length];
} else {
return ChatManager.FROM_COLOR;
}
}
}
/**
* Adds the Settings and Subject Button to the right Toolbar
* @param room
*/
private void addRoomConfigureButtons(GroupChatRoom room) {
RolloverButton settings = UIComponentRegistry.getButtonFactory().createSettingsButton();
RolloverButton thema = UIComponentRegistry.getButtonFactory().createTemaButton();
RolloverButton register = UIComponentRegistry.getButtonFactory().createRegisterButton();
room.addControllerButton(settings);
room.addControllerButton(thema);
room.addControllerButton(register);
settings.addActionListener(new AbstractAction() {
private static final long serialVersionUID = 6780230647854132857L;
@Override
public void actionPerformed(ActionEvent e) {
try {
ChatFrame chatFrame = SparkManager.getChatManager()
.getChatContainer().getChatFrame();
Form form = chat.getConfigurationForm().createAnswerForm();
new DataFormDialog(chatFrame, chat, form);
} catch (XMPPException xmpe) {
getTranscriptWindow().insertNotificationMessage(
xmpe.getMessage(), ChatManager.ERROR_COLOR);
scrollToBottom();
}
}
});
thema.addActionListener(new AbstractAction() {
private static final long serialVersionUID = -9205709825867979395L;
@Override
public void actionPerformed(ActionEvent e) {
String newSubject = JOptionPane.showInputDialog(getChatRoom(),
Res.getString("message.enter.new.subject") + ":",
Res.getString("title.change.subject"),
JOptionPane.QUESTION_MESSAGE);
if (ModelUtil.hasLength(newSubject)) {
try {
chat.changeSubject(newSubject);
} catch (XMPPException xmpee) {
getTranscriptWindow().insertNotificationMessage(
xmpee.getMessage(),
ChatManager.ERROR_COLOR);
scrollToBottom();
}
}
}
});
register.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
Form form = chat.getRegistrationForm();
ChatFrame chatFrame = SparkManager.getChatManager()
.getChatContainer().getChatFrame();
new AnswerFormDialog(chatFrame, chat, form);
} catch (XMPPException xmpe) {
getTranscriptWindow().insertNotificationMessage(
xmpe.getMessage(), ChatManager.ERROR_COLOR);
scrollToBottom();
}
}
});
}
}