All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.laf.tabbedpane.TabbedPanePainter Maven / Gradle / Ivy

The newest version!
package com.alee.laf.tabbedpane;

import com.alee.api.jdk.Objects;
import com.alee.laf.WebLookAndFeel;
import com.alee.managers.style.Bounds;
import com.alee.painter.AbstractPainter;
import com.alee.painter.SectionPainter;
import com.alee.utils.GraphicsUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.laf.FocusType;
import com.alee.utils.laf.WebBorder;

import javax.swing.*;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.View;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.Map;
import java.util.Vector;

/**
 * Basic painter for {@link JTabbedPane} component.
 * It is used as {@link WTabbedPaneUI} default painter.
 *
 * @param  component type
 * @param  component UI type
 * @author Alexandr Zernov
 * @author Mikle Garin
 */

public class TabbedPanePainter extends AbstractPainter
        implements ITabbedPanePainter
{
    /**
     * Style settings.
     */
    protected int round = WebTabbedPaneStyle.round;
    protected int shadeWidth = WebTabbedPaneStyle.shadeWidth;
    protected Color selectedTopBg = WebTabbedPaneStyle.selectedTopBg;
    protected Color selectedBottomBg = WebTabbedPaneStyle.selectedBottomBg;
    protected Color topBg = WebTabbedPaneStyle.topBg;
    protected Color bottomBg = WebTabbedPaneStyle.bottomBg;
    protected Color tabBorderColor = WebTabbedPaneStyle.tabBorderColor;
    protected Color contentBorderColor = WebTabbedPaneStyle.contentBorderColor;
    protected boolean paintBorderOnlyOnSelectedTab = WebTabbedPaneStyle.paintBorderOnlyOnSelectedTab;
    protected boolean forceUseSelectedTabBgColors = WebTabbedPaneStyle.forceUseSelectedTabBgColors;
    protected boolean paintOnlyTopBorder = WebTabbedPaneStyle.paintOnlyTopBorder;

    /**
     * Listeners.
     */
    protected transient FocusAdapter focusAdapter;

    /**
     * Painting variables.
     */
    protected transient boolean tabsOverlapBorder;
    protected transient boolean tabsOpaque;
    protected transient int textIconGap;
    protected transient Vector htmlViews;
    protected transient int tabRuns[];
    protected transient Rectangle rects[];
    protected transient int maxTabHeight;
    protected transient int maxTabWidth;
    protected transient int runCount;
    protected transient boolean scrollableTabLayoutEnabled;

    @Override
    protected void installPropertiesAndListeners ()
    {
        super.installPropertiesAndListeners ();
        installRuntimeVariables ();
        installTabbedPaneFocusListeners ();
    }

    @Override
    protected void uninstallPropertiesAndListeners ()
    {
        uninstallTabbedPaneFocusListeners ();
        uninstallRuntimeVariables ();
        super.uninstallPropertiesAndListeners ();
    }

    @Override
    protected void propertyChanged ( final String property, final Object oldValue, final Object newValue )
    {
        // Perform basic actions on property changes
        super.propertyChanged ( property, oldValue, newValue );

        // Updating border upon style change
        if ( Objects.equals ( property, WebLookAndFeel.TABBED_PANE_STYLE_PROPERTY ) )
        {
            updateBorder ();
        }
    }

    /**
     * Installs runtime variables.
     */
    protected void installRuntimeVariables ()
    {
        tabsOverlapBorder = UIManager.getBoolean ( "TabbedPane.tabsOverlapBorder" );
        tabsOpaque = UIManager.getBoolean ( "TabbedPane.tabsOpaque" );
        textIconGap = UIManager.getInt ( "TabbedPane.textIconGap" );
    }

    /**
     * Uninstalls runtime variables.
     */
    protected void uninstallRuntimeVariables ()
    {
        tabsOverlapBorder = false;
        tabsOpaque = false;
        textIconGap = 0;
    }

    /**
     * Installs custom {@link JTabbedPane} focus listeners for appropriate updates on focus change.
     * todo Remove upon switchin to appropriate styling
     */
    protected void installTabbedPaneFocusListeners ()
    {
        focusAdapter = new FocusAdapter ()
        {
            @Override
            public void focusGained ( final FocusEvent e )
            {
                component.repaint ();
            }

            @Override
            public void focusLost ( final FocusEvent e )
            {
                component.repaint ();
            }
        };
        component.addFocusListener ( focusAdapter );
    }

    /**
     * Uninstalls custom {@link JTabbedPane} focus listeners.
     * todo Remove upon switchin to appropriate styling
     */
    protected void uninstallTabbedPaneFocusListeners ()
    {
        component.removeFocusListener ( focusAdapter );
        focusAdapter = null;
    }

    /**
     * Updates component with complete border.
     * This border takes painter borders and component margin and padding into account.
     */
    @Override
    public void updateBorder ()
    {
        if ( component != null )
        {
            // Preserve old borders
            if ( SwingUtils.isPreserveBorders ( component ) )
            {
                return;
            }

            final Insets bgInsets = new Insets ( 0, 0, 0, 0 );
            if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
            {
                // Standalone style border
                final Insets sbi = new Insets ( shadeWidth, shadeWidth, shadeWidth, shadeWidth );
                component.setBorder ( new WebBorder ( SwingUtils.max ( bgInsets, sbi ) ) );
            }
            else
            {
                // Attached style border
                component.setBorder ( new WebBorder ( bgInsets ) );
            }
        }
    }

    public int getShadeWidth ()
    {
        return shadeWidth;
    }

    public void setShadeWidth ( final int shadeWidth )
    {
        this.shadeWidth = shadeWidth;
        updateBorder ();
    }

    public int getRound ()
    {
        return round;
    }

    public void setRound ( final int round )
    {
        this.round = round;
    }

    public Color getSelectedTopBg ()
    {
        return selectedTopBg;
    }

    public void setSelectedTopBg ( final Color selectedTopBg )
    {
        this.selectedTopBg = selectedTopBg;
    }

    public Color getSelectedBottomBg ()
    {
        return selectedBottomBg;
    }

    public void setSelectedBottomBg ( final Color selectedBottomBg )
    {
        this.selectedBottomBg = selectedBottomBg;
    }

    public Color getTopBg ()
    {
        return topBg;
    }

    public void setTopBg ( final Color topBg )
    {
        this.topBg = topBg;
    }

    public Color getBottomBg ()
    {
        return bottomBg;
    }

    public void setBottomBg ( final Color bottomBg )
    {
        this.bottomBg = bottomBg;
    }

    public Color getTabBorderColor ()
    {
        return tabBorderColor;
    }

    public void setTabBorderColor ( final Color tabBorderColor )
    {
        this.tabBorderColor = tabBorderColor;
    }

    public Color getContentBorderColor ()
    {
        return contentBorderColor;
    }

    public void setContentBorderColor ( final Color contentBorderColor )
    {
        this.contentBorderColor = contentBorderColor;
    }

    public boolean isPaintBorderOnlyOnSelectedTab ()
    {
        return paintBorderOnlyOnSelectedTab;
    }

    public void setPaintBorderOnlyOnSelectedTab ( final boolean paintBorderOnlyOnSelectedTab )
    {
        this.paintBorderOnlyOnSelectedTab = paintBorderOnlyOnSelectedTab;
    }

    public boolean isForceUseSelectedTabBgColors ()
    {
        return forceUseSelectedTabBgColors;
    }

    public void setForceUseSelectedTabBgColors ( final boolean forceUseSelectedTabBgColors )
    {
        this.forceUseSelectedTabBgColors = forceUseSelectedTabBgColors;
    }

    public boolean isPaintOnlyTopBorder ()
    {
        return paintOnlyTopBorder;
    }

    public void setPaintOnlyTopBorder ( final boolean paintOnlyTopBorder )
    {
        this.paintOnlyTopBorder = paintOnlyTopBorder;
    }

    @Override
    public void paint ( final Graphics2D g2d, final C c, final U ui, final Bounds bounds )
    {
        final Map hints = SwingUtils.setupTextAntialias ( g2d );

        runCount = component.getTabRunCount ();
        htmlViews = ui.getHtmlViews ();
        tabRuns = ui.getTabRuns ();
        rects = ui.getRects ();
        maxTabHeight = ui.getMaxTabHeight ();
        maxTabWidth = ui.getMaxTabWidth ();
        scrollableTabLayoutEnabled = component.getLayout () instanceof WebBasicTabbedPaneUI.TabbedPaneScrollLayout;

        final int selectedIndex = component.getSelectedIndex ();
        final int tabPlacement = component.getTabPlacement ();

        ensureCurrentLayout ();

        // Paint content border and tab area
        if ( tabsOverlapBorder )
        {
            paintContentBorder ( g2d, tabPlacement, selectedIndex );
        }
        // If scrollable tabs are enabled, the tab area will be
        // painted by the scrollable tab panel instead.
        //
        if ( !scrollableTabLayoutEnabled )
        { // WRAP_TAB_LAYOUT
            paintTabArea ( g2d, tabPlacement, selectedIndex );
        }
        if ( !tabsOverlapBorder )
        {
            paintContentBorder ( g2d, tabPlacement, selectedIndex );
        }

        // Cleaning
        htmlViews = null;
        tabRuns = null;
        rects = null;

        SwingUtils.restoreTextAntialias ( g2d, hints );
    }

    protected void ensureCurrentLayout ()
    {
        if ( !component.isValid () )
        {
            component.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 ( !component.isValid () )
        {
            final BasicTabbedPaneUI.TabbedPaneLayout layout = ( BasicTabbedPaneUI.TabbedPaneLayout ) component.getLayout ();
            layout.calculateLayoutInfo ();
        }
    }

    /**
     * Paints the tabs in the tab area.
     * Invoked by paint().
     * The graphics parameter must be a valid {@code Graphics}
     * object.  Tab placement may be either:
     * {@code JTabbedPane.TOP}, {@code JTabbedPane.BOTTOM},
     * {@code JTabbedPane.LEFT}, or {@code JTabbedPane.RIGHT}.
     * The selected index must be a valid tabbed pane tab index (0 to
     * tab count - 1, inclusive) or -1 if no tab is currently selected.
     * The handling of invalid parameters is unspecified.
     *
     * @param g             the graphics object to use for rendering
     * @param tabPlacement  the placement for the tabs within the JTabbedPane
     * @param selectedIndex the tab index of the selected component
     */
    protected void paintTabArea ( final Graphics g, final int tabPlacement, final int selectedIndex )
    {
        final int tabCount = component.getTabCount ();

        final Rectangle iconRect = new Rectangle ();
        final Rectangle textRect = new Rectangle ();
        final Rectangle clipRect = g.getClipBounds ();

        final int runCount = component.getTabRunCount ();

        // Paint tabRuns of tabs from back to front
        for ( int i = runCount - 1; i >= 0; i-- )
        {
            final int start = tabRuns[ i ];
            final int next = tabRuns[ i == runCount - 1 ? 0 : i + 1 ];
            final int end = next != 0 ? next - 1 : tabCount - 1;
            for ( int j = start; j <= end; j++ )
            {
                if ( j != selectedIndex && rects[ j ].intersects ( clipRect ) )
                {
                    paintTab ( g, tabPlacement, rects, j, iconRect, textRect );
                }
            }
        }

        // Paint selected tab if its in the front run
        // since it may overlap other tabs
        if ( selectedIndex >= 0 && rects[ selectedIndex ].intersects ( clipRect ) )
        {
            paintTab ( g, tabPlacement, rects, selectedIndex, iconRect, textRect );
        }
    }

    protected void paintTab ( final Graphics g, final int tabPlacement, final Rectangle[] rects, final int tabIndex,
                              final Rectangle iconRect, final Rectangle textRect )
    {
        final Rectangle tabRect = rects[ tabIndex ];
        final int selectedIndex = component.getSelectedIndex ();
        final boolean isSelected = selectedIndex == tabIndex;

        if ( tabsOpaque || component.isOpaque () )
        {
            paintTabBackground ( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
        }

        //paintTabBorder ( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );

        final String title = component.getTitleAt ( tabIndex );
        final Font font = component.getFont ();
        final FontMetrics metrics = SwingUtils.getFontMetrics ( component, g, font );
        final Icon icon = getIconForTab ( tabIndex );

        layoutLabel ( tabPlacement, metrics, tabIndex, title, icon, tabRect, iconRect, textRect, isSelected );

        if ( component.getTabComponentAt ( tabIndex ) == null )
        {
            String clippedTitle = title;

            if ( scrollableTabLayoutEnabled )
            {
                final WebBasicTabbedPaneUI.CroppedEdge croppedEdge = ui.getTabScroller ().croppedEdge;
                if ( croppedEdge.isParamsSet () && croppedEdge.getTabIndex () == tabIndex && isHorizontalTabPlacement () )
                {
                    final int availTextWidth = croppedEdge.getCropline () - ( textRect.x - tabRect.x ) - croppedEdge.getCroppedSideWidth ();
                    clippedTitle = SwingUtils.clipStringIfNecessary ( null, metrics, title, availTextWidth );
                }
            }

            paintText ( g, tabPlacement, font, metrics, tabIndex, clippedTitle, textRect, isSelected );

            paintIcon ( g, tabPlacement, tabIndex, icon, iconRect, isSelected );
        }
    }

    protected boolean isHorizontalTabPlacement ()
    {
        return component.getTabPlacement () == JTabbedPane.TOP || component.getTabPlacement () == JTabbedPane.BOTTOM;
    }

    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;

        final View v = getTextViewForTab ( tabIndex );
        if ( v != null )
        {
            component.putClientProperty ( "html", v );
        }

        SwingUtilities
                .layoutCompoundLabel ( component, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER,
                        SwingUtilities.TRAILING, tabRect, iconRect, textRect, textIconGap );

        component.putClientProperty ( "html", null );

        final int xNudge = getTabLabelShiftX ( tabPlacement, tabIndex, isSelected );
        final int yNudge = getTabLabelShiftY ( tabPlacement, tabIndex, isSelected );
        iconRect.x += xNudge;
        iconRect.y += yNudge;
        textRect.x += xNudge;
        textRect.y += yNudge;
    }

    protected void paintIcon ( final Graphics g, final int tabPlacement, final int tabIndex, final Icon icon, final Rectangle iconRect,
                               final boolean isSelected )
    {
        if ( icon != null )
        {
            icon.paintIcon ( component, g, iconRect.x, iconRect.y );
        }
    }

    /**
     * Returns the text View object required to render stylized text (HTML) for
     * the specified tab or null if no specialized text rendering is needed
     * for this tab. This is provided to support html rendering inside tabs.
     *
     * @param tabIndex the index of the tab
     * @return the text view to render the tab's text or null if no
     * specialized rendering is required
     * @since 1.4
     */
    protected View getTextViewForTab ( final int tabIndex )
    {
        if ( htmlViews != null )
        {
            return ( View ) htmlViews.elementAt ( tabIndex );
        }
        return null;
    }

    private void paintCustomFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final Shape shape,
                                    final Boolean mouseover, Boolean hasFocus )
    {
        hasFocus = hasFocus != null ? hasFocus : component.hasFocus () && component.isEnabled ();
        if ( hasFocus && focusType.equals ( FocusType.componentFocus ) )
        {
            final Object aa = GraphicsUtils.setupAntialias ( g2d );
            final Stroke os = GraphicsUtils.setupStroke ( g2d,
                    new BasicStroke ( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1, new float[]{ 1, 2 }, 0 ) );

            g2d.setPaint ( new Color ( 160, 160, 160 ) );
            g2d.draw ( shape );

            GraphicsUtils.restoreStroke ( g2d, os );
            GraphicsUtils.restoreAntialias ( g2d, aa );
        }
        else if ( focusType.equals ( FocusType.fieldFocus ) && ( hasFocus || mouseover != null && mouseover ) )
        {
            final Object aa = GraphicsUtils.setupAntialias ( g2d );
            final Stroke os = GraphicsUtils.setupStroke ( g2d, new BasicStroke ( 1.5f ) );

            g2d.setPaint ( hasFocus ? new Color ( 85, 142, 239 ) : new Color ( 85, 142, 239, 128 ) );
            g2d.draw ( shape );

            GraphicsUtils.restoreStroke ( g2d, os );
            GraphicsUtils.restoreAntialias ( g2d, aa );
        }
    }

    protected void paintTabBackground ( final Graphics g, final int tabPlacement, final int tabIndex, final int x, final int y, final int w,
                                        final int h, final boolean isSelected )
    {
        final Graphics2D g2d = ( Graphics2D ) g;
        final Object aa = GraphicsUtils.setupAntialias ( g2d );

        // Border shape
        final GeneralPath borderShape = createTabShape ( TabShapeType.border, tabPlacement, x, y, w, h, isSelected );

        // Tab shade
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            final GeneralPath shadeShape = createTabShape ( TabShapeType.shade, tabPlacement, x, y, w, h, isSelected );
            GraphicsUtils.drawShade ( g2d, shadeShape, new Color ( 210, 210, 210 ), shadeWidth,
                    new Rectangle2D.Double ( 0, 0, component.getWidth (), y + h ), round > 0 );
        }

        // Tab background
        final GeneralPath bgShape = createTabShape ( TabShapeType.background, tabPlacement, x, y, w, h, isSelected );
        final SectionPainter backgroundPainterAt = ui.getBackgroundPainterAt ( tabIndex );
        if ( backgroundPainterAt != null && isSelected )
        {
            // todo This is a hack used to paint background as section
            final Shape old = GraphicsUtils.intersectClip ( g2d, bgShape );
            paintSection ( backgroundPainterAt, g2d, new Rectangle ( x, y, w, h ) );
            GraphicsUtils.restoreClip ( g2d, old );
        }
        else
        {
            final Point topPoint = getTopTabBgPoint ( tabPlacement, x, y, w, h );
            final Point bottomPoint = getBottomTabBgPoint ( tabPlacement, x, y, w, h );
            if ( isSelected )
            {
                if ( forceUseSelectedTabBgColors )
                {
                    g2d.setPaint (
                            new GradientPaint ( topPoint.x, topPoint.y, selectedTopBg, bottomPoint.x, bottomPoint.y, selectedBottomBg ) );
                }
                else
                {
                    Color bg = component.getBackgroundAt ( tabIndex );
                    bg = bg != null ? bg : component.getBackground ();
                    g2d.setPaint ( new GradientPaint ( topPoint.x, topPoint.y, selectedTopBg, bottomPoint.x, bottomPoint.y, bg ) );
                }
            }
            else
            {
                g2d.setPaint ( new GradientPaint ( topPoint.x, topPoint.y, topBg, bottomPoint.x, bottomPoint.y, bottomBg ) );
            }
            g2d.fill ( isSelected ? borderShape : bgShape );
        }

        // Tab border
        g2d.setPaint ( tabBorderColor );
        g2d.draw ( borderShape );

        // Tab focus
        final boolean drawFocus = isSelected && component.isFocusOwner ();
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            paintCustomFocus ( g2d, null, FocusType.fieldFocus, borderShape, null, drawFocus );
        }
        //        else if ( drawFocus )
        //        {
        //            g2d.setPaint ( StyleConstants.fieldFocusColor );
        //            g2d.drawLine ( x, y + 1, x + w, y + 1 );
        //        }

        //todo Paint selected tab together with area

        GraphicsUtils.restoreAntialias ( g2d, aa );
    }

    protected Icon getIconForTab ( final int tabIndex )
    {
        return !component.isEnabled () || !component.isEnabledAt ( tabIndex ) ? component.getDisabledIconAt ( tabIndex ) :
                component.getIconAt ( tabIndex );
    }

    protected void paintText ( final Graphics g, final int tabPlacement, final Font font, final FontMetrics metrics, final int tabIndex,
                               final String title, final Rectangle textRect, final boolean isSelected )
    {
        g.setFont ( font );
        final View v = getTextViewForTab ( tabIndex );
        if ( v != null )
        {
            // html
            v.paint ( g, textRect );
        }
        else
        {
            // plain text
            final int mnemIndex = component.getDisplayedMnemonicIndexAt ( tabIndex );

            if ( component.isEnabled () && component.isEnabledAt ( tabIndex ) )
            {
                Color fg = component.getForegroundAt ( tabIndex );
                if ( isSelected && fg instanceof UIResource )
                {
                    final Color selectedFG = UIManager.getColor ( "TabbedPane.selectedForeground" );
                    if ( selectedFG != null )
                    {
                        fg = selectedFG;
                    }
                }
                g.setColor ( fg );
                SwingUtils.drawStringUnderlineCharAt ( g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent () );
            }
            else
            {
                // tab disabled
                g.setColor ( component.getBackgroundAt ( tabIndex ).brighter () );
                SwingUtils.drawStringUnderlineCharAt ( g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent () );
                g.setColor ( component.getBackgroundAt ( tabIndex ).darker () );
                SwingUtils.drawStringUnderlineCharAt ( g, title, mnemIndex, textRect.x - 1, textRect.y + metrics.getAscent () - 1 );
            }
        }
    }

    protected GeneralPath createTabShape ( final TabShapeType tabShapeType, final int tabPlacement, int x, final int y, int w, final int h,
                                           final boolean isSelected )
    {
        // Fix for basic layouting of selected left-sided tab x coordinate
        final Insets insets = component.getInsets ();
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.attached ) && isSelected )
        {
            // todo fix for other tabPlacement values aswell
            if ( tabPlacement == JTabbedPane.TOP && x == insets.left )
            {
                x = x - 1;
                w = w + 1;
            }
        }

        final int actualRound = ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) ? round : 0;
        final GeneralPath bgShape = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );

        if ( !isSelected && paintBorderOnlyOnSelectedTab )
        {
            return bgShape;
        }

        if ( tabPlacement == JTabbedPane.TOP )
        {
            bgShape.moveTo ( x, y + h + getChange ( tabShapeType ) );
            bgShape.lineTo ( x, y + actualRound );
            bgShape.quadTo ( x, y, x + actualRound, y );
            bgShape.lineTo ( x + w - actualRound, y );
            bgShape.quadTo ( x + w, y, x + w, y + actualRound );
            bgShape.lineTo ( x + w, y + h + getChange ( tabShapeType ) );
        }
        else if ( tabPlacement == JTabbedPane.BOTTOM )
        {
            bgShape.moveTo ( x, y - getChange ( tabShapeType ) );
            bgShape.lineTo ( x, y + h - actualRound );
            bgShape.quadTo ( x, y + h, x + actualRound, y + h );
            bgShape.lineTo ( x + w - actualRound, y + h );
            bgShape.quadTo ( x + w, y + h, x + w, y + h - actualRound );
            bgShape.lineTo ( x + w, y - getChange ( tabShapeType ) );
        }
        else if ( tabPlacement == JTabbedPane.LEFT )
        {
            bgShape.moveTo ( x + w + getChange ( tabShapeType ), y );
            bgShape.lineTo ( x + actualRound, y );
            bgShape.quadTo ( x, y, x, y + actualRound );
            bgShape.lineTo ( x, y + h - actualRound );
            bgShape.quadTo ( x, y + h, x + actualRound, y + h );
            bgShape.lineTo ( x + w + getChange ( tabShapeType ), y + h );
        }
        else
        {
            bgShape.moveTo ( x - getChange ( tabShapeType ), y );
            bgShape.lineTo ( x + w - actualRound, y );
            bgShape.quadTo ( x + w, y, x + w, y + actualRound );
            bgShape.lineTo ( x + w, y + h - actualRound );
            bgShape.quadTo ( x + w, y + h, x + w - actualRound, y + h );
            bgShape.lineTo ( x - getChange ( tabShapeType ), y + h );
        }
        return bgShape;
    }

    protected int getChange ( final TabShapeType tabShapeType )
    {
        if ( tabShapeType.equals ( TabShapeType.shade ) )
        {
            return -( round > 0 ? round : 1 );
        }
        else if ( tabShapeType.equals ( TabShapeType.border ) )
        {
            return -1;
        }
        else if ( tabShapeType.equals ( TabShapeType.backgroundPainter ) )
        {
            return 2;
        }
        else
        {
            return 0;
        }
    }

    protected Point getTopTabBgPoint ( final int tabPlacement, final int x, final int y, final int w, final int h )
    {
        if ( tabPlacement == JTabbedPane.TOP )
        {
            return new Point ( x, y );
        }
        else if ( tabPlacement == JTabbedPane.BOTTOM )
        {
            return new Point ( x, y + h );
        }
        else if ( tabPlacement == JTabbedPane.LEFT )
        {
            return new Point ( x, y );
        }
        else
        {
            return new Point ( x + w, y );
        }
    }

    protected Point getBottomTabBgPoint ( final int tabPlacement, final int x, final int y, final int w, final int h )
    {
        if ( tabPlacement == JTabbedPane.TOP )
        {
            return new Point ( x, y + h - 4 );
        }
        else if ( tabPlacement == JTabbedPane.BOTTOM )
        {
            return new Point ( x, y + 4 );
        }
        else if ( tabPlacement == JTabbedPane.LEFT )
        {
            return new Point ( x + w - 4, y );
        }
        else
        {
            return new Point ( x + 4, y );
        }
    }

    protected void paintContentBorder ( final Graphics2D g2d, final int tabPlacement, final int selectedIndex )
    {
        final Object aa = GraphicsUtils.setupAntialias ( g2d );

        final int tabAreaSize = getTabAreaLength ( tabPlacement );

        Insets bi = component.getInsets ();
        if ( tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM )
        {
            bi = new Insets ( bi.top, bi.left, bi.bottom, bi.right + 1 );
        }
        else
        {
            bi = new Insets ( bi.top, bi.left, bi.bottom + 1, bi.right );
        }

        // Selected tab bounds
        final Rectangle selected = selectedIndex != -1 ? ui.getTabBounds ( component, selectedIndex ) : null;

        // Background shape
        final Shape bs = createBackgroundShape ( tabPlacement, tabAreaSize, bi, selected );

        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            // Proper clip
            final GeneralPath clip = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
            clip.append ( new Rectangle2D.Double ( 0, 0, component.getWidth (), component.getHeight () ), false );
            clip.append ( bs, false );
            GraphicsUtils.drawShade ( g2d, bs, new Color ( 210, 210, 210 ), shadeWidth, clip, round > 0 );

            //            // Corners covering for large round
            //            if ( round > 2 )
            //            {
            //                Insets tai = getTabAreaInsets ( tabPlacement );
            //                if ( tabPlacement == TOP )
            //                {
            //                    if ( selected.x > bi.left )
            //                    {
            //                        g2d.setPaint ( bottomBg );
            //                        g2d.fillRect ( bi.left + tai.left, bi.top + tabAreaSize, round, round );
            //
            //                        g2d.setPaint ( StyleConstants.darkBorderColor );
            //                        g2d.drawLine ( bi.left + tai.left, bi.top + tabAreaSize, bi.left + tai.left,
            //                                bi.top + tabAreaSize + round );
            //                    }
            ////                    if ( selected.x + selected.width < tabPane.getWidth () - bi.right )
            ////                    {
            ////                        g2d.setPaint ( bottomBg );
            ////                        g2d.fillRect ( bi.rightbi.left + tai.left, bi.top + tabAreaSize, round, round );
            ////
            ////                        g2d.setPaint ( StyleConstants.darkBorderColor );
            ////                        g2d.drawLine ( bi.left + tai.left, bi.top + tabAreaSize, bi.left + tai.left,
            ////                                bi.top + tabAreaSize + round );
            ////                    }
            //                }
            //            }

            // Area background
            final SectionPainter backgroundPainterAt = ui.getBackgroundPainterAt ( selectedIndex );
            if ( backgroundPainterAt != null )
            {
                // todo This is a hack used to paint background as section
                final Shape old = GraphicsUtils.intersectClip ( g2d, bs );
                paintSection ( backgroundPainterAt, g2d, bs.getBounds () );
                GraphicsUtils.restoreClip ( g2d, old );
            }
            else
            {
                final Color bg = selectedIndex != -1 ? component.getBackgroundAt ( selectedIndex ) : null;
                g2d.setPaint ( bg != null ? bg : component.getBackground () );
                g2d.fill ( bs );
            }

            // Area border
            g2d.setPaint ( contentBorderColor );
            g2d.draw ( bs );

            // Area focus
            paintCustomFocus ( g2d, null, FocusType.fieldFocus, bs, null, component.isFocusOwner () );
        }
        else
        {
            // Area background
            final SectionPainter backgroundPainterAt = ui.getBackgroundPainterAt ( selectedIndex );
            if ( backgroundPainterAt != null )
            {
                // todo This is a hack used to paint background as section
                paintSection ( backgroundPainterAt, g2d, bs.getBounds () );
            }
            else
            {
                final Color bg = selectedIndex != -1 ? component.getBackgroundAt ( selectedIndex ) : null;
                g2d.setPaint ( bg != null ? bg : component.getBackground () );
                g2d.fill ( bs );
            }

            // todo draw for other tabPlacement values aswell
            // Area border
            g2d.setPaint ( contentBorderColor );
            if ( tabPlacement == JTabbedPane.TOP )
            {
                if ( selected != null )
                {
                    if ( bi.left < selected.x )
                    {
                        g2d.drawLine ( bi.left, bi.top + tabAreaSize, selected.x, bi.top + tabAreaSize );
                    }
                    if ( selected.x + selected.width < component.getWidth () - bi.right )
                    {
                        g2d.drawLine ( selected.x + selected.width, bi.top + tabAreaSize, component.getWidth () - bi.right,
                                bi.top + tabAreaSize );
                    }
                }
                else
                {
                    g2d.drawLine ( bi.left, bi.top + tabAreaSize, component.getWidth () - bi.right, bi.top + tabAreaSize );
                }
            }
            //            else if ( tabPlacement == JTabbedPane.BOTTOM )
            //            {
            //                //
            //            }
            //            else if ( tabPlacement == JTabbedPane.LEFT )
            //            {
            //                //
            //            }
            //            else if ( tabPlacement == JTabbedPane.RIGHT )
            //            {
            //                //
            //            }
        }

        GraphicsUtils.restoreAntialias ( g2d, aa );
    }

    protected int getTabAreaLength ( final int tabPlacement )
    {
        return tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM ?
                calculateTabAreaHeight ( tabPlacement, runCount, maxTabHeight ) - 1 :
                calculateTabAreaWidth ( tabPlacement, runCount, maxTabWidth ) - 1;
    }

    protected int calculateTabAreaHeight ( final int tabPlacement, final int horizRunCount, final int maxTabHeight )
    {
        final Insets tabAreaInsets = ui.getTabAreaInsets ( tabPlacement );
        final int tabRunOverlay = ui.getTabRunOverlay ( tabPlacement );
        return horizRunCount > 0 ? horizRunCount * ( maxTabHeight - tabRunOverlay ) + tabRunOverlay +
                tabAreaInsets.top + tabAreaInsets.bottom : 0;
    }

    protected int calculateTabAreaWidth ( final int tabPlacement, final int vertRunCount, final int maxTabWidth )
    {
        final Insets tabAreaInsets = ui.getTabAreaInsets ( tabPlacement );
        final int tabRunOverlay = ui.getTabRunOverlay ( tabPlacement );
        return vertRunCount > 0 ? vertRunCount * ( maxTabWidth - tabRunOverlay ) + tabRunOverlay +
                tabAreaInsets.left + tabAreaInsets.right : 0;
    }

    protected Shape createBackgroundShape ( final int tabPlacement, final int tabAreaSize, final Insets bi, final Rectangle selected )
    {
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            if ( selected != null )
            {
                final GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
                if ( tabPlacement == JTabbedPane.TOP )
                {
                    final int topY = bi.top + tabAreaSize;
                    gp.moveTo ( selected.x, topY );
                    if ( selected.x > bi.left + round && round > 0 )
                    {
                        gp.lineTo ( bi.left + round, topY );
                        gp.quadTo ( bi.left, topY, bi.left, topY + round );
                    }
                    else
                    {
                        gp.lineTo ( bi.left, topY );
                    }
                    if ( round > 0 )
                    {
                        gp.lineTo ( bi.left, component.getHeight () - bi.bottom - round );
                        gp.quadTo ( bi.left, component.getHeight () - bi.bottom, bi.left + round, component.getHeight () - bi.bottom );
                        gp.lineTo ( component.getWidth () - bi.right - round, component.getHeight () - bi.bottom );
                        gp.quadTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom, component.getWidth () - bi.right,
                                component.getHeight () - bi.bottom - round );
                    }
                    else
                    {
                        if ( paintOnlyTopBorder )
                        {
                            gp.moveTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom );
                        }
                        else
                        {
                            gp.lineTo ( bi.left, component.getHeight () - bi.bottom );
                            gp.lineTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom );
                        }
                    }
                    if ( selected.x + selected.width < component.getWidth () - bi.right - round && round > 0 )
                    {
                        gp.lineTo ( component.getWidth () - bi.right, topY + round );
                        gp.quadTo ( component.getWidth () - bi.right, topY, component.getWidth () - bi.right - round, topY );
                    }
                    else
                    {
                        if ( paintOnlyTopBorder )
                        {
                            gp.moveTo ( component.getWidth () - bi.right, topY );
                        }
                        else
                        {
                            gp.lineTo ( component.getWidth () - bi.right, topY );
                        }
                    }
                    gp.lineTo ( selected.x + selected.width, topY );
                }
                else if ( tabPlacement == JTabbedPane.BOTTOM )
                {
                    final int bottomY = component.getHeight () - bi.bottom - tabAreaSize;
                    gp.moveTo ( selected.x, bottomY );
                    if ( selected.x > bi.left + round && round > 0 )
                    {
                        gp.lineTo ( bi.left + round, bottomY );
                        gp.quadTo ( bi.left, bottomY, bi.left, bottomY - round );
                    }
                    else
                    {
                        gp.lineTo ( bi.left, bottomY );
                    }
                    if ( round > 0 )
                    {
                        gp.lineTo ( bi.left, bi.top + round );
                        gp.quadTo ( bi.left, bi.top, bi.left + round, bi.top );
                        gp.lineTo ( component.getWidth () - bi.right - round, bi.top );
                        gp.quadTo ( component.getWidth () - bi.right, bi.top, component.getWidth () - bi.right, bi.top + round );
                    }
                    else
                    {
                        gp.lineTo ( bi.left, bi.top );
                        gp.lineTo ( component.getWidth () - bi.right, bi.top );
                    }
                    if ( selected.x + selected.width < component.getWidth () - bi.right - round && round > 0 )
                    {
                        gp.lineTo ( component.getWidth () - bi.right, bottomY - round );
                        gp.quadTo ( component.getWidth () - bi.right, bottomY, component.getWidth () - bi.right - round, bottomY );
                    }
                    else
                    {
                        gp.lineTo ( component.getWidth () - bi.right, bottomY );
                    }
                    gp.lineTo ( selected.x + selected.width, bottomY );
                }
                else if ( tabPlacement == JTabbedPane.LEFT )
                {
                    final int leftX = bi.left + tabAreaSize;
                    gp.moveTo ( leftX, selected.y );
                    if ( selected.y > bi.top + round && round > 0 )
                    {
                        gp.lineTo ( leftX, bi.top + round );
                        gp.quadTo ( leftX, bi.top, leftX + round, bi.top );
                    }
                    else
                    {
                        gp.lineTo ( leftX, bi.top );
                    }
                    if ( round > 0 )
                    {
                        gp.lineTo ( component.getWidth () - bi.right - round, bi.top );
                        gp.quadTo ( component.getWidth () - bi.right, bi.top, component.getWidth () - bi.right, bi.top + round );
                        gp.lineTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom - round );
                        gp.quadTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom,
                                component.getWidth () - bi.right - round, component.getHeight () - bi.bottom );
                    }
                    else
                    {
                        gp.lineTo ( component.getWidth () - bi.right, bi.top );
                        gp.lineTo ( component.getWidth () - bi.right, component.getHeight () - bi.bottom );
                    }
                    if ( selected.y + selected.height < component.getHeight () - bi.bottom - round && round > 0 )
                    {
                        gp.lineTo ( leftX + round, component.getHeight () - bi.bottom );
                        gp.quadTo ( leftX, component.getHeight () - bi.bottom, leftX, component.getHeight () - bi.bottom - round );
                    }
                    else
                    {
                        gp.lineTo ( leftX, component.getHeight () - bi.bottom );
                    }
                    gp.lineTo ( leftX, selected.y + selected.height );
                }
                else
                {
                    final int rightX = component.getWidth () - bi.right - tabAreaSize;
                    gp.moveTo ( rightX, selected.y );
                    if ( selected.y > bi.top + round && round > 0 )
                    {
                        gp.lineTo ( rightX, bi.top + round );
                        gp.quadTo ( rightX, bi.top, rightX - round, bi.top );
                    }
                    else
                    {
                        gp.lineTo ( rightX, bi.top );
                    }
                    if ( round > 0 )
                    {
                        gp.lineTo ( bi.left + round, bi.top );
                        gp.quadTo ( bi.left, bi.top, bi.left, bi.top + round );
                        gp.lineTo ( bi.left, component.getHeight () - bi.bottom - round );
                        gp.quadTo ( bi.left, component.getHeight () - bi.bottom, bi.left + round, component.getHeight () - bi.bottom );
                    }
                    else
                    {
                        gp.lineTo ( bi.left, bi.top );
                        gp.lineTo ( bi.left, component.getHeight () - bi.bottom );
                    }
                    if ( selected.y + selected.height < component.getHeight () - bi.bottom - round && round > 0 )
                    {
                        gp.lineTo ( rightX - round, component.getHeight () - bi.bottom );
                        gp.quadTo ( rightX, component.getHeight () - bi.bottom, rightX, component.getHeight () - bi.bottom - round );
                    }
                    else
                    {
                        gp.lineTo ( rightX, component.getHeight () - bi.bottom );
                    }
                    gp.lineTo ( rightX, selected.y + selected.height );
                }
                return gp;
            }
            else
            {
                final boolean top = tabPlacement == JTabbedPane.TOP;
                final boolean bottom = tabPlacement == JTabbedPane.BOTTOM;
                final boolean left = tabPlacement == JTabbedPane.LEFT;
                final boolean right = tabPlacement == JTabbedPane.RIGHT;
                return new RoundRectangle2D.Double ( bi.left + ( left ? tabAreaSize : 0 ), bi.top + ( top ? tabAreaSize : 0 ),
                        component.getWidth () - bi.left - bi.right -
                                ( left || right ? tabAreaSize : 0 ), component.getHeight () - bi.top - bi.bottom -
                        ( top || bottom ? tabAreaSize : 0 ), round * 2, round * 2 );
            }
        }
        else
        {
            final int x = bi.left + ( tabPlacement == JTabbedPane.LEFT ? tabAreaSize : 0 );
            final int y = bi.top + ( tabPlacement == JTabbedPane.TOP ? tabAreaSize : 0 );
            final int width = component.getWidth () - bi.left - bi.right -
                    ( tabPlacement == JTabbedPane.LEFT || tabPlacement == JTabbedPane.RIGHT ? tabAreaSize : 0 );
            final int height = component.getHeight () - bi.top - bi.bottom -
                    ( tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM ? tabAreaSize : 0 );
            return new Rectangle ( x, y, width + 1, height );
        }
    }

    protected int getTabLabelShiftX ( final int tabPlacement, final int tabIndex, final boolean isSelected )
    {
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            final Rectangle tabRect = rects[ tabIndex ];
            switch ( tabPlacement )
            {
                case JTabbedPane.LEFT:
                    return isSelected ? -1 : 1;

                case JTabbedPane.RIGHT:
                    return isSelected ? 1 : -1;

                case JTabbedPane.BOTTOM:
                case JTabbedPane.TOP:
                default:
                    return tabRect.width % 2;
            }
        }
        else
        {
            return 0;
        }
    }

    protected int getTabLabelShiftY ( final int tabPlacement, final int tabIndex, final boolean isSelected )
    {
        if ( ui.getTabbedPaneStyle ().equals ( TabbedPaneStyle.standalone ) )
        {
            final Rectangle tabRect = rects[ tabIndex ];
            switch ( tabPlacement )
            {
                case JTabbedPane.BOTTOM:
                    return isSelected ? 1 : -1;

                case JTabbedPane.LEFT:
                case JTabbedPane.RIGHT:
                    return tabRect.height % 2;

                case JTabbedPane.TOP:
                default:
                    return isSelected ? -1 : 1;
            }
        }
        else
        {
            return 0;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy