/*
* David Bismut, davidou@mageos.com
* Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004
* Ecole des Mines de Nantes, France
*/
/*
*
* Extended from
* @(#)BasicTabbedPaneUI.java 1.126 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package tab;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.InputMapUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.View;
import net.java.dev.spellcast.utilities.JComponentUtilities;
/**
* UI for <code>CloseAndMaxTabbedPane</code>.
* <p>
* Credits to:
*
* @author Amy Fowler
* @author Philip Milne
* @author Steve Wilson
* @author Tom Santos
* @author Dave Moore
*/
public class CloseTabPaneUI
extends BasicTabbedPaneUI
{
// Instance variables initialized at installation
private ContainerListener containerListener;
private ArrayList htmlViews;
protected ArrayList tabStates = new ArrayList();
private Hashtable mnemonicToIndexMap;
/**
* InputMap used for mnemonics. Only non-null if the JTabbedPane has mnemonics associated with it. Lazily created in
* initMnemonics.
*/
private InputMap mnemonicInputMap;
// For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
protected ScrollableTabSupport tabScroller;
private int tabCount;
protected MyMouseMotionListener motionListener;
// UI creation
private final int INACTIVE = 0;
private final int OVER = 1;
private final int PRESSED = 2;
protected static final int BUTTONSIZE = 16;
protected static final int WIDTHDELTA = 8;
private static BufferedImage closeRedImgB;
private static BufferedImage closeRedImgI;
private static JButton closeRedB;
private static BufferedImage closeGrayImgB;
private static BufferedImage closeGrayImgI;
private static JButton closeGrayB;
static
{
try
{
CloseTabPaneUI.closeRedImgI = ImageIO.read( JComponentUtilities.getResource( "xred.gif" ) );
}
catch ( IOException e1 )
{
e1.printStackTrace();
}
CloseTabPaneUI.closeRedImgB =
new BufferedImage( CloseTabPaneUI.BUTTONSIZE, CloseTabPaneUI.BUTTONSIZE, BufferedImage.TYPE_INT_ARGB );
try
{
CloseTabPaneUI.closeGrayImgI = ImageIO.read( JComponentUtilities.getResource( "xgray.gif" ) );
}
catch ( IOException e1 )
{
e1.printStackTrace();
}
CloseTabPaneUI.closeGrayImgB =
new BufferedImage( CloseTabPaneUI.BUTTONSIZE, CloseTabPaneUI.BUTTONSIZE, BufferedImage.TYPE_INT_ARGB );
}
private int overTabIndex = -1;
private int closeIndexStatus = this.INACTIVE;
private int maxIndexStatus = this.INACTIVE;
private boolean mousePressed = false;
protected JPopupMenu actionPopupMenu;
protected JMenuItem closeItem;
public CloseTabPaneUI()
{
super();
// Paint the red close icon
CloseTabPaneUI.closeRedB = new JButton();
CloseTabPaneUI.closeRedB.setSize( CloseTabPaneUI.BUTTONSIZE, CloseTabPaneUI.BUTTONSIZE );
CloseTabPaneUI.closeRedB.setMargin( new Insets( 0, 0, 0, 0 ) );
CloseTabPaneUI.closeRedB.setBorder( BorderFactory.createEmptyBorder() );
CloseTabPaneUI.closeRedB.setContentAreaFilled( false );
// Paint the gray close icon
CloseTabPaneUI.closeGrayB = new JButton();
CloseTabPaneUI.closeGrayB.setSize( CloseTabPaneUI.BUTTONSIZE, CloseTabPaneUI.BUTTONSIZE );
CloseTabPaneUI.closeGrayB.setMargin( new Insets( 0, 0, 0, 0 ) );
CloseTabPaneUI.closeGrayB.setBorder( BorderFactory.createEmptyBorder() );
CloseTabPaneUI.closeGrayB.setContentAreaFilled( false );
// Create a popup menu
this.actionPopupMenu = new JPopupMenu();
this.closeItem = new JMenuItem( "Close" );
this.closeItem.addActionListener( new ActionListener()
{
public void actionPerformed( final ActionEvent e )
{
( (CloseTabbedPane) CloseTabPaneUI.this.tabPane ).fireCloseTabEvent(
null, CloseTabPaneUI.this.tabPane.getSelectedIndex() );
}
} );
this.setPopupMenu();
}
public boolean highlightTab( final int tabIndex )
{
for ( int i = this.tabStates.size(); i <= tabIndex; ++i )
{
this.tabStates.add( Boolean.FALSE );
}
if ( this.tabStates.get( tabIndex ) == Boolean.TRUE )
{
return false;
}
this.tabStates.set( tabIndex, Boolean.TRUE );
return true;
}
protected boolean isOneActionButtonEnabled()
{
return this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON;
}
public boolean isCloseEnabled()
{
return this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON;
}
public static final int NO_CLOSE_ICON = 0;
public static final int RED_CLOSE_ICON = 1;
public static final int GRAY_CLOSE_ICON = 2;
private int closeIconStyle = CloseTabPaneUI.NO_CLOSE_ICON;
public void setCloseIconStyle( final int style )
{
this.closeIconStyle = style;
this.setPopupMenu();
}
public int getCloseIconStyle()
{
return this.closeIconStyle;
}
private void setPopupMenu()
{
this.actionPopupMenu.removeAll();
if ( this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON )
{
this.actionPopupMenu.add( this.closeItem );
}
}
@Override
protected int calculateTabWidth( final int tabPlacement, final int tabIndex, final FontMetrics metrics )
{
int delta = 2;
if ( !this.isOneActionButtonEnabled() )
{
delta += 6;
}
else if ( this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON )
{
delta += CloseTabPaneUI.BUTTONSIZE + CloseTabPaneUI.WIDTHDELTA;
}
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) + delta + ( this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON ? 20 : 5 );
}
@Override
protected int calculateTabHeight( final int tabPlacement, final int tabIndex, final int fontHeight )
{
return super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) + 2;
}
@Override
protected void layoutLabel( final int tabPlacement, final FontMetrics metrics, final int tabIndex,
final String title, final Icon icon, final Rectangle tabRect, final Rectangle iconRect,
final Rectangle textRect, final boolean isSelected )
{
textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
View v = this.getTextViewForTab( tabIndex );
if ( v != null )
{
this.tabPane.putClientProperty( "html", v );
}
SwingUtilities.layoutCompoundLabel(
this.tabPane, metrics, title, icon, SwingConstants.CENTER, SwingConstants.LEFT, SwingConstants.CENTER,
SwingConstants.CENTER, tabRect, iconRect, textRect, this.textIconGap );
this.tabPane.putClientProperty( "html", null );
iconRect.x = tabRect.x + 8;
textRect.x = iconRect.x + iconRect.width + this.textIconGap;
}
@Override
protected MouseListener createMouseListener()
{
return new MyMouseHandler();
}
protected ScrollableTabButton createScrollableTabButton( final int direction )
{
return new ScrollableTabButton( direction );
}
protected Rectangle newCloseRect( final Rectangle rect )
{
int dx = rect.x + rect.width;
int dy = ( rect.y + rect.height ) / 2 - 6;
return new Rectangle(
dx - CloseTabPaneUI.BUTTONSIZE - CloseTabPaneUI.WIDTHDELTA, dy, CloseTabPaneUI.BUTTONSIZE,
CloseTabPaneUI.BUTTONSIZE );
}
protected void updateOverTab( final int x, final int y )
{
if ( this.overTabIndex != ( this.overTabIndex = this.getTabAtLocation( x, y ) ) )
{
this.tabScroller.tabPanel.repaint();
}
}
protected void updateCloseIcon( final int x, final int y )
{
if ( this.overTabIndex != -1 )
{
int newCloseIndexStatus = this.INACTIVE;
Rectangle closeRect = this.newCloseRect( this.rects[ this.overTabIndex ] );
if ( closeRect.contains( x, y ) )
{
newCloseIndexStatus = this.mousePressed ? this.PRESSED : this.OVER;
}
if ( this.closeIndexStatus != ( this.closeIndexStatus = newCloseIndexStatus ) )
{
this.tabScroller.tabPanel.repaint();
}
}
}
private void setTabIcons( final int x, final int y )
{
//if the mouse isPressed
if ( !this.mousePressed )
{
this.updateOverTab( x, y );
}
if ( this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON )
{
this.updateCloseIcon( x, y );
}
}
public static ComponentUI createUI( final JComponent c )
{
return new CloseTabPaneUI();
}
/**
* Invoked by <code>installUI</code> to create a layout manager object to manage the <code>JTabbedPane</code>.
*
* @return a layout manager object
* @see TabbedPaneLayout
* @see javax.swing.JTabbedPane#getTabLayoutPolicy
*/
@Override
protected LayoutManager createLayoutManager()
{
return new TabbedPaneScrollLayout();
}
/*
* In an attempt to preserve backward compatibility for programs which have extended BasicTabbedPaneUI to do their
* own layout, the UI uses the installed layoutManager (and not tabLayoutPolicy) to determine if scrollTabLayout is
* enabled.
*/
/**
* Creates and installs any required subcomponents for the JTabbedPane. Invoked by installUI.
*
* @since 1.4
*/
@Override
protected void installComponents()
{
if ( this.tabScroller == null )
{
this.tabScroller = new ScrollableTabSupport( this.tabPane.getTabPlacement() );
this.tabPane.add( this.tabScroller.viewport );
this.tabPane.add( this.tabScroller.scrollForwardButton );
this.tabPane.add( this.tabScroller.scrollBackwardButton );
}
}
/**
* Removes any installed subcomponents from the JTabbedPane. Invoked by uninstallUI.
*
* @since 1.4
*/
@Override
protected void uninstallComponents()
{
this.tabPane.remove( this.tabScroller.viewport );
this.tabPane.remove( this.tabScroller.scrollForwardButton );
this.tabPane.remove( this.tabScroller.scrollBackwardButton );
if ( this.htmlViews != null )
{
this.htmlViews.clear();
}
if ( this.tabStates != null )
{
this.tabStates.clear();
}
this.resetMnemonics();
this.htmlViews = null;
this.tabStates = null;
this.tabScroller = null;
}
@Override
protected void installListeners()
{
if ( ( this.propertyChangeListener = this.createPropertyChangeListener() ) != null )
{
this.tabPane.addPropertyChangeListener( this.propertyChangeListener );
}
if ( ( this.tabChangeListener = this.createChangeListener() ) != null )
{
this.tabPane.addChangeListener( this.tabChangeListener );
}
if ( ( this.mouseListener = this.createMouseListener() ) != null )
{
this.tabScroller.tabPanel.addMouseListener( this.mouseListener );
}
if ( ( this.focusListener = this.createFocusListener() ) != null )
{
this.tabPane.addFocusListener( this.focusListener );
}
// PENDING(api) : See comment for ContainerHandler
if ( ( this.containerListener = new ContainerHandler() ) != null )
{
this.tabPane.addContainerListener( this.containerListener );
if ( this.tabPane.getTabCount() > 0 )
{
this.htmlViews = this.createHTMLArrayList();
}
}
if ( ( this.motionListener = new MyMouseMotionListener() ) != null )
{
this.tabScroller.tabPanel.addMouseMotionListener( this.motionListener );
}
}
@Override
protected void uninstallListeners()
{
if ( this.mouseListener != null )
{
this.tabScroller.tabPanel.removeMouseListener( this.mouseListener );
this.mouseListener = null;
}
if ( this.motionListener != null )
{
this.tabScroller.tabPanel.removeMouseMotionListener( this.motionListener );
this.motionListener = null;
}
if ( this.focusListener != null )
{
this.tabPane.removeFocusListener( this.focusListener );
this.focusListener = null;
}
// PENDING(api): See comment for ContainerHandler
if ( this.containerListener != null )
{
this.tabPane.removeContainerListener( this.containerListener );
this.containerListener = null;
if ( this.htmlViews != null )
{
this.htmlViews.clear();
this.htmlViews = null;
}
}
if ( this.tabChangeListener != null )
{
this.tabPane.removeChangeListener( this.tabChangeListener );
this.tabChangeListener = null;
}
if ( this.propertyChangeListener != null )
{
this.tabPane.removePropertyChangeListener( this.propertyChangeListener );
this.propertyChangeListener = null;
}
}
@Override
protected ChangeListener createChangeListener()
{
return new TabSelectionHandler();
}
@Override
protected void installKeyboardActions()
{
InputMap km = this.getMyInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
SwingUtilities.replaceUIInputMap( this.tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km );
km = this.getMyInputMap( JComponent.WHEN_FOCUSED );
SwingUtilities.replaceUIInputMap( this.tabPane, JComponent.WHEN_FOCUSED, km );
ActionMap am = this.createMyActionMap();
SwingUtilities.replaceUIActionMap( this.tabPane, am );
this.tabScroller.scrollForwardButton.setAction( am.get( "scrollTabsForwardAction" ) );
this.tabScroller.scrollBackwardButton.setAction( am.get( "scrollTabsBackwardAction" ) );
}
InputMap getMyInputMap( final int condition )
{
if ( condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT )
{
return (InputMap) UIManager.get( "TabbedPane.ancestorInputMap" );
}
else if ( condition == JComponent.WHEN_FOCUSED )
{
return (InputMap) UIManager.get( "TabbedPane.focusInputMap" );
}
return null;
}
ActionMap createMyActionMap()
{
ActionMap map = new ActionMapUIResource();
map.put( "navigateNext", new NextAction() );
map.put( "navigatePrevious", new PreviousAction() );
map.put( "navigateRight", new RightAction() );
map.put( "navigateLeft", new LeftAction() );
map.put( "navigateUp", new UpAction() );
map.put( "navigateDown", new DownAction() );
map.put( "navigatePageUp", new PageUpAction() );
map.put( "navigatePageDown", new PageDownAction() );
map.put( "setSelectedIndex", new SetSelectedIndexAction() );
map.put( "scrollTabsForwardAction", new ScrollTabsForwardAction() );
map.put( "scrollTabsBackwardAction", new ScrollTabsBackwardAction() );
return map;
}
@Override
protected void uninstallKeyboardActions()
{
SwingUtilities.replaceUIActionMap( this.tabPane, null );
SwingUtilities.replaceUIInputMap( this.tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null );
SwingUtilities.replaceUIInputMap( this.tabPane, JComponent.WHEN_FOCUSED, null );
}
/**
* Reloads the mnemonics. This should be invoked when a memonic changes, when the title of a mnemonic changes, or
* when tabs are added/removed.
*/
private void updateMnemonics()
{
this.resetMnemonics();
for ( int counter = this.tabPane.getTabCount() - 1; counter >= 0; counter-- )
{
int mnemonic = this.tabPane.getMnemonicAt( counter );
if ( mnemonic > 0 )
{
this.addMnemonic( counter, mnemonic );
}
}
}
/**
* Resets the mnemonics bindings to an empty state.
*/
private void resetMnemonics()
{
if ( this.mnemonicToIndexMap != null )
{
this.mnemonicToIndexMap.clear();
this.mnemonicInputMap.clear();
}
}
/**
* Adds the specified mnemonic at the specified index.
*/
private void addMnemonic( final int index, final int mnemonic )
{
if ( this.mnemonicToIndexMap == null )
{
this.initMnemonics();
}
this.mnemonicInputMap.put( KeyStroke.getKeyStroke( mnemonic, Event.ALT_MASK ), "setSelectedIndex" );
this.mnemonicToIndexMap.put( new Integer( mnemonic ), new Integer( index ) );
}
/**
* Installs the state needed for mnemonics.
*/
private void initMnemonics()
{
this.mnemonicToIndexMap = new Hashtable();
this.mnemonicInputMap = new InputMapUIResource();
this.mnemonicInputMap.setParent( SwingUtilities.getUIInputMap(
this.tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ) );
SwingUtilities.replaceUIInputMap(
this.tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, this.mnemonicInputMap );
}
// UI Rendering
@Override
public void paint( final Graphics g, final JComponent c )
{
int tc = this.tabPane.getTabCount();
if ( this.tabCount != tc )
{
this.tabCount = tc;
this.updateMnemonics();
}
int selectedIndex = this.tabPane.getSelectedIndex();
int tabPlacement = this.tabPane.getTabPlacement();
this.ensureCurrentLayout();
// Paint content border
this.paintContentBorder( g, tabPlacement, selectedIndex );
}
@Override
protected void paintTab( final Graphics g, final int tabPlacement, final Rectangle[] rects, final int tabIndex,
final Rectangle iconRect, final Rectangle textRect )
{
Rectangle tabRect = rects[ tabIndex ];
int selectedIndex = this.tabPane.getSelectedIndex();
boolean isSelected = selectedIndex == tabIndex;
boolean isOver = this.overTabIndex == tabIndex;
if ( isSelected && tabIndex < this.tabStates.size() )
{
this.tabStates.set( tabIndex, Boolean.FALSE );
}
Graphics2D g2 = null;
Shape save = null;
boolean cropShape = false;
int cropx = 0;
int cropy = 0;
if ( g instanceof Graphics2D )
{
g2 = (Graphics2D) g;
// Render visual for cropped tab edge...
Rectangle viewRect = this.tabScroller.viewport.getViewRect();
int cropline;
cropline = viewRect.x + viewRect.width;
if ( tabRect.x < cropline && tabRect.x + tabRect.width > cropline )
{
cropx = cropline - 1;
cropy = tabRect.y;
cropShape = true;
}
if ( cropShape )
{
save = g2.getClip();
g2.clipRect( tabRect.x, tabRect.y, tabRect.width, tabRect.height );
}
}
this.paintTabBackground(
g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
this.paintTabBorder( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
String title = this.tabPane.getTitleAt( tabIndex );
Font font = this.tabPane.getFont();
FontMetrics metrics = g.getFontMetrics( font );
Icon icon = this.getIconForTab( tabIndex );
this.layoutLabel( tabPlacement, metrics, tabIndex, title, icon, tabRect, iconRect, textRect, isSelected );
this.paintText( g, tabPlacement, font, metrics, tabIndex, title, textRect, isSelected );
this.paintIcon( g, tabPlacement, tabIndex, icon, iconRect, isSelected );
this.paintFocusIndicator( g, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected );
if ( cropShape )
{
this.paintCroppedTabEdge( g, tabPlacement, tabIndex, isSelected, cropx, cropy );
g2.setClip( save );
}
else if ( this.closeIconStyle != CloseTabPaneUI.NO_CLOSE_ICON && isOver )
{
int dx = tabRect.x + tabRect.width - CloseTabPaneUI.BUTTONSIZE - CloseTabPaneUI.WIDTHDELTA;
int dy = ( tabRect.y + tabRect.height ) / 2 - 6;
if ( isSelected && this.closeIconStyle == CloseTabPaneUI.GRAY_CLOSE_ICON )
{
this.paintCloseIcon( g2, dx, dy, true );
}
else if ( !isSelected && this.closeIconStyle == CloseTabPaneUI.RED_CLOSE_ICON )
{
this.paintCloseIcon( g2, dx, dy, false );
}
}
}
protected void paintCloseIcon( final Graphics g, final int dx, final int dy, final boolean isSelected )
{
if ( isSelected )
{
this.paintActionButton(
g, dx, dy, this.closeIndexStatus, false, CloseTabPaneUI.closeGrayB, CloseTabPaneUI.closeGrayImgB );
g.drawImage( CloseTabPaneUI.closeGrayImgI, dx, dy + 1, null );
}
else
{
this.paintActionButton(
g, dx, dy, this.closeIndexStatus, false, CloseTabPaneUI.closeRedB, CloseTabPaneUI.closeRedImgB );
g.drawImage( CloseTabPaneUI.closeRedImgI, dx, dy + 1, null );
}
}
protected void paintActionButton( final Graphics g, final int dx, final int dy, final int status,
final boolean isOver, final JButton button, final BufferedImage image )
{
button.setBorder( null );
// button.setBackground(tabScroller.tabPanel.getBackground());
button.paint( image.getGraphics() );
g.drawImage( image, dx, dy, null );
}
/*
* This method will create and return a polygon shape for the given tab rectangle which has been cropped at the
* specified cropline with a torn edge visual. e.g. A "File" tab which has cropped been cropped just after the "i":
* ------------- | ..... | | . | | ... . | | . . | | . . | | . . | -------------- The x, y arrays below define the
* pattern used to create a "torn" edge segment which is repeated to fill the edge of the tab. For tabs placed on
* TOP and BOTTOM, this righthand torn edge is created by line segments which are defined by coordinates obtained by
* subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on LEFT or
* RIGHT, the bottom torn edge is created by subtracting xCropLen[i] from (tab.y + tab.height) and adding
* yCropLen[i] to (tab.x).
*/
private void paintCroppedTabEdge( final Graphics g, final int tabPlacement, final int tabIndex,
final boolean isSelected, final int x, final int y )
{
g.setColor( this.shadow );
g.drawLine( x, y, x, y + this.rects[ tabIndex ].height );
}
private void ensureCurrentLayout()
{
if ( !this.tabPane.isValid() )
{
this.tabPane.validate();
}
/*
* If tabPane doesn't have a peer yet, the validate() call will silently fail. We handle that by forcing a
* layout if tabPane is still invalid. See bug 4237677.
*/
if ( !this.tabPane.isValid() )
{
TabbedPaneLayout layout = (TabbedPaneLayout) this.tabPane.getLayout();
layout.calculateLayoutInfo();
}
}
/**
* Returns the bounds of the specified tab in the coordinate space of the JTabbedPane component. This is required
* because the tab rects are by default defined in the coordinate space of the component where they are rendered,
* which could be the JTabbedPane (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT). This method
* should be used whenever the tab rectangle must be relative to the JTabbedPane itself and the result should be
* placed in a designated Rectangle object (rather than instantiating and returning a new Rectangle each time). The
* tab index parameter must be a valid tabbed pane tab index (0 to tab count - 1, inclusive). The destination
* rectangle parameter must be a valid <code>Rectangle</code> instance. The handling of invalid parameters is
* unspecified.
*
* @param tabIndex the index of the tab
* @param dest the rectangle where the result should be placed
* @return the resulting rectangle
* @since 1.4
*/
@Override
protected Rectangle getTabBounds( final int tabIndex, final Rectangle dest )
{
dest.width = this.rects[ tabIndex ].width;
dest.height = this.rects[ tabIndex ].height;
Point vpp = this.tabScroller.viewport.getLocation();
Point viewp = this.tabScroller.viewport.getViewPosition();
dest.x = this.rects[ tabIndex ].x + vpp.x - viewp.x;
dest.y = this.rects[ tabIndex ].y + vpp.y - viewp.y;
return dest;
}
private int getTabAtLocation( final int x, final int y )
{
this.ensureCurrentLayout();
int tabCount = this.tabPane.getTabCount();
for ( int i = 0; i < tabCount; i++ )
{
if ( this.rects[ i ].contains( x, y ) )
{
return i;
}
}
return -1;
}
public int getOverTabIndex()
{
return this.overTabIndex;
}
/**
* Returns the index of the tab closest to the passed in location, note that the returned tab may not contain the
* location x,y.
*/
private int getClosestTab( final int x, final int y )
{
int min = 0;
int tabCount = Math.min( this.rects.length, this.tabPane.getTabCount() );
int max = tabCount;
int tabPlacement = this.tabPane.getTabPlacement();
boolean useX = tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM;
int want = useX ? x : y;
while ( min != max )
{
int current = ( max + min ) / 2;
int minLoc;
int maxLoc;
if ( useX )
{
minLoc = this.rects[ current ].x;
maxLoc = minLoc + this.rects[ current ].width;
}
else
{
minLoc = this.rects[ current ].y;
maxLoc = minLoc + this.rects[ current ].height;
}
if ( want < minLoc )
{
max = current;
if ( min == max )
{
return Math.max( 0, current - 1 );
}
}
else if ( want >= maxLoc )
{
min = current;
if ( max - min <= 1 )
{
return Math.max( current + 1, tabCount - 1 );
}
}
else
{
return current;
}
}
return min;
}
// REMIND(aim,7/29/98): This method should be made
// protected in the next release where
// API changes are allowed
//
boolean requestMyFocusForVisibleComponent()
{
return false;
}
private class RightAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.EAST );
}
};
private class LeftAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.WEST );
}
};
private class UpAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.NORTH );
}
};
private class DownAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.SOUTH );
}
};
private class NextAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.NEXT );
}
};
private class PreviousAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.navigateSelectedTab( SwingConstants.PREVIOUS );
}
};
private class PageUpAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
int tabPlacement = pane.getTabPlacement();
if ( tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM )
{
ui.navigateSelectedTab( SwingConstants.WEST );
}
else
{
ui.navigateSelectedTab( SwingConstants.NORTH );
}
}
};
private class PageDownAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
int tabPlacement = pane.getTabPlacement();
if ( tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM )
{
ui.navigateSelectedTab( SwingConstants.EAST );
}
else
{
ui.navigateSelectedTab( SwingConstants.SOUTH );
}
}
};
/**
* Selects a tab in the JTabbedPane based on the String of the action command. The tab selected is based on the
* first tab that has a mnemonic matching the first character of the action command.
*/
private class SetSelectedIndexAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = (JTabbedPane) e.getSource();
if ( pane != null && pane.getUI() instanceof CloseTabPaneUI )
{
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
String command = e.getActionCommand();
if ( command != null && command.length() > 0 )
{
int mnemonic = e.getActionCommand().charAt( 0 );
if ( mnemonic >= 'a' && mnemonic <= 'z' )
{
mnemonic -= 'a' - 'A';
}
Integer index = (Integer) ui.mnemonicToIndexMap.get( new Integer( mnemonic ) );
if ( index != null && pane.isEnabledAt( index.intValue() ) )
{
pane.setSelectedIndex( index.intValue() );
}
}
}
}
};
private class ScrollTabsForwardAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = null;
Object src = e.getSource();
if ( src instanceof JTabbedPane )
{
pane = (JTabbedPane) src;
}
else if ( src instanceof ScrollableTabButton )
{
pane = (JTabbedPane) ( (ScrollableTabButton) src ).getParent();
}
else
{
return; // shouldn't happen
}
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.tabScroller.scrollForward( pane.getTabPlacement() );
}
}
private class ScrollTabsBackwardAction
extends AbstractAction
{
public void actionPerformed( final ActionEvent e )
{
JTabbedPane pane = null;
Object src = e.getSource();
if ( src instanceof JTabbedPane )
{
pane = (JTabbedPane) src;
}
else if ( src instanceof ScrollableTabButton )
{
pane = (JTabbedPane) ( (ScrollableTabButton) src ).getParent();
}
else
{
return; // shouldn't happen
}
CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
ui.tabScroller.scrollBackward( pane.getTabPlacement() );
}
}
/**
* This inner class is marked "public" due to a compiler bug. This class should be treated as a
* "protected" inner class. Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
private class TabbedPaneScrollLayout
extends TabbedPaneLayout
{
@Override
protected int preferredTabAreaHeight( final int tabPlacement, final int width )
{
return CloseTabPaneUI.this.calculateMaxTabHeight( tabPlacement );
}
@Override
protected int preferredTabAreaWidth( final int tabPlacement, final int height )
{
return CloseTabPaneUI.this.calculateMaxTabWidth( tabPlacement );
}
@Override
public void layoutContainer( final Container parent )
{
int tabPlacement = CloseTabPaneUI.this.tabPane.getTabPlacement();
int tabCount = CloseTabPaneUI.this.tabPane.getTabCount();
Insets insets = CloseTabPaneUI.this.tabPane.getInsets();
int selectedIndex = CloseTabPaneUI.this.tabPane.getSelectedIndex();
Component visibleComponent = CloseTabPaneUI.this.getVisibleComponent();
this.calculateLayoutInfo();
if ( selectedIndex < 0 )
{
if ( visibleComponent != null )
{
// The last tab was removed, so remove the component
CloseTabPaneUI.this.setVisibleComponent( null );
}
}
else
{
Component selectedComponent = CloseTabPaneUI.this.tabPane.getComponentAt( selectedIndex );
boolean shouldChangeFocus = false;
// In order to allow programs to use a single component
// as the display for multiple tabs, we will not change
// the visible compnent if the currently selected tab
// has a null component. This is a bit dicey, as we don't
// explicitly state we support this in the spec, but since
// programs are now depending on this, we're making it work.
//
if ( selectedComponent != null )
{
CloseTabPaneUI.this.setVisibleComponent( selectedComponent );
}
int tx, ty, tw, th; // tab area bounds
int cx, cy, cw, ch; // content area bounds
Insets contentInsets = CloseTabPaneUI.this.getContentBorderInsets( tabPlacement );
Rectangle bounds = CloseTabPaneUI.this.tabPane.getBounds();
int numChildren = CloseTabPaneUI.this.tabPane.getComponentCount();
if ( numChildren > 0 )
{
// calculate tab area bounds
tw = bounds.width - insets.left - insets.right;
th =
CloseTabPaneUI.this.calculateTabAreaHeight(
tabPlacement, CloseTabPaneUI.this.runCount, CloseTabPaneUI.this.maxTabHeight );
tx = insets.left;
ty = insets.top;
// calculate content area bounds
cx = tx + contentInsets.left;
cy = ty + th + contentInsets.top;
cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right;
ch = bounds.height - insets.top - insets.bottom - th - contentInsets.top - contentInsets.bottom;
for ( int i = 0; i < numChildren; i++ )
{
Component child = CloseTabPaneUI.this.tabPane.getComponent( i );
if ( child instanceof ScrollableTabViewport )
{
JViewport viewport = (JViewport) child;
Rectangle viewRect = viewport.getViewRect();
int vw = tw;
int vh = th;
int totalTabWidth =
CloseTabPaneUI.this.rects[ tabCount - 1 ].x + CloseTabPaneUI.this.rects[ tabCount - 1 ].width;
if ( totalTabWidth > tw )
{
// Need to allow space for scrollbuttons
vw = Math.max( tw - 36, 36 );;
if ( totalTabWidth - viewRect.x <= vw )
{
// Scrolled to the end, so ensure the
// viewport size is
// such that the scroll offset aligns with a
// tab
vw = totalTabWidth - viewRect.x;
}
}
child.setBounds( tx, ty, vw, vh );
}
else if ( child instanceof ScrollableTabButton )
{
ScrollableTabButton scrollbutton = (ScrollableTabButton) child;
Dimension bsize = scrollbutton.getPreferredSize();
int bx = 0;
int by = 0;
int bw = bsize.width;
int bh = bsize.height;
boolean visible = false;
int totalTabWidth =
CloseTabPaneUI.this.rects[ tabCount - 1 ].x + CloseTabPaneUI.this.rects[ tabCount - 1 ].width;
if ( totalTabWidth > tw )
{
int dir = scrollbutton.scrollsForward() ? SwingConstants.EAST : SwingConstants.WEST;
scrollbutton.setDirection( dir );
visible = true;
bx =
dir == SwingConstants.EAST ? bounds.width - insets.left - bsize.width : bounds.width - insets.left - 2 * bsize.width;
by = tabPlacement == SwingConstants.TOP ? ty + th - bsize.height : ty;
}
child.setVisible( visible );
if ( visible )
{
child.setBounds( bx, by, bw, bh );
}
}
else
{
// All content children...
child.setBounds( cx, cy, cw, ch );
}
}
if ( shouldChangeFocus )
{
if ( !CloseTabPaneUI.this.requestMyFocusForVisibleComponent() )
{
CloseTabPaneUI.this.tabPane.requestFocusInWindow();
}
}
}
}
}
@Override
protected void calculateTabRects( final int tabPlacement, final int tabCount )
{
FontMetrics metrics = CloseTabPaneUI.this.getFontMetrics();
Insets tabAreaInsets = CloseTabPaneUI.this.getTabAreaInsets( tabPlacement );
int i;
int x = tabAreaInsets.left - 2;
int y = tabAreaInsets.top;
int totalWidth = 0;
int totalHeight = 0;
//
// Calculate bounds within which a tab run must fit
//
CloseTabPaneUI.this.maxTabHeight = CloseTabPaneUI.this.calculateMaxTabHeight( tabPlacement );
CloseTabPaneUI.this.runCount = 0;
CloseTabPaneUI.this.selectedRun = -1;
if ( tabCount == 0 )
{
return;
}
CloseTabPaneUI.this.selectedRun = 0;
CloseTabPaneUI.this.runCount = 1;
// Run through tabs and lay them out in a single run
Rectangle rect;
for ( i = 0; i < tabCount; i++ )
{
rect = CloseTabPaneUI.this.rects[ i ];
if ( i > 0 )
{
rect.x = CloseTabPaneUI.this.rects[ i - 1 ].x + CloseTabPaneUI.this.rects[ i - 1 ].width - 1;
}
else
{
CloseTabPaneUI.this.tabRuns[ 0 ] = 0;
CloseTabPaneUI.this.maxTabWidth = 0;
totalHeight += CloseTabPaneUI.this.maxTabHeight;
rect.x = x;
}
rect.width = CloseTabPaneUI.this.calculateTabWidth( tabPlacement, i, metrics );
totalWidth = rect.x + rect.width;
CloseTabPaneUI.this.maxTabWidth = Math.max( CloseTabPaneUI.this.maxTabWidth, rect.width );
rect.y = y;
rect.height = CloseTabPaneUI.this.maxTabHeight;
}
//tabPanel.setSize(totalWidth, totalHeight);
CloseTabPaneUI.this.tabScroller.tabPanel.setPreferredSize( new Dimension( totalWidth, totalHeight ) );
}
}
private class ScrollableTabSupport
implements ChangeListener
{
public ScrollableTabViewport viewport;
public ScrollableTabPanel tabPanel;
public ScrollableTabButton scrollForwardButton;
public ScrollableTabButton scrollBackwardButton;
public int leadingTabIndex;
private final Point tabViewPosition = new Point( 0, 0 );
ScrollableTabSupport( final int tabPlacement )
{
this.viewport = new ScrollableTabViewport();
this.tabPanel = new ScrollableTabPanel();
this.viewport.setView( this.tabPanel );
this.viewport.addChangeListener( this );
this.scrollForwardButton = CloseTabPaneUI.this.createScrollableTabButton( SwingConstants.EAST );
this.scrollBackwardButton = CloseTabPaneUI.this.createScrollableTabButton( SwingConstants.WEST );
// scrollForwardButton = new ScrollableTabButton(EAST);
// scrollBackwardButton = new ScrollableTabButton(WEST);
}
public void scrollForward( final int tabPlacement )
{
Dimension viewSize = this.viewport.getViewSize();
Rectangle viewRect = this.viewport.getViewRect();
if ( tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM )
{
if ( viewRect.width >= viewSize.width - viewRect.x )
{
return; // no room left to scroll
}
}
else if ( viewRect.height >= viewSize.height - viewRect.y )
{
return;
}
this.setLeadingTabIndex( tabPlacement, this.leadingTabIndex + 1 );
}
public void scrollBackward( final int tabPlacement )
{
if ( this.leadingTabIndex == 0 )
{
return; // no room left to scroll
}
this.setLeadingTabIndex( tabPlacement, this.leadingTabIndex - 1 );
}
public void setLeadingTabIndex( final int tabPlacement, final int index )
{
this.leadingTabIndex = index;
Dimension viewSize = this.viewport.getViewSize();
Rectangle viewRect = this.viewport.getViewRect();
this.tabViewPosition.x =
this.leadingTabIndex == 0 ? 0 : CloseTabPaneUI.this.rects[ this.leadingTabIndex ].x;
if ( viewSize.width - this.tabViewPosition.x < viewRect.width )
{
// We've scrolled to the end, so adjust the viewport size
// to ensure the view position remains aligned on a tab boundary
Dimension extentSize = new Dimension( viewSize.width - this.tabViewPosition.x, viewRect.height );
this.viewport.setExtentSize( extentSize );
}
this.viewport.setViewPosition( this.tabViewPosition );
}
public void stateChanged( final ChangeEvent e )
{
JViewport viewport = (JViewport) e.getSource();
int tabPlacement = CloseTabPaneUI.this.tabPane.getTabPlacement();
int tabCount = CloseTabPaneUI.this.tabPane.getTabCount();
Rectangle vpRect = viewport.getBounds();
Dimension viewSize = viewport.getViewSize();
Rectangle viewRect = viewport.getViewRect();
this.leadingTabIndex = CloseTabPaneUI.this.getClosestTab( viewRect.x, viewRect.y );
if ( CloseTabPaneUI.this.rects.length <= this.leadingTabIndex )
{
return;
}
// If the tab isn't right aligned, adjust it.
if ( this.leadingTabIndex + 1 < tabCount )
{
if ( CloseTabPaneUI.this.rects[ this.leadingTabIndex ].x < viewRect.x )
{
this.leadingTabIndex++ ;
}
}
Insets contentInsets = CloseTabPaneUI.this.getContentBorderInsets( tabPlacement );
CloseTabPaneUI.this.tabPane.repaint( vpRect.x, vpRect.y + vpRect.height, vpRect.width, contentInsets.top );
this.scrollBackwardButton.setEnabled( viewRect.x > 0 );
this.scrollForwardButton.setEnabled( this.leadingTabIndex < tabCount - 1 && viewSize.width - viewRect.x > viewRect.width );
}
@Override
public String toString()
{
return new String(
"viewport.viewSize=" + this.viewport.getViewSize() + "\n" + "viewport.viewRectangle=" + this.viewport.getViewRect() + "\n" + "leadingTabIndex=" + this.leadingTabIndex + "\n" + "tabViewPosition=" + this.tabViewPosition );
}
}
private class ScrollableTabViewport
extends JViewport
implements UIResource
{
public ScrollableTabViewport()
{
super();
this.setScrollMode( JViewport.SIMPLE_SCROLL_MODE );
}
}
private class ScrollableTabPanel
extends JPanel
implements UIResource
{
public ScrollableTabPanel()
{
this.setLayout( null );
}
@Override
public void paintComponent( final Graphics g )
{
super.paintComponent( g );
CloseTabPaneUI.this.paintTabArea(
g, CloseTabPaneUI.this.tabPane.getTabPlacement(), CloseTabPaneUI.this.tabPane.getSelectedIndex() );
}
}
protected class ScrollableTabButton
extends BasicArrowButton
implements UIResource, SwingConstants
{
public ScrollableTabButton( final int direction )
{
super(
direction, UIManager.getColor( "TabbedPane.selected" ), UIManager.getColor( "TabbedPane.shadow" ),
UIManager.getColor( "TabbedPane.darkShadow" ), UIManager.getColor( "TabbedPane.highlight" ) );
}
public boolean scrollsForward()
{
return this.direction == SwingConstants.EAST || this.direction == SwingConstants.SOUTH;
}
}
/**
* This inner class is marked "public" due to a compiler bug. This class should be treated as a
* "protected" inner class. Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
public class TabSelectionHandler
implements ChangeListener
{
public void stateChanged( final ChangeEvent e )
{
JTabbedPane tabPane = (JTabbedPane) e.getSource();
tabPane.revalidate();
tabPane.repaint();
if ( tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT )
{
int index = tabPane.getSelectedIndex();
if ( index < CloseTabPaneUI.this.rects.length && index != -1 )
{
CloseTabPaneUI.this.tabScroller.tabPanel.scrollRectToVisible( CloseTabPaneUI.this.rects[ index ] );
}
}
}
}
/**
* This inner class is marked "public" due to a compiler bug. This class should be treated as a
* "protected" inner class. Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
/*
* GES 2/3/99: The container listener code was added to support HTML rendering of tab titles. Ideally, we would be
* able to listen for property changes when a tab is added or its text modified. At the moment there are no such
* events because the Beans spec doesn't allow 'indexed' property changes (i.e. tab 2's text changed from A to B).
* In order to get around this, we listen for tabs to be added or removed by listening for the container events. we
* then queue up a runnable (so the component has a chance to complete the add) which checks the tab title of the
* new component to see if it requires HTML rendering. The Views (one per tab title requiring HTML rendering) are
* stored in the htmlViews ArrayList, which is only allocated after the first time we run into an HTML tab. Note
* that this vector is kept in step with the number of pages, and nulls are added for those pages whose tab title do
* not require HTML rendering. This makes it easy for the paint and layout code to tell whether to invoke the HTML
* engine without having to check the string during time-sensitive operations. When we have added a way to listen
* for tab additions and changes to tab text, this code should be removed and replaced by something which uses that.
*/
private class ContainerHandler
implements ContainerListener
{
public void componentAdded( final ContainerEvent e )
{
JTabbedPane tp = (JTabbedPane) e.getContainer();
Component child = e.getChild();
if ( child instanceof UIResource )
{
return;
}
int index = tp.indexOfComponent( child );
String title = tp.getTitleAt( index );
boolean isHTML = BasicHTML.isHTMLString( title );
if ( isHTML )
{
if ( CloseTabPaneUI.this.htmlViews == null )
{
CloseTabPaneUI.this.htmlViews = CloseTabPaneUI.this.createHTMLArrayList();
}
else
{ // ArrayList already exists
View v = BasicHTML.createHTMLView( tp, title );
CloseTabPaneUI.this.htmlViews.add( index, v );
}
}
else if ( CloseTabPaneUI.this.htmlViews != null )
{
CloseTabPaneUI.this.htmlViews.add( index, null );
}
}
public void componentRemoved( final ContainerEvent e )
{
JTabbedPane tp = (JTabbedPane) e.getContainer();
Component child = e.getChild();
if ( child instanceof UIResource )
{
return;
}
// NOTE 4/15/2002 (joutwate):
// This fix is implemented using client properties since there is
// currently no IndexPropertyChangeEvent. Once
// IndexPropertyChangeEvents have been added this code should be
// modified to use it.
Integer indexObj = (Integer) tp.getClientProperty( "__index_to_remove__" );
if ( indexObj != null )
{
int index = indexObj.intValue();
if ( CloseTabPaneUI.this.htmlViews != null && CloseTabPaneUI.this.htmlViews.size() >= index )
{
CloseTabPaneUI.this.htmlViews.remove( index );
}
}
}
}
private ArrayList createHTMLArrayList()
{
ArrayList htmlViews = new ArrayList();
int count = this.tabPane.getTabCount();
if ( count > 0 )
{
for ( int i = 0; i < count; i++ )
{
String title = this.tabPane.getTitleAt( i );
if ( BasicHTML.isHTMLString( title ) )
{
htmlViews.add( BasicHTML.createHTMLView( this.tabPane, title ) );
}
else
{
htmlViews.add( null );
}
}
}
return htmlViews;
}
class MyMouseHandler
extends MouseHandler
{
public MyMouseHandler()
{
super();
}
@Override
public void mousePressed( final MouseEvent e )
{
if ( CloseTabPaneUI.this.closeIndexStatus == CloseTabPaneUI.this.OVER )
{
CloseTabPaneUI.this.closeIndexStatus = CloseTabPaneUI.this.PRESSED;
CloseTabPaneUI.this.tabScroller.tabPanel.repaint();
return;
}
if ( CloseTabPaneUI.this.maxIndexStatus == CloseTabPaneUI.this.OVER )
{
CloseTabPaneUI.this.maxIndexStatus = CloseTabPaneUI.this.PRESSED;
CloseTabPaneUI.this.tabScroller.tabPanel.repaint();
return;
}
if ( CloseTabPaneUI.this.closeIndexStatus == CloseTabPaneUI.this.PRESSED || CloseTabPaneUI.this.maxIndexStatus == CloseTabPaneUI.this.PRESSED )
{
return;
}
super.mousePressed( e );
}
@Override
public void mouseClicked( final MouseEvent e )
{
super.mousePressed( e );
if ( e.getClickCount() > 1 && CloseTabPaneUI.this.overTabIndex != -1 )
{
( (CloseTabbedPane) CloseTabPaneUI.this.tabPane ).fireDoubleClickTabEvent(
e, CloseTabPaneUI.this.overTabIndex );
}
}
@Override
public void mouseReleased( final MouseEvent e )
{
if ( CloseTabPaneUI.this.overTabIndex == -1 )
{
if ( e.isPopupTrigger() )
{
( (CloseTabbedPane) CloseTabPaneUI.this.tabPane ).firePopupOutsideTabEvent( e );
}
return;
}
if ( CloseTabPaneUI.this.isOneActionButtonEnabled() && e.isPopupTrigger() )
{
super.mousePressed( e );
CloseTabPaneUI.this.closeIndexStatus = CloseTabPaneUI.this.INACTIVE; //Prevent undesired action when
CloseTabPaneUI.this.maxIndexStatus = CloseTabPaneUI.this.INACTIVE; //right-clicking on icons
CloseTabPaneUI.this.actionPopupMenu.show( CloseTabPaneUI.this.tabScroller.tabPanel, e.getX(), e.getY() );
return;
}
if ( CloseTabPaneUI.this.closeIndexStatus == CloseTabPaneUI.this.PRESSED )
{
boolean shouldClose =
CloseTabPaneUI.this.overTabIndex >= CloseTabPaneUI.this.tabStates.size() || CloseTabPaneUI.this.tabStates.get( CloseTabPaneUI.this.overTabIndex ) == Boolean.FALSE;
if ( CloseTabPaneUI.this.closeIconStyle == CloseTabPaneUI.GRAY_CLOSE_ICON )
{
shouldClose &= CloseTabPaneUI.this.tabPane.getSelectedIndex() == CloseTabPaneUI.this.overTabIndex;
}
else
{
shouldClose &= CloseTabPaneUI.this.tabPane.getSelectedIndex() != CloseTabPaneUI.this.overTabIndex;
}
if ( shouldClose )
{
CloseTabPaneUI.this.closeIndexStatus = CloseTabPaneUI.this.OVER;
CloseTabPaneUI.this.tabScroller.tabPanel.repaint();
( (CloseTabbedPane) CloseTabPaneUI.this.tabPane ).fireCloseTabEvent(
e, CloseTabPaneUI.this.overTabIndex );
return;
}
}
if ( CloseTabPaneUI.this.maxIndexStatus == CloseTabPaneUI.this.PRESSED )
{
CloseTabPaneUI.this.maxIndexStatus = CloseTabPaneUI.this.OVER;
CloseTabPaneUI.this.tabScroller.tabPanel.repaint();
( (CloseTabbedPane) CloseTabPaneUI.this.tabPane ).fireMaxTabEvent( e, CloseTabPaneUI.this.overTabIndex );
return;
}
}
@Override
public void mouseExited( final MouseEvent e )
{
if ( !CloseTabPaneUI.this.mousePressed )
{
CloseTabPaneUI.this.overTabIndex = -1;
CloseTabPaneUI.this.tabScroller.tabPanel.repaint();
}
}
}
class MyMouseMotionListener
implements MouseMotionListener
{
public void mouseMoved( final MouseEvent e )
{
if ( CloseTabPaneUI.this.actionPopupMenu.isVisible() )
{
return; //No updates when popup is visible
}
CloseTabPaneUI.this.mousePressed = false;
CloseTabPaneUI.this.setTabIcons( e.getX(), e.getY() );
}
public void mouseDragged( final MouseEvent e )
{
if ( CloseTabPaneUI.this.actionPopupMenu.isVisible() )
{
return; //No updates when popup is visible
}
CloseTabPaneUI.this.mousePressed = true;
CloseTabPaneUI.this.setTabIcons( e.getX(), e.getY() );
}
}
}