001 /* 002 Copyright (C) 2003 Adam Olsen 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU General Public License as published by 006 the Free Software Foundation; either version 1, or (at your option) 007 any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU General Public License for more details. 013 014 You should have received a copy of the GNU General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 017 */ 018 019 package com.valhalla.jbother.groupchat; 020 021 import java.awt.*; 022 import java.awt.event.*; 023 import java.beans.*; 024 import java.io.*; 025 import java.text.SimpleDateFormat; 026 import java.util.*; 027 import java.util.regex.Pattern; 028 import javax.swing.*; 029 import javax.swing.event.*; 030 import javax.swing.text.html.HTMLDocument; 031 import org.jivesoftware.smack.*; 032 import org.jivesoftware.smack.packet.*; 033 import com.valhalla.gui.*; 034 import com.valhalla.jbother.*; 035 import com.valhalla.jbother.jabber.BuddyStatus; 036 import com.valhalla.jbother.jabber.smack.*; 037 import com.valhalla.settings.Settings; 038 039 /** 040 * This is the panel that contains a groupchat conversation. It is placed in a JTabbedPane in 041 * GroupChat frame. 042 * 043 * @author Adam Olsen 044 **/ 045 public class ChatRoomPanel extends JPanel implements LogViewerCaller, TabFramePanel 046 { 047 private ResourceBundle resources = ResourceBundle.getBundle( "JBotherBundle", Locale.getDefault() ); 048 private JTextField textEntryArea = new JTextField( 60 ); 049 050 private StringBuffer conversationText = new StringBuffer(); 051 private JScrollPane conversationScroll; 052 private ConversationArea conversationArea = new ConversationArea(); 053 054 private JMenuItem 055 clearItem = new JMenuItem( resources.getString( "clear" ) ), 056 logItem = new JMenuItem( resources.getString( "viewLog" ) ), 057 newItem = new JMenuItem( resources.getString( "joinRoom" ) ), 058 leaveItem = new JMenuItem( resources.getString( "leaveRoom" ) ), 059 topicItem = new JMenuItem( resources.getString( "setRoomSubject" ) ), 060 nickItem = new JMenuItem( resources.getString( "changeNickname" ) ); 061 062 private JButton topicButton = new JButton( resources.getString( "changeButton" ) ); 063 private JPopupMenu popMenu = new JPopupMenu(); 064 065 private int oldMaximum = 0; 066 private HTMLDocument document = (HTMLDocument)conversationArea.getDocument(); 067 private JPanel scrollPanel = new JPanel( new GridLayout( 1, 0 ) ); 068 private JSplitPane mainPanel = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT ); 069 private JSplitPane container; 070 private GroupChat chat; 071 private String chatroom, nickname; 072 private Hashtable buddyStatuses = new Hashtable(); 073 private GroupChatNickList nickList; 074 private WaitDialog wait = new WaitDialog( resources.getString( "pleaseWait" ), resources.getString( "attemptingToJoin" ) ); 075 private String subject = resources.getString( "noSubject" ); 076 private JLabel subjectLabel = new JLabel(); 077 private int countOfNewMessages = 0; 078 // for logging 079 private File logFile; 080 private PrintWriter logOut; 081 private FileWriter fw; 082 private String currentNick; 083 private ChatRoomPanel thisPointer = this; 084 private boolean listenersAdded = false; 085 086 /** 087 * This sets up the appearance of the chatroom window 088 * @param chatroom the chatroom address 089 * @param nickname the nickname to use when joining 090 **/ 091 public ChatRoomPanel( String chatroom, String nickname ) 092 { 093 this.chatroom = chatroom; 094 this.nickname = nickname; 095 096 BuddyList.getInstance().startTabFrame(); 097 098 setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) ); 099 setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) ); 100 101 JPanel subjectPanel = new JPanel(); 102 subjectPanel.setLayout( new BoxLayout( subjectPanel, BoxLayout.Y_AXIS ) ); 103 subjectPanel.add( subjectLabel ); 104 subjectPanel.add( Box.createHorizontalGlue() ); 105 add( subjectPanel ); 106 add( Box.createRigidArea( new Dimension( 0, 5 ) ) ); 107 108 add( mainPanel ); 109 110 nickList = new GroupChatNickList( this ); 111 112 String divLocString = Settings.getInstance().getProperty( "chatWindowDividerLocation" ); 113 int divLoc = 0; 114 115 if( divLocString != null ) 116 { 117 divLoc = Integer.parseInt( divLocString ); 118 } 119 else { 120 Dimension dimension = BuddyList.getInstance().getTabFrame().getSize(); 121 divLoc = (int)dimension.getWidth() - 127; 122 } 123 124 mainPanel.setDividerLocation( divLoc ); 125 mainPanel.setOneTouchExpandable( true ); 126 mainPanel.setResizeWeight( 1 ); 127 mainPanel.addPropertyChangeListener( "lastDividerLocation", new DividerListener() ); 128 129 conversationScroll = new JScrollPane( conversationArea ); 130 conversationScroll.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS ); 131 conversationArea.setScroll( conversationScroll ); 132 133 setUpPopMenu(); 134 135 scrollPanel.add( conversationScroll ); 136 137 JPanel containerPanel = new JPanel(); 138 GridBagLayout grid = new GridBagLayout(); 139 GridBagConstraints c = new GridBagConstraints(); 140 141 c.gridx = 0; 142 c.gridy = 0; 143 c.fill = GridBagConstraints.BOTH; 144 c.weightx = 1; 145 c.weighty = 1; 146 147 c.gridwidth = 1; 148 149 grid.setConstraints( scrollPanel, c ); 150 containerPanel.setLayout( grid ); 151 containerPanel.add( scrollPanel ); 152 153 c.gridy++; 154 c.weightx = 1; 155 c.weighty = 0; 156 c.fill = GridBagConstraints.HORIZONTAL; 157 158 Component sep = Box.createRigidArea( new Dimension( 0, 5 ) ); 159 grid.setConstraints( sep, c ); 160 containerPanel.add( sep ); 161 162 c.gridy++; 163 164 textEntryArea.setBorder( BorderFactory.createEtchedBorder() ); 165 grid.setConstraints( textEntryArea, c ); 166 167 containerPanel.add( textEntryArea ); 168 169 mainPanel.add( containerPanel ); 170 mainPanel.add( nickList ); 171 172 textEntryArea.grabFocus(); 173 textEntryArea.setFocusTraversalKeysEnabled(false); //for disable focus traversal with TAB 174 setSubject( subject ); 175 176 addListeners(); 177 } 178 179 /** 180 * @return true if the TabFrame panel listeners have already been added to this panel 181 */ 182 public boolean listenersAdded() { return listenersAdded; } 183 184 /** 185 * Sets whether or not the TabFrame panel listeners have been added 186 * @param added true if they have been added 187 */ 188 public void setListenersAdded( boolean added ) { this.listenersAdded = added; } 189 190 /** 191 * @return the input area of this panel 192 */ 193 public Component getInputComponent() { return textEntryArea; } 194 195 /** 196 * @return the JList representing the nicklist 197 */ 198 public JList getNickList() { return nickList.getList(); } 199 200 /** 201 * @return the text entry area 202 */ 203 public JTextField getTextEntryArea() { return textEntryArea; } 204 205 /** 206 * Listens for a change in the divider location, and saves it for later retreival 207 * @author Adam Olsen 208 * @version 1.0 209 */ 210 private class DividerListener implements PropertyChangeListener 211 { 212 public void propertyChange( PropertyChangeEvent e ) 213 { 214 Settings.getInstance().setProperty( "chatWindowDividerLocation", e.getOldValue().toString() ); 215 BuddyList.getInstance().getTabFrame().saveStates(); 216 } 217 } 218 219 /** 220 * Updates the font in the ConversationArea 221 * @param font the font to update to 222 */ 223 public void updateStyle( Font font ) { conversationArea.loadStyleSheet( font ); } 224 225 /** 226 * Look for a right click, and show a pop up menu 227 * @author Adam Olsen 228 * @version 1.0 229 **/ 230 class RightClickListener extends MouseAdapter 231 { 232 public void mousePressed( MouseEvent e ) { checkPop( e ); } 233 public void mouseReleased( MouseEvent e ) { checkPop( e ); } 234 public void mouseClicked( MouseEvent e ) { checkPop( e ); } 235 236 public void checkPop( MouseEvent e ) 237 { 238 // look for the popup trigger.. usually a right click 239 if( e.isPopupTrigger() ) 240 { 241 popMenu.show( e.getComponent(), e.getX(), e.getY() ); 242 } 243 } 244 } 245 246 /** 247 * Add the various menu items to the popup menu 248 **/ 249 private void setUpPopMenu() 250 { 251 MenuItemListener listener = new MenuItemListener(); 252 253 conversationArea.addMouseListener( new RightClickListener() ); 254 255 popMenu.add( clearItem ); 256 popMenu.add( topicItem ); 257 popMenu.add( nickItem ); 258 popMenu.addSeparator(); 259 popMenu.add( logItem ); 260 261 popMenu.addSeparator(); 262 popMenu.add( newItem ); 263 popMenu.add( leaveItem ); 264 265 topicButton.addActionListener( listener ); 266 clearItem.addActionListener( listener ); 267 logItem.addActionListener( listener ); 268 newItem.addActionListener( listener ); 269 leaveItem.addActionListener( listener ); 270 topicItem.addActionListener( listener ); 271 nickItem.addActionListener( listener ); 272 } 273 274 /** 275 * Listens for items to be selected in the menu 276 * @author Adam Olsen 277 * @version 1.0 278 */ 279 private class MenuItemListener implements ActionListener 280 { 281 public void actionPerformed( ActionEvent e ) 282 { 283 if( e.getSource() == nickItem ) changeNickHandler(); 284 if( e.getSource() == leaveItem ) 285 { 286 BuddyList.getInstance().getTabFrame().removePanel( thisPointer ); 287 BuddyList.getInstance().stopTabFrame(); 288 } 289 if( e.getSource() == topicItem || e.getSource() == topicButton ) topicHandler(); 290 if( e.getSource() == clearItem ) conversationArea.setText( "" ); 291 if( e.getSource() == newItem ) 292 { 293 GroupChatBookmarks gc = new GroupChatBookmarks(); 294 gc.show(); 295 gc.toFront(); 296 } 297 298 if( e.getSource() == logItem ) new LogViewerDialog( thisPointer, getRoomName() ); 299 } 300 } 301 302 /** 303 * Opens the log window for this chat room 304 */ 305 public void openLogWindow() 306 { 307 new LogViewerDialog( this, getRoomName() ); 308 } 309 310 /** 311 * Adds a buddy to the nickname list 312 * @param buddy the buddy to add 313 */ 314 public void addBuddy( String buddy ) { nickList.addBuddy( buddy ); } 315 316 /** 317 * Removes a buddy from the nick list 318 * @param buddy the buddy to remove 319 */ 320 public void removeBuddy( String buddy ) 321 { 322 nickList.removeBuddy( buddy ); 323 buddyStatuses.remove( buddy ); 324 } 325 326 /** 327 * Opens the log file 328 */ 329 public void startLog() 330 { 331 // for loggingg 332 if( Settings.getInstance().getBoolean( "keepLogs" ) ) 333 { 334 try { 335 String logFileName = LogViewerDialog.getDateName() + ".log.html"; 336 String logFileDir = JBother.profileDir + File.separatorChar + 337 "logs" + File.separatorChar + getRoomName().replaceAll( "@", "_at_" ).replaceAll( "\\/", "-" ); 338 339 File logDir = new File( logFileDir ); 340 341 if( !logDir.isDirectory() && !logDir.mkdirs() ) Standard.warningMessage( this, resources.getString( "log" ), 342 resources.getString( "couldNotCreateLogDir" ) ); 343 logFile = new File( logDir, logFileName ); 344 fw = new FileWriter( logFile, true ); 345 logOut = new PrintWriter( fw, true ); 346 } 347 catch( IOException e ) { com.valhalla.Logger.debug( "Could not open logfile." ); } 348 } 349 } 350 351 /** 352 * Gets the BuddyStatus represending a user in the room 353 * @param user the BuddyStatus to get 354 * @return the requested BuddyStatus 355 */ 356 public BuddyStatus getBuddyStatus( String user ) 357 { 358 if( !buddyStatuses.containsKey( user ) ) 359 { 360 BuddyStatus buddy = new BuddyStatus( user ); 361 buddy.setName( user.substring( user.indexOf( "/" ) + 1, user.length() ) ); 362 buddyStatuses.put( user, buddy ); 363 } 364 365 return (BuddyStatus)buddyStatuses.get( user ); 366 } 367 368 /** 369 * Returns the tab name for the TabFramePanel 370 * @return the panel name 371 */ 372 public String getPanelName() { return getShortRoomName(); } 373 374 /** 375 * Returns the tooltip for the tab in the TabFrame 376 * @return the tooltip for this tab in the tab frame 377 */ 378 public String getTooltip() { return getRoomName(); } 379 380 /** 381 * Returns the window title 382 * @return the window title for the TabFrame when this tab is selected 383 */ 384 public String getWindowTitle() { return resources.getString( "groupChat" ) + ": " + getRoomName(); } 385 386 /** 387 * Gets the short room name - for example, if you are talking in 388 * jdev@conference.jabber.org, it would return "jdev" 389 * @return short room name 390 **/ 391 public String getShortRoomName() 392 { 393 return chat.getRoom().replaceAll( "\\@.*", "" ); 394 } 395 396 /** 397 * Gets the entire room name, server included 398 * @return gets the room address 399 **/ 400 public String getRoomName() 401 { 402 return chat.getRoom(); 403 } 404 405 /** 406 * Starts the groupchat. Sets up a thread to connect, and start that thread 407 **/ 408 public void startChat() 409 { 410 if( !BuddyList.getInstance().checkConnection() ) return; 411 412 chat = new GroupChat( BuddyList.getInstance().getConnection(), chatroom ); 413 414 //set up the presence and message listeners so that they are 415 //ready from the momemnt we start the chat. 416 chat.addMessageListener( new GroupChatMessagePacketListener( this ) ); 417 chat.addParticipantListener( new GroupParticipantListener( this ) ); 418 wait.setVisible( true ); 419 420 Thread thread = new Thread( new JoinChatThread() ); 421 thread.start(); 422 423 startLog(); 424 } 425 426 /** 427 * Asks for a new topic, then sets the new topic 428 */ 429 private void topicHandler() 430 { 431 //find out what they want the subject to be 432 String result = (String)JOptionPane.showInputDialog( null, 433 resources.getString( "enterSubject" ), 434 resources.getString( "setSubject" ), JOptionPane.QUESTION_MESSAGE, null, null, subject ); 435 436 if( result != null && !result.equals( "" ) ) 437 { 438 //send a packet to the server with a subject and an empty body 439 Message packet = new Message( getRoomName(), Message.Type.GROUP_CHAT ); 440 packet.setSubject( result ); 441 packet.setBody( "/me " + resources.getString( "hasSetTopic" ) + " " + result ); 442 443 if( !BuddyList.getInstance().checkConnection() ) 444 { 445 BuddyList.getInstance().connectionError(); 446 return; 447 } 448 else { 449 BuddyList.getInstance().getConnection().sendPacket( packet ); 450 } 451 452 setSubject( result ); 453 } 454 } 455 456 /** 457 * Asks for a new nickname, and sends a nickname change request 458 */ 459 private void changeNickHandler() 460 { 461 if( currentNick == null ) currentNick = chat.getNickname(); 462 String result = (String)JOptionPane.showInputDialog( null, 463 resources.getString( "enterNickname" ), 464 resources.getString( "setNickname" ), JOptionPane.QUESTION_MESSAGE, null, null, currentNick ); 465 466 if( result != null && !result.equals( "" ) ) 467 { 468 //send a packet to the server with a subject and an empty body 469 Presence packet = new Presence( Presence.Type.AVAILABLE, BuddyList.getInstance().getCurrentStatusString(), 0, BuddyList.getInstance().getCurrentPresenceMode() ); 470 packet.setTo( getRoomName() + '/' + result ); 471 packet.setFrom( getRoomName() + '/' + currentNick ); 472 473 if( !BuddyList.getInstance().checkConnection() ) 474 { 475 BuddyList.getInstance().connectionError(); 476 return; 477 } 478 else { 479 BuddyList.getInstance().getConnection().sendPacket( packet ); 480 } 481 482 currentNick = result; 483 } 484 } 485 486 487 /** 488 * Adds the event listeners for the various components in this chatwindows 489 **/ 490 public void addListeners() 491 { 492 //set up the window so you can press enter in the text box and 493 //that will send the message. 494 Action SendMessageAction = new AbstractAction() 495 { 496 public void actionPerformed( ActionEvent e ) 497 { 498 sendHandler(); 499 } 500 }; 501 502 Action nickCompletionAction = new AbstractAction() 503 { 504 public void actionPerformed( ActionEvent e ) 505 { 506 nickCompletionHandler(); 507 } 508 }; 509 510 //set it up so that if they drag in the conversation window, it grabs the focus 511 conversationArea.addMouseMotionListener( new MouseMotionAdapter() 512 { 513 public void mouseDragged( MouseEvent e ) 514 { 515 conversationArea.grabFocus(); 516 } 517 } ); 518 519 //set it up so that if there isn't any selected text in the conversation area 520 //the textentryarea grabs the focus. 521 conversationArea.addFocusListener( new FocusListener() 522 { 523 public void focusGained( FocusEvent e ) 524 { 525 if( conversationArea.getSelectedText() == null ) 526 { 527 textEntryArea.requestFocus(); 528 } 529 else conversationArea.grabFocus(); 530 } 531 532 public void focusLost( FocusEvent e ) { } 533 } ); 534 535 Action closeAction = new AbstractAction() 536 { 537 public void actionPerformed( ActionEvent e ) 538 { 539 BuddyList.getInstance().getTabFrame().removePanel( thisPointer ); 540 BuddyList.getInstance().stopTabFrame(); 541 } 542 }; 543 544 KeyStroke enterStroke = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ); 545 textEntryArea.getInputMap().put( enterStroke, SendMessageAction ); 546 547 KeyStroke tabStroke = KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ); 548 textEntryArea.getInputMap().put( tabStroke, nickCompletionAction ); 549 550 textEntryArea.getInputMap().put( KeyStroke.getKeyStroke( KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ), closeAction ); 551 } 552 553 /** 554 * Leaves this room and removes it from the groupchat frame 555 */ 556 public void leave() 557 { 558 closeLog(); 559 try { 560 chat.leave(); 561 } 562 catch( IllegalStateException e ) { } 563 } //leave the chatroom 564 565 /** 566 * Focuses the text entry area 567 */ 568 public void focusYourself() 569 { 570 conversationArea.moveToEnd(); 571 textEntryArea.grabFocus(); 572 573 countOfNewMessages = 0; 574 JTabbedPane tabPane = BuddyList.getInstance().getTabFrame().getTabPane(); 575 tabPane.setTitleAt(tabPane.indexOfComponent(this),this.getShortRoomName()); 576 } 577 578 /** 579 * Gets the nickname currently being used in the chat room 580 * @return the nickname being used in the chatroom 581 */ 582 public String getNickname() 583 { 584 if( currentNick == null ) currentNick = chat.getNickname(); 585 return currentNick; 586 } 587 588 /** 589 * Recieves a message 590 * @param from who it's from 591 * @param message the message 592 */ 593 public void recieveMessage( String from, String message ) 594 { 595 message = ConversationText.replaceText( message, false ); //replace emoticons, links, etc... 596 boolean messageToMe = false; 597 if( from.equals( "" ) || from.toLowerCase().equals( chat.getRoom().toLowerCase() ) ) 598 { 599 //server message 600 conversationArea.append( "<div>"+getDate() + "<font color='green'> -> " + message + "</font></div>" ); 601 } 602 else { 603 604 boolean highLightedSound = false; 605 606 if( message.startsWith( " /me " ) ) 607 { 608 message = message.replaceAll( "^ \\/me ", "" ); 609 conversationArea.append( "<div>"+getDate() + 610 " <b><font color='maroon'>* " + from + "</font></b> " + message + "</div>" ); 611 } 612 else if( message.replaceAll( "<[^>]*>", "" ).matches( ".*(^|\\W)" + nickname + "\\W.*" ) ) 613 { 614 messageToMe = true; 615 conversationArea.append( "<div style='background-color: #FFC4C4;'>"+getDate() + " <b><font color='#16569e'>" + from + "</font></b>: " + message + "</div>" ); 616 com.valhalla.jbother.sound.SoundPlayer.play( "groupHighlightedSound" ); 617 highLightedSound = true; 618 } 619 else { 620 conversationArea.append( "<div>"+getDate() + " <font color='#16569e'><b>" + 621 from + "</b></font>: " + message + "</div>" ); 622 } 623 624 if( !highLightedSound ) com.valhalla.jbother.sound.SoundPlayer.play( "groupRecievedSound" ); 625 } 626 627 try { 628 JTabbedPane tabPane = BuddyList.getInstance().getTabFrame().getTabPane(); 629 if (tabPane.getSelectedComponent() != this) { 630 countOfNewMessages++; 631 String title = this.getShortRoomName()+" ("+countOfNewMessages+")"; 632 if (messageToMe) title = "*"+title; 633 tabPane.setTitleAt(tabPane.indexOfComponent(this),title); 634 } 635 } 636 catch( Exception ex ) { } 637 638 if( logOut != null ) 639 { 640 logOut.println( conversationArea.getLastHTML() ); 641 } 642 } 643 644 /** 645 * Closes the log file 646 */ 647 public void closeLog() 648 { 649 if( fw != null ) 650 { 651 try { 652 fw.close(); 653 } 654 catch( IOException e ) { } 655 } 656 } 657 658 /** 659 * @return a String representing the current time the format: [Hour:Minute:Second] 660 */ 661 public String getDate() 662 { 663 SimpleDateFormat formatter = new SimpleDateFormat( "[HH:mm:ss]" ); 664 String date = formatter.format( new Date() ); 665 return date; 666 } 667 668 /** 669 * Sets the subject of the room 670 * @param subject the subject to set 671 */ 672 public void setSubject( String subject ) 673 { 674 this.subject = subject; 675 if( BuddyList.getInstance().getTabFrame() != null ) 676 BuddyList.getInstance().getTabFrame().setSubject( this ); 677 678 subjectLabel.setText( "<html><b>" + resources.getString( "subject" ) + ": </b>" + subject + "</html>" ); 679 } 680 681 /** 682 * Returns the current room subject 683 * @return the current room subject 684 */ 685 public String getSubject() { return this.subject; } 686 687 /** 688 * Gets all the buddy statuses in the room 689 * @return all BuddyStatuses 690 */ 691 public Hashtable getBuddyStatuses() { return this.buddyStatuses; } 692 693 /** 694 * Sends the message currently in the textentryarea 695 */ 696 private void sendHandler() 697 { 698 String text = textEntryArea.getText(); 699 700 Message message = new Message(); 701 message.setTo( getRoomName() ); 702 message.setBody( text ); 703 message.setType( Message.Type.GROUP_CHAT ); 704 705 if( !textEntryArea.getText().equals( "" ) ) 706 { 707 try { 708 chat.sendMessage( message ); 709 } 710 catch( XMPPException e ) 711 { 712 com.valhalla.Logger.debug( "Could not send message." ); 713 } 714 715 textEntryArea.setText( "" ); 716 } 717 } 718 719 /** 720 * Implementation of Tab nick completion in the textEntryArea 721 */ 722 private void nickCompletionHandler() 723 { 724 String text = textEntryArea.getText(); 725 726 /* if we have nothing => do nothing */ 727 if( !text.equals( "" ) ) { 728 int caretPosition = textEntryArea.getCaretPosition(); 729 int startPosition = text.lastIndexOf(" ", caretPosition - 1) + 1; 730 String nickPart = text.substring(startPosition, caretPosition); 731 Vector matches = new Vector(); 732 733 java.util.List keys = new ArrayList( buddyStatuses.keySet() ); 734 Iterator iterator = keys.iterator(); 735 736 while( iterator.hasNext() ) { 737 BuddyStatus buddy = (BuddyStatus)buddyStatuses.get(iterator.next()); 738 try { 739 String nick = buddy.getUser().substring(buddy.getUser().lastIndexOf("/")+1); 740 if (nick.toLowerCase().startsWith(nickPart.toLowerCase())) { 741 matches.add(nick); 742 } 743 } catch(java.lang.NullPointerException e) { 744 } 745 } 746 747 if (matches.size() > 0) { 748 String append = ""; 749 750 if (matches.size() > 1) { 751 String nickPartNew = (String)matches.firstElement(); 752 String nick = ""; 753 String hint = nickPartNew + ", "; 754 int nickPartLen = nickPart.length(); 755 for( int i = 1; i < matches.size(); i++ ){ 756 nick = (String)matches.get(i); 757 hint += nick + ", "; 758 for( int j = 1; j <= nick.length()-nickPartLen; j++ ){ 759 if (!nickPartNew.regionMatches(true,nickPartLen,nick,nickPartLen,j)){ 760 nickPartNew = nickPartNew.substring(0, nickPartLen+j-1); 761 break; 762 } 763 } 764 } 765 if (nickPart.length() != nickPartNew.length()) { 766 nickPart = nickPartNew; 767 } 768 // emphasize differense in matches by bold and append hint to the conversationArea 769 // hint = hint.replaceAll() can't be used here because of its case sensitive nature 770 Pattern pattern = Pattern.compile("(" + nickPart + ")([^,]+), ", Pattern.CASE_INSENSITIVE); 771 hint = pattern.matcher(hint).replaceAll("$1<b>$2</b>, "); 772 conversationArea.append( "<div style=\"color: #16569e;\">" + hint.substring(0,hint.length()-2) + "</div>" ); 773 } else { 774 nickPart = (String)matches.firstElement(); 775 if( startPosition == 0 ) append = ": "; 776 else append = " "; 777 } 778 779 String newText = text.substring( 0, startPosition ); 780 newText += nickPart + append; 781 newText += text.substring(caretPosition); 782 textEntryArea.setText( newText ); 783 /* Set caret to the appropriate position */ 784 textEntryArea.setCaretPosition(startPosition + nickPart.length() + append.length()); 785 } 786 787 } /* end of the lazy "if" */ 788 } 789 790 /** 791 * Joins the chatroom and adds this chatroomwindow to the TabFrame 792 * @author Adam Olsen 793 * @version 1.0 794 */ 795 class JoinChatThread implements Runnable 796 { 797 private String errorMessage; 798 799 public void run() 800 { 801 try { 802 chat.join( nickname, 30500 ); 803 } 804 catch( XMPPException e ) 805 { 806 if( e.getXMPPError() != null ) errorMessage = e.getXMPPError().getMessage(); 807 else errorMessage = e.getMessage(); 808 if( errorMessage == null ) errorMessage = resources.getString( "unknownError" ); 809 } 810 811 SwingUtilities.invokeLater( new Runnable() 812 { 813 public void run() 814 { 815 wait.dispose(); 816 if( errorMessage != null ) 817 { 818 Standard.warningMessage( thisPointer, resources.getString( "joinChat" ), errorMessage ); 819 chat.leave(); 820 new GroupChatBookmarks().setVisible( true ); 821 BuddyList.getInstance().stopTabFrame(); 822 } 823 else { 824 BuddyList.getInstance().addTabPanel( thisPointer ); 825 } 826 } 827 } ); 828 } 829 } 830 }