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