001    /*
002     *  Copyright (C) 2003 Adam Olsen
003     *  This program is free software; you can redistribute it and/or modify
004     *  it under the terms of the GNU General Public License as published by
005     *  the Free Software Foundation; either version 1, or (at your option)
006     *  any later version.
007     *  This program is distributed in the hope that it will be useful,
008     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
009     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
010     *  GNU General Public License for more details.
011     *  You should have received a copy of the GNU General Public License
012     *  along with this program; if not, write to the Free Software
013     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
014     */
015    package com.valhalla.jbother;
016    
017    import java.awt.Component;
018    import java.awt.Dimension;
019    import java.awt.Font;
020    import java.awt.GridLayout;
021    import java.awt.Toolkit;
022    import java.awt.event.*;
023    import java.beans.PropertyChangeEvent;
024    import java.beans.PropertyChangeListener;
025    import java.net.URL;
026    import java.util.ArrayList;
027    import java.util.Date;
028    import java.util.Hashtable;
029    import java.util.Iterator;
030    import java.util.Locale;
031    import java.util.ResourceBundle;
032    import java.util.Set;
033    
034    import javax.swing.AbstractAction;
035    import javax.swing.Action;
036    import javax.swing.BorderFactory;
037    import javax.swing.Box;
038    import javax.swing.BoxLayout;
039    import javax.swing.ImageIcon;
040    import javax.swing.JButton;
041    import javax.swing.JComboBox;
042    import javax.swing.JComponent;
043    import javax.swing.JFrame;
044    import javax.swing.JLabel;
045    import javax.swing.JList;
046    import javax.swing.JPanel;
047    import javax.swing.JScrollPane;
048    import javax.swing.JSplitPane;
049    import javax.swing.KeyStroke;
050    import javax.swing.ListCellRenderer;
051    import javax.swing.SwingUtilities;
052    import javax.swing.text.JTextComponent;
053    
054    import org.jivesoftware.smack.Chat;
055    import org.jivesoftware.smack.XMPPException;
056    import org.jivesoftware.smack.packet.Message;
057    import org.jivesoftware.smack.packet.Presence;
058    import org.jivesoftware.smackx.MessageEventManager;
059    import org.jivesoftware.smackx.muc.MultiUserChat;
060    
061    import com.valhalla.gui.*;
062    import com.valhalla.jbother.jabber.BuddyStatus;
063    import com.valhalla.jbother.jabber.MUCBuddyStatus;
064    import com.valhalla.jbother.jabber.smack.SecureExtension;
065    import com.valhalla.jbother.menus.ConversationPopupMenu;
066    import com.valhalla.jbother.plugins.events.*;
067    import com.valhalla.misc.GnuPG;
068    import com.valhalla.settings.Settings;
069    
070    /**
071     *  Handles conversations between two users. It is usually associated with a
072     *  BuddyStatus.
073     *
074     *@author     Adam Olsen
075     *@author     Andrey Zakirov
076     *@created    September 9, 2005
077     *@version    1.1
078     *@see        com.valhalla.jbother.jabber.BuddyStatus
079     */
080    public class ChatPanel extends ConversationPanel {
081        private ResourceBundle resources = ResourceBundle.getBundle(
082                "JBotherBundle", Locale.getDefault());
083    
084        //private StringBuffer conversationText = new StringBuffer();
085        private boolean offlineMessage = false;
086    
087        private ConversationPopupMenu popMenu = new ConversationPopupMenu(this,
088                conversationArea);
089    
090        //private HTMLDocument document =
091        // (HTMLDocument)conversationArea.getDocument();
092        private JSplitPane container;
093    
094        private JPanel buttonPanel = new JPanel();
095    
096        private JPanel scrollPanel = new JPanel(new GridLayout(1, 0));
097    
098        //private JCheckBox useHTML = new JCheckBox( "Use HTML" );
099        // for logging
100        private JComboBox resourceBox = new JComboBox();
101    
102        private JLabel typingLabel = new JLabel(Standard.getIcon("images/nottyping.png"));
103    
104        private JButton clearButton = new JButton(Standard.getIcon("images/buttons/New24.gif"));
105    
106        private JButton emoteButton = new JButton(Standard.getIcon("images/buttons/smiley.gif"));
107    
108        private boolean divSetUp = false;
109    
110        private DividerListener dividerListener = new DividerListener();
111    
112        private Hashtable chats = new Hashtable();
113    
114        private boolean isTyping = false;
115    
116        private javax.swing.Timer typingTimer = new javax.swing.Timer(13000,
117                new TypingHandler());
118    
119        private JButton encryptButton = new JButton();
120        private String selected = null;
121        private JScrollPane scroll = new JScrollPane(textEntryArea);
122    
123        /**
124         *  Sets up the ChatPanel - creates all visual components and adds event
125         *  listeners
126         *
127         *@param  buddy      the buddy to associate with
128         */
129        public ChatPanel(final BuddyStatus buddy) {
130            super(buddy);
131    
132            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
133            setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
134    
135            // create two fields, one for where you type your message to be sent,
136            // and the other where you see the conversation that has already happened.
137            textEntryArea.setLineWrap(true);
138            textEntryArea.setWrapStyleWord(true);
139    
140            conversationArea.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
141    
142            container = new JSplitPane(JSplitPane.VERTICAL_SPLIT, conversationArea,
143                    scroll);
144            container.setResizeWeight(1);
145    
146            JPanel containerPanel = new JPanel();
147            containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.X_AXIS));
148            containerPanel.add(container);
149    
150            JPanel bottomPanel = new JPanel();
151            bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
152    
153            if (buddy.getUser().indexOf("/") >= 0) {
154                resourceBox.setEnabled(false);
155            }
156    
157            resourceBox.addActionListener(
158                new ActionListener() {
159                    public void actionPerformed(ActionEvent e) {
160                        selected = (String) resourceBox.getSelectedItem();
161                    }
162                });
163    
164            resourceBox.setRenderer(new PresenceComboBoxRenderer());
165            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
166    
167            JPanel resourcePanel = new JPanel();
168            resourcePanel.setLayout(new BoxLayout(resourcePanel, BoxLayout.Y_AXIS));
169            resourcePanel.add(Box.createVerticalGlue());
170            resourcePanel.add(resourceBox);
171    
172            buttonPanel.add(resourcePanel);
173            typingLabel.setPreferredSize(new Dimension(26, 26));
174            buttonPanel.add(typingLabel);
175            typingLabel.setToolTipText(resources.getString("notTypingReply"));
176    
177            emoteButton.setPreferredSize(new Dimension(26, 26));
178            buttonPanel.add(emoteButton);
179    
180            if (!buddy.isEncrypting()) {
181                encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_no.png"));
182            } else {
183                encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_yes.png"));
184            }
185    
186            encryptButton.setPreferredSize(new Dimension(26, 26));
187            if (JBotherLoader.isGPGEnabled()
188                     && BuddyList.getInstance().getGnuPGPassword() != null) {
189                buttonPanel.add(encryptButton);
190            }
191    
192            String gnupgSecretKey = Settings.getInstance().getProperty(
193                    "gnupgSecretKeyID");
194    
195            if (gnupgSecretKey == null) {
196                encryptButton.setEnabled(false);
197            }
198    
199            clearButton.setPreferredSize(new Dimension(26, 26));
200            buttonPanel.add(clearButton);
201    
202            bottomPanel.add(buttonPanel);
203    
204            add(containerPanel);
205            add(Box.createRigidArea(new Dimension(0, 5)));
206            add(bottomPanel);
207    
208            textEntryArea.grabFocus();
209    
210            textEntryArea.addKeyListener(
211                new KeyAdapter() {
212                    public void keyTyped(KeyEvent e) {
213                        if (lastReceived != null && !isTyping
214                                 && e.getKeyChar() != KeyEvent.VK_ENTER) {
215                            if (Settings.getInstance().getBoolean(
216                                    "sendTypingNotification")
217                                     && buddy.getComposingID() != null
218                                     && buddy.getComposingID().equals(
219                                    lastReceived.getPacketID())) {
220                                ConnectorThread.getInstance().getMessageEventManager()
221                                        .sendComposingNotification(
222                                        lastReceived.getFrom(),
223                                        lastReceived.getPacketID());
224    
225                                isTyping = true;
226    
227                                if (typingTimer.isRunning()) {
228                                    typingTimer.restart();
229                                } else {
230                                    typingTimer.start();
231                                }
232                            }
233                        }
234                    }
235                });
236    
237            addListeners();
238            updateResources();
239        }
240    
241        public void removeScroll() { scroll.setViewportView(null); }
242    
243    
244        /**
245         *  Description of the Method
246         */
247        public void enableEncrypt() {
248            encryptButton.setEnabled(true);
249        }
250    
251    
252        /**
253         *  Description of the Method
254         */
255        public void disableEncrypt() {
256            encryptButton.setEnabled(true);
257        }
258    
259    
260        /**
261         *  Description of the Class
262         *
263         *@author     synic
264         *@created    September 9, 2005
265         */
266        private class TypingHandler implements ActionListener {
267            /**
268             *  Description of the Method
269             *
270             *@param  e  Description of the Parameter
271             */
272            public void actionPerformed(ActionEvent e) {
273                if (lastReceived != null
274                         && buddy.getComposingID() != null
275                         && buddy.getComposingID()
276                        .equals(lastReceived.getPacketID())) {
277                    ConnectorThread.getInstance().getMessageEventManager()
278                            .sendCancelledNotification(lastReceived.getFrom(),
279                            lastReceived.getPacketID());
280                }
281    
282                typingTimer.stop();
283                isTyping = false;
284            }
285        }
286    
287    
288        /**
289         *  Sets the isTyping attribute of the ChatPanel object
290         *
291         *@param  typing  The new isTyping value
292         */
293        public void setIsTyping(boolean typing) {
294            if (buddy.size() <= 0) {
295                return;
296            }
297            String s = "images/typing.png";
298            if (!typing) {
299                s = "images/nottyping.png";
300            }
301            typingLabel.setIcon(Standard.getIcon(s));
302    
303            if (typing) {
304                typingLabel.setToolTipText(resources.getString("typingReply"));
305            } else {
306                typingLabel.setToolTipText(resources.getString("notTypingReply"));
307            }
308            typingLabel.validate();
309        }
310    
311    
312        /**
313         *  Description of the Method
314         */
315        public void removeDividerListener() {
316            container.removePropertyChangeListener(dividerListener);
317        }
318    
319    
320        /**
321         *@return    the input area of this panel
322         */
323        public JComponent getInputComponent() {
324            return textEntryArea;
325        }
326    
327    
328        /**
329         *  Sets up the Divider
330         */
331        public void setUpDivider() {
332            String modifier = "";
333            if (Settings.getInstance().getBoolean("useTabbedWindow")) {
334                modifier = "tabbed_";
335            }
336    
337            String stringHeight = Settings.getInstance().getProperty(
338                    "conversationWindowHeight");
339    
340            // set up the divider location from settings
341            String divLocString = Settings.getInstance().getProperty(
342                    modifier + "conversationWindowDividerLocation");
343            int divLoc = 30;
344    
345            try {
346                if (divLocString != null) {
347                    divLoc = Integer.parseInt(divLocString);
348                } else {
349                    divLoc = Integer.parseInt(stringHeight) - 117;
350                }
351            } catch (NumberFormatException ex) {
352                ex.printStackTrace();
353            }
354    
355            container.setDividerLocation(divLoc);
356    
357            if (!divSetUp) {
358                container.addPropertyChangeListener("lastDividerLocation",
359                        dividerListener);
360                divSetUp = true;
361            }
362        }
363    
364    
365        /**
366         *@return    the ChatPanel's JSPlitPane
367         */
368        public JSplitPane getSplitPane() {
369            return container;
370        }
371    
372    
373        /**
374         *  Listens for the user to move the divider, and saves it's location
375         *
376         *@author     Adam Olsen
377         *@created    September 9, 2005
378         *@version    1.0
379         */
380        private class DividerListener implements PropertyChangeListener {
381            /**
382             *  Description of the Method
383             *
384             *@param  e  Description of the Parameter
385             */
386            public void propertyChange(PropertyChangeEvent e) {
387                String modifier = "";
388                if (Settings.getInstance().getBoolean("useTabbedWindow")) {
389                    modifier = "tabbed_";
390                }
391    
392                if (e.getOldValue().toString().equals("-1")) {
393                    return;
394                }
395    
396                Settings.getInstance().setProperty(
397                        modifier + "conversationWindowDividerLocation",
398                        e.getOldValue().toString());
399            }
400        }
401    
402        /**
403         *  Gets the ComboBox with all the buddy's resources
404         *
405         *@return    the ComboBox
406         */
407        public JComboBox getResourceBox() {
408            return resourceBox;
409        }
410    
411    
412        /**
413         *  Updates the JComboBox with the buddy's current resources
414         */
415        public void updateResources() {
416            SwingUtilities.invokeLater(
417                new Runnable() {
418                    public void run() {
419                        if (selected == null) {
420                            selected = "";
421                        }
422                        com.valhalla.Logger.debug("updating resources " + buddy.getUser());
423                        resourceBox.removeAllItems();
424                        resourceBox.addItem(resources.getString("defaultResource"));
425                        resourceBox.addItem(resources.getString("allResources"));
426    
427                        if (buddy.getUser().indexOf("/") >= 0) {
428                            return;
429                        }
430    
431                        Iterator i = buddy.keySet().iterator();
432    
433                        int count = 2;
434    
435                        int sel = 0;
436                        while (i.hasNext()) {
437                            String key = (String) i.next();
438                            if (!key.equals("N/A")) {
439                                resourceBox.addItem(key);
440                                if (key.equals(selected)) {
441                                    sel = count;
442                                    com.valhalla.Logger.debug("sel" + selected);
443                                }
444                                count++;
445                            }
446                        }
447    
448                        if (count == 2) {
449                            sel = 0;
450                            resourceBox.setEnabled(false);
451                        } else if (count == 3) {
452                            sel = 2;
453                            resourceBox.setEnabled(false);
454                        } else {
455                            resourceBox.setEnabled(true);
456                        }
457    
458                        if (selected.equals(resources.getString("allResources"))) {
459                            sel = 1;
460                        }
461                        if (sel > 0 && sel <= resourceBox.getItemCount()) {
462                            resourceBox.setSelectedIndex(sel);
463                        } else {
464                            resourceBox.setSelectedIndex(0);
465                        }
466    
467                        resourceBox.repaint();
468                    }
469                });
470        }
471    
472    
473        /**
474         *  Adds the various event listeners for the components that are a part of
475         *  this frame
476         */
477    
478    
479        private void addListeners() {
480    
481            clearButton.setToolTipText(resources.getString("clear"));
482            emoteButton.addActionListener(
483                new ActionListener() {
484                    public void actionPerformed(ActionEvent e) {
485                        JFrame f = frame;
486                        if (f == null) {
487                            f = BuddyList.getInstance().getTabFrame();
488                        }
489                        ConversationFormatter.getInstance().displayEmoticonChooser(f, emoteButton,
490                                textEntryArea);
491                    }
492                });
493    
494            encryptButton.addActionListener(
495                new ActionListener() {
496                    public void actionPerformed(ActionEvent e) {
497    
498                        if (buddy.isEncrypting()) {
499                            buddy.isEncrypting(false);
500                            encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_no.png"));
501                        } else {
502                            buddy.isEncrypting(true);
503                            encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_yes.png"));
504                        }
505                    }
506                });
507    
508            clearButton.addActionListener(
509                new ActionListener() {
510                    public void actionPerformed(ActionEvent e) {
511                        conversationArea.setText("");
512                    }
513                });
514    
515            //if the press enter, send the message
516            Action sendMessageAction =
517                new AbstractAction() {
518                    public void actionPerformed(ActionEvent e) {
519                        sendHandler(null);
520                    }
521                };
522    
523            Action shiftEnterAction =
524                new AbstractAction() {
525                    public void actionPerformed(ActionEvent e) {
526                        int pos = textEntryArea.getCaretPosition();
527                        if (pos < 0) {
528                            pos = 0;
529                        }
530    
531                        textEntryArea.insert("\n", pos);
532    
533                        try {
534                            textEntryArea.setCaretPosition(pos + 1);
535                        } catch (IllegalArgumentException ex) {
536                        }
537                    }
538                };
539    
540            Action checkCloseAction =
541                new AbstractAction() {
542                    public void actionPerformed(ActionEvent e) {
543                        checkCloseHandler();
544                    }
545                };
546    
547            Action closeAction =
548                new AbstractAction() {
549                    public void actionPerformed(ActionEvent e) {
550                        closeHandler();
551                    }
552                };
553    
554            //set it up so that if there isn't any selected text in the
555            // conversation area
556            //the textentryarea grabs the focus.
557            conversationArea.getTextPane().addMouseListener(new MouseAdapter() {
558                public void mouseReleased(MouseEvent e) {
559                    if (conversationArea.getSelectedText() == null) {
560                        textEntryArea.requestFocus();
561                    }
562                }
563            });
564    
565    
566            conversationArea.getTextPane().addMouseListener(new RightClickListener(popMenu));
567            CopyPasteContextMenu.registerComponent(conversationArea.getTextPane());
568    
569            textEntryArea.getInputMap()
570                    .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
571                    sendMessageAction);
572            textEntryArea.getInputMap()
573                    .put(
574                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
575                    java.awt.event.InputEvent.SHIFT_MASK),
576                    shiftEnterAction);
577            textEntryArea.getInputMap().put(
578                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
579                    shiftEnterAction);
580            textEntryArea.getInputMap().put(
581                    KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
582                    checkCloseAction);
583            textEntryArea.getInputMap().put(
584                    KeyStroke.getKeyStroke(KeyEvent.VK_K, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
585                    closeAction);
586        }
587    
588    
589    
590        /**
591         *  Gets the textEntryArea attribute of the ChatPanel object
592         *
593         *@return    The textEntryArea value
594         */
595        public JTextComponent getTextEntryArea() {
596            return textEntryArea;
597        }
598    
599    
600        /**
601         *  Displays a message in the window when the buddy signs off
602         */
603        public void signedOff() {
604            SwingUtilities.invokeLater(
605                new Runnable() {
606                    public void run() {
607                        setIsTyping(false);
608                        conversationArea.append(getDate(null) + " " + buddy.getName() + " "+
609                                resources.getString("signedOff") + "\n",
610                                ConversationArea.SERVER);
611                    }
612                });
613        }
614    
615    
616        /**
617         *  Displays a message in the window when a buddy signs on
618         */
619        public void signedOn() {
620    
621            SwingUtilities.invokeLater(
622                new Runnable() {
623                    public void run() {
624                        conversationArea.append(getDate(null) + " " + buddy.getName() + " " +
625                                resources.getString("signedOn") + "\n",
626                                ConversationArea.SERVER);
627                    }
628                });
629        }
630    
631    
632        /**
633         *  Displays a "disconnected" message"
634         */
635        public void disconnected() {
636            conversationArea.append(getDate(null) + " *** " +
637                    resources.getString("disconnected") + "\n",
638                    ConversationArea.BLACK, true);
639    
640            chats = new Hashtable();
641        }
642    
643    
644        /**
645         *  Description of the Method
646         *
647         *@param  text  Description of the Parameter
648         */
649        public void messageEvent(String text) {
650             conversationArea.append(getDate(null) + " *** " +
651                    text + "\n",
652                    ConversationArea.BLACK, true);
653        }
654    
655    
656        /**
657         *  Receives a message
658         *
659         *@param  sbj            the message subject
660         *@param  body           the message body
661         *@param  resource       the resource the message came from if there is one
662         *@param  delayInfo      Description of the Parameter
663         *@param  date           Description of the Parameter
664         *@param  decryptedFlag  Description of the Parameter
665         *@param  verifiedFlag   Description of the Parameter
666         */
667        public void receiveMessage(final String sbj, final String delayInfo,
668                final String body, final String resource, final Date date,
669                final boolean decryptedFlag, final boolean verifiedFlag) {
670            SwingUtilities.invokeLater(
671                new Runnable() {
672                    public void run() {
673                        String extraInfo = delayInfo;
674                        setIsTyping(false);
675                        receiveMessage();
676    
677                        JFrame f = frame;
678                        if (f == null) {
679                            f = BuddyList.getInstance().getTabFrame();
680                        }
681    
682                        if (f.isFocused() == true) {
683    //                    SwingUtilities.invokeLater(new Runnable() {
684    //                         public void run() {
685                            buddy.sendNotDisplayedID();
686    //                         }
687    //                    });
688                        }
689    
690                        if (resource != null && buddy.getUser().indexOf("/") < 0 && !resource.equals("")) {
691                            int c = 0;
692                            selected = resource;
693                            boolean select = false;
694                            for (int i = 0; i < resourceBox.getModel().getSize(); i++) {
695                                if (((String) resourceBox.getModel().getElementAt(i)).equals(resource)) {
696                                    select = true;
697                                    break;
698                                }
699                                c++;
700                            }
701    
702                            if(select) resourceBox.setSelectedIndex(c);
703                        }
704                        String newBody = body;
705    
706                        String name = buddy.getName();
707                        if (name == null) {
708                            name = buddy.getUser();
709                        }
710    
711                        ImageIcon enc = null;
712                        ImageIcon sig = null;
713                        if (decryptedFlag) {
714                            enc = Standard.getIcon("images/encrypted.gif");
715    
716                        }
717                        if (verifiedFlag) {
718                            sig = Standard.getIcon("images/signed.gif");
719                        }
720                        if (newBody.startsWith("/me ")) {
721                            newBody = newBody.replaceAll("^\\/me ", "");
722                            conversationArea.append(getDate(date));
723                            conversationArea.append(" *" + name, ConversationArea.BLACK, true);
724                            conversationArea.append(extraInfo, ConversationArea.BLACK);
725                            if( sig != null ) conversationArea.appendIcon(sig);
726                            if( enc != null ) conversationArea.appendIcon(enc);
727                            conversationArea.append(" " + newBody + "\n", ConversationArea.BLACK);
728                        } else {
729                            conversationArea.append(getDate(date), ConversationArea.SENDER);
730                            conversationArea.append(" " + name, ConversationArea.SENDER, true);
731                            conversationArea.append(extraInfo, ConversationArea.BLACK);
732                            if( sig != null ) conversationArea.appendIcon(sig);
733                            if( enc != null ) conversationArea.appendIcon(enc);
734                            conversationArea.append(": " + newBody + "\n", ConversationArea.BLACK);
735                        }
736                    }
737                });
738    
739        }
740    
741    
742        /**
743         *  Sends the message in the TextEntryArea
744         *
745         *@param  allText  Description of the Parameter
746         */
747        public void sendHandler(String allText) {
748            String areaTextComplete;
749            final String areaText;
750            if (allText != null) {
751                areaTextComplete = allText;
752            } else {
753                areaTextComplete = textEntryArea.getText();
754            }
755    
756            if (!areaTextComplete.equals("")) {
757                if (!BuddyList.getInstance().checkConnection()) {
758                    BuddyList.getInstance().connectionError();
759                    return;
760                }
761                if ((areaTextComplete.startsWith("/all ") == true || areaTextComplete.startsWith("/ame ") == true)) {
762                    if (areaTextComplete.startsWith("/ame ") == true) {
763                        areaTextComplete = areaTextComplete.replaceAll("^/ame ",
764                                "/me ");
765                    } else {
766                        areaTextComplete = areaTextComplete.replaceAll("^/all ", "");
767                    }
768                    if (allText == null) {
769                        Hashtable buddyStatuses = BuddyList.getInstance()
770                                .getBuddyStatuses();
771                        if (buddyStatuses != null) {
772                            Iterator iterator = buddyStatuses.keySet().iterator();
773                            while (iterator.hasNext()) {
774                                String user2 = (String) iterator.next();
775                                BuddyStatus buddy2 = (BuddyStatus) buddyStatuses.get(user2);
776                                if (buddy2.equals(buddy) == false
777                                         && buddy2.getConversation() != null
778                                         && buddy2.getConversation() instanceof ChatPanel) {
779                                    ((ChatPanel) buddy2.getConversation())
780                                            .sendHandler(areaTextComplete);
781                                }
782                            }
783                        }
784                    }
785                }
786    
787                areaText = areaTextComplete;
788    
789                if ((buddy.isEncrypting())
790                         &&
791                // ( BuddyList.getInstance().isEncrypting() ) &&
792                        ((buddy.getPubKey() == null) || (buddy.getPubKey() == null))) {
793                    KeySelectDialog dialog = new KeySelectDialog("pub");
794                    dialog.showDialog();
795                    if (dialog.getID() != null) {
796                        buddy.setPubKey(dialog.getID());
797                    } else {
798                        buddy.isEncrypting(false);
799                        encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_no.png"));
800                    }
801                }
802    
803                if (!sendBuddyMessage(areaText)) {
804                    return;
805                }
806                isTyping = false;
807                typingTimer.stop();
808    
809                SwingUtilities.invokeLater(
810                    new Runnable() {
811                        public void run() {
812    
813                            String text = areaText;
814    
815                            if (text.startsWith("/me ")) {
816                                text = text.replaceAll("^/me ", "");
817                                conversationArea.append(getDate(null), ConversationArea.RECEIVER);
818                                conversationArea.append( " *" + BuddyList.getInstance().getMyName() + " ", ConversationArea.RECEIVER, true);
819                                conversationArea.append(text + "\n", ConversationArea.BLACK);
820                            } else {
821                                conversationArea.append(getDate(null), ConversationArea.RECEIVER);
822                                conversationArea.append(" " + BuddyList.getInstance().getMyName()+ ": ", ConversationArea.RECEIVER, true);
823                                conversationArea.append(text + "\n", ConversationArea.BLACK);
824                            }
825    
826                            textEntryArea.setText("");
827                        }
828                    });
829            }
830        }
831    
832    
833        /**
834         *  Sends the message to the resource in the JComboBox
835         *
836         *@param  text  the message to send
837         *@return       Description of the Return Value
838         */
839        public boolean sendBuddyMessage(String text) {
840            String to = buddy.getUser();
841            int sel = resourceBox.getSelectedIndex();
842    
843            SecureExtension secureExtension = new SecureExtension();
844            SecureExtension signedExtension = new SecureExtension("signed");
845    
846            // if they've selected a resource, send to it
847            if (sel != 0 && sel != 1 && sel != -1) {
848                to += "/" + (String) resourceBox.getSelectedItem();
849            }
850    
851            ArrayList send = new ArrayList();
852    
853            if (sel != 1 || resourceBox.getItemCount() <= 2) {
854                send.add(to);
855            }
856            // if they've selected to send to all resources, send to all
857            else {
858                Set keys = buddy.keySet();
859                Iterator i = keys.iterator();
860                while (i.hasNext()) {
861                    String key = (String) i.next();
862                    if (!key.equals("N/A")) {
863                        send.add(buddy.getUser() + "/" + key);
864                    }
865                }
866            }
867    
868            String gnupgSecurityVariant = Settings.getInstance().getProperty(
869                    "gnupgSecurityVariant");
870            String gnupgSecretKey = Settings.getInstance().getProperty(
871                    "gnupgSecretKeyID");
872            String gnupgPublicKey = buddy.getPubKey();
873            if (JBotherLoader.isGPGEnabled() &&
874            // BuddyList.getInstance().isEncrypting()
875                    buddy.isEncrypting() && (gnupgSecretKey != null)
876                     && (gnupgPublicKey != null)) {
877                GnuPG gnupg = new GnuPG();
878                String encryptedData = null;
879                String signedData = null;
880    
881                if (gnupgSecurityVariant == null) {
882                    gnupgSecurityVariant = "0";
883                    Settings.getInstance().setProperty("gnupgSecurityVariant", "0");
884                }
885    
886                if ((gnupgSecurityVariant.equals("0"))
887                         || (gnupgSecurityVariant.equals("1"))) {
888                    encryptedData = gnupg.encryptExtension(text, gnupgSecretKey,
889                            gnupgPublicKey);
890                    if (encryptedData != null) {
891                        secureExtension.setData(encryptedData);
892                    }
893                }
894                if ((gnupgSecurityVariant.equals("0"))
895                         || (gnupgSecurityVariant.equals("2"))) {
896                    signedData = gnupg.signExtension(text, gnupgSecretKey);
897                    if (signedData != null) {
898                        signedExtension.setData(signedData);
899                    }
900                }
901    
902                if ((encryptedData == null) && (signedData == null)) {
903                    buddy.isEncrypting(false);
904                    encryptButton.setIcon(Standard.getIcon("images/buttons/ssl_no.png"));
905                    Standard.warningMessage(null,
906                            resources.getString("gnupgError"), resources.getString("gnupgErrorEncrypting")
907                             + ".\n\n"
908                             + resources.getString("reason")
909                             + ":\n\n"
910                             + gnupg.getResult()
911                             + gnupg.getErrorString()
912                             + "\n"
913                             + resources.getString("gnupgTryOrSendUnencrypted")
914                             + ".");
915                    return false;
916                }
917            }
918    
919            for (int i = 0; i < send.size(); i++) {
920                Chat chat = null;
921    
922                if (buddy instanceof MUCBuddyStatus) {
923                    MultiUserChat muc = ((MUCBuddyStatus) buddy).getMUC();
924                    chat = muc.createPrivateChat(buddy.getUser());
925                } else {
926                    chat = (Chat) chats.get((String) send.get(i));
927                }
928    
929                if (chat == null) {
930                    chat = BuddyList.getInstance().getConnection().createChat(
931                            (String) send.get(i));
932                    chats.put((String) send.get(i), chat);
933                }
934    
935                Message message = chat.createMessage();
936                if (secureExtension.getData() != null) {
937                    message.setBody("[This message is encrypted]");
938                    message.addExtension(secureExtension);
939                } else {
940                    message.setBody(text);
941                }
942                if (signedExtension.getData() != null) {
943                    message.addExtension(signedExtension);
944                }
945    
946    
947                if (buddy.isAskForDelivered()) {
948                    BuddyList.getInstance().putEventMessage(message.getPacketID(), message.getTo(), getDate(null), 1);
949                }
950                if (buddy.isAskForDisplayed()) {
951                    BuddyList.getInstance().putEventMessage(message.getPacketID(), message.getTo(), getDate(null), 2);
952                }
953                if (buddy.isAskForOffline()) {
954                    BuddyList.getInstance().putEventMessage(message.getPacketID(), message.getTo(), getDate(null), 3);
955                }
956    
957                MessageEventManager.addNotificationsRequests(message, buddy.isAskForOffline(), buddy.isAskForDelivered(), buddy.isAskForDisplayed(), true);
958                MessageSendingEvent event = new MessageSendingEvent(this);
959                event.setMessage(message);
960                com.valhalla.pluginmanager.PluginChain.fireEvent(event);
961    
962                try {
963                    if (BuddyList.getInstance().checkConnection()) {
964                        chat.sendMessage(message);
965                    } else {
966                        BuddyList.getInstance().connectionError();
967                    }
968                } catch (XMPPException e) {
969                    com.valhalla.Logger.debug("Could not send message: "
970                             + e.getMessage());
971                }
972            }
973    
974            return true;
975        }
976    
977        public void closeHandler() {
978            removeScroll();
979            super.closeHandler();
980        }
981    
982    
983        /**
984         *  Creates the containing frame
985         */
986        public void createFrame() {
987            frame = new JFrame();
988            frame.setContentPane(this);
989            frame.pack();
990    
991            frame.setIconImage(Standard.getImage("frameicon.png"));
992    
993            frame.addWindowListener(
994                new WindowAdapter() {
995                    public void windowClosing(WindowEvent e) {
996                        if (Settings.getInstance().getProperty("preserveMessages") == null) {
997                            closeHandler();
998                        } else {
999                            startTimer();
1000                            frame.setVisible(false);
1001                        }
1002                    }
1003                });
1004    //        frame.addFocusListener(new FocusListener() {
1005    
1006    //        JFrame f = frame;
1007    
1008            //      if (frame !=null)
1009    //        {
1010    //
1011            final BuddyStatus buddy2 = buddy;
1012            frame.addWindowFocusListener(
1013                new WindowFocusListener() {
1014                    public void windowGainedFocus(WindowEvent e) {
1015    //                SwingUtilities.invokeLater(new Runnable() {
1016    //                    public void run() {
1017                        buddy2.sendNotDisplayedID();
1018    //                    }
1019    //                });
1020                    }
1021    
1022    
1023                    public void windowLostFocus(WindowEvent e) {
1024                    }
1025                });
1026    //        }
1027    //        else
1028    //        {
1029    
1030            String title = buddy.getUser();
1031            if (buddy.getName() != null) {
1032    
1033                if ((buddy.getName().toLowerCase()).matches(buddy.getUser())) {
1034                    title = buddy.getName();
1035                } else {
1036                    title = buddy.getName() + " (" + buddy.getUser() + ")";
1037                }
1038    
1039    //            title = buddy.getName()
1040    //            + " (" + title + ")";
1041            }
1042    
1043            frame.setTitle(title);
1044            frame.pack();
1045    
1046            String stringWidth = Settings.getInstance().getProperty(
1047                    "conversationWindowWidth");
1048            String stringHeight = Settings.getInstance().getProperty(
1049                    "conversationWindowHeight");
1050    
1051            if (stringWidth == null) {
1052                stringWidth = "400";
1053            }
1054            if (stringHeight == null) {
1055                stringHeight = "340";
1056            }
1057    
1058            frame.setSize(new Dimension(Integer.parseInt(stringWidth), Integer.parseInt(stringHeight)));
1059    
1060            // add a resize window listener
1061            frame.addComponentListener(
1062                new ComponentAdapter() {
1063                    public void componentResized(ComponentEvent e) {
1064                        Dimension dim = frame.getSize();
1065                        Settings.getInstance().setProperty("conversationWindowWidth",
1066                                new Integer((int) dim.getWidth()).toString());
1067                        Settings.getInstance().setProperty("conversationWindowHeight",
1068                                new Integer((int) dim.getHeight()).toString());
1069                    }
1070                });
1071    
1072            Standard.cascadePlacement(frame);
1073    
1074            setUpDivider();
1075            validate();
1076        }
1077    
1078    
1079        /**
1080         *  This renders the resource combo box - and displays icons for the online
1081         *  status of each resource
1082         *
1083         *@author     Adam Olsen
1084         *@created    September 9, 2005
1085         *@version    1.0
1086         */
1087        class PresenceComboBoxRenderer extends JLabel implements ListCellRenderer {
1088            /**
1089             *  Gets the listCellRendererComponent attribute of the
1090             *  PresenceComboBoxRenderer object
1091             *
1092             *@param  list          Description of the Parameter
1093             *@param  value         Description of the Parameter
1094             *@param  index         Description of the Parameter
1095             *@param  isSelected    Description of the Parameter
1096             *@param  cellHasFocus  Description of the Parameter
1097             *@return               The listCellRendererComponent value
1098             */
1099            public Component getListCellRendererComponent(JList list, Object value,
1100                    int index, boolean isSelected, boolean cellHasFocus) {
1101                Presence.Mode mode = null;
1102                if (value == null) {
1103                    value = "";
1104                }
1105    
1106                if (buddy.size() != 0) {
1107                    mode = buddy.getPresence(buddy.getHighestResource());
1108                }
1109    
1110                if (value.toString().equals(resources.getString("allResources"))) {
1111                    mode = Presence.Mode.AVAILABLE;
1112                }
1113    
1114                if (!value.toString()
1115                        .equals(resources.getString("defaultResource"))
1116                         && !value.toString().equals(
1117                        resources.getString("allResources"))) {
1118                    mode = buddy.getPresence(value.toString());
1119                }
1120    
1121                ImageIcon icon = StatusIconCache.getStatusIcon(mode);
1122                if (icon != null) {
1123                    setIcon(icon);
1124                }
1125                setText(value.toString());
1126    
1127                return this;
1128            }
1129        }
1130    }
1131