/*
 * Copyright 1998-2004 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package javax.swing.plaf.metal;

import java.awt.Component;
import java.awt.Container;
import java.awt.LayoutManager;
import java.awt.Adjustable;
import java.awt.event.AdjustmentListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Insets;
import java.awt.Color;
import java.awt.IllegalComponentStateException;

import java.beans.*;

import javax.swing.*;
import javax.swing.event.*;

import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicScrollBarUI;


/**
 * Implementation of ScrollBarUI for the Metal Look and Feel
 * <p>
 *
 * @author Tom Santos
 * @author Steve Wilson
 */
public class MetalScrollBarUI extends BasicScrollBarUI
{
    private static Color shadowColor;
    private static Color highlightColor;
    private static Color darkShadowColor;
    private static Color thumbColor;
    private static Color thumbShadow;
    private static Color thumbHighlightColor;


    protected MetalBumps bumps;

    protected MetalScrollButton increaseButton;
    protected MetalScrollButton decreaseButton;

    protected  int scrollBarWidth;

    public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding";
    protected boolean isFreeStanding = true;

    public static ComponentUI createUI( JComponent c )
    {
        return new MetalScrollBarUI();
    }

    protected void installDefaults() {
        scrollBarWidth = ((Integer)(UIManager.get( "ScrollBar.width" ))).intValue();
        super.installDefaults();
        bumps = new MetalBumps( 10, 10, thumbHighlightColor, thumbShadow, thumbColor );
    }

    protected void installListeners(){
        super.installListeners();
        ((ScrollBarListener)propertyChangeListener).handlePropertyChange( scrollbar.getClientProperty( FREE_STANDING_PROP ) );
    }

    protected PropertyChangeListener createPropertyChangeListener(){
        return new ScrollBarListener();
    }

    protected void configureScrollBarColors()
    {
        super.configureScrollBarColors();
        shadowColor         = UIManager.getColor("ScrollBar.shadow");
        highlightColor      = UIManager.getColor("ScrollBar.highlight");
        darkShadowColor     = UIManager.getColor("ScrollBar.darkShadow");
        thumbColor          = UIManager.getColor("ScrollBar.thumb");
        thumbShadow         = UIManager.getColor("ScrollBar.thumbShadow");
        thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");


    }

    public Dimension getPreferredSize( JComponent c )
    {
        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
        {
            return new Dimension( scrollBarWidth, scrollBarWidth * 3 + 10 );
        }
        else  // Horizontal
        {
            return new Dimension( scrollBarWidth * 3 + 10, scrollBarWidth );
        }

    }

    /** Returns the view that represents the decrease view.
      */
    protected JButton createDecreaseButton( int orientation )
    {
        decreaseButton = new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
        return decreaseButton;
    }

    /** Returns the view that represents the increase view. */
    protected JButton createIncreaseButton( int orientation )
    {
        increaseButton =  new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
        return increaseButton;
    }

    protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds )
    {
        g.translate( trackBounds.x, trackBounds.y );

        boolean leftToRight = MetalUtils.isLeftToRight(c);

        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
        {
            if ( !isFreeStanding ) {
                trackBounds.width += 2;
                if ( !leftToRight ) {
                    g.translate( -1, 0 );
                }
            }

            if ( c.isEnabled() ) {
                g.setColor( darkShadowColor );
                g.drawLine( 0, 0, 0, trackBounds.height - 1 );
                g.drawLine( trackBounds.width - 2, 0, trackBounds.width - 2, trackBounds.height - 1 );
                g.drawLine( 2, trackBounds.height - 1, trackBounds.width - 1, trackBounds.height - 1);
                g.drawLine( 2, 0, trackBounds.width - 2, 0 );

                g.setColor( shadowColor );
                //      g.setColor( Color.red);
                g.drawLine( 1, 1, 1, trackBounds.height - 2 );
                g.drawLine( 1, 1, trackBounds.width - 3, 1 );
                if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
                    int y = thumbRect.y + thumbRect.height - trackBounds.y;
                    g.drawLine( 1, y, trackBounds.width-1, y);
                }
                g.setColor(highlightColor);
                g.drawLine( trackBounds.width - 1, 0, trackBounds.width - 1, trackBounds.height - 1 );
            } else {
                MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
            }

            if ( !isFreeStanding ) {
                trackBounds.width -= 2;
                if ( !leftToRight ) {
                    g.translate( 1, 0 );
                }
            }
        }
        else  // HORIZONTAL
        {
            if ( !isFreeStanding ) {
                trackBounds.height += 2;
            }

            if ( c.isEnabled() ) {
                g.setColor( darkShadowColor );
                g.drawLine( 0, 0, trackBounds.width - 1, 0 );  // top
                g.drawLine( 0, 2, 0, trackBounds.height - 2 ); // left
                g.drawLine( 0, trackBounds.height - 2, trackBounds.width - 1, trackBounds.height - 2 ); // bottom
                g.drawLine( trackBounds.width - 1, 2, trackBounds.width - 1, trackBounds.height - 1 ); // right

                g.setColor( shadowColor );
                //      g.setColor( Color.red);
                g.drawLine( 1, 1, trackBounds.width - 2, 1 );  // top
                g.drawLine( 1, 1, 1, trackBounds.height - 3 ); // left
                g.drawLine( 0, trackBounds.height - 1, trackBounds.width - 1, trackBounds.height - 1 ); // bottom
                if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
                    int x = thumbRect.x + thumbRect.width - trackBounds.x;
                    g.drawLine( x, 1, x, trackBounds.height-1);
                }
            } else {
                MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
            }

            if ( !isFreeStanding ) {
                trackBounds.height -= 2;
            }
        }

        g.translate( -trackBounds.x, -trackBounds.y );
    }

    protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds )
    {
        if (!c.isEnabled()) {
            return;
        }

        if (MetalLookAndFeel.usingOcean()) {
            oceanPaintThumb(g, c, thumbBounds);
            return;
        }

        boolean leftToRight = MetalUtils.isLeftToRight(c);

        g.translate( thumbBounds.x, thumbBounds.y );

        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
        {
            if ( !isFreeStanding ) {
                thumbBounds.width += 2;
                if ( !leftToRight ) {
                    g.translate( -1, 0 );
                }
            }

            g.setColor( thumbColor );
            g.fillRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );

            g.setColor( thumbShadow );
            g.drawRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );

            g.setColor( thumbHighlightColor );
            g.drawLine( 1, 1, thumbBounds.width - 3, 1 );
            g.drawLine( 1, 1, 1, thumbBounds.height - 2 );

            bumps.setBumpArea( thumbBounds.width - 6, thumbBounds.height - 7 );
            bumps.paintIcon( c, g, 3, 4 );

            if ( !isFreeStanding ) {
                thumbBounds.width -= 2;
                if ( !leftToRight ) {
                    g.translate( 1, 0 );
                }
            }
        }
        else  // HORIZONTAL
        {
            if ( !isFreeStanding ) {
                thumbBounds.height += 2;
            }

            g.setColor( thumbColor );
            g.fillRect( 0, 0, thumbBounds.width - 1, thumbBounds.height - 2 );

            g.setColor( thumbShadow );
            g.drawRect( 0, 0, thumbBounds.width - 1, thumbBounds.height - 2 );

            g.setColor( thumbHighlightColor );
            g.drawLine( 1, 1, thumbBounds.width - 3, 1 );
            g.drawLine( 1, 1, 1, thumbBounds.height - 3 );

            bumps.setBumpArea( thumbBounds.width - 7, thumbBounds.height - 6 );
            bumps.paintIcon( c, g, 4, 3 );

            if ( !isFreeStanding ) {
                thumbBounds.height -= 2;
            }
        }

        g.translate( -thumbBounds.x, -thumbBounds.y );
    }

    private void oceanPaintThumb(Graphics g, JComponent c,
                                   Rectangle thumbBounds) {
        boolean leftToRight = MetalUtils.isLeftToRight(c);

        g.translate(thumbBounds.x, thumbBounds.y);

        if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
            if (!isFreeStanding) {
                thumbBounds.width += 2;
                if (!leftToRight) {
                    g.translate(-1, 0);
                }
            }

            if (thumbColor != null) {
                g.setColor(thumbColor);
                g.fillRect(0, 0, thumbBounds.width - 2,thumbBounds.height - 1);
            }

            g.setColor(thumbShadow);
            g.drawRect(0, 0, thumbBounds.width - 2, thumbBounds.height - 1);

            g.setColor(thumbHighlightColor);
            g.drawLine(1, 1, thumbBounds.width - 3, 1);
            g.drawLine(1, 1, 1, thumbBounds.height - 2);

            MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
                                    thumbBounds.width - 4,
                                    thumbBounds.height - 3, false);

            int gripSize = thumbBounds.width - 8;
            if (gripSize > 2 && thumbBounds.height >= 10) {
                g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
                int gripY = thumbBounds.height / 2 - 2;
                for (int counter = 0; counter < 6; counter += 2) {
                    g.fillRect(4, counter + gripY, gripSize, 1);
                }

                g.setColor(MetalLookAndFeel.getWhite());
                gripY++;
                for (int counter = 0; counter < 6; counter += 2) {
                    g.fillRect(5, counter + gripY, gripSize, 1);
                }
            }
            if (!isFreeStanding) {
                thumbBounds.width -= 2;
                if (!leftToRight) {
                    g.translate(1, 0);
                }
            }
        }
        else { // HORIZONTAL
            if (!isFreeStanding) {
                thumbBounds.height += 2;
            }

            if (thumbColor != null) {
                g.setColor(thumbColor);
                g.fillRect(0, 0, thumbBounds.width - 1,thumbBounds.height - 2);
            }

            g.setColor(thumbShadow);
            g.drawRect(0, 0, thumbBounds.width - 1, thumbBounds.height - 2);

            g.setColor(thumbHighlightColor);
            g.drawLine(1, 1, thumbBounds.width - 2, 1);
            g.drawLine(1, 1, 1, thumbBounds.height - 3);

            MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
                                    thumbBounds.width - 3,
                                    thumbBounds.height - 4, true);

            int gripSize = thumbBounds.height - 8;
            if (gripSize > 2 && thumbBounds.width >= 10) {
                g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
                int gripX = thumbBounds.width / 2 - 2;
                for (int counter = 0; counter < 6; counter += 2) {
                    g.fillRect(gripX + counter, 4, 1, gripSize);
                }

                g.setColor(MetalLookAndFeel.getWhite());
                gripX++;
                for (int counter = 0; counter < 6; counter += 2) {
                    g.fillRect(gripX + counter, 5, 1, gripSize);
                }
            }

            if (!isFreeStanding) {
                thumbBounds.height -= 2;
            }
        }

        g.translate( -thumbBounds.x, -thumbBounds.y );
    }

    protected Dimension getMinimumThumbSize()
    {
        return new Dimension( scrollBarWidth, scrollBarWidth );
    }

    /**
      * This is overridden only to increase the invalid area.  This
      * ensures that the "Shadow" below the thumb is invalidated
      */
    protected void setThumbBounds(int x, int y, int width, int height)
    {
        /* If the thumbs bounds haven't changed, we're done.
         */
        if ((thumbRect.x == x) &&
            (thumbRect.y == y) &&
            (thumbRect.width == width) &&
            (thumbRect.height == height)) {
            return;
        }

        /* Update thumbRect, and repaint the union of x,y,w,h and
         * the old thumbRect.
         */
        int minX = Math.min(x, thumbRect.x);
        int minY = Math.min(y, thumbRect.y);
        int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
        int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);

        thumbRect.setBounds(x, y, width, height);
        scrollbar.repaint(minX, minY, (maxX - minX)+1, (maxY - minY)+1);
    }



    class ScrollBarListener extends BasicScrollBarUI.PropertyChangeHandler
    {
        public void propertyChange(PropertyChangeEvent e)
        {
            String name = e.getPropertyName();
            if ( name.equals( FREE_STANDING_PROP ) )
            {
                handlePropertyChange( e.getNewValue() );
            }
            else {
                super.propertyChange( e );
            }
        }

        public void handlePropertyChange( Object newValue )
        {
            if ( newValue != null )
            {
                boolean temp = ((Boolean)newValue).booleanValue();
                boolean becameFlush = temp == false && isFreeStanding == true;
                boolean becameNormal = temp == true && isFreeStanding == false;

                isFreeStanding = temp;

                if ( becameFlush ) {
                    toFlush();
                }
                else if ( becameNormal ) {
                    toFreeStanding();
                }
            }
            else
            {

                if ( !isFreeStanding ) {
                    isFreeStanding = true;
                    toFreeStanding();
                }

                // This commented-out block is used for testing flush scrollbars.
/*
                if ( isFreeStanding ) {
                    isFreeStanding = false;
                    toFlush();
                }
*/
            }

            if ( increaseButton != null )
            {
                increaseButton.setFreeStanding( isFreeStanding );
            }
            if ( decreaseButton != null )
            {
                decreaseButton.setFreeStanding( isFreeStanding );
            }
        }

        protected void toFlush() {
            scrollBarWidth -= 2;
        }

        protected void toFreeStanding() {
            scrollBarWidth += 2;
        }
    } // end class ScrollBarListener
}
