diff options
author | Yana Stamcheva <yana@jitsi.org> | 2012-09-23 17:23:33 +0000 |
---|---|---|
committer | Yana Stamcheva <yana@jitsi.org> | 2012-09-23 17:23:33 +0000 |
commit | 86a0325947316d5e4b229a7f178f87a4f12be52c (patch) | |
tree | c83d09fad6da0566b0f4f310ce651da5660c9674 /resources/styles | |
parent | 9dfae463286f44520f3ce9dda3e4ea01ff35f852 (diff) | |
download | jitsi-86a0325947316d5e4b229a7f178f87a4f12be52c.zip jitsi-86a0325947316d5e4b229a7f178f87a4f12be52c.tar.gz jitsi-86a0325947316d5e4b229a7f178f87a4f12be52c.tar.bz2 |
New enhanced interface.
Diffstat (limited to 'resources/styles')
-rw-r--r-- | resources/styles/defaultStyle.css | 14 | ||||
-rw-r--r-- | resources/styles/defaultStyle.txt | 65 | ||||
-rw-r--r-- | resources/styles/stylebackup.txt | 1949 |
3 files changed, 2023 insertions, 5 deletions
diff --git a/resources/styles/defaultStyle.css b/resources/styles/defaultStyle.css index a737e43..356cecd 100644 --- a/resources/styles/defaultStyle.css +++ b/resources/styles/defaultStyle.css @@ -1,6 +1,6 @@ h1 { - background-color: #c6d0e1; + background-color: #6a6868; margin: 0px; text-align: center; font-weight: bold; @@ -10,14 +10,18 @@ h1 h2 { margin: 0px; - font-size: 100%; - color: #ef7b1e; + padding-top: 4px; + padding-left: 10px; + font-size: 10px; + color: #488fe7; } h3 { margin: 0px; - font-size: 100%; + padding-top: 4px; + padding-left: 10px; + font-size: 10px; color: #2e538b; } @@ -47,8 +51,8 @@ h6 p { margin: 0px; + width: 100%; font-size: 100%; - font-weight: bold; color: #62BD80; } diff --git a/resources/styles/defaultStyle.txt b/resources/styles/defaultStyle.txt new file mode 100644 index 0000000..60badf5 --- /dev/null +++ b/resources/styles/defaultStyle.txt @@ -0,0 +1,65 @@ +h1 +{ + background-color: #6a6868; + margin: 0px; + text-align: center; + font-weight: bold; + font-size: 100%; +} + +h2 +{ + margin: 0px; + font-size: 100%; + color: #488fe7; +} + +h3 +{ + margin: 0px; + font-size: 100%; + color: #2e538b; +} + +h4 +{ + margin: 0px; + font-size: 100%; + font-weight: bold; + color: #FFC875; +} + +h5 +{ + margin: 0px; + font-size: 100%; + font-weight: bold; + color: #CC0000; +} + +h6 +{ + margin: 0px; + font-size: 100%; + font-weight: bold; + color: #990000; +} +p +{ + margin: 0px; + width: 100%; + font-size: 100%; + font-weight: bold; + color: #62BD80; +} + +div +{ + font-size: 100%; + color: #000000; +} + +h7 +{ + background-color: #FFF4AB; +} diff --git a/resources/styles/stylebackup.txt b/resources/styles/stylebackup.txt new file mode 100644 index 0000000..acedf98 --- /dev/null +++ b/resources/styles/stylebackup.txt @@ -0,0 +1,1949 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main.chat; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.Map; +import java.util.regex.*; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import javax.swing.text.html.*; +import javax.swing.text.html.HTML.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.customcontrols.*; +import net.java.sip.communicator.impl.gui.main.chat.history.*; +import net.java.sip.communicator.impl.gui.main.chat.menus.*; +import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.replacement.*; +import net.java.sip.communicator.service.replacement.smilies.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; +import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.util.swing.SwingWorker; + +/** + * The <tt>ChatConversationPanel</tt> is the panel, where all sent and received + * messages appear. All data is stored in an HTML document. An external CSS file + * is applied to the document to provide the look&feel. All smileys and link + * strings are processed and finally replaced by corresponding images and HTML + * links. + * + * @author Yana Stamcheva + * @author Lubomir Marinov + * @author Adam Netocny + */ +public class ChatConversationPanel + extends SCScrollPane + implements HyperlinkListener, + MouseListener, + ClipboardOwner, + Skinnable +{ + /** + * The <tt>Logger</tt> used by the <tt>ChatConversationPanel</tt> class and + * its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(ChatConversationPanel.class); + + /** + * The closing tag of the <code>PLAINTEXT</code> HTML element. + */ + private static final String END_PLAINTEXT_TAG = "</PLAINTEXT>"; + + /** + * The opening tag of the <code>PLAINTEXT</code> HTML element. + */ + private static final String START_PLAINTEXT_TAG = "<PLAINTEXT>"; + + /** + * The regular expression (in the form of compiled <tt>Pattern</tt>) which + * matches URLs for the purposed of turning them into links. + */ + private static final Pattern URL_PATTERN + = Pattern.compile( + "(" + + "(\\bwww\\.[^\\s<>\"]+\\.[^\\s<>\"]+/*[?#]*(\\w+[&=;?]\\w+)*\\b)" // wwwURL + + "|" + + "(\\bjitsi\\:[^\\s<>\"]+\\.[^\\s<>\"]*\\b)" // internalURL + + "|" + + "(\\b\\w+://[^\\s<>\"]+/*[?#]*(\\w+[&=;?]\\w+)*\\b)" // protocolURL + + ")"); + + /** + * List for observing text messages. + */ + private Set<ChatLinkClickedListener> chatLinkClickedListeners = + new HashSet<ChatLinkClickedListener>(); + + /** + * The component rendering chat conversation panel text. + */ + private final JTextPane chatTextPane = new MyTextPane(); + + /** + * The editor kit used by the text component. + */ + private final HTMLEditorKit editorKit; + + /** + * The document used by the text component. + */ + private HTMLDocument document; + + /** + * The parent container. + */ + private final ChatConversationContainer chatContainer; + + /** + * The menu shown on right button mouse click. + */ + private final ChatRightButtonMenu rightButtonMenu; + + /** + * The currently shown href. + */ + private String currentHref; + + /** + * The copy link item, contained in the right mouse click menu. + */ + private final JMenuItem copyLinkItem; + + /** + * The open link item, contained in the right mouse click menu. + */ + private final JMenuItem openLinkItem; + + /** + * The right mouse click menu separator. + */ + private final JSeparator copyLinkSeparator = new JSeparator(); + + /** + * The timestamp of the last incoming message. + */ + private long lastIncomingMsgTimestamp; + + /** + * Indicates if this component is rendering a history conversation. + */ + private final boolean isHistory; + + /** + * The html text content type. + */ + public static final String HTML_CONTENT_TYPE = "text/html"; + + /** + * The plain text content type. + */ + public static final String TEXT_CONTENT_TYPE = "text/plain"; + + /** + * The indicator which determines whether an automatic scroll to the bottom + * of {@link #chatTextPane} is to be performed. + */ + private boolean scrollToBottomIsPending = false; + + private final static String INCOMING_MESSAGE_IMAGE_PATH + = GuiActivator.getResources().getImageURL( + "service.gui.lookandfeel.INCOMING_MESSAGE_BACKGROUND").toString(); + + /** + * The implementation of the routine which scrolls {@link #chatTextPane} to its + * bottom. + */ + private final Runnable scrollToBottomRunnable = new Runnable() + { + /* + * Implements Runnable#run(). + */ + public void run() + { + JScrollBar verticalScrollBar = getVerticalScrollBar(); + + if (verticalScrollBar != null) + { + // We need to call both methods in order to be sure to scroll + // to the bottom of the text even when the user has selected + // something (changed the caret) or when a new tab has been + // added or the window has been resized. + verticalScrollBar.setValue(verticalScrollBar.getMaximum()); + chatTextPane.setCaretPosition(document.getLength()); + } + } + }; + + /** + * Creates an instance of <tt>ChatConversationPanel</tt>. + * + * @param chatContainer The parent <tt>ChatConversationContainer</tt>. + */ + public ChatConversationPanel(ChatConversationContainer chatContainer) + { + editorKit = new SIPCommHTMLEditorKit(this); + + this.chatContainer = chatContainer; + + isHistory = (chatContainer instanceof HistoryWindow); + + this.rightButtonMenu = new ChatRightButtonMenu(this); + + this.document = (HTMLDocument) editorKit.createDefaultDocument(); + + this.chatTextPane.setEditorKitForContentType("text/html", editorKit); + this.chatTextPane.setEditorKit(editorKit); + this.chatTextPane.setEditable(false); + this.chatTextPane.setDocument(document); + this.chatTextPane.setDragEnabled(true); + + chatTextPane.putClientProperty( + JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + Constants.loadSimpleStyle( + document.getStyleSheet(), chatTextPane.getFont()); + + this.chatTextPane.addHyperlinkListener(this); + this.chatTextPane.addMouseListener(this); + this.chatTextPane.setCursor( + Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + + this.setWheelScrollingEnabled(true); + + this.setViewportView(chatTextPane); + + this.setBorder(null); + + this.setHorizontalScrollBarPolicy( + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + ToolTipManager.sharedInstance().registerComponent(chatTextPane); + + String copyLinkString + = GuiActivator.getResources().getI18NString("service.gui.COPY_LINK"); + + copyLinkItem + = new JMenuItem(copyLinkString, + new ImageIcon(ImageLoader.getImage(ImageLoader.COPY_ICON))); + + copyLinkItem.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + StringSelection stringSelection = new StringSelection( + currentHref); + Clipboard clipboard = Toolkit.getDefaultToolkit() + .getSystemClipboard(); + clipboard.setContents(stringSelection, + ChatConversationPanel.this); + } + }); + + String openLinkString + = GuiActivator.getResources().getI18NString( + "service.gui.OPEN_IN_BROWSER"); + + openLinkItem = + new JMenuItem( + openLinkString, + new ImageIcon(ImageLoader.getImage(ImageLoader.BROWSER_ICON))); + + openLinkItem.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + GuiActivator.getBrowserLauncher().openURL(currentHref); + + // after opening the link remove the currentHref to avoid + // clicking on the window to gain focus to open the link again + ChatConversationPanel.this.currentHref = ""; + } + }); + + openLinkItem.setMnemonic( + GuiActivator.getResources().getI18nMnemonic( + "service.gui.OPEN_IN_BROWSER")); + + copyLinkItem.setMnemonic( + GuiActivator.getResources().getI18nMnemonic( + "service.gui.COPY_LINK")); + + /* + * When we append a new message (regardless of whether it is a string or + * an UI component), we want to make it visible in the viewport of this + * JScrollPane so that the user can see it. + */ + ComponentListener componentListener = new ComponentAdapter() + { + @Override + public void componentResized(ComponentEvent e) + { + synchronized (scrollToBottomRunnable) + { + if (!scrollToBottomIsPending) + return; + scrollToBottomIsPending = false; + + /* + * Yana Stamcheva, pointed out that Java 5 (on Linux only?) + * needs invokeLater for JScrollBar. + */ + SwingUtilities.invokeLater(scrollToBottomRunnable); + } + } + }; + + chatTextPane.addComponentListener(componentListener); + getViewport().addComponentListener(componentListener); + } + + /** + * Overrides Component#setBounds(int, int, int, int) in order to determine + * whether an automatic scroll of #chatTextPane to its bottom will be + * necessary at a later time in order to keep its vertical scroll bar to its + * bottom after the realization of the resize if it is at its bottom before + * the resize. + */ + @Override + public void setBounds(int x, int y, int width, int height) + { + synchronized (scrollToBottomRunnable) + { + JScrollBar verticalScrollBar = getVerticalScrollBar(); + + if (verticalScrollBar != null) + { + BoundedRangeModel verticalScrollBarModel + = verticalScrollBar.getModel(); + + if ((verticalScrollBarModel.getValue() + + verticalScrollBarModel.getExtent() + >= verticalScrollBarModel.getMaximum()) + || !verticalScrollBar.isVisible()) + scrollToBottomIsPending = true; + } + } + + super.setBounds(x, y, width, height); + } + + /** + * Initializes the editor by adding a header containing the date. + * TODO: remove if not used anymore + */ +// private void initEditor() +// { +// Element root = this.document.getDefaultRootElement(); +// +// Date date = new Date(System.currentTimeMillis()); +// +// String chatHeader = "<h1>" + GuiUtils.formatDate(date) + " " + "</h1>"; +// +// try +// { +// this.document.insertAfterStart(root, chatHeader); +// } +// catch (BadLocationException e) +// { +// logger.error("Insert in the HTMLDocument failed.", e); +// } +// catch (IOException e) +// { +// logger.error("Insert in the HTMLDocument failed.", e); +// } +// } + + /** + * Retrieves the contents of the sent message with the given ID. + * + * @param messageUID The ID of the message to retrieve. + * @return The contents of the message, or null if the message is not found. + */ + public String getMessageContents(String messageUID) + { + Element root = document.getDefaultRootElement(); + Element e = document.getElement(root, Attribute.ID, messageUID); + if (e == null) + { + logger.warn("Could not find message with ID" + messageUID); + return null; + } + + int elemLen = e.getEndOffset() - e.getStartOffset(); + String res = null; + try + { + res = document.getText(e.getStartOffset(), elemLen); + } + catch (BadLocationException exc) + { + logger.warn("Could not get message contents for message " + + "with ID" + messageUID, exc); + } + return res; + } + + /** + * Creates a tag that shows the last edit time of a message, in the format + * (Edited at ...). + * If <tt>date < 0</tt>, returns an empty tag that serves as a placeholder + * for future corrections of this message. + * + * @param messageUID The ID of the edited message. + * @param date The date when the message was last edited, or -1 to generate + * an empty tag. + * @return The string representation of the tag. + */ + private String generateEditedAtTag(String messageUID, long date) + { + StringBuilder res = new StringBuilder(); + // Use a <cite /> tag here as most of the other inline tags (e.g. h1-7, + // b, i) cause different problems when used in setOuterHTML. + res.append("<cite id='"); + res.append(messageUID); + res.append("-editedAt'> "); + if (date > 0) + { + res.append(" "); + String contents = GuiActivator.getResources().getI18NString( + "service.gui.EDITED_AT", + new String[] { GuiUtils.formatTime(date) } + ); + res.append(contents); + } + res.append("</cite>"); + return res.toString(); + } + + /** + * Processes the message given by the parameters. + * + * @param chatMessage the message + * @param keyword a substring of <tt>chatMessage</tt> to be highlighted upon + * display of <tt>chatMessage</tt> in the UI + * @return the processed message + */ + public String processMessage(ChatMessage chatMessage, String keyword) + { + String contactName = chatMessage.getContactName(); + String contactDisplayName = chatMessage.getContactDisplayName(); + if (contactDisplayName == null + || contactDisplayName.trim().length() <= 0) + contactDisplayName = contactName; + + String contentType = chatMessage.getContentType(); + long date = chatMessage.getDate(); + String messageType = chatMessage.getMessageType(); + String messageTitle = chatMessage.getMessageTitle(); + String message = chatMessage.getMessage(); + String messageUID = chatMessage.getMessageUID(); + + String msgID = "message"; + String msgHeaderID = "messageHeader"; + String chatString = ""; + String endHeaderTag = ""; + String dateString = getDateString(date); + String idAttr = messageUID == null ? "" : " id='" + messageUID + "'"; + String dateAttr = " date='" + date + "'"; + String editedAtTag = generateEditedAtTag(messageUID, -1); + + String startHistoryDivTag + = "<DIV identifier=\"" + msgID + "\" style=\"color:#707070;\">"; + String startSystemDivTag + = "<DIV identifier=\"systemMessage\" style=\"color:#627EB7;\">"; + String endDivTag = "</DIV>"; + + String startPlainTextTag; + String endPlainTextTag; + + if (HTML_CONTENT_TYPE.equals(contentType)) + { + startPlainTextTag = ""; + endPlainTextTag = ""; + } + else + { + startPlainTextTag = START_PLAINTEXT_TAG; + endPlainTextTag = END_PLAINTEXT_TAG; + } + + if (messageType.equals(Chat.INCOMING_MESSAGE)) + { + this.lastIncomingMsgTimestamp = System.currentTimeMillis(); + + StringBuffer headerBuffer = new StringBuffer(); + + headerBuffer.append("<h2 identifier=\"" + msgHeaderID + "\""); + headerBuffer.append(dateAttr + ">"); + headerBuffer.append("<a style=\"color:#488fe7;"); + headerBuffer.append("font-weight:bold;"); + headerBuffer.append("text-decoration:none;\" "); + headerBuffer.append("href=\"" + contactName + "\">"); + headerBuffer.append(dateString + contactDisplayName + " at " + + GuiUtils.formatTime(date) + editedAtTag); + headerBuffer.append("</a></h2>"); + + chatString = createIncomingMessageTag(msgID + "\"" + idAttr, + headerBuffer.toString(), + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag); +System.out.println("CHAT STRING OSHTE TUUUUUK=======" + chatString); +// chatString = "<h2 identifier=\"" + msgHeaderID + "\"" +// + " date=\"" + date + "\">" +// + "<a style=\"color:#488fe7;" +// + "font-weight:bold;" +// + "text-decoration:none;\" " +// + "href=\"" + contactName + "\">"; +// +// endHeaderTag = "</a></h2>"; + +// String startDivTag = "<DIV identifier=\"" + msgID +// + "\" style=\""+ createIncomingMessageStyle()+"\">"; +// +// chatString +// += dateString + contactDisplayName + " at " +// + GuiUtils.formatTime(date) +// + endHeaderTag + startDivTag + startPlainTextTag +// + formatMessage(message, contentType, keyword) +// + endPlainTextTag + endDivTag; + + } + else if (messageType.equals(Chat.SMS_MESSAGE)) + { + chatString = "<h2 identifier=\"" + + msgHeaderID + + "\" date=\"" + + date + "\">"; + + endHeaderTag = "</h2>"; + + String startDivTag = "<DIV identifier=\"" + msgID + + "\" style=\""+ createSmsMessageStyle()+"\">"; + + chatString + += "SMS: " + dateString + contactName + " at " + + GuiUtils.formatTime(date) + endHeaderTag + startDivTag + + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag + endDivTag; + } + else if (messageType.equals(Chat.OUTGOING_MESSAGE)) + { + chatString = "<h3 identifier=\"" + msgHeaderID + "\"" + + dateAttr + ">" + + "<a style=\"color:#6a6868;" + + "font-weight:bold;" + + "text-decoration:none;\" " + + "href=\"" + contactName + "\">"; + + endHeaderTag = "</a></h3>"; + + String startDivTag = "<DIV identifier=\"" + msgID + + "\" style=\""+ createOutgoingMessageStyle()+"\">"; + + chatString + += dateString + contactDisplayName + " at " + + GuiUtils.formatTime(date) + editedAtTag + endHeaderTag + + startDivTag + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag + endDivTag; + } + else if (messageType.equals(Chat.STATUS_MESSAGE)) + { + chatString = "<h4 identifier=\"statusMessage\" date=\"" + + date + "\">"; + endHeaderTag = "</h4>"; + + chatString + += GuiUtils.formatTime(date) + " " + contactName + " " + message + + endHeaderTag; + } + else if (messageType.equals(Chat.ACTION_MESSAGE)) + { + chatString = "<p identifier=\"actionMessage\" date=\"" + + date + "\">"; + endHeaderTag = "</p>"; + + chatString += "* " + GuiUtils.formatTime(date) + + " " + contactName + " " + + message + + endHeaderTag; + } + else if (messageType.equals(Chat.SYSTEM_MESSAGE)) + { + chatString + += startSystemDivTag + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag + endDivTag; + } + else if (messageType.equals(Chat.ERROR_MESSAGE)) + { + chatString = "<h6 identifier=\"" + + msgHeaderID + + "\" date=\"" + + date + "\">"; + + endHeaderTag = "</h6>"; + + String errorIcon = "<IMG SRC='" + + ImageLoader.getImageUri(ImageLoader.EXCLAMATION_MARK) + + "' </IMG>"; + + chatString += errorIcon + + messageTitle + + endHeaderTag + "<h5>" + message + "</h5>"; + } + else if (messageType.equals(Chat.HISTORY_INCOMING_MESSAGE)) + { + chatString = "<h2 identifier=\"" + msgHeaderID + "\"" + + dateAttr + ">" + + "<a style=\"color:#488fe7;" + + "font-weight:bold;" + + "text-decoration:none;\" " + + "href=\"" + contactName + "\">"; + + endHeaderTag = "</a></h2>"; + + chatString + += dateString + contactDisplayName + + " at " + GuiUtils.formatTime(date) + endHeaderTag + + editedAtTag + startHistoryDivTag + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag + endDivTag; + } + else if (messageType.equals(Chat.HISTORY_OUTGOING_MESSAGE)) + { + chatString = "<h3 identifier=\"" + msgHeaderID + "\"" + + dateAttr + ">" + + "<a style=\"color:#6a6868;" + + "font-weight:bold;" + + "text-decoration:none;\" " + + "href=\"" + contactName + "\">"; + + endHeaderTag = "</h3>"; + + chatString + += dateString + + contactDisplayName + " at " + GuiUtils.formatTime(date) + + editedAtTag + endHeaderTag + + startHistoryDivTag + startPlainTextTag + + formatMessage(message, contentType, keyword) + + endPlainTextTag + endDivTag; + } + + return chatString; + } + + /** + * Processes the message given by the parameters. + * + * @param chatMessage the message. + * @return the formatted message + */ + public String processMessage(ChatMessage chatMessage) + { + return processMessage(chatMessage, null); + } + + /** + * Replaces the contents of the message with ID of the corrected message + * specified in chatMessage, with this message. + * + * @param chatMessage A <tt>ChatMessage</tt> that contains all the required + * information to correct the old message. + */ + public void correctMessage(ChatMessage chatMessage) + { + String correctedUID = chatMessage.getCorrectedMessageUID(); + Element root = document.getDefaultRootElement(); + Element e = document.getElement(root, Attribute.ID, correctedUID); + if (e == null) + { + logger.warn("Could not find message with ID " + correctedUID); + return; + } + int len = e.getEndOffset() - e.getStartOffset(); + + StringBuilder newContents = new StringBuilder(); + String bgColor = GuiActivator.getResources().getColorString( + "service.gui.CHAT_EDIT_MESSAGE_BACKGROUND"); + newContents.append("<div identifier='message' id='"); + newContents.append(chatMessage.getMessageUID()); + newContents.append("' bgcolor='"); + newContents.append(bgColor); + newContents.append("'>"); + if (chatMessage.getContentType().equals(TEXT_CONTENT_TYPE)) + { + newContents.append(START_PLAINTEXT_TAG); + newContents.append(chatMessage.getMessage()); + newContents.append(END_PLAINTEXT_TAG); + } + else + { + newContents.append(chatMessage.getMessage()); + } + newContents.append("</div>"); + + Element header = document.getElement(root, Attribute.ID, + correctedUID + "-editedAt"); + + try + { + if (header != null) + { + String newHeaderContents = generateEditedAtTag( + chatMessage.getMessageUID(), chatMessage.getDate()); + document.setOuterHTML(header, newHeaderContents); + } + document.setOuterHTML(e, newContents.toString()); + } + catch (BadLocationException ex) + { + logger.error("Could not replace chat message", ex); + } + catch (IOException ex) + { + logger.error("Could not replace chat message", ex); + } + } + + /** + * Appends the given string at the end of the contained in this panel + * document. + * + * @param chatString the string to append + */ + public void appendMessageToEnd(String chatString, String contentType) + { + synchronized (scrollToBottomRunnable) + { + Element root = document.getDefaultRootElement(); + +// Need to call explicitly scrollToBottom, because for some +// reason the componentResized event isn't fired every time we +// add text. +// Replaced by the code on line: 573. +// +// scrollToBottomIsPending = true; +System.out.println("CHAT STRING+=========" + chatString); + try + { + document + .insertAfterEnd( + root.getElement(root.getElementCount() - 1), + chatString); + + // Need to call explicitly scrollToBottom, because for some + // reason the componentResized event isn't fired every time we + // add text. + SwingUtilities.invokeLater(scrollToBottomRunnable); + } + catch (BadLocationException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + catch (IOException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + if (!isHistory) + ensureDocumentSize(); + + // Process replacements. + final Element elem; + /* + * Check to make sure element isn't the first element in the HTML + * document. + */ + if (!(root.getElementCount() < 2)) + { + elem = root.getElement(root.getElementCount() - 2); + } + else + elem = root.getElement(1); + + /* + * Replacements will be processed only if it is enabled in the + * property + */ + if (GuiActivator.getConfigurationService().getBoolean( + ReplacementProperty.REPLACEMENT_ENABLE, true) + || GuiActivator.getConfigurationService().getBoolean( + ReplacementProperty.getPropertyName("SMILEY"), true)) + { + processReplacement(elem, chatString, contentType); + } + } + } + + /** + * Formats the given message. Processes the messages and replaces links to + * video/image sources with their previews or any other substitution. Spawns + * a separate thread for replacement. + * + * @param elem the element in the HTML Document. + * @param chatString the message. + */ + private void processReplacement(final Element elem, + final String chatString, + final String contentType) + { + final String chatFinal = chatString; + + SwingWorker worker = new SwingWorker() + { + public Object construct() throws Exception + { + String temp = "", msgStore = chatFinal; + + boolean isEnabled + = GuiActivator.getConfigurationService().getBoolean( + ReplacementProperty.REPLACEMENT_ENABLE, true); + + Map<String, ReplacementService> listSources + = GuiActivator.getReplacementSources(); + + Iterator<Entry<String, ReplacementService>> entrySetIter + = listSources.entrySet().iterator(); + + for (int i = 0; i < listSources.size(); i++) + { + Map.Entry<String, ReplacementService> entry + = entrySetIter.next(); + + ReplacementService source = entry.getValue(); + + boolean isSmiley + = source instanceof SmiliesReplacementService; + + if (!(GuiActivator.getConfigurationService().getBoolean( + ReplacementProperty.getPropertyName(source + .getSourceName()), true) && (isEnabled || isSmiley))) + continue; + + String sourcePattern = source.getPattern(); + Pattern p = Pattern.compile(sourcePattern, + Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + + Matcher m = p.matcher(msgStore); + + String startPlainTextTag = ""; + String endPlainTextTag = ""; + + if (!HTML_CONTENT_TYPE.equals(contentType)) + { + startPlainTextTag = START_PLAINTEXT_TAG; + endPlainTextTag = END_PLAINTEXT_TAG; + } + + int count = 0, startPos = 0; + StringBuffer msgBuff = new StringBuffer(); + + while (m.find()) + { + count++; + msgBuff.append(msgStore.substring(startPos, m.start())); + startPos = m.end(); + + temp = source.getReplacement(m.group()); + + if(!temp.equals(m.group(0)) || source.getSourceName() + .equals("DIRECTIMAGE")) + { + if(isSmiley) + { + msgBuff.append(endPlainTextTag); + msgBuff.append("<IMG SRC=\""); + } + else + { + msgBuff.append( + "<IMG HEIGHT=\"90\" WIDTH=\"120\" SRC=\""); + } + + msgBuff.append(temp); + msgBuff.append("\" BORDER=\"0\" ALT=\""); + msgBuff.append(m.group(0)); + msgBuff.append("\"></IMG>"); + + if(isSmiley) + msgBuff.append(startPlainTextTag); + } + else + { + msgBuff.append( + msgStore.substring(m.start(), m.end())); + } + } + + msgBuff.append(msgStore.substring(startPos)); + + /* + * replace the msgStore variable with the current replaced + * message before next iteration + */ + if (!msgBuff.toString().equals(msgStore)) + { + msgStore = msgBuff.toString(); + } + } + + if (!msgStore.equals(chatFinal)) + { + synchronized (scrollToBottomRunnable) + { + scrollToBottomIsPending = true; + document.setOuterHTML(elem, msgStore.toString() + .substring(msgStore.indexOf("<DIV"))); + } + } + return ""; + } + }; + worker.start(); + } + + /** + * Ensures that the document won't become too big. When the document reaches + * a certain size the first message in the page is removed. + */ + private void ensureDocumentSize() + { + if (document.getLength() > Chat.CHAT_BUFFER_SIZE) + { + int msgElementCount = 0; + + Element firstMsgElement = null; + + int firstMsgIndex = 0; + + Element rootElement = this.document.getDefaultRootElement(); + // Count how many messages we have in the document. + for (int i = 0; i < rootElement.getElementCount(); i++) + { + String idAttr = (String) rootElement.getElement(i) + .getAttributes().getAttribute("identifier"); + + if(idAttr != null + && (idAttr.equals("message") + || idAttr.equals("statusMessage") + || idAttr.equals("systemMessage"))) + { + if(firstMsgElement == null) + { + firstMsgElement = rootElement.getElement(i); + firstMsgIndex = i; + } + + msgElementCount++; + } + } + + // If we doesn't have any known elements in the document or if we + // have only one long message we don't want to remove it. + if(firstMsgElement == null || msgElementCount < 2) + return; + + try + { + // Remove the header of the message if such exists. + if(firstMsgIndex > 0) + { + Element headerElement = rootElement.getElement(firstMsgIndex - 1); + + String idAttr = (String) headerElement + .getAttributes().getAttribute("identifier"); + + if(idAttr != null && idAttr.equals("messageHeader")) + { + this.document.remove(headerElement.getStartOffset(), + headerElement.getEndOffset() + - headerElement.getStartOffset()); + } + } + + // Remove the message itself. + this.document.remove(firstMsgElement.getStartOffset(), + firstMsgElement.getEndOffset() + - firstMsgElement.getStartOffset()); + } + catch (BadLocationException e) + { + logger.error("Error removing messages from chat: ", e); + } + } + } + + /** + * Highlights keywords searched in the history. + * + * @param message the source message + * @param contentType the content type + * @param keyword the searched keyword + * @return the formatted message + */ + private String processKeyword( String message, + String contentType, + String keyword) + { + String startPlainTextTag; + String endPlainTextTag; + + if (HTML_CONTENT_TYPE.equals(contentType)) + { + startPlainTextTag = ""; + endPlainTextTag = ""; + } + else + { + startPlainTextTag = START_PLAINTEXT_TAG; + endPlainTextTag = END_PLAINTEXT_TAG; + } + + Matcher m + = Pattern.compile(Pattern.quote(keyword), Pattern.CASE_INSENSITIVE) + .matcher(message); + StringBuffer msgBuffer = new StringBuffer(); + int prevEnd = 0; + + while (m.find()) + { + msgBuffer.append(message.substring(prevEnd, m.start())); + prevEnd = m.end(); + + String keywordMatch = m.group().trim(); + + msgBuffer.append(endPlainTextTag); + msgBuffer.append("<b>"); + msgBuffer.append(keywordMatch); + msgBuffer.append("</b>"); + msgBuffer.append(startPlainTextTag); + } + + /* + * If the keyword didn't match, let the outside world be able to + * discover it. + */ + if (prevEnd == 0) + return message; + + msgBuffer.append(message.substring(prevEnd)); + return msgBuffer.toString(); + } + + /** + * Formats the given message. Processes all smiley chars, new lines and + * links. + * + * @param message the message to be formatted + * @param contentType the content type of the message to be formatted + * @param keyword the word to be highlighted + * @return the formatted message + */ + private String formatMessage(String message, + String contentType, + String keyword) + { + // If the message content type is HTML we won't process links and + // new lines, but only the smileys. + if (!HTML_CONTENT_TYPE.equals(contentType)) + { + + /* + * We disallow HTML in plain-text messages. But processKeyword + * introduces HTML. So we'll allow HTML if processKeyword has + * introduced it in order to not break highlighting. + */ + boolean processHTMLChars; + + if ((keyword != null) && (keyword.length() != 0)) + { + String messageWithProcessedKeyword + = processKeyword(message, contentType, keyword); + + /* + * The same String instance will be returned if there was no + * keyword match. Calling #equals() is expensive so == is + * intentional. + */ + processHTMLChars = (messageWithProcessedKeyword == message); + message = messageWithProcessedKeyword; + } + else + processHTMLChars = true; + + message + = processNewLines( + processLinksAndHTMLChars(message, processHTMLChars)); + } + // If the message content is HTML, we process br and img tags. + else + { + if ((keyword != null) && (keyword.length() != 0)) + message = processKeyword(message, contentType, keyword); + message = processImgTags(processBrTags(message)); + } + + return message; + } + + /** + * Formats all links in a given message and optionally escapes special HTML + * characters such as <, >, & and " in order to prevent HTML + * injection in plain-text messages such as writing + * <code></PLAINTEXT></code>, HTML which is going to be rendered as + * such and <code><PLAINTEXT></code>. The two procedures are carried + * out in one call in order to not break URLs which contain special HTML + * characters such as &. + * + * @param message The source message string. + * @param processHTMLChars <tt>true</tt> to escape the special HTML chars; + * otherwise, <tt>false</tt> + * @return The message string with properly formatted links. + */ + private String processLinksAndHTMLChars(String message, + boolean processHTMLChars) + { + Matcher m = URL_PATTERN.matcher(message); + StringBuffer msgBuffer = new StringBuffer(); + int prevEnd = 0; + + while (m.find()) + { + String fromPrevEndToStart = message.substring(prevEnd, m.start()); + + if (processHTMLChars) + fromPrevEndToStart = processHTMLChars(fromPrevEndToStart); + msgBuffer.append(fromPrevEndToStart); + prevEnd = m.end(); + + String url = m.group().trim(); + + msgBuffer.append(END_PLAINTEXT_TAG); + msgBuffer.append("<A href=\""); + if (url.startsWith("www")) + msgBuffer.append("http://"); + msgBuffer.append(url); + msgBuffer.append("\">"); + msgBuffer.append(url); + msgBuffer.append("</A>"); + msgBuffer.append(START_PLAINTEXT_TAG); + } + + String fromPrevEndToEnd = message.substring(prevEnd); + + if (processHTMLChars) + fromPrevEndToEnd = processHTMLChars(fromPrevEndToEnd); + msgBuffer.append(fromPrevEndToEnd); + + return msgBuffer.toString(); + } + + /** + * Escapes special HTML characters such as <, >, & and " in + * the specified message. + * + * @param message the message to be processed + * @return the processed message with escaped special HTML characters + */ + private String processHTMLChars(String message) + { + return + message + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + + /** + * Formats message new lines. + * + * @param message The source message string. + * @return The message string with properly formatted new lines. + */ + private String processNewLines(String message) + { + + /* + * <br> tags are needed to visualize a new line in the html format, but + * when copied to the clipboard they are exported to the plain text + * format as ' ' and not as '\n'. + * + * See bug N4988885: + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988885 + * + * To fix this we need " " - the HTML-Code for ASCII-Character No.10 + * (Line feed). + */ + return + message + .replaceAll( + "\n", + END_PLAINTEXT_TAG + "<BR/> " + START_PLAINTEXT_TAG); + } + + /** + * Opens a link in the default browser when clicked and shows link url in a + * popup on mouseover. + * + * @param e The HyperlinkEvent. + */ + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) + { + String href = e.getDescription(); + + this.currentHref = href; + } + else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) + { + this.currentHref = ""; + } + } + + /** + * Returns the text pane of this conversation panel. + * + * @return The text pane of this conversation panel. + */ + public JTextPane getChatTextPane() + { + return chatTextPane; + } + + /** + * Returns the time of the last received message. + * + * @return The time of the last received message. + */ + public long getLastIncomingMsgTimestamp() + { + return lastIncomingMsgTimestamp; + } + + /** + * When a right button click is performed in the editor pane, a popup menu + * is opened. + * In case of the Scheme being internal, it won't open the Browser but + * instead it will trigger the forwarded action. + * + * @param e The MouseEvent. + */ + public void mouseClicked(MouseEvent e) + { + Point p = e.getPoint(); + SwingUtilities.convertPointToScreen(p, e.getComponent()); + + if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0 + || (e.isControlDown() && !e.isMetaDown())) + { + openContextMenu(p); + } + else if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0 + && currentHref != null && currentHref.length() != 0) + { + URI uri; + try + { + uri = new URI(currentHref); + } + catch (URISyntaxException e1) + { + logger.error("Invalid URL", e1); + return; + } + if(uri.getScheme().equals("jitsi")) + { + for(ChatLinkClickedListener l:chatLinkClickedListeners) + { + l.chatLinkClicked(uri); + } + } + else + GuiActivator.getBrowserLauncher().openURL(currentHref); + + // after opening the link remove the currentHref to avoid + // clicking on the window to gain focus to open the link again + this.currentHref = ""; + } + } + + /** + * Opens this panel context menu at the given point. + * + * @param p the point where to position the left-top cornet of the context + * menu + */ + private void openContextMenu(Point p) + { + if (currentHref != null && currentHref.length() != 0 + && !currentHref.startsWith("jitsi://")) + { + rightButtonMenu.insert(openLinkItem, 0); + rightButtonMenu.insert(copyLinkItem, 1); + rightButtonMenu.insert(copyLinkSeparator, 2); + } + else + { + rightButtonMenu.remove(openLinkItem); + rightButtonMenu.remove(copyLinkItem); + rightButtonMenu.remove(copyLinkSeparator); + } + + if (chatTextPane.getSelectedText() != null) + { + rightButtonMenu.enableCopy(); + } + else + { + rightButtonMenu.disableCopy(); + } + rightButtonMenu.setInvoker(chatTextPane); + rightButtonMenu.setLocation(p.x, p.y); + rightButtonMenu.setVisible(true); + } + + public void mousePressed(MouseEvent e) {} + + public void mouseReleased(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) {} + + public void mouseExited(MouseEvent e) {} + + public void lostOwnership(Clipboard clipboard, Transferable contents) {} + + /** + * Returns the chat container. + * + * @return the chat container + */ + public ChatConversationContainer getChatContainer() + { + return chatContainer; + } + + /** + * Copies the selected conversation panel content to the clipboard. + */ + public void copyConversation() + { + this.chatTextPane.copy(); + } + + /** + * Creates new document and all the messages that will be processed in the + * future will be appended in it. + */ + public void clear() + { + this.document = (HTMLDocument) editorKit.createDefaultDocument(); + Constants.loadSimpleStyle( + document.getStyleSheet(), chatTextPane.getFont()); + } + + /** + * Sets the given document to the editor pane in this panel. + * + * @param document the document to set + */ + public void setContent(HTMLDocument document) + { + synchronized (scrollToBottomRunnable) + { + scrollToBottomIsPending = true; + + this.document = document; + chatTextPane.setDocument(this.document); + } + } + + /** + * Sets the default document contained in this panel, created on init or + * when clear is invoked. + */ + public void setDefaultContent() + { + setContent(document); + } + + /** + * Returns the document contained in this panel. + * + * @return the document contained in this panel + */ + public HTMLDocument getContent() + { + return (HTMLDocument) this.chatTextPane.getDocument(); + } + + /** + * Returns the right button popup menu. + * + * @return the right button popup menu + */ + public ChatRightButtonMenu getRightButtonMenu() + { + return rightButtonMenu; + } + + /** + * Returns the date of the first message in the current page. + * + * @return the date of the first message in the current page + */ + public Date getPageFirstMsgTimestamp() + { + Element rootElement = this.document.getDefaultRootElement(); + + Element firstMessageElement = null; + + for(int i = 0; i < rootElement.getElementCount(); i ++) + { + String idAttr = (String) rootElement.getElement(i) + .getAttributes().getAttribute("identifier"); + + if (idAttr != null && idAttr.equals("messageHeader")) + { + firstMessageElement = rootElement.getElement(i); + break; + } + } + + if(firstMessageElement == null) + return new Date(Long.MAX_VALUE); + + String dateObject = firstMessageElement + .getAttributes().getAttribute("date").toString(); + + return new Date(Long.parseLong(dateObject)); + } + + /** + * Returns the date of the last message in the current page. + * + * @return the date of the last message in the current page + */ + public Date getPageLastMsgTimestamp() + { + Element rootElement = this.document.getDefaultRootElement(); + + Element lastMessageElement = null; + + for(int i = rootElement.getElementCount() - 1; i >= 0; i --) + { + String idAttr = (String) rootElement.getElement(i) + .getAttributes().getAttribute("identifier"); + + if (idAttr != null && idAttr.equals("messageHeader")) + { + lastMessageElement = rootElement.getElement(i); + break; + } + } + + if(lastMessageElement == null) + return new Date(0); + + String dateObject = lastMessageElement + .getAttributes().getAttribute("date").toString(); + + return new Date(Long.parseLong(dateObject)); + } + + /** + * Formats HTML tags <br/> to <br> or <BR/> to <BR>. + * The reason of this function is that the ChatPanel does not support + * <br /> closing tags (XHTML syntax), thus we have to remove every + * slash from each <br /> tags. + * @param message The source message string. + * @return The message string with properly formatted <br> tags. + */ + private String processBrTags(String message) + { + // The resulting message after being processed by this function. + StringBuffer processedMessage = new StringBuffer(); + + // Compile the regex to match something like <br .. /> or <BR .. />. + // This regex is case sensitive and keeps the style or other + // attributes of the <br> tag. + Matcher m + = Pattern.compile("<\\s*[bB][rR](.*?)(/\\s*>)").matcher(message); + int start = 0; + + // while we find some <br /> closing tags with a slash inside. + while(m.find()) + { + // First, we have to copy all the message preceding the <br> tag. + processedMessage.append(message.substring(start, m.start())); + // Then, we find the position of the slash inside the tag. + int slash_index = m.group().lastIndexOf("/"); + // We copy the <br> tag till the slash exclude. + processedMessage.append(m.group().substring(0, slash_index)); + // We copy all the end of the tag following the slash exclude. + processedMessage.append(m.group().substring(slash_index+1)); + start = m.end(); + } + // Finally, we have to add the end of the message following the last + // <br> tag, or the whole message if there is no <br> tag. + processedMessage.append(message.substring(start)); + + return processedMessage.toString(); + } + + /** + * Formats HTML tags <img ... /> to < img ... ></img> or + * <IMG ... /> to <IMG></IMG>. + * The reason of this function is that the ChatPanel does not support + * <img /> tags (XHTML syntax). + * Thus, we remove every slash from each <img /> and close it with a + * separate closing tag. + * @param message The source message string. + * @return The message string with properly formatted <img> tags. + */ + private String processImgTags(String message) + { + // The resulting message after being processed by this function. + StringBuffer processedMessage = new StringBuffer(); + + // Compile the regex to match something like <img ... /> or + // <IMG ... />. This regex is case sensitive and keeps the style, + // src or other attributes of the <img> tag. + Pattern p = Pattern.compile("<\\s*[iI][mM][gG](.*?)(/\\s*>)"); + Matcher m = p.matcher(message); + int slash_index; + int start = 0; + + // while we find some <img /> self-closing tags with a slash inside. + while(m.find()){ + // First, we have to copy all the message preceding the <img> tag. + processedMessage.append(message.substring(start, m.start())); + // Then, we find the position of the slash inside the tag. + slash_index = m.group().lastIndexOf("/"); + // We copy the <img> tag till the slash exclude. + processedMessage.append(m.group().substring(0, slash_index)); + // We copy all the end of the tag following the slash exclude. + processedMessage.append(m.group().substring(slash_index+1)); + // We close the tag with a separate closing tag. + processedMessage.append("</img>"); + start = m.end(); + } + // Finally, we have to add the end of the message following the last + // <img> tag, or the whole message if there is no <img> tag. + processedMessage.append(message.substring(start)); + + return processedMessage.toString(); + } + + /** + * Extend Editor pane to add URL tooltips. + */ + private class MyTextPane + extends JTextPane + { + /** + * Returns the string to be used as the tooltip for <i>event</i>. + * + * @param event the <tt>MouseEvent</tt> + * @return the string to be used as the tooltip for <i>event</i>. + */ + @Override + public String getToolTipText(MouseEvent event) + { + return + ((currentHref != null) && (currentHref.length() != 0)) + ? currentHref + : null; + } + } + + /** + * Adds a custom component at the end of the conversation. + * + * @param component the component to add at the end of the conversation. + */ + public void addComponent(ChatConversationComponent component) + { + synchronized (scrollToBottomRunnable) + { + StyleSheet styleSheet = document.getStyleSheet(); + Style style + = styleSheet + .addStyle( + StyleConstants.ComponentElementName, + styleSheet.getStyle("body")); + + // The image must first be wrapped in a style + style + .addAttribute( + AbstractDocument.ElementNameAttribute, + StyleConstants.ComponentElementName); + + TransparentPanel wrapPanel + = new TransparentPanel(new BorderLayout()); + + wrapPanel.add(component, BorderLayout.NORTH); + + style + .addAttribute(StyleConstants.ComponentAttribute, wrapPanel); + style.addAttribute("identifier", "messageHeader"); + style.addAttribute("date", component.getDate().getTime()); + + scrollToBottomIsPending = true; + + // Insert the component style at the end of the text + try + { + document + .insertString(document.getLength(), "ignored text", style); + } + catch (BadLocationException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + } + } + + /** + * Registers a new link click listener. + * + * @param listener the object that should be notified when an internal + * link was clicked. + */ + public void addChatLinkClickedListener(ChatLinkClickedListener listener) + { + if(!chatLinkClickedListeners.contains(listener)) + chatLinkClickedListeners.add(listener); + } + + /** + * Remove a registered link click listener. + * + * @param listener a registered click listener to remove + */ + public void removeChatLinkClickedListener(ChatLinkClickedListener listener) + { + chatLinkClickedListeners.remove(listener); + } + + /** + * Returns the date string to show for the given date. + * + * @param date the date to format + * @return the date string to show for the given date + */ + public static String getDateString(long date) + { + if (GuiUtils.compareDatesOnly(date, System.currentTimeMillis()) < 0) + { + StringBuffer dateStrBuf = new StringBuffer(); + + GuiUtils.formatDate(date, dateStrBuf); + dateStrBuf.append(" "); + return dateStrBuf.toString(); + } + + return ""; + } + + /** + * Reloads images. + */ + public void loadSkin() + { + openLinkItem.setIcon( + new ImageIcon(ImageLoader.getImage(ImageLoader.BROWSER_ICON))); + copyLinkItem.setIcon( + new ImageIcon(ImageLoader.getImage(ImageLoader.COPY_ICON))); + + getRightButtonMenu().loadSkin(); + } + + /** + * Highlights the string in multi user chat. + * + * @param message the message to process + * @param contentType the content type of the message + * @param keyWord the keyword to highlight + * @return the message string with the keyword highlighted + */ + public String processChatRoomHighlight(String message, String contentType, + String keyWord) + { + return processKeyword(message, contentType, keyWord); + } + + public String processMeCommand(ChatMessage chatMessage) + { + String contentType = chatMessage.getContentType(); + String message = chatMessage.getMessage(); + + String msgID = "message"; + String chatString = ""; + String endHeaderTag = ""; + + String startDivTag = "<DIV identifier=\"" + msgID + "\">"; + String endDivTag = "</DIV>"; + + String startPlainTextTag; + String endPlainTextTag; + + if (HTML_CONTENT_TYPE.equals(contentType)) + { + startPlainTextTag = ""; + endPlainTextTag = ""; + } + else + { + startPlainTextTag = START_PLAINTEXT_TAG; + endPlainTextTag = END_PLAINTEXT_TAG; + } + + if (message.length() > 4 && message.substring(0, 4).equals("/me ")) + { + chatString = startDivTag + "<B><I>"; + + endHeaderTag = "</I></B>" + endDivTag; + + chatString += + + processHTMLChars("*** " + chatMessage.getContactName() + " " + + message.substring(4)) + + endHeaderTag; + + Map<String, ReplacementService> listSources = + GuiActivator.getReplacementSources(); + + Iterator<Entry<String, ReplacementService>> entrySetIter = + listSources.entrySet().iterator(); + StringBuffer msgStore = new StringBuffer(chatString); + + for (int i = 0; i < listSources.size(); i++) + { + Map.Entry<String, ReplacementService> entry = + entrySetIter.next(); + + ReplacementService source = entry.getValue(); + + boolean isSmiley = source instanceof SmiliesReplacementService; + if (isSmiley) + { + String sourcePattern = source.getPattern(); + Pattern p = + Pattern.compile(sourcePattern, Pattern.CASE_INSENSITIVE + | Pattern.DOTALL); + Matcher m = p.matcher(msgStore); + + StringBuffer msgTemp = new StringBuffer(chatString); + + while (m.find()) + { + msgTemp.insert(m.start(), startPlainTextTag); + msgTemp.insert(m.end() + startPlainTextTag.length(), + endPlainTextTag); + + } + if (msgTemp.length() != msgStore.length()) + msgStore = msgTemp; + } + } + + return msgStore.toString(); + } + else + return ""; + } + + private static String createIncomingMessageTag( + String messageID, + String incomingMessageHeader, + String incomingMessageParagraph) + { + StringBuffer messageBuff = new StringBuffer(); + +// <div class="box"> +// <div class="topleft"> +// <div class="topright"> +// <div class="messageDiv"> +// <h3>Header</h3> +// <p>Text</p> +// </div> +// </div> +// </div> +// <div class="bottomleft"> +// <div class="bottomright"> +// </div> +// </div> +// </div> + + messageBuff.append("<div " + createBoxStyle() + ">"); + messageBuff.append("<div " + createTopLeftStyle() + ">"); + messageBuff.append("<div " + createTopRightStyle() + ">"); + messageBuff.append("<div identifier=\"" + messageID + + "\" " + createMessageDivStyle() + ">"); + messageBuff.append(incomingMessageHeader); + messageBuff.append(incomingMessageParagraph); + messageBuff.append("</div>"); + messageBuff.append("</div>"); + messageBuff.append("</div>"); + messageBuff.append("<div " + createBottomLeftStyle() + ">"); + messageBuff.append("<div " + createBottomRightStyle() + ">"); + messageBuff.append("</div>"); + messageBuff.append("</div>"); + messageBuff.append("</div>"); + + return messageBuff.toString(); + } + + private static String createOutgoingMessageStyle() + { + StringBuffer styleBuff = new StringBuffer(); + + styleBuff.append("background-image:"); + styleBuff.append("url('bundle://30.0:1/resources/images/impl/gui/lookandfeel/selectedTabMiddle.png');"); + styleBuff.append("background-repeat:"); + styleBuff.append("repeat-x;"); + + return styleBuff.toString(); + } + + private static String createSmsMessageStyle() + { + StringBuffer styleBuff = new StringBuffer(); + + styleBuff.append("background-image:"); + styleBuff.append("url('bundle://30.0:1/resources/images/impl/gui/lookandfeel/tabRight.png');"); + styleBuff.append("background-repeat:"); + styleBuff.append("repeat-x;"); + + return styleBuff.toString(); + } + +// .box { +// width: 100%; +// margin: 0px auto; +// } + private static String createBoxStyle() + { + return "style=\"width: 100%;" + + " margin-top: 0px;" + + " margin-bottom: 0px;" + + " margin-left: auto;" + + " margin-right: auto;\""; + } + +// .box div.topleft { +// display: block; +// background: url("i/box-bg.png") top left no-repeat white; +// padding: 0em 0em 0em 1.0em; +// } + private static String createTopLeftStyle() + { + return "style=\"display: block;" + + " background-image: url('"+INCOMING_MESSAGE_IMAGE_PATH+"');" + + " background-repeat: no-repeat;" + + " background-position: top left;" + + " background-color: #FFFFFF;" + + " padding-top: 0em;" + + " padding-right: 0em;" + + " padding-bottom: 0em;" + + " padding-left: 0em;" + + "\""; + } + +// .box div.topright { +// display: block; +// background: url("i/box-bg.png") top right no-repeat white; +// padding: 1.0em; +// margin: -1.0em 0 0 1.0em; +// } + private static String createTopRightStyle() + { + return "style=\"display: block;" + + " background-image: url('"+INCOMING_MESSAGE_IMAGE_PATH+"');" + + " background-repeat: no-repeat;" + + " background-position: top right;" + + " background-color: #FFFFFF;" + + " padding-top: 1em;" + + " padding-right: 1em;" + + " padding-bottom: 1em;" + + " padding-left: 1em;" + + " margin-top: -1.0em;" + + " margin-right: 0em;" + + " margin-bottom: 0em;" + + " margin-left: 1.0em;" + + "\""; + } + +// .box div.bottomleft { +// display: block; +// height: 55px; +// margin-top: -1.0em; +// background: url("i/box-bg.png") bottom left no-repeat white; +// } + private static String createBottomLeftStyle() + { + return "style=\"display: block;" + + " height: 25px;" + + " margin-top: -1.0em;" + + " background-image: url('"+INCOMING_MESSAGE_IMAGE_PATH+"');" + + " background-repeat: no-repeat;" + + " background-position: bottom left;" + + " background-color: #FFFFFF;" + + "\""; + } + +// .box div.bottomright { +// display: block; +// background: url("i/box-bg.png") bottom right no-repeat white; +// height: 55px; +// margin-left: 3.0em; +// } + private static String createBottomRightStyle() + { + return "style=\"display: block;" + + " height: 25px;" + + " margin-left: 3.0em;" + + " background-image: url('"+INCOMING_MESSAGE_IMAGE_PATH+"');" + + " background-repeat: no-repeat;" + + " background-position: bottom right;" + + " background-color: #FFFFFF;" + + "\""; + } + +// .box div.topright div { +// margin-right: 1.5em; +// } + private static String createMessageDivStyle() + { + return "style=\"margin-right: 1.5em;\""; + } + +// .box h4 { +// margin-bottom: 0.4em; +// background-image: none; +// background-repeat: no-repeat; +// margin:0; +// padding:0; +// text-align:center; +// padding-bottom:15px; +// } +}
\ No newline at end of file |