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    }