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.beans.PropertyChangeEvent;
024    import java.beans.PropertyChangeListener;
025    import java.util.Hashtable;
026    import java.util.Locale;
027    import java.util.ResourceBundle;
028    
029    import javax.swing.border.*;
030    import javax.swing.*;
031    
032    import org.jivesoftware.smack.packet.Presence;
033    import org.jivesoftware.smackx.muc.*;
034    
035    import com.valhalla.gui.DialogTracker;
036    import com.valhalla.gui.Standard;
037    import com.valhalla.jbother.groupchat.ChatRoomPanel;
038    import com.valhalla.jbother.groupchat.GroupChatBookmarks;
039    import com.valhalla.jbother.plugins.events.ExitingEvent;
040    import com.valhalla.jbother.jabber.BuddyStatus;
041    import com.valhalla.pluginmanager.PluginChain;
042    import com.valhalla.settings.Settings;
043    import net.infonode.tabbedpanel.*;
044    import net.infonode.tabbedpanel.titledtab.*;
045    import net.infonode.util.*;
046    import net.infonode.tabbedpanel.theme.*;
047    import net.infonode.gui.colorprovider.*;
048    import net.infonode.gui.hover.*;
049    
050    /**
051     * Contains all of the groupchat windows in tabs
052     *
053     * @author Adam Olsen
054     * @author Andrey Zakirov
055     * @version 1.1
056     */
057    public class TabFrame extends JFrame {
058        private ResourceBundle resources = ResourceBundle.getBundle(
059                "JBotherBundle", Locale.getDefault());
060    
061        private JPanel container = new JPanel(new BorderLayout());
062    
063        private TabbedPanel tabPane = new TabbedPanel();
064    
065        private TabListener tabListener = null;
066    
067        private JMenuBar menuBar = new JMenuBar();
068    
069        private JMenu optionMenu = new JMenu(resources.getString("options"));
070    
071        private JMenuItem newItem = new JMenuItem(resources.getString("joinRoom")),
072                leaveItem = new JMenuItem(resources.getString("leaveAll")),
073                closeItem = new JMenuItem(resources.getString("closeButton"));
074    
075        private Hashtable queueCounts = new Hashtable();
076    
077        private GCTabHandler switchListener = new GCTabHandler();
078    
079        private WindowAdapter windowListener = null;
080    
081        private TabFocusListener tabFocusListener = null;
082    
083        private CloseMenu close = new CloseMenu();
084    
085        private JSplitPane pane;
086    
087        private static boolean tabListenerAdded = false;
088    
089        private MyFocusListener focusListener = new MyFocusListener();
090        private javax.swing.Timer focusTimer = new javax.swing.Timer( 50, focusListener );
091        //private ShapedGradientTheme theme = new ShapedGradientTheme();
092        //private SmallFlatTheme theme = new SmallFlatTheme();
093        private ShapedGradientTheme theme = new ShapedGradientTheme(0f, 0f, new FixedColorProvider(new Color(150, 150, 150)),null);
094    
095        /**
096         * Constructor sets the frame up and adds a listener to the JTabPane so that
097         * if a tab is changed the title of this frame reflects the topic and the
098         * name of the room in the tab
099         */
100        public TabFrame() {
101            super("JBother");
102    
103            setIconImage(Standard.getImage("frameicon.png"));
104    
105            setContentPane(container);
106            container.add(tabPane, BorderLayout.CENTER);
107    
108            // add the tab switch listener so the title of the frame reflects the
109            // current tab
110            tabListener = new TabAdapter() {
111                public void tabSelected(TabStateChangedEvent e) {
112                    if( tabPane.getSelectedTab() == null) return;
113                    if (tabPane.getSelectedTab().getContentComponent() != null) {
114                        final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
115                        setTitle(panel.getWindowTitle());
116                        focusComponent( panel.getInputComponent() );
117                        clearTab(panel);
118                    }
119                }
120            };
121    
122            tabFocusListener = new TabFocusListener();
123    
124            optionMenu.add(newItem);
125            optionMenu.add(leaveItem);
126    
127            addListeners();
128    
129            menuBar.add(optionMenu);
130            windowListener = new WindowAdapter() {
131                public void windowClosing(WindowEvent e) {
132                    saveStates();
133                    closeHandler();
134                }
135            };
136    
137            // if they press the close button, we wanna handle leaving of the rooms
138            addWindowListener(windowListener);
139    
140            setPreferredLocation();
141            pack();
142    
143            String stringWidth = Settings.getInstance().getProperty(
144                    "chatFrameWidth");
145            String stringHeight = Settings.getInstance().getProperty(
146                    "chatFrameHeight");
147    
148            if (stringWidth == null)
149                stringWidth = "635";
150            if (stringHeight == null)
151                stringHeight = "450";
152    
153            setSize(new Dimension(Integer.parseInt(stringWidth), Integer
154                    .parseInt(stringHeight)));
155            DialogTracker.addDialog(this, true, false);
156    
157            addComponentListener(new ComponentAdapter() {
158                public void componentResized(ComponentEvent e) {
159                    saveStates();
160                }
161            });
162    
163            com.valhalla.Logger.debug("TabFrame is being created");
164    
165            addComponentListener(new MoveListener());
166            addTabListeners();
167        }
168    
169        /**
170         * When the focus is gained on a tab, this class focuses the input
171         * component for this tab
172         */
173        class TabFocusListener implements FocusListener {
174            public void focusLost(FocusEvent e) {
175            }
176    
177            public void focusGained(FocusEvent e) {
178                final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
179                if (panel != null)
180                {
181                    focusComponent( panel.getInputComponent() );
182                }
183                if (panel instanceof ConversationPanel)
184                {
185                    (((ConversationPanel) panel).getBuddy()).sendNotDisplayedID();
186                }
187            }
188        }
189    
190        /**
191         * Focuses the input component on a tab
192         */
193        private void focusComponent( Component comp )
194        {
195            focusListener.setComponent( comp );
196            if( !focusTimer.isRunning() ) focusTimer.start();
197            else focusTimer.restart();
198        }
199    
200        /**
201         * Waits a few seconds to focus the new component when a tab is selected
202         */
203        class MyFocusListener implements ActionListener
204        {
205            private Component comp = null;
206            public void setComponent( Component comp ) { this.comp = comp; }
207            public void actionPerformed( ActionEvent e )
208            {
209                SwingUtilities.invokeLater( new Runnable()
210                {
211                    public void run() { if( comp != null ) comp.requestFocus(); comp = null; }
212                } );
213    
214                //comp = null;
215    
216                focusTimer.stop();
217            }
218        }
219    
220        /**
221         * Adds event listeners to this tab frame
222         */
223        private void addTabListeners() {
224            tabPane.addTabListener(tabListener);
225            tabPane.addFocusListener(tabFocusListener);
226            KeyboardFocusManager.getCurrentKeyboardFocusManager()
227                   .addKeyEventPostProcessor(switchListener);
228    
229    
230            Direction d = Direction.DOWN;
231            String dSetting = Settings.getInstance().getProperty( "tabOrientation", "Down" );
232            if( dSetting.equals( "Up" ) ) d = Direction.UP;
233            else if( dSetting.equals( "Right" ) ) d = Direction.RIGHT;
234            else if( dSetting.equals( "Left" ) ) d = Direction.LEFT;
235    
236            tabPane.getProperties().setTabAreaOrientation(d);
237            tabPane.getProperties().setAutoSelectTab(true);
238            tabPane.getProperties().setTabReorderEnabled(true);
239            tabPane.getProperties().setTabLayoutPolicy(TabLayoutPolicy.COMPRESSION);
240            tabPane.getProperties().setTabDropDownListVisiblePolicy(TabDropDownListVisiblePolicy.MORE_THAN_ONE_TAB);
241            tabPane.getProperties().addSuperObject(theme.getTabbedPanelProperties());
242        }
243    
244        /**
245         * Removes all event listeners from the TabbedPanel
246         */
247        public void removeTabListeners() {
248            tabPane.removeTabListener(tabListener);
249            tabPane.removeFocusListener(tabFocusListener);
250    
251            KeyboardFocusManager.getCurrentKeyboardFocusManager()
252                    .removeKeyEventPostProcessor(switchListener);
253        }
254    
255        /**
256         * Docks the BuddyList to this frame
257         * @param list The buddy list to dock
258         */
259        public void dockBuddyList(final BuddyList list) {
260            container.remove(tabPane);
261            pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
262            pane.setResizeWeight(.7);
263    
264            String dockWhere = Settings.getInstance().getProperty("dockOption", "Left");
265            if( dockWhere.equals( "Left" ) )
266            {
267                pane.add(list);
268                pane.add(tabPane);
269            }
270            else {
271                pane.add(tabPane);
272                pane.add(list);
273            }
274    
275            container.add(pane, BorderLayout.CENTER);
276            removeWindowListener(windowListener);
277            windowListener = new WindowAdapter() {
278                public void windowClosing(WindowEvent e) {
279                    saveStates();
280    
281                    ExitingEvent event = new ExitingEvent(list);
282                    PluginChain.fireEvent(event);
283                    if (event.getExit()) {
284                        closeHandler();
285                        list.quitHandler();
286                    }
287                }
288            };
289    
290            addWindowListener(windowListener);
291    
292            String divLocString = Settings.getInstance().getProperty(
293                    "dockedBuddyListDivLocation");
294            int divLoc = 150;
295    
296            try {
297                if (divLocString != null) {
298                    divLoc = Integer.parseInt(divLocString);
299                }
300    
301            } catch (NumberFormatException ex) {
302            }
303    
304            addComponentListener(new MoveListener());
305    
306            pane.setDividerLocation(divLoc);
307            pane.addPropertyChangeListener("lastDividerLocation",
308                    new DividerListener());
309    
310            validate();
311        }
312    
313        /**
314         * Listens for movement in the tab frame, and saves the new position
315         */
316        class MoveListener extends ComponentAdapter {
317            public void componentMoved(ComponentEvent e) {
318                saveStates();
319            }
320        }
321    
322        /**
323         * Undocks the BuddyList from this tab frame
324         */
325        public void undock() {
326            container.remove(tabPane);
327            removeTabListeners();
328            TabFrame frame = new TabFrame();
329            frame.setTabPane(tabPane);
330            BuddyList.getInstance().setTabFrame(frame);
331            frame.setVisible(true);
332            dispose();
333            if (tabPane.getTabCount() > 0 && tabPane.getTabAt(0) != null)
334                tabPane.setSelectedTab(tabPane.getTabAt(0));
335        }
336    
337        /**
338         * Sets the tab pane to be used for this frame
339         * This is done because unless a new tab pane is created when docking
340         * windows, weird things happen
341         * @param pane the new TabbedPanel
342         */
343        private void setTabPane(TabbedPanel pane) {
344            try {
345                remove(this.tabPane);
346            } catch (Exception e) {
347            }
348            this.tabPane = pane;
349            com.valhalla.Logger.debug("Setting tab pane");
350    
351            addTabListeners();
352            getContentPane().add(pane, BorderLayout.CENTER);
353            validate();
354        }
355    
356        /**
357         * Listens for the user to move the divider, and saves it's location
358         *
359         * @author Adam Olsen
360         * @version 1.0
361         */
362        private class DividerListener implements PropertyChangeListener {
363            public void propertyChange(PropertyChangeEvent e) {
364                Settings.getInstance().setProperty("dockedBuddyListDivLocation",
365                        e.getOldValue().toString());
366            }
367        }
368    
369        /**
370         * Marks a tab for a TabFramePanel if it's not already selected
371         *
372         * @param panel
373         *            the panel to mark
374         */
375        public void markTab(TabFramePanel panel, boolean messageToMe) {
376            if( tabPane.getSelectedTab() == null ) return;
377            if (tabPane.getSelectedTab().getContentComponent() != panel) {
378                Integer i = (Integer) queueCounts.get(panel);
379                if (i == null)
380                    i = new Integer(1);
381    
382                int index = tabPane.getTabIndex(panel.getTab());
383                if (index == -1) return;
384    
385                Icon icon = Standard.getIcon("images/newmessage.png");
386                if( messageToMe ) icon = Standard.getIcon("images/newhighlight.png");
387    
388                TitledTab tab = panel.getTab();
389                String tip = panel.getPanelToolTip();
390                tip = htmlPad("<b>" + tip + "</b><br>" +  i + " new messages");
391                tab.setToolTipText(tip);
392                tab.setIcon(icon);
393    
394                queueCounts.put(panel, new Integer(i.intValue() + 1));
395            }
396        }
397    
398        /**
399         * Returns an html enclosed and 3 pixel padding string for tab tooltips
400         * @return html for tab tooltips
401         */
402        private String htmlPad(String html)
403        {
404            return "<html><div style='padding: 3px;'>" + html + "</div></html>";
405        }
406    
407        /**
408         * Clears the tab message queue count
409         */
410        public void clearTab(TabFramePanel panel) {
411    
412            String name = panel.getPanelName();
413            TitledTab tab = panel.getTab();
414            if( tab == null ) return;
415    
416            tab.setToolTipText(htmlPad(panel.getPanelToolTip()));
417    
418            tab.setIcon(Standard.getIcon("images/nomessage.png"));
419            queueCounts.remove(panel);
420            if (panel instanceof ChatRoomPanel) {
421                ((ChatRoomPanel) panel).resetMessageToMe();
422            }
423        }
424    
425        /**
426         * Saves the size of the chat frame
427         */
428        public void saveStates() {
429            if (isVisible()) {
430                Point location = new Point(getLocationOnScreen());
431                Settings.getInstance().setProperty("tabFrameX",
432                        new Double(location.getX()).toString());
433                Settings.getInstance().setProperty("tabFrameY",
434                        new Double(location.getY()).toString());
435            } else {
436                com.valhalla.Logger.debug("TabFrame is not visible");
437            }
438    
439            Dimension size = getSize();
440            Integer width = new Integer((int) size.getWidth());
441            Integer height = new Integer((int) size.getHeight());
442            Settings.getInstance().setProperty("chatFrameWidth", width.toString());
443            Settings.getInstance()
444                    .setProperty("chatFrameHeight", height.toString());
445        }
446    
447        /**
448         * Switches the current tab in the tab frame
449         */
450        public void switchTab(TabbedPanel tabPane) {
451            com.valhalla.Logger.debug("Switching the tab");
452            int current = tabPane.getTabIndex(tabPane.getSelectedTab());
453            current++;
454            if (current >= tabPane.getTabCount())
455                current = 0;
456            tabPane.setSelectedTab(tabPane.getTabAt(current));
457            TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(current).getContentComponent();
458            if (panel != null)
459                focusComponent( panel.getInputComponent() );
460        }
461    
462        /**
463         * Loads the saved settings from any previous settings
464         */
465        private void setPreferredLocation() {
466            //load the settings from the settings file
467            String xString = Settings.getInstance().getProperty("tabFrameX");
468            String yString = Settings.getInstance().getProperty("tabFrameY");
469    
470            if (yString == null)
471                yString = "100";
472            if (xString == null)
473                xString = "100";
474    
475            double x = 100;
476            double y = 100;
477    
478            try {
479                x = Double.parseDouble(xString);
480                y = Double.parseDouble(yString);
481            } catch (NumberFormatException e) {
482                com.valhalla.Logger.logException(e);
483            }
484    
485            if (x < -50.0)
486                x = 100.0;
487            if (y < -50.0)
488                y = 100.0;
489    
490            setLocation((int) x, (int) y);
491        }
492    
493        /**
494         * Adds the various event listeners
495         *
496         * @author Adam Olsen
497         * @version 1.0
498         */
499        private void addListeners() {
500            MenuItemListener listener = new MenuItemListener();
501            newItem.addActionListener(listener);
502            leaveItem.addActionListener(listener);
503        }
504    
505        public void addFrameListener (BuddyStatus buddy)
506        {
507            final BuddyStatus buddy2=buddy;
508            addWindowFocusListener ( new WindowFocusListener() {
509                public void windowGainedFocus (WindowEvent e) {
510                    SwingUtilities.invokeLater(new Runnable() {
511                        public void run() {
512                            buddy2.sendNotDisplayedID();
513                        }
514                    });
515                }
516                public void windowLostFocus(WindowEvent e) {
517                }
518            });
519    
520        }
521    
522        /**
523         * Listens for a menu item to be clicked
524         *
525         * @author Adam Olsen
526         * @version 1.0
527         */
528        private class MenuItemListener implements ActionListener {
529            public void actionPerformed(ActionEvent e) {
530                if (e.getSource() == newItem)
531                    new GroupChatBookmarks(TabFrame.this).setVisible(true);
532                if (e.getSource() == leaveItem)
533                    closeHandler();
534            }
535        }
536    
537        /**
538         * Updates the font in all the chat conversationareas
539         *
540         * @param font
541         *            the font to update to
542         */
543        public void updateStyles(Font font) {
544            for (int i = 0; i < tabPane.getTabCount(); i++) {
545                TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
546                panel.updateStyle(font);
547            }
548        }
549    
550        /**
551         * Set the status in all the rooms
552         *
553         * @param mode
554         *            the presence mode
555         * @param status
556         *            the status string
557         */
558        public void setStatus(Presence.Mode mode, String status) {
559            for (int i = 0; i < tabPane.getTabCount(); i++) {
560                TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
561                if (panel instanceof ChatRoomPanel) {
562                    ChatRoomPanel window = (ChatRoomPanel) panel;
563                    MultiUserChat chat = window.getChat();
564                    if( chat == null || !chat.isJoined() ) continue;
565    
566                    //set up a packet to be sent to my user in every groupchat
567                    Presence presence = new Presence(Presence.Type.AVAILABLE,
568                            status, 0, mode);
569                    presence.setTo(window.getRoomName() + '/'
570                            + window.getNickname());
571    
572                    if (!BuddyList.getInstance().checkConnection()) {
573                        BuddyList.getInstance().connectionError();
574                        return;
575                    }
576    
577                    BuddyList.getInstance().getConnection().sendPacket(presence);
578                }
579            }
580        }
581    
582        /**
583         * This not only closes the window, but it leaves all the rooms like it
584         * should
585         */
586        public void closeHandler() {
587            removeTabListeners();
588            leaveAll();
589        }
590    
591        /**
592         * Since there is no way to check to see if a message is from someone in a
593         * chat room, we check to see if the message is coming from the same server
594         * as a chatroom we are in.
595         *
596         * @param server
597         *            the server to check
598         */
599        public boolean isRoomOpen(String server) {
600            for (int i = 0; i < tabPane.getTabCount(); i++) {
601                TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
602                if (panel instanceof ChatRoomPanel) {
603                    ChatRoomPanel window = (ChatRoomPanel) panel;
604                    if (server.toLowerCase().equals(
605                            window.getRoomName().toLowerCase()))
606                        return true;
607                }
608            }
609    
610            return false;
611        }
612    
613        /**
614         * If there is a chatroom open in this frame with a server name, this
615         * returns the ChatRoomPanel that contains it
616         *
617         * @param server
618         *            the name of the room to get the ChatRoomPanel for
619         * @return the ChatRoomPanel requested, or <tt>null</tt> if it could not
620         *         be found
621         */
622        public ChatRoomPanel getChatPanel(String server) {
623            for (int i = 0; i < tabPane.getTabCount(); i++) {
624                TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
625                if (panel instanceof ChatRoomPanel) {
626                    ChatRoomPanel window = (ChatRoomPanel) panel;
627                    if (server.toLowerCase().equals(
628                            window.getRoomName().toLowerCase()))
629                        return window;
630                }
631            }
632    
633            return null;
634        }
635    
636        /**
637         * This leaves a chatroom and removes the associated ChatRoomPanel from the
638         * TabPane
639         *
640         * @param window
641         *            the room to leave
642         */
643        public void removePanel(TabFramePanel panel) {
644            queueCounts.remove(panel);
645            TitledTab tab = panel.getTab();
646            tab.setHighlightedStateTitleComponent(null);
647            tab.setNormalStateTitleComponent(null);
648            tab.setDisabledStateTitleComponent(null);
649            tab.getProperties().setHoverListener(null);
650    
651            tabPane.removeTab(tab);
652            tabPane.validate();
653            if(panel instanceof ConversationPanel) MessageDelegator.getInstance().removePanel((ConversationPanel)panel);
654    
655            if (panel instanceof ChatRoomPanel) {
656                ((ChatRoomPanel) panel).leave();
657                ((ChatRoomPanel) panel).removed();
658                panel = null;
659            }
660    
661            try {
662                panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
663            }
664            catch( NullPointerException npe ) { panel = null; }
665    
666            if (panel != null) {
667                setTitle(panel.getWindowTitle());
668                TitledTab t = panel.getTab();
669                t.setText(panel.getPanelName());
670             } else {
671                setTitle("JBother");
672            }
673    
674            BuddyList.getInstance().stopTabFrame();
675        }
676    
677        /**
678         * Sets the subject of a ChatRoomPanel based on a message that was received
679         * from the GroupChat server with &lt;subject&gt; in it
680         *
681         * @param window
682         *            the window to set the subject for
683         */
684        public void setSubject(ChatRoomPanel window) {
685            if( tabPane.getSelectedTab() == null) return;
686            if (!(tabPane.getSelectedTab().getContentComponent() instanceof ChatRoomPanel))
687                return;
688            if ((ChatRoomPanel) tabPane.getSelectedTab().getContentComponent() == window) {
689                setTitle(resources.getString("groupChat") + ": "
690                        + window.getRoomName());
691                validate();
692            }
693        }
694    
695        /**
696         * @param panel
697         *            the panel to check
698         * @return true if the tab panel is currently displayed in the tab frame
699         */
700        public boolean contains(TabFramePanel panel) {
701            for (int i = 0; i < tabPane.getTabCount(); i++) {
702                TabFramePanel p = (TabFramePanel) tabPane.getTabAt(i).getContentComponent();
703                if (p == panel)
704                    return true;
705            }
706    
707            return false;
708        }
709    
710        /**
711         * Returns the number of rooms currently open in the frame
712         *
713         * @return the number of rooms still open
714         */
715        public int tabsLeft() {
716            return tabPane.getTabCount();
717        }
718    
719        /**
720         * Adds a chat room to the frame
721         *
722         * @param window
723         *            the room to add
724         */
725        public void addPanel(final TabFramePanel panel) {
726            panel.setListenersAdded(true);
727    
728            String name = panel.getPanelName();
729            String orient = Settings.getInstance().getProperty("tabOrientation", "Down");
730    
731            final TitledTab tab = new TitledTab( name, Standard.getIcon("images/nomessage.png"), 
732                (JComponent) panel, null );
733            tab.setToolTipText(htmlPad(panel.getPanelToolTip()));
734            
735            tab.getProperties().addSuperObject(theme.getTitledTabProperties());
736    
737            CloseButton b = new CloseButton(tab);
738            final CloseButton temp = b;
739            tab.setHighlightedStateTitleComponent(b);
740    
741            if( !Settings.getInstance().getBoolean("closeButtonOnAll")) b = null;
742            tab.setNormalStateTitleComponent(b);
743            tab.setDisabledStateTitleComponent(b);
744            tab.addMouseListener(new MouseAdapter()
745            {
746                public void mousePressed(MouseEvent e)
747                {
748                   if(e.isPopupTrigger())
749                    {
750                        close.setTab(tab);
751                        close.show(tab, e.getX(), e.getY());
752                    }
753    
754                }
755    
756            } );
757    
758            tab.getProperties().setHoverListener( new HoverListener()
759                {
760                    public void mouseEntered(HoverEvent e)
761                    {
762                        if(Settings.getInstance().getBoolean("closeButtonOnAll")) return;
763                        tab.setNormalStateTitleComponent(temp);
764                        tab.setDisabledStateTitleComponent(temp);
765                        tab.validate();
766                    }
767    
768                    public void mouseExited(HoverEvent e)
769                    {
770                        if(Settings.getInstance().getBoolean("closeButtonOnAll")) return;
771                        tab.setNormalStateTitleComponent(null);
772                        tab.setDisabledStateTitleComponent(null);
773                        tab.validate();
774                    }
775                } );
776    
777    
778    
779            tabPane.addTab( tab );
780            panel.setTab( tab );
781            if (panel instanceof ChatRoomPanel)
782                tabPane.setSelectedTab(tab);
783        }
784    
785        /**
786         * Revalidates the close button on a tab
787         * @param selected whether or not this tab is selected
788         */
789        public void resetCloseButtons(boolean selected)
790        {
791            for( int i = 0; i < tabPane.getTabCount(); i++ )
792            {
793                TitledTab tab = (TitledTab)tabPane.getTabAt(i);
794                CloseButton button = new CloseButton(tab);
795                tab.setHighlightedStateTitleComponent(button);
796                if( !selected) button = null;
797    
798                tab.setNormalStateTitleComponent(button);
799                tab.setDisabledStateTitleComponent(button);
800                tab.validate();
801            }
802        }
803    
804    
805        /**
806         * Switches the tab based on CTRL+n keys
807         */
808        class GCTabHandler implements KeyEventPostProcessor {
809            boolean first = false;
810    
811            public boolean postProcessKeyEvent(KeyEvent e) {
812                Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager()
813                        .getFocusedWindow();
814    
815                if (!(w instanceof TabFrame))
816                    return false;
817                TabbedPanel tabPane = ((TabFrame) w).getTabPane();
818    
819                // get the ASCII character code for the character typed
820                int numPressed = (int) e.getKeyChar();
821    
822                int mask = KeyEvent.CTRL_MASK;
823                if (System.getProperty("mrj.version") != null) {
824                    mask = KeyEvent.META_DOWN_MASK;
825                }
826    
827                // the integer characters start at ASCII table number 49, so we
828                // subtract 49
829                numPressed -= 49;
830    
831                // if the new ASCII value is between 0 and 8, then the
832                // key pressed was 1 through 9 - which is what we want
833                // also check that the CTRL key was being held down
834                if ((numPressed >= 0 && numPressed <= 8)
835                        && (e.getModifiers() & mask) == Toolkit.getDefaultToolkit()
836                                .getMenuShortcutKeyMask()) {
837                    e.consume();
838    
839                    if (tabPane.getTabCount() >= numPressed)
840                        tabPane.setSelectedTab(tabPane.getTabAt(numPressed));
841                } else if (e.getKeyCode() == KeyEvent.VK_TAB
842                        && (e.getModifiers() & mask) == Toolkit.getDefaultToolkit()
843                                .getMenuShortcutKeyMask()) {
844                    if (first == false) {
845                        first = true;
846                    } else {
847                        switchTab(tabPane);
848                        first = false;
849    
850                        final TabFramePanel panel = (TabFramePanel) tabPane.getSelectedTab().getContentComponent();
851                        focusComponent( panel.getInputComponent() );
852    
853                    }
854                }
855    
856                return true;
857            }
858        }
859    
860        /**
861         * Leaves all chatrooms (for if they close the window)
862         */
863        public void leaveAll() {
864            com.valhalla.Logger.debug("There are " + tabPane.getTabCount()
865                    + " rooms");
866    
867            int tabCount = tabPane.getTabCount();
868            for (int i = 0; i < tabCount; i++) {
869                TabFramePanel panel = (TabFramePanel) tabPane.getTabAt(0).getContentComponent();
870    
871                if (panel instanceof ChatRoomPanel) {
872                    ChatRoomPanel window = (ChatRoomPanel) panel;
873                    //if this frame is closed as a result of connection loss and we
874                    // try to leave
875                    //the channel, it will not work, so we need to catch it.
876                    window.removed();
877                    try {
878                        window.leave();
879                        
880                    } catch (IllegalStateException e) {
881                        com.valhalla.Logger
882                                .debug("Caught Illegal State Exception when leaving window: "
883                                        + window.toString());
884                    }
885    
886                    BuddyList.getInstance().removeTabPanel(panel);
887                } else {
888                    ((ConversationPanel) panel).checkCloseHandler();
889                }
890            }
891    
892            BuddyList.getInstance().stopTabFrame();
893        }
894    
895        /**
896         * @return Returns the tabPane.
897         */
898        public TabbedPanel getTabPane() {
899            return tabPane;
900        }
901    
902        class CloseMenu extends JPopupMenu
903        {
904            JMenuItem closeItem = new JMenuItem(resources.getString("closeButton"));
905            JMenuItem closeAll = new JMenuItem(resources.getString("closeAllButton"));
906            private TitledTab tab;
907            public CloseMenu()
908            {
909                add(closeItem);
910                add(closeAll);
911    
912                closeItem.addActionListener(new ActionListener()
913                {
914                    public void actionPerformed(ActionEvent e)
915                    {
916                        if( tab == null ) return;
917                        TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
918    
919                        if (panel instanceof ConversationPanel) {
920                            ((ConversationPanel) panel).checkCloseHandler();
921                        } else {
922                            removePanel(panel);
923                        }
924                    }
925    
926                } );
927    
928                closeAll.addActionListener(new ActionListener()
929                {
930                    public void actionPerformed(ActionEvent e)
931                    {
932                        int count = tabPane.getTabCount();
933                        for( int i = 0; i < count; i++ )
934                        {
935                            TitledTab tab = (TitledTab)tabPane.getTabAt(0);
936                            TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
937    
938                            if (panel instanceof ConversationPanel) {
939                                ((ConversationPanel) panel).checkCloseHandler();
940                            } else {
941                                removePanel(panel);
942                            }
943                        }
944                    }
945    
946                } );
947            }
948    
949            public void setTab(TitledTab tab)
950            {
951                this.tab = tab;
952            }
953        }
954    
955    
956        class CloseButton extends JLabel
957        {
958            TitledTab tab;
959            public CloseButton( final TitledTab tab )
960            {
961                super(Standard.getIcon("images/buttons/close.png"));
962                setPreferredSize( new Dimension( 15, 8 ) );
963                this.tab = tab;
964                setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
965                setToolTipText( resources.getString( "closeButton" ) );
966    
967                addMouseListener( new MouseAdapter()
968                {
969                    public void mouseEntered( MouseEvent e )
970                    {
971                        setBorder(BorderFactory.createEtchedBorder());
972                        validate();
973                    }
974    
975                    public void mouseExited( MouseEvent e )
976                    {
977                        setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
978                        validate();
979                    }
980    
981                    public void mouseClicked( MouseEvent e )
982                    {
983                        TabFramePanel panel = (TabFramePanel)tab.getContentComponent();
984    
985                        if (panel instanceof ConversationPanel) {
986                            ((ConversationPanel) panel).checkCloseHandler();
987                        } else {
988                            removePanel(panel);
989                        }
990    
991                    }
992    
993                } );
994            }
995    
996        }
997    }