/* * David Bismut, davidou@mageos.com * Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 * Ecole des Mines de Nantes, France */ package tab; import java.awt.Window; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.util.EventListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JTabbedPane; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.event.EventListenerList; import javax.swing.plaf.TabbedPaneUI; /** * A JTabbedPane with some added UI functionalities. A close and max/detach icons are added to every tab, typically to * let the user close or detach the tab by clicking on these icons. * * @version 1.1 06/07/04 * @author David Bismut, davidou@mageos.com */ public class CloseTabbedPane extends JTabbedPane { private int overTabIndex = -1; private final CloseTabPaneUI paneUI; //private CloseTabProxyUI paneUI; public CloseTabbedPane() { super( SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT ); super.setOpaque( true ); this.paneUI = new CloseTabPaneEnhancedUI(); //paneUI = (CloseTabProxyUI) CloseTabProxyUI.createUI(this);//new CloseTabProxyUI((TabbedPaneUI)UIManager.getUI(this)); super.setUI( this.paneUI ); } /** * Returns the index of the last tab on which the mouse did an action. */ public int getOverTabIndex() { return this.overTabIndex; } /** * Returns <code>true</code> if the close icon is enabled. */ public boolean isCloseEnabled() { return this.paneUI.isCloseEnabled(); } /** * Override JTabbedPane method. Does nothing. */ @Override public void setUI( final TabbedPaneUI ui ) { } /** * Sets whether the tabbedPane should have a close icon or not. * * @param b whether the tabbedPane should have a close icon or not */ public void setCloseIconStyle( final int style ) { this.paneUI.setCloseIconStyle( style ); } /** * Detaches the <code>index</code> tab in a seperate frame. When the frame is closed, the tab is automatically * reinserted into the tabbedPane. * * @param index index of the tabbedPane to be detached */ public void detachTab( final int index ) { if ( index < 0 || index >= this.getTabCount() ) { return; } final JFrame frame = new JFrame(); Window parentWindow = SwingUtilities.windowForComponent( this ); final int tabIndex = index; final JComponent c = (JComponent) this.getComponentAt( tabIndex ); final Icon icon = this.getIconAt( tabIndex ); final String title = this.getTitleAt( tabIndex ); final String toolTip = this.getToolTipTextAt( tabIndex ); final Border border = c.getBorder(); this.removeTabAt( index ); c.setPreferredSize( c.getSize() ); frame.setTitle( title ); frame.getContentPane().add( c ); frame.setLocation( parentWindow.getLocation() ); frame.pack(); frame.addWindowListener( new WindowAdapter() { @Override public void windowClosing( final WindowEvent event ) { frame.dispose(); CloseTabbedPane.this.insertTab( title, icon, c, toolTip, Math.min( tabIndex, CloseTabbedPane.this.getTabCount() ) ); c.setBorder( border ); CloseTabbedPane.this.setSelectedComponent( c ); } } ); WindowFocusListener windowFocusListener = new WindowFocusListener() { long start; long end; public void windowGainedFocus( WindowEvent e ) { this.start = System.currentTimeMillis(); } public void windowLostFocus( WindowEvent e ) { this.end = System.currentTimeMillis(); long elapsed = this.end - this.start; //System.out.println(elapsed); if ( elapsed < 100 ) { frame.toFront(); } frame.removeWindowFocusListener( this ); } }; /* * This is a small hack to avoid Windows GUI bug, that prevent a new window from stealing focus (without this * windowFocusListener, most of the time the new frame would just blink from foreground to background). A * windowFocusListener is added to the frame, and if the time between the frame beeing in foreground and the * frame beeing in background is less that 100ms, it just brings the windows to the front once again. Then it * removes the windowFocusListener. Note that this hack would not be required on Linux or UNIX based systems. */ frame.addWindowFocusListener( windowFocusListener ); frame.setVisible( true ); frame.toFront(); } /** * Adds a <code>CloseListener</code> to the tabbedPane. * * @param l the <code>CloseListener</code> to add * @see #fireCloseTabEvent * @see #removeCloseListener */ public synchronized void addCloseListener( final CloseListener l ) { this.listenerList.add( CloseListener.class, l ); } /** * Adds a <code>MaxListener</code> to the tabbedPane. * * @param l the <code>MaxListener</code> to add * @see #fireMaxTabEvent * @see #removeMaxListener */ public synchronized void addMaxListener( final MaxListener l ) { this.listenerList.add( MaxListener.class, l ); } /** * Adds a <code>DoubleClickListener</code> to the tabbedPane. * * @param l the <code>DoubleClickListener</code> to add * @see #fireDoubleClickTabEvent * @see #removeDoubleClickListener */ public synchronized void addDoubleClickListener( final DoubleClickListener l ) { this.listenerList.add( DoubleClickListener.class, l ); } /** * Adds a <code>PopupOutsideListener</code> to the tabbedPane. * * @param l the <code>PopupOutsideListener</code> to add * @see #firePopupOutsideTabEvent * @see #removePopupOutsideListener */ public synchronized void addPopupOutsideListener( final PopupOutsideListener l ) { this.listenerList.add( PopupOutsideListener.class, l ); } /** * Removes a <code>CloseListener</code> from this tabbedPane. * * @param l the <code>CloseListener</code> to remove * @see #fireCloseTabEvent * @see #addCloseListener */ public synchronized void removeCloseListener( final CloseListener l ) { this.listenerList.remove( CloseListener.class, l ); } /** * Removes a <code>MaxListener</code> from this tabbedPane. * * @param l the <code>MaxListener</code> to remove * @see #fireMaxTabEvent * @see #addMaxListener */ public synchronized void removeMaxListener( final MaxListener l ) { this.listenerList.remove( MaxListener.class, l ); } /** * Removes a <code>DoubleClickListener</code> from this tabbedPane. * * @param l the <code>DoubleClickListener</code> to remove * @see #fireDoubleClickTabEvent * @see #addDoubleClickListener */ public synchronized void removeDoubleClickListener( final DoubleClickListener l ) { this.listenerList.remove( DoubleClickListener.class, l ); } /** * Removes a <code>PopupOutsideListener</code> from this tabbedPane. * * @param l the <code>PopupOutsideListener</code> to remove * @see #firePopupOutsideTabEvent * @see #addPopupOutsideListener */ public synchronized void removePopupOutsideListener( final PopupOutsideListener l ) { this.listenerList.remove( PopupOutsideListener.class, l ); } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to every <code>CloseListener</code>. The * method also updates the <code>overTabIndex</code> of the tabbedPane with a value coming from the UI. This * method method is called each time a <code>MouseEvent</code> is received from the UI when the user clicks on the * close icon of the tab which index is <code>overTabIndex</code>. * * @param e the <code>MouseEvent</code> to be sent * @param overTabIndex the index of a tab, usually the tab over which the mouse is * @see #addCloseListener * @see EventListenerList */ public void fireCloseTabEvent( final MouseEvent e, final int overTabIndex ) { this.overTabIndex = overTabIndex; EventListener closeListeners[] = this.getListeners( CloseListener.class ); for ( int i = 0; i < closeListeners.length; i++ ) { ( (CloseListener) closeListeners[ i ] ).closeOperation( e, overTabIndex ); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to every <code>MaxListener</code>. The * method also updates the <code>overTabIndex</code> of the tabbedPane with a value coming from the UI. This * method method is called each time a <code>MouseEvent</code> is received from the UI when the user clicks on the * max icon of the tab which index is <code>overTabIndex</code>. * * @param e the <code>MouseEvent</code> to be sent * @param overTabIndex the index of a tab, usually the tab over which the mouse is * @see #addMaxListener * @see EventListenerList */ public void fireMaxTabEvent( final MouseEvent e, final int overTabIndex ) { this.overTabIndex = overTabIndex; EventListener maxListeners[] = this.getListeners( MaxListener.class ); for ( int i = 0; i < maxListeners.length; i++ ) { ( (MaxListener) maxListeners[ i ] ).maxOperation( e ); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to every <code>DoubleClickListener</code>. * The method also updates the <code>overTabIndex</code> of the tabbedPane with a value coming from the UI. This * method method is called each time a <code>MouseEvent</code> is received from the UI when the user double-clicks * on the tab which index is <code>overTabIndex</code>. * * @param e the <code>MouseEvent</code> to be sent * @param overTabIndex the index of a tab, usually the tab over which the mouse is * @see #addDoubleClickListener * @see EventListenerList */ public void fireDoubleClickTabEvent( final MouseEvent e, final int overTabIndex ) { this.overTabIndex = overTabIndex; EventListener dClickListeners[] = this.getListeners( DoubleClickListener.class ); for ( int i = 0; i < dClickListeners.length; i++ ) { ( (DoubleClickListener) dClickListeners[ i ] ).doubleClickOperation( e ); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to every <code>PopupOutsideListener</code>. * The method also sets the <code>overTabIndex</code> to -1. This method method is called each time a * <code>MouseEvent</code> is received from the UI when the user right-clicks on the inactive part of a * tabbedPane. * * @param e the <code>MouseEvent</code> to be sent * @see #addPopupOutsideListener * @see EventListenerList */ public void firePopupOutsideTabEvent( final MouseEvent e ) { this.overTabIndex = -1; EventListener popupListeners[] = this.getListeners( PopupOutsideListener.class ); for ( int i = 0; i < popupListeners.length; i++ ) { ( (PopupOutsideListener) popupListeners[ i ] ).popupOutsideOperation( e ); } } public void highlightTab( final int tabIndex ) { if ( this.paneUI.highlightTab( tabIndex ) ) { this.repaint(); } } @Override public boolean isFocusable() { return false; } }