/* * Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For questions, comments etc, * use the website at http://jeti.jabberstudio.org * or mail/IM me at jeti@jabber.org * * Created on 28-apr-2003 */ package nu.fw.jeti.ui; import java.awt.*; import java.awt.event.*; import java.text.DateFormat; import java.text.MessageFormat; import java.util.*; import java.util.List; import javax.swing.Timer; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.*; import javax.swing.border.*; import javax.swing.ImageIcon; import nu.fw.jeti.events.ChatEndedListener; import nu.fw.jeti.jabber.Backend; import nu.fw.jeti.jabber.JID; import nu.fw.jeti.jabber.JIDStatus; import nu.fw.jeti.jabber.elements.*; import nu.fw.jeti.plugins.*; import nu.fw.jeti.util.Base64; import nu.fw.jeti.util.I18N; import nu.fw.jeti.util.Preferences; import nu.fw.jeti.util.JavaScriptServerExpresso; import nu.fw.jeti.util.ServerExpresso; /** * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br * @Date : 05/11/2008 * @Brief : classe focusWindow, faz com que o titulo fique piscando. */ class focusWindow implements ActionListener { private boolean flag = false; private JFrame window; private String title; private Timer timer = new Timer(1000, this); private String name; public focusWindow() { timer.start(); } public void load(JFrame window, String name) { this.window = window; this.title = window.getTitle(); this.name = name; } public void actionPerformed(ActionEvent evt) { if( window.getExtendedState() == window.ICONIFIED ) { if(flag) window.setTitle("## " + this.name + " fala..."); else window.setTitle(this.title); flag = !flag; } else { timer.stop(); window.setTitle(this.title); } } } /** * class defining basic feature of a chat window * used by groupchatwindow and chatwindow * @author E.S. de Boer * */ //Do not add text to txtUitvoer outside this class, otherwise the text will //not scroll to the end public class ChatSplitPane extends JSplitPane { private JID contactJID; private String me; private Backend backend; private JScrollPane scrlInvoer = new JScrollPane(); private JTextPane txtInvoer = new JTextPane(); private JScrollPane scrlUitvoer = new JScrollPane(); private JTextPane txtUitvoer = new ToolTipTextpane(); private String contactName; private String thread; private boolean enterSends = false; private boolean showTimestamp = false; private Emoticons emoticons; private JPanel pnlBottom = new JPanel(); private BorderLayout borderLayout1 = new BorderLayout(); private JPanel pnlControl = new JPanel(); private JPopupMenu popupMenu = new JPopupMenu(); private JPopupMenu popupSelectedMenu = new JPopupMenu(); private String composingID; private boolean typing = false; private FormattedMessage xhtml; private boolean groupChat; private Spell spell; private Translator translator; private Translator links; private Translator xmppuri; private Translator colormessages; private Notifiers titleTimer; private Notifiers titleFlash; private SimpleAttributeSet colorAttributeSet = new SimpleAttributeSet(); private boolean toFrontOnNewMessage = false; private static DateFormat dateFormat = DateFormat.getTimeInstance(); DateFormat shortDate= DateFormat.getDateInstance(DateFormat.SHORT); DateFormat shortTime= DateFormat.getTimeInstance(DateFormat.SHORT); private volatile boolean scrolls=true; private volatile boolean systemScroll=true; private Date date = new Date(); private ImageIcon avatar; class ToolTipTextpane extends JTextPane { //Tooltip on textpane, show time of message public String getToolTipText(MouseEvent e) { int location = txtUitvoer.viewToModel(e.getPoint()); StyledDocument doc = (StyledDocument) txtUitvoer.getDocument(); AttributeSet set = doc.getCharacterElement(location).getAttributes(); return (String) set.getAttribute("time"); } } /** * Groupchat splitpane constructor * @param backend * @param to * @param toName * @param me * @param thread * @param groupChat * @param type * @param menu * @param frame * */ public ChatSplitPane(Backend backend, JID to,String toName,String me,JMenu menu,JFrame frame) { groupChat = true; contactJID = to; contactName = toName; this.backend = backend; this.me = me; toFrontOnNewMessage = Preferences.getBoolean("groupchat", "toFrontOnNewMessage", false); init("groupchat",menu); setParentFrame(frame); } /** * 1-1 chat splitpane constructor * @param backend * @param to * @param me * @param thread * @param menu * @param frame */ public ChatSplitPane(Backend backend, JIDStatus jidStatus,String me,String thread,JMenu menu,JFrame frame) { this.groupChat = false; contactJID = jidStatus.getCompleteJID(); contactName = jidStatus.getNick(); avatar = jidStatus.getAvatar(); this.backend = backend; this.me = me; this.thread = thread; toFrontOnNewMessage = Preferences.getBoolean("jeti", "toFrontOnNewMessage", false); init(jidStatus.getType(),menu); setParentFrame(frame); } /** * Add a menu to the menu invoked when the user presses on * the right mousebutton and there is text selected * @param submenu The menu to add */ public void addToSelectedTextPopupMenu(JMenu submenu) { popupSelectedMenu.add(submenu); } public String getSelectedText() { String text = txtUitvoer.getSelectedText(); if(text!=null) return text; else return txtInvoer.getSelectedText(); } public void init(String type,JMenu menu) { JCheckBoxMenuItem chkItem = new JCheckBoxMenuItem(I18N.gettext("main.chat.menu.To_front_on_new_message")); chkItem.setSelected(toFrontOnNewMessage); chkItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { toFrontOnNewMessage= ((JCheckBoxMenuItem)e.getSource()).isSelected(); } }); menu.add(chkItem); if (PluginsInfo.isPluginLoaded("emoticons")) { initEmoticons(type,menu); } Translate translate=null; if (PluginsInfo.isPluginLoaded("translate")) { translate = (Translate) PluginsInfo.newPluginInstance("translate"); translate.init(this,txtInvoer); txtUitvoer.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(SwingUtilities.isRightMouseButton(e)) { if(txtUitvoer.getSelectedText()!=null) { popupSelectedMenu.show(e.getComponent(), e.getX(), e.getY()); } } } }); } if (PluginsInfo.isPluginLoaded("spell")) { spell = (Spell) PluginsInfo.newPluginInstance("spell"); spell.addChangeDictoryMenuEntry(menu); } if (PluginsInfo.isPluginLoaded("links")) { links = (Translator) PluginsInfo.newPluginInstance("links"); links.init(txtUitvoer); } if (PluginsInfo.isPluginLoaded("xmppuri")) { xmppuri = (Translator) PluginsInfo.newPluginInstance("xmppuri"); xmppuri.init(txtUitvoer); } if (PluginsInfo.isPluginLoaded("colormessages")) { colormessages = (Translator) PluginsInfo.newPluginInstance("colormessages"); } if (PluginsInfo.isPluginLoaded("titlescroller")) { titleTimer = (Notifiers) PluginsInfo.newPluginInstance("titlescroller"); } if (PluginsInfo.isPluginLoaded("titleflash")) { titleFlash = (Notifiers) PluginsInfo.newPluginInstance("titleflash"); } if(PluginsInfo.isPluginLoaded("windowsutils")) { NativeUtils util =(NativeUtils)PluginsInfo.newPluginInstance("windowsutils"); util.addChatMenus(menu,this); } //translator = PluginsInfo.getTranslator(); if(emoticons != null || spell != null || translate!=null) { txtInvoer.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { if(txtInvoer.getSelectedText()!=null) { popupSelectedMenu.show(e.getComponent(), e.getX(), e.getY()); } else if(spell != null) { if(!spell.rightClick(txtInvoer,e) && emoticons != null) popupMenu.show(e.getComponent(), e.getX(), e.getY()); } else popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); } ToolTipManager.sharedInstance().registerComponent(txtUitvoer); enterSends = Preferences.getBoolean("jeti","enterSends",true); showTimestamp = Preferences.getBoolean("jeti","showTimestamp",true); try { jbInit(); } catch (Exception e) { e.printStackTrace(); } if (PluginsInfo.isPluginLoaded("xhtml")) { //String contactJid = contactJID.toString(); xhtml = (FormattedMessage) PluginsInfo.newPluginInstance("xhtml"); xhtml.initXHTML(this, txtInvoer, pnlControl, contactJID, contactName); } String t = ""; //if(!groupChat) //notify(); if (thread == null)t = "no id - "; Document doc = txtUitvoer.getDocument(); try { SimpleAttributeSet sas = new SimpleAttributeSet(); StyleConstants.setForeground(sas, Color.gray); doc.insertString(doc.getLength(), t + I18N.gettext("main.chat.Chat_started_on") + " " + DateFormat.getDateTimeInstance().format(new Date()), sas); } catch (BadLocationException e) { e.printStackTrace(); } //solves bug iconofied frame then no system message and wrong layout //pack(); setBorder(null); setSize(300, 350); } public JTextPane getTextInput() { return txtInvoer; } public void setParentFrame(JFrame frame) { if (titleTimer !=null) { titleTimer.init(frame,contactName); } if (titleFlash != null) { titleFlash.init(frame, contactName); } } private void initEmoticons(String type,JMenu menu) { //showEmoticons = true; emoticons = (Emoticons) PluginsInfo.newPluginInstance("emoticons"); try { emoticons.init(txtUitvoer, pnlControl, txtInvoer, popupMenu,type,menu); } catch (IllegalStateException ex) { ex.printStackTrace(); //showEmoticons = false; PluginsInfo.unloadPlugin("emoticons"); emoticons = null; return; } } public void close() { if(groupChat)contactJID = new JID(contactJID.getUser(),contactJID.getDomain(),me); for(Iterator j = backend.getListeners(ChatEndedListener.class);j.hasNext();) {//exit ((ChatEndedListener)j.next()).chatEnded(contactJID); } } // public boolean compareJID(JID jid) // { // return from.equals(jid); // } // // public String getThread() // { // return thread; // } public void appendMessage(Message message,String name) { if (name == null) { appendSystemMessage(message.getBody()); } else if(name.equals(me)) { showMessage(message,new Color(156, 23, 23),Preferences.getString("jeti", "ownName", me)); } else { if(titleTimer!=null ) { titleTimer.start( MessageFormat.format( I18N.gettext("main.chat.{0}_says_{1}"), new Object[] {name,message.getBody()})); } if(titleFlash!=null) { titleFlash.start(""); } if(!groupChat)contactJID = message.getFrom(); showMessage(message, new Color(17, 102, 6), name); } } public void appendSystemMessage(final String message) { SwingUtilities.invokeLater(new Runnable() { public void run() { final Document doc = txtUitvoer.getDocument(); final Point viewPos = scrlUitvoer.getViewport().getViewPosition(); final boolean scroll = scrolls;//cache scroll value because insertstring will update it systemScroll=true; SimpleAttributeSet sas = new SimpleAttributeSet(); StyleConstants.setForeground(sas, Color.gray); String timeStamp; if (showTimestamp) timeStamp = formatTime(new Date()) + " "; else timeStamp = ""; try { doc.insertString(doc.getLength(), "\n"+ timeStamp + message, sas); } catch (BadLocationException e) { e.printStackTrace(); } if(scroll) { txtUitvoer.setCaretPosition(doc.getLength()); scrolls=true; } else { scrlUitvoer.getViewport().setViewPosition(viewPos); } } }); } private void showMessage(final Message message,final Color color,final String name) {//two threads can write to display (typing & receiving) SwingUtilities.invokeLater(new Runnable() { public void run() { String text = message.getBody(); Date tempdelay =null; List tempWordList=null; date.setTime(System.currentTimeMillis()); // Loop through extensions if (message.hasExtensions()) { for (Iterator i = message.getExtensions(); i.hasNext();) { Extension extension = (Extension) i.next(); if (extension instanceof XDelay) { tempdelay = ((XDelay) extension).getDate(); } else if(xhtml!=null) { if(tempWordList==null) tempWordList = xhtml.getWordList(extension); } } } if (xhtml!=null && !name.equals(me) && !groupChat) { xhtml.useXHTML(tempWordList!=null, name); } if (tempWordList==null) { tempWordList = createWordList(text); } // Mark eventual weblinks (if links extension is active) if(links!=null) { links.translate(tempWordList); } if(xmppuri!=null) { xmppuri.translate(tempWordList); } if(groupChat) {//me replace with nick for(Iterator i = tempWordList.iterator();i.hasNext();) { Word w = (Word) i.next(); if(w.word.equals("/me")) { w.word = name; } } } // Handle error messages if (message.getType().equals("error")) { insertError(tempWordList, message); } // Insert eventual emoticons if (emoticons != null) { emoticons.insertEmoticons(tempWordList); } //TODO make a new translator thing for here and below if (translator != null) translator.translate(tempWordList); final Date delay = tempdelay; final List wordList = tempWordList; Point viewPos = scrlUitvoer.getViewport().getViewPosition(); boolean scroll = scrolls;//cache scroll value because insertstring will update it systemScroll=true; Document doc = txtUitvoer.getDocument(); try { doc.insertString(doc.getLength(),"\n",null); if (delay != null) { SimpleAttributeSet sas = new SimpleAttributeSet(); StyleConstants.setForeground(sas, Color.darkGray); doc.insertString(doc.getLength(), formatTime(delay) + " ", sas); } else if (showTimestamp) {// Eventual timestamp SimpleAttributeSet sas = new SimpleAttributeSet(); StyleConstants.setForeground(sas, Color.gray); doc.insertString(doc.getLength(), formatTime(date) + " ", sas); } StyleConstants.setForeground(colorAttributeSet, color); // Print originator if not error if (!message.getType().equals("error")) { colorAttributeSet.addAttribute("time",dateFormat.format(date)); doc.insertString(doc.getLength(),name+ ": ",colorAttributeSet); } // Insert words from wordlist for(Iterator i = wordList.iterator();i.hasNext();) { Word w = (Word) i.next(); doc.insertString(doc.getLength(),w.toString(), w.getAttributes()); } if (scroll) { txtUitvoer.setCaretPosition(doc.getLength()); scroll=true; } else { scrlUitvoer.getViewport().setViewPosition(viewPos); } } catch (BadLocationException e) { e.printStackTrace(); } /** * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br * @Date : 04/11/2008 * @Brief : Trecho de codigo abaixo, faz com que a janela fique piscando caso esta esteja * minimizada na barra de tarefas. Criada a classe focusWindow. */ JFrame frame = (JFrame)getTopLevelAncestor(); if(toFrontOnNewMessage) { if(!getTopLevelAncestor().isFocusOwner()) { if(getTopLevelAncestor() instanceof JFrame) { if( frame.getExtendedState() == Frame.ICONIFIED ) { frame.setState(Frame.NORMAL); } frame.toFront(); } else txtInvoer.requestFocus(); } } if( frame.getExtendedState() == Frame.ICONIFIED ) { frame.setVisible(true); focusWindow focusW = new focusWindow(); focusW.load(frame, name); } } }); } /** * Format a timestamp for display. */ private String formatTime(Date date) { String result = ""; if((System.currentTimeMillis() -date.getTime())> 600000) {//message is older then 10 min Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.AM_PM, Calendar.AM); if(date.before(cal.getTime())) {//print date if it is the previous day result += shortDate.format(date)+ " "; } } result+="["+shortTime.format(date)+"]"; return result; } // Insert error message into wordList private void insertError(List wordList, Message message) { String error; boolean replace = false; switch (message.getErrorCode()) { case 404: error = MessageFormat.format( I18N.gettext("main.error.User_{0}_could_not_be_found"), new Object[] {message.getFrom()}); replace = true; break; default: error = I18N.gettext("main.error.Error_in_chat"); break; } // Create list of error words and make them red List errorWords = createWordList(error); SimpleAttributeSet sas = new SimpleAttributeSet(); StyleConstants.setForeground(sas, Color.red); for(Iterator i = errorWords.iterator();i.hasNext();) { ((Word)i.next()).addAttributes(sas); } if (!replace) { errorWords.addAll(wordList); } wordList.clear(); wordList.addAll(errorWords); } //TODO move to util?? public static List createWordList(String text) { List wordList = new ArrayList(); StringBuffer temp = new StringBuffer(); for(int i = 0;i0)wordList.add(new Word(temp)); } private String getPhotoExpresso(String Jid) { ServerExpresso JidPhoto = new ServerExpresso(); return JidPhoto.PhotoUid(Jid); } private void jbInit() throws Exception { Color c = UIManager.getColor("TextPane.foreground"); txtInvoer.setForeground(c); txtUitvoer.setForeground(c); setResizeWeight(0.9); setOrientation(JSplitPane.VERTICAL_SPLIT); setBottomComponent(pnlBottom); scrlInvoer.setPreferredSize(new Dimension(100, 100)); txtInvoer.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(KeyEvent e) { txtInvoer_keyPressed(e); } public void keyReleased(KeyEvent e) { if(spell!=null)spell.keyReleased(e,txtInvoer); } }); txtUitvoer.setEditable(false); txtUitvoer.getDocument().putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" ); txtInvoer.getDocument().putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" ); addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { txtInvoer.requestFocusInWindow(); } }); /** * @Author : Rodrigo Souza * @Date : 15/10/2008 * @Description : Trecho de codigo abaixo ajusta as dimensoes da foto (avatar) * e demais paineis dentro da janela de conversa. */ pnlBottom.setLayout(borderLayout1); JPanel text = new JPanel(); text.setLayout(new BoxLayout(text,BoxLayout.PAGE_AXIS)); text.add(pnlControl); text.add(scrlInvoer); pnlBottom.add(text, BorderLayout.CENTER); /** * @Author : Alexandre Luiz Correia - alexandrecorreia@celepar.pr.gov.br * @Date : 10/09/2008 * @Description : Avatar - Carrega as Photos do Ldap */ // Border Border raisedetched; raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); // String String userJid = contactJID.toString(); /** * @Author : Alexandre Correia / Rodrigo Souza * @Date : 04/03/2009 * @Description : Conecta com a classe ServerExpresso(nu.fw.jeti.util.ServerExpresso) no Expresso e passa * a variavel userJid(Ex.: teste@nome_maquina), recebendo uma string em base64. */ String imgUser = getPhotoExpresso(userJid); byte[] data = Base64.decode(imgUser); ImageIcon icon = new ImageIcon(data); JLabel avatar = new JLabel(icon); avatar.setBorder(raisedetched); pnlBottom.add(avatar, BorderLayout.EAST); add(pnlBottom, JSplitPane.BOTTOM); add(scrlUitvoer, JSplitPane.TOP); scrlUitvoer.getViewport().add(txtUitvoer, null); scrlInvoer.getViewport().add(txtInvoer, null); scrlUitvoer.getVerticalScrollBar().getModel().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { BoundedRangeModel model =(BoundedRangeModel)e.getSource(); if(!model.getValueIsAdjusting()) { if(!systemScroll) {//only update if scroll is performed by the user if(model.getMaximum()-model.getExtent()-model.getValue()>10) { scrolls=false; } else scrolls=true; } systemScroll=false; } } }); setOneTouchExpandable(true); setDividerLocation(170); } public void send() { if (txtInvoer.getText().length()==0)return; boolean sendXHTML = false; XExtension html = null; XExtension ownHtml = null; List wordList=null; if(xhtml != null)wordList = xhtml.makeWordListFromDocument(); if(wordList==null)wordList=createWordList(txtInvoer.getText()); StringBuffer temp = new StringBuffer(); //TODO Translator //if (translator != null) translator.translate(wordList); for(Iterator i = wordList.iterator();i.hasNext();) { temp.append(i.next()); } String text = temp.toString(); if(colormessages!=null) { colormessages.translate(wordList); } if(xhtml != null) { html = xhtml.getXHTMLExtension(wordList); //ownwordlist will be edited so make deep copy ArrayList tempList = new ArrayList(); try { for(Iterator i = wordList.iterator();i.hasNext();) { tempList.add(((Word)i.next()).clone()); } } catch (CloneNotSupportedException e) { e.printStackTrace(); } ownHtml = xhtml.getXHTMLExtension(tempList); sendXHTML = xhtml.sendXML(); } Message message = null; if(groupChat) { if(html!=null) message = new Message(contactJID,text ,html); else message = new Message(contactJID,text); } else { MessageBuilder b = new MessageBuilder(); b.type = "chat"; b.setTo(contactJID); b.setId(backend.getIdentifier()); b.thread = thread; b.addXExtension(new XMessageEvent("composing", null)); b.body = text; if(sendXHTML)b.addXExtension(html); message =((Message) b.build()); showMessage(new Message(text, contactJID, null,thread,ownHtml),new Color(156, 23, 23), "Eu ( " + nu.fw.jeti.applet.Jeti.CNNAME +" ) "); } backend.sendMessage(message); txtInvoer.getHighlighter().removeAllHighlights(); txtInvoer.setText(""); txtInvoer.requestFocus(); typing = false; } public void composingID(String id) { composingID = id; } void txtInvoer_keyPressed(KeyEvent e) { /** * Author(s): Alexandre Correia / Rodrigo Souza * Date : 02/03/2009 * Brief : Limpa o tempo do Javascript */ JavaScriptServerExpresso cleanSt = new nu.fw.jeti.util.JavaScriptServerExpresso(); cleanSt.cleanStatus(); if(!groupChat) { if (txtInvoer.getText().length() < 2 && typing) { typing = false; if (composingID != null) { //send not composing backend.send(new Message(null, contactJID, null, new XMessageEvent(null, composingID))); } } else if (txtInvoer.getText().length() > 0 && !typing) { typing = true; if (composingID != null) { //send composing backend.send(new Message(null, contactJID, null, new XMessageEvent("composing", composingID))); } } } if (e.getKeyCode() == KeyEvent.VK_ENTER) { if (enterSends) { if ((e.getModifiers() == InputEvent.SHIFT_MASK) || (e.getModifiers() == InputEvent.CTRL_MASK)) { Document doc = txtInvoer.getDocument(); try { doc.insertString(txtInvoer.getCaretPosition(), "\n", null); } catch (BadLocationException e3) {e3.printStackTrace();} } else { send(); e.consume(); } } else if ((e.getModifiers() == InputEvent.SHIFT_MASK) || (e.getModifiers() == InputEvent.CTRL_MASK)) { send(); } } } } /* * Overrides for emacs * Local variables: * tab-width: 4 * End: */