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; 020 021 import java.awt.*; 022 import java.awt.event.*; 023 import java.util.*; 024 import javax.swing.*; 025 import javax.swing.event.*; 026 import org.jivesoftware.smack.packet.Presence; 027 import com.valhalla.settings.Settings; 028 import com.valhalla.gui.*; 029 import com.valhalla.jbother.*; 030 import com.valhalla.jbother.groupchat.*; 031 import com.valhalla.jbother.jabber.*; 032 033 /** 034 * Contains all of the groupchat windows in tabs 035 * 036 * @author Adam Olsen 037 * @version 1.0 038 **/ 039 public class TabFrame extends JFrame 040 { 041 private ResourceBundle resources = ResourceBundle.getBundle( "JBotherBundle", Locale.getDefault() ); 042 private JPanel container = new JPanel( new BorderLayout() ); 043 private JTabbedPane tabPane = new JTabbedPane(); 044 private JMenuBar menuBar = new JMenuBar(); 045 private JMenu optionMenu = new JMenu( resources.getString( "options" ) ); 046 private JMenuItem newItem = new JMenuItem( resources.getString( "joinRoom" ) ), 047 leaveItem = new JMenuItem( resources.getString( "leaveAll" ) ); 048 private Hashtable queueCounts = new Hashtable(); 049 private GCTabHandler tabListener = new GCTabHandler(); 050 051 /** 052 * Constructor sets the frame up and adds a listener to the JTabPane so that if 053 * a tab is changed the title of this frame reflects the topic and the name of the room 054 * in the tab 055 **/ 056 public TabFrame() 057 { 058 super( "Chat Window" ); 059 setTitle( resources.getString( "chatWindowTitle" ) ); 060 061 ImageIcon icon = StatusIconCache.getStatusIcon(org.jivesoftware.smack.packet.Presence.Mode.AVAILABLE); 062 if (icon != null ) setIconImage( icon.getImage() ); 063 064 setContentPane( container ); 065 tabPane.setTabPlacement( JTabbedPane.BOTTOM ) ; 066 container.add( tabPane, BorderLayout.CENTER ); 067 068 //add the tab switch listener so the title of the frame reflects the current tab 069 //contents 070 tabPane.addChangeListener( new ChangeListener() 071 { 072 public void stateChanged( ChangeEvent e ) 073 { 074 if( tabPane.getSelectedComponent() != null ) 075 { 076 TabFramePanel panel = (TabFramePanel)tabPane.getSelectedComponent(); 077 setTitle( panel.getWindowTitle() ); 078 panel.focusYourself(); 079 tabPane.setTitleAt( tabPane.indexOfComponent( (Component)panel ), panel.getPanelName() ); 080 queueCounts.remove( panel ); 081 } 082 } 083 } ); 084 085 optionMenu.add( newItem ); 086 optionMenu.add( leaveItem ); 087 088 addListeners(); 089 090 menuBar.add( optionMenu ); 091 092 //if they press the close button, we wanna handle leaving of the rooms 093 //gracefully 094 addWindowListener( new WindowAdapter() 095 { 096 public void windowClosing( WindowEvent e ) { closeHandler(); } 097 } ); 098 099 setPreferredLocation(); 100 pack(); 101 102 String stringWidth = Settings.getInstance().getProperty( "chatFrameWidth" ); 103 String stringHeight = Settings.getInstance().getProperty( "chatFrameHeight" ); 104 if( stringWidth == null ) stringWidth = "635"; 105 if( stringHeight == null ) stringHeight = "450"; 106 107 setSize( new Dimension( Integer.parseInt( stringWidth ), Integer.parseInt( stringHeight ) ) ); 108 DialogTracker.addDialog( this, true, false ); 109 110 addComponentListener( new ComponentAdapter() 111 { 112 public void componentResized( ComponentEvent e ) 113 { 114 saveStates(); 115 } 116 } ); 117 } 118 119 /** 120 * Marks a tab for a TabFramePanel if it's not already selected 121 * @param panel the panel to mark 122 */ 123 public void markTab( TabFramePanel panel ) 124 { 125 if( tabPane.getSelectedComponent() != panel ) 126 { 127 Integer i = (Integer)queueCounts.get( panel ); 128 if( i == null ) i = new Integer( 1 ); 129 130 int index = tabPane.indexOfComponent( (Component)panel ); 131 tabPane.setTitleAt( index, panel.getPanelName() + " (" + i.intValue() + ")" ); 132 133 queueCounts.put( panel, new Integer( i.intValue() + 1 ) ); 134 } 135 } 136 137 /** 138 * Saves the size of the chat frame 139 */ 140 public void saveStates() 141 { 142 Dimension size = getSize(); 143 Integer width = new Integer( (int)size.getWidth() ); 144 Integer height = new Integer( (int)size.getHeight() ); 145 Settings.getInstance().setProperty( "chatFrameWidth", width.toString() ); 146 Settings.getInstance().setProperty( "chatFrameHeight", height.toString() ); 147 } 148 149 /** 150 * Loads the saved settings from any previous settings 151 */ 152 private void setPreferredLocation() 153 { 154 //load the settings from the settings file 155 String xString = Settings.getInstance().getProperty( "chatFrameX" ); 156 String yString = Settings.getInstance().getProperty( "chatFrameY" ); 157 158 if( yString == null ) yString = "100"; 159 if( xString == null ) xString = "100"; 160 161 double x = Double.parseDouble( xString ); 162 double y = Double.parseDouble( yString ); 163 164 if( x < 0.0 ) x = 100.0; 165 if( y < 0.0 ) y = 100.0; 166 167 setLocation( (int)x, (int)y ); 168 } 169 170 /** 171 * Adds the various event listeners 172 * @author Adam Olsen 173 * @version 1.0 174 */ 175 private void addListeners() 176 { 177 MenuItemListener listener = new MenuItemListener(); 178 newItem.addActionListener( listener ); 179 leaveItem.addActionListener( listener ); 180 tabPane.addMouseListener( new MouseAdapter() 181 { 182 public void mouseReleased( MouseEvent e ) 183 { 184 TabFramePanel panel = (TabFramePanel)tabPane.getSelectedComponent(); 185 panel.focusYourself(); 186 } 187 } ); 188 } 189 190 /** 191 * Listens for a menu item to be clicked 192 * @author Adam Olsen 193 * @version 1.0 194 */ 195 private class MenuItemListener implements ActionListener 196 { 197 public void actionPerformed( ActionEvent e ) 198 { 199 if( e.getSource() == newItem ) new GroupChatBookmarks().setVisible( true ); 200 if( e.getSource() == leaveItem ) closeHandler(); 201 } 202 } 203 204 /** 205 * Updates the font in all the chat conversationareas 206 * @param font the font to update to 207 */ 208 public void updateStyles( Font font ) 209 { 210 for( int i = 0; i < tabPane.getTabCount(); i++ ) 211 { 212 TabFramePanel panel = (TabFramePanel)tabPane.getComponentAt( i ); 213 panel.updateStyle( font ); 214 } 215 } 216 217 218 /** 219 * Set the status in all the rooms 220 * @param mode the presence mode 221 * @param status the status string 222 **/ 223 public void setStatus( Presence.Mode mode, String status ) 224 { 225 for( int i = 0; i < tabPane.getTabCount(); i++ ) 226 { 227 TabFramePanel panel = (TabFramePanel)tabPane.getComponentAt( i ); 228 if( panel instanceof ChatRoomPanel ) 229 { 230 ChatRoomPanel window = (ChatRoomPanel)panel; 231 232 //set up a packet to be sent to my user in every groupchat 233 Presence presence = new Presence( Presence.Type.AVAILABLE, status, 0, mode ); 234 presence.setTo( window.getRoomName() + '/' + window.getNickname() ); 235 236 if( !BuddyList.getInstance().checkConnection() ) 237 { 238 BuddyList.getInstance().connectionError(); 239 return; 240 } 241 242 BuddyList.getInstance().getConnection().sendPacket( presence ); 243 } 244 } 245 } 246 247 /** 248 * This not only closes the window, but it leaves all the rooms like it should 249 **/ 250 public void closeHandler() 251 { 252 leaveAll(); 253 } 254 255 /** 256 * Since there is no way to check to see if a message is from someone in a chat room, we check to see if 257 * the message is coming from the same server as a chatroom we are in. 258 * @param server the server to check 259 **/ 260 public boolean isRoomOpen( String server ) 261 { 262 for( int i = 0; i < tabPane.getTabCount(); i++ ) 263 { 264 TabFramePanel panel = (TabFramePanel)tabPane.getComponentAt( i ); 265 if( panel instanceof ChatRoomPanel ) 266 { 267 ChatRoomPanel window = (ChatRoomPanel)panel; 268 if( server.toLowerCase().equals( window.getRoomName().toLowerCase() ) ) return true; 269 } 270 } 271 272 return false; 273 } 274 275 /** 276 * If there is a chatroom open in this frame with a server name, this returns the ChatRoomPanel that contains it 277 * @param server the name of the room to get the ChatRoomPanel for 278 * @return the ChatRoomPanel requested, or <tt>null</tt> if it could not be found 279 **/ 280 public ChatRoomPanel getChatPanel( String server ) 281 { 282 for( int i = 0; i < tabPane.getTabCount(); i++ ) 283 { 284 TabFramePanel panel = (TabFramePanel)tabPane.getComponentAt( i ); 285 if( panel instanceof ChatRoomPanel ) 286 { 287 ChatRoomPanel window = (ChatRoomPanel)panel; 288 if( server.toLowerCase().equals( window.getRoomName().toLowerCase() ) ) return window; 289 } 290 } 291 292 return null; 293 } 294 295 /** 296 * This leaves a chatroom and removes the associated ChatRoomPanel from the TabPane 297 * @param window the room to leave 298 **/ 299 public void removePanel( TabFramePanel panel ) 300 { 301 tabPane.remove( (Component)panel ); 302 tabPane.validate(); 303 304 if( panel instanceof ChatRoomPanel ) 305 { 306 ((ChatRoomPanel)panel).leave(); 307 } 308 } 309 310 /** 311 * Sets the subject of a ChatRoomPanel based on a message that was recieved from the GroupChat server with 312 * <subject> in it 313 * @param window the window to set the subject for 314 **/ 315 public void setSubject( ChatRoomPanel window ) 316 { 317 if( !( tabPane.getSelectedComponent() instanceof ChatRoomPanel ) ) return; 318 if( (ChatRoomPanel)tabPane.getSelectedComponent() == window ) 319 { 320 setTitle( resources.getString( "groupChat" ) + ": " + window.getRoomName() ); 321 validate(); 322 } 323 } 324 325 /** 326 * @param panel the panel to check 327 * @return true if the tab panel is currently displayed in the tab frame 328 */ 329 public boolean contains( TabFramePanel panel ) 330 { 331 for( int i = 0; i < tabPane.getTabCount(); i++ ) 332 { 333 TabFramePanel p = (TabFramePanel)tabPane.getComponentAt( i ); 334 if( p == panel ) return true; 335 } 336 337 return false; 338 } 339 340 /** 341 * Returns the number of rooms currently open in the frame 342 * @return the number of rooms still open 343 **/ 344 public int tabsLeft() { return tabPane.getTabCount(); } 345 346 /** 347 * Adds a chat room to the frame 348 * @param window the room to add 349 */ 350 public void addPanel( TabFramePanel panel ) 351 { 352 tabPane.addTab( panel.getPanelName(), (Component)panel ); 353 if( panel instanceof ChatRoomPanel ) 354 tabPane.setSelectedComponent( (Component)panel ); 355 panel.getInputComponent().addKeyListener( tabListener ); 356 panel.setListenersAdded( true ); 357 } 358 359 /** 360 * Switches the current tab in the tab frame 361 */ 362 public void switchTab() 363 { 364 int current = tabPane.getSelectedIndex(); 365 current++; 366 if( current >= tabPane.getTabCount() ) current = 0; 367 tabPane.setSelectedIndex( current ); 368 } 369 370 /** 371 * Switches the tab based on CTRL+n keys 372 */ 373 class GCTabHandler extends KeyAdapter 374 { 375 public void keyTyped( KeyEvent e ) 376 { 377 // get the ASCII character code for the character typed 378 int numPressed = (int)e.getKeyChar(); 379 380 // the integer characters start at ASCII table number 49, so we subtract 49 381 numPressed -= 49; 382 383 // if the new ASCII value is between 0 and 8, then the 384 // key pressed was 1 through 9 - which is what we want 385 // also check that the CTRL key was being held down 386 if( ( numPressed >= 0 && numPressed <= 8 ) && 387 ( e.getModifiers() & KeyEvent.CTRL_MASK ) == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ) 388 { 389 e.consume(); 390 391 if( tabPane.getTabCount() >= numPressed ) 392 tabPane.setSelectedIndex( numPressed ); 393 } 394 } 395 } 396 397 /** 398 * Leaves all chatrooms (for if they close the window) 399 **/ 400 public void leaveAll() 401 { 402 com.valhalla.Logger.debug( "There are " + tabPane.getTabCount() + " rooms" ); 403 404 int tabCount = tabPane.getTabCount(); 405 for( int i = 0; i < tabCount; i++ ) 406 { 407 TabFramePanel panel = (TabFramePanel)tabPane.getComponentAt( 0 ); 408 409 if( panel instanceof ChatRoomPanel ) 410 { 411 ChatRoomPanel window = (ChatRoomPanel)panel; 412 //if this frame is closed as a result of connection loss and we try to leave 413 //the channel, it will not work, so we need to catch it. 414 try { 415 window.leave(); 416 } 417 catch( IllegalStateException e ) 418 { 419 com.valhalla.Logger.debug( "Caught Illegal State Exception when leaving window: " + window.toString() ); 420 } 421 422 BuddyList.getInstance().removeTabPanel( panel ); 423 } 424 else { 425 ((ConversationPanel)panel).checkCloseHandler(); 426 } 427 } 428 429 BuddyList.getInstance().stopTabFrame(); 430 } 431 432 /** 433 * @return Returns the tabPane. 434 */ 435 public JTabbedPane getTabPane() { 436 return tabPane; 437 } 438 }