blob: a9b8f2646114a72257c1b9b42fcd100664fe54e6 [file] [log] [blame]
/****************************************************************************
* Copyright (c) 2007, 2009 Remy Suen, Composent, Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Remy Suen <remy.suen@gmail.com> - initial API and implementation
* Hiroyuki Inaba <hiroyuki.inaba@gmail.com> - Bug 259856 The error message when the chat message cannot be sent is not correct.
*****************************************************************************/
package org.eclipse.ecf.presence.ui;
import java.text.SimpleDateFormat;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.util.ECFException;
import org.eclipse.ecf.internal.presence.ui.Activator;
import org.eclipse.ecf.internal.presence.ui.Messages;
import org.eclipse.ecf.presence.im.*;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;
public class MessagesView extends ViewPart {
private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("(hh:mm:ss a)"); //$NON-NLS-1$
public static final String VIEW_ID = "org.eclipse.ecf.presence.ui.MessagesView"; //$NON-NLS-1$
private static final int[] WEIGHTS = {75, 25};
private CTabFolder tabFolder;
private Color redColor;
private Color blueColor;
private Map tabs;
private boolean showTimestamps = true;
public MessagesView() {
tabs = new HashMap();
}
public void createPartControl(Composite parent) {
boolean useTraditionalTabFolder = PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS);
tabFolder = new CTabFolder(parent, SWT.CLOSE);
tabFolder.setTabPosition(SWT.BOTTOM);
tabFolder.setSimple(useTraditionalTabFolder);
PlatformUI.getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS) && !tabFolder.isDisposed()) {
tabFolder.setSimple(((Boolean) event.getNewValue()).booleanValue());
tabFolder.redraw();
}
}
});
tabFolder.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Iterator it = tabs.values().iterator();
while (it.hasNext()) {
ChatTab tab = (ChatTab) it.next();
if (tab.item == e.item) {
tab.inputText.setFocus();
break;
}
}
}
});
tabFolder.addCTabFolder2Listener(new CTabFolder2Adapter() {
public void close(CTabFolderEvent e) {
Iterator it = tabs.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
ChatTab tab = (ChatTab) tabs.get(key);
if (tab.item == e.item) {
tabs.remove(key);
break;
}
}
}
});
IMenuManager manager = getViewSite().getActionBars().getMenuManager();
IAction timestampAction = new Action(Messages.MessagesView_ShowTimestamps, IAction.AS_CHECK_BOX) {
public void run() {
showTimestamps = !showTimestamps;
}
};
timestampAction.setChecked(true);
IAction clearChatLogAction = new Action(Messages.MessagesView_ClearChatLog) {
public void run() {
CTabItem item = tabFolder.getSelection();
if (item != null) {
Iterator iterator = tabs.values().iterator();
while (iterator.hasNext()) {
ChatTab tab = (ChatTab) iterator.next();
if (tab.item == item) {
if (MessageDialog.openConfirm(tabFolder.getShell(), Messages.MessagesView_ClearChatLogDialogTitle, Messages.MessagesView_ClearChatLogDialogMessage)) {
synchronized (tab) {
tab.chatText.setText(""); //$NON-NLS-1$
}
}
return;
}
}
}
}
};
manager.add(clearChatLogAction);
manager.add(timestampAction);
redColor = new Color(parent.getDisplay(), 255, 0, 0);
blueColor = new Color(parent.getDisplay(), 0, 0, 255);
}
public void dispose() {
redColor.dispose();
blueColor.dispose();
super.dispose();
}
private ChatTab getTab(IChatMessageSender messageSender, ITypingMessageSender typingSender, ID localID, ID userID, String localName) {
ChatTab tab = (ChatTab) tabs.get(userID);
if (tab == null) {
tab = new ChatTab(messageSender, typingSender, localID, userID, localName);
tabs.put(userID, tab);
}
return tab;
}
/**
* Display a message to notify the current user that a typing event has
* occurred.
*
* @param event
* the typing message event
*/
public void displayTypingNotification(ITypingMessageEvent event) {
ChatTab tab = null;
synchronized (tabs) {
tab = (ChatTab) tabs.get(event.getFromID());
}
if (tab != null) {
tab.showIsTyping(event.getTypingMessage().isTyping());
}
}
/**
* Opens a new tab for conversing with a user.
*
* @param messageSender
* the <tt>IChatMessageSender</tt> interface that can be used
* to send messages to the other user
* @param typingSender
* the <tt>ITypingMessageSender</tt> interface to notify the
* other user that the current user is typing a message,
* <tt>null</tt> if unsupported
* @param localID
* the ID of the local user
* @param remoteID
* the ID of the remote user
* @since 2.4
*/
public synchronized void openTab(IChatMessageSender messageSender, ITypingMessageSender typingSender, ID localID, ID remoteID, String localName) {
Assert.isNotNull(messageSender);
Assert.isNotNull(localID);
Assert.isNotNull(remoteID);
ChatTab tab = getTab(messageSender, typingSender, localID, remoteID, localName);
// if there is only one tab, select this tab
if (tabs.size() == 1) {
tabFolder.setSelection(tab.item);
}
}
private static String getLocalName(ID id) {
IChatID cID = (IChatID) id.getAdapter(IChatID.class);
return (cID == null) ? id.getName() : cID.getUsername();
}
/**
* @since 2.3
*/
public synchronized void openTab(IChatMessageSender messageSender, ITypingMessageSender typingSender, ID localID, ID remoteID) {
openTab(messageSender, typingSender, localID, remoteID, getLocalName(remoteID));
}
/**
* @since 2.4
*/
public synchronized void selectTab(IChatMessageSender messageSender, ITypingMessageSender typingSender, ID localID, ID userID, String localName) {
ChatTab tab = getTab(messageSender, typingSender, localID, userID, localName);
tabFolder.setSelection(tab.item);
tab.inputText.setFocus();
}
public synchronized void selectTab(IChatMessageSender messageSender, ITypingMessageSender typingSender, ID localID, ID userID) {
selectTab(messageSender, typingSender, localID, userID, getLocalName(userID));
}
/**
* Display a chat message from a remote user in their designated chat box.
*
* @param message
* a chat message that has been sent to the local user
*/
public synchronized void showMessage(IChatMessage message) {
Assert.isNotNull(message);
ID remoteID = message.getFromID();
ChatTab tab = (ChatTab) tabs.get(remoteID);
if (tab != null) {
tab.append(remoteID, message.getBody());
}
}
public void setFocus() {
CTabItem item = tabFolder.getSelection();
if (item != null) {
for (Iterator it = tabs.values().iterator(); it.hasNext();) {
ChatTab tab = (ChatTab) it.next();
if (tab.item == item) {
tab.inputText.setFocus();
break;
}
}
}
}
private class ChatTab {
private CTabItem item;
private StyledText chatText;
private Text inputText;
private IChatMessageSender icms;
private ITypingMessageSender itms;
private ID localID;
private ID remoteID;
private boolean sendTyping = false;
private boolean isFirstMessage = true;
private String localName;
private ChatTab(IChatMessageSender icms, ITypingMessageSender itms, ID localID, ID remoteID, String localName) {
this.icms = icms;
this.itms = itms;
this.localID = localID;
this.remoteID = remoteID;
this.localName = localName;
constructWidgets();
addListeners();
}
private void addListeners() {
inputText.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
switch (e.keyCode) {
case SWT.CR :
case SWT.KEYPAD_CR :
if (e.stateMask == 0) {
String text = inputText.getText();
inputText.setText(""); //$NON-NLS-1$
try {
if (!text.equals("")) { //$NON-NLS-1$
icms.sendChatMessage(remoteID, text);
}
append(localID, text);
} catch (ECFException ex) {
String message = ex.getMessage();
if (message == null || message.equals("")) { //$NON-NLS-1$
message = ex.getStatus().getMessage();
if (message == null || message.equals("")) { //$NON-NLS-1$
message = ex.getCause().getMessage();
}
}
if (message == null || message.equals("")) { //$NON-NLS-1$
setContentDescription(Messages.MessagesView_CouldNotSendMessage);
} else {
setContentDescription(NLS.bind(Messages.MessagesView_CouldNotSendMessageCauseKnown, message));
}
}
e.doit = false;
sendTyping = false;
}
break;
}
}
});
inputText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (!sendTyping && itms != null) {
sendTyping = true;
try {
itms.sendTypingMessage(remoteID, true, null);
} catch (ECFException ex) {
// ignored since this is not really that important
return;
}
}
}
});
ScrollBar vscrollBar = chatText.getVerticalBar();
if (vscrollBar != null) {
vscrollBar.addSelectionListener(scrollSelectionListener);
chatText.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
ScrollBar bar = chatText.getVerticalBar();
if (bar != null)
bar.removeSelectionListener(scrollSelectionListener);
}
});
}
}
private SelectionListener scrollSelectionListener = new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
// do nothing
}
public void widgetSelected(SelectionEvent e) {
if (shouldScrollToEnd(chatText))
boldTabTitle(false);
}
};
private boolean shouldScrollToEnd(StyledText chatText1) {
Point locAtEnd = chatText1.getLocationAtOffset(chatText1.getText().length());
Rectangle bounds = chatText1.getBounds();
if (locAtEnd.y > bounds.height + 5)
return false;
return true;
}
private void append(ID fromID, String body) {
boolean scrollToEnd = shouldScrollToEnd(chatText);
if (!isFirstMessage) {
chatText.append(Text.DELIMITER);
}
int length = chatText.getCharCount();
String name = localName;
if (fromID.equals(remoteID)) {
if (showTimestamps) {
chatText.append(FORMATTER.format(new Date(System.currentTimeMillis())) + ' ');
chatText.setStyleRange(new StyleRange(length, 13, redColor, null));
length = chatText.getCharCount();
}
chatText.append(name + ": " + body); //$NON-NLS-1$
chatText.setStyleRange(new StyleRange(length, name.length() + 1, redColor, null, SWT.BOLD));
setContentDescription(""); //$NON-NLS-1$
if (isFirstMessage) {
final MessageNotificationPopup popup = new MessageNotificationPopup(getSite().getWorkbenchWindow(), tabFolder.getShell(), remoteID);
popup.setContent(name, body);
popup.open();
new UIJob(tabFolder.getDisplay(), "Close Popup Job") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
Shell shell = popup.getShell();
if (shell != null && !shell.isDisposed()) {
popup.close();
}
return Status.OK_STATUS;
}
}.schedule(5000);
}
} else {
if (showTimestamps) {
chatText.append(FORMATTER.format(new Date(System.currentTimeMillis())) + ' ');
chatText.setStyleRange(new StyleRange(length, 13, blueColor, null));
length = chatText.getCharCount();
}
chatText.append(name + ": " + body); //$NON-NLS-1$
chatText.setStyleRange(new StyleRange(length, name.length() + 1, blueColor, null, SWT.BOLD));
}
isFirstMessage = false;
if (scrollToEnd)
chatText.invokeAction(ST.TEXT_END);
boldTabTitle(!scrollToEnd);
}
private StyledText createStyledTextWidget(Composite parent) {
try {
SourceViewer result = new SourceViewer(parent, null, null, true, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
result.configure(new TextSourceViewerConfiguration(EditorsUI.getPreferenceStore()));
result.setDocument(new Document());
return result.getTextWidget();
} catch (Exception e) {
Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING, Messages.MessagesView_WARNING_HYPERLINKING_NOT_AVAILABLE, e));
return new StyledText(parent, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
} catch (NoClassDefFoundError e) {
Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING, Messages.MessagesView_WARNING_HYPERLINKING_NOT_AVAILABLE, e));
return new StyledText(parent, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
}
}
private void boldTabTitle(boolean bold) {
Font oldFont = item.getFont();
FontData[] fd = oldFont.getFontData();
item.setFont(new Font(oldFont.getDevice(), fd[0].getName(), fd[0].getHeight(), (bold) ? SWT.BOLD : SWT.NORMAL));
}
private void constructWidgets() {
item = new CTabItem(tabFolder, SWT.NONE);
Composite parent = new Composite(tabFolder, SWT.NONE);
parent.setLayout(new FillLayout());
SashForm sash = new SashForm(parent, SWT.VERTICAL);
chatText = createStyledTextWidget(sash);
inputText = new Text(sash, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
sash.setWeights(WEIGHTS);
Menu menu = new Menu(chatText);
MenuItem mi = new MenuItem(menu, SWT.PUSH);
mi.setText(Messages.MessagesView_Copy);
mi.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(org.eclipse.ui.ISharedImages.IMG_TOOL_COPY));
mi.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String text = chatText.getSelectionText();
if (!text.equals("")) { //$NON-NLS-1$
chatText.copy();
}
}
});
mi = new MenuItem(menu, SWT.PUSH);
mi.setText(Messages.MessagesView_SelectAll);
mi.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
chatText.selectAll();
}
});
chatText.setMenu(menu);
item.setControl(parent);
item.setText(localName);
}
private void showIsTyping(boolean isTyping) {
setContentDescription(isTyping ? NLS.bind(Messages.MessagesView_TypingNotification, localName) : ""); //$NON-NLS-1$
}
}
}