Package no.ugland.utransprod.util

Source Code of no.ugland.utransprod.util.InfiniteProgressPanel$Ticker

/*
* Copyright (c) 2005, romain guy (romain.guy@jext.org) and craig wickesser
* (craig@codecraig.com) All rights reserved. Redistribution and use in source
* and binary forms, with or without modification, are permitted provided that
* the following conditions are met: * Redistributions of source code must
* retain the above copyright notice, this list of conditions and the following
* disclaimer. * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. *
* Neither the name of the <ORGANIZATION> nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.ugland.utransprod.util;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;

/**
* An infinite progress panel displays a rotating figure and a message to notice
* the user of a long, duration unknown task. The shape and the text are drawn
* upon a white veil which alpha level (or shield value) lets the underlying
* component shine through. This panel is meant to be used asa <i>glass pane</i>
* in the window performing the long operation.
* <p>
* Contrary to regular glass panes, you don't need to set it visible or not by
* yourself. Once you've started the animation all the mouse events are
* intercepted by this panel, preventing them from being forwared to the
* underlying components.
* <p>
* The panel can be controlled by the <code>start()</code>,
* <code>stop()</code> and <code>interrupt()</code> methods.
* <p>
* Example:
* <p>
* <pre>
*    InfiniteProgressPanel pane = new InfiniteProgressPanel();
*     frame.setGlassPane(pane);
*     ... later in some other EDT event (otherwise the panel doesn't know the size and draws real funky)
*     pane.start()
* </pre>
* <p>
* Several properties can be configured at creation time. The message and its
* font can be changed at runtime. Changing the font can be done using
* <code>setFont()</code> and <code>setForeground()</code>.
* <p>
* If you experience performance issues, prefer the
* <code>PerformanceInfiniteProgressPanel</code>.
* <p>
* For cancelable progress use the <code>CancelableProgressPanel</code> or the
* <code>CancelableProgressAdapter</code> with a Panel.
* @author Romain Guy, 17/02/2005
* @since 1.0 <br>
*        $Revision: 1.5 $
*/

public class InfiniteProgressPanel extends JComponent implements MouseListener,
        CancelableAdaptee {
    private static final long serialVersionUID = 3546080263571714356L;

    /** Contains the bars composing the circular shape. */
    private Ticker ticker = null;

    /**
     * The animation thread is responsible for fade in/out and rotation.
     */
    private Thread animation = null;

    /**
     * Notifies whether the animation is running or not.
     */
    boolean started = false;

    /**
     * Alpha level of the veil, used for fade in/out.
     */
    int alphaLevel = 0;

    /**
     * Duration of the veil's fade in/out.
     */
    int rampDelay = 300;

    /**
     * Alpha level of the veil.
     */
    private float shield = 0.70f;

    /**
     * Message displayed below the circular shape.
     */
    private String text = "";

    /**
     * Amount of bars composing the circular shape.
     */
    int barsCount = 14;

    /**
     * Amount of frames per seconde. Lowers this to save CPU.
     */
    float fps = 15.0f;

    /**
     * Rendering hints to set anti aliasing.
     */
    private RenderingHints hints = null;

    /**
     * An infiniteProgressAdapter to performa special drawing, ex: a cancel
     * button.
     */
    InfiniteProgressAdapter infiniteProgressAdapter = null;

    /**
     * Creates a new progress panel with default values:<br />
     * <ul>
     * <li>No message</li>
     * <li>14 bars</li>.
     * <li> Veil's alpha level is 70%</li>
     * <li>15 frames per second</li>
     * <li>Fade in/out last 300 ms</li>
     * </ul>
     */
    public InfiniteProgressPanel() {
        this("");
    }

    /**
     * Creates a new progress panel with default values:<br />.
     * <ul>
     * <li>14 bars</li>
     * <li>Veil's alpha level is 70%</li>
     * <li>15 frames per second</li>
     * <li>Fade in/out last 300 ms</li>
     * </ul>
     * @param aText
     *            The message to be displayed. Can be null or empty.
     */
    public InfiniteProgressPanel(final String aText) {
        this(aText, 14);
    }

    /**
     * Creates a new progress panel with default values:<br />.
     * <ul>
     * <li>Veil's alpha level is 70%</li>
     * <li>15 frames per second</li>
     * <li>Fade in/out last 300 ms</li>
     * </ul>
     * @param aText
     *            The message to be displayed. Can be null or empty.
     * @param aBarsCount
     *            The amount of bars composing the circular shape
     */
    public InfiniteProgressPanel(final String aText, final int aBarsCount) {
        this(aText, aBarsCount, 0.70f);
    }

    /**
     * Creates a new progress panel with default values:<br />.
     * <ul>
     * <li>15 frames per second</li>
     * <li>Fade in/out last 300 ms</li>
     * </ul>
     * @param aText
     *            The message to be displayed. Can be null or empty.
     * @param aBarsCount
     *            The amount of bars composing the circular shape.
     * @param aShield
     *            The alpha level between 0.0 and 1.0 of the colored shield (or
     *            veil).
     */
    public InfiniteProgressPanel(final String aText, final int aBarsCount,
            final float aShield) {
        this(aText, aBarsCount, aShield, 15.0f);
    }

    /**
     * Creates a new progress panel with default values:<br />.
     * <ul>
     * <li>Fade in/out last 300 ms</li>
     * </ul>
     * @param aText
     *            The message to be displayed. Can be null or empty.
     * @param aBarsCount
     *            The amount of bars composing the circular shape.
     * @param aShield
     *            The alpha level between 0.0 and 1.0 of the colored shield (or
     *            veil).
     * @param aFps
     *            The number of frames per second. Lower this value to decrease
     *            CPU usage.
     */
    public InfiniteProgressPanel(final String aText, final int aBarsCount,
            final float aShield, final float aFps) {
        this(aText, aBarsCount, aShield, aFps, 300);
    }

    /**
     * Creates a new progress panel.
     * @param aText
     *            The message to be displayed. Can be null or empty.
     * @param aBarsCount
     *            The amount of bars composing the circular shape.
     * @param aShield
     *            The alpha level between 0.0 and 1.0 of the colored shield (or
     *            veil).
     * @param aFps
     *            The number of frames per second. Lower this value to decrease
     *            CPU usage.
     * @param aRampDelay
     *            The duration, in milli seconds, of the fade in and the fade
     *            out of the veil.
     */
    public InfiniteProgressPanel(final String aText, final int aBarsCount,
            final float aShield, final float aFps, final int aRampDelay) {
        this.text = aText;

        if (aRampDelay >= 0) {
            this.rampDelay = aRampDelay;
        } else {
            this.rampDelay = 0;
        }
        if (aShield >= 0.0f) {
            this.shield = aShield;
        } else {
            this.shield = 0.0f;
        }
        if (aFps > 0.0f) {
            this.fps = aFps;
        } else {
            this.fps = 15.0f;
        }
        if (aBarsCount > 0) {
            this.barsCount = aBarsCount;
        } else {
            this.barsCount = 14;
        }

        this.hints = new RenderingHints(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        this.hints.put(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        this.hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    }

    /**
     * Changes the displayed message at runtime.
     * @param aText
     *            The message to be displayed. Can be null or empty.
     */
    public final void setText(final String aText) {
        this.text = aText;
        repaint();
    }

    /**
     * Returns the current displayed message.
     * @return melding
     */
    public final String getText() {
        return text;
    }

    /**
     * @param aInfiniteProgressAdapter
     *            an infiniteProgressAdapter to perform special drawing (ex: a
     *            cancel button)
     */
    public final void setInfiniteProgressAdapter(
            final InfiniteProgressAdapter aInfiniteProgressAdapter) {
        this.infiniteProgressAdapter = aInfiniteProgressAdapter;
    }

    /**
     * Adds a listener to the cancel button in this progress panel.
     * @param listener
     * @throws RuntimeException
     *             if the infiniteProgressAdapter is null or is not a
     *             CancelableProgessAdapter
     */
    public final void addCancelListener(final ActionListener listener) {
        if (infiniteProgressAdapter instanceof CancelableProgessAdapter) {
            ((CancelableProgessAdapter) infiniteProgressAdapter)
                    .addCancelListener(listener);
        } else {
            throw new RuntimeException(
                    "Expected CancelableProgessAdapter for cancel listener.  Adapter is "
                            + infiniteProgressAdapter);
        }
    }

    /**
     * Removes a listener to the cancel button in this progress panel.
     * @param listener
     * @throws RuntimeException
     *             if the infiniteProgressAdapter is null or is not a
     *             CancelableProgessAdapter
     */
    public final void removeCancelListener(final ActionListener listener) {
        if (infiniteProgressAdapter instanceof CancelableProgessAdapter) {
            ((CancelableProgessAdapter) infiniteProgressAdapter)
                    .removeCancelListener(listener);
        } else {
            throw new RuntimeException(
                    "Expected CancelableProgessAdapter for cancel listener.  Adapter is "
                            + infiniteProgressAdapter);
        }
    }

    /**
     * Starts the waiting animation by fading the veil in, then rotating the
     * shapes. This method handles the visibility of the glass pane.
     */
    public final void start() {
        addMouseListener(this);
        setVisible(true);
        buildTicker();
        animation = new Thread(new Animator(true));
        if (infiniteProgressAdapter != null) {
            infiniteProgressAdapter.animationStarting();
        }
        animation.start();
    }

    /**
     * Stops the waiting animation by stopping the rotation of the circular
     * shape and then by fading out the veil. This methods sets the panel
     * invisible at the end.
     */
    public final void stop() {
        if (infiniteProgressAdapter != null) {
            infiniteProgressAdapter.animationStopping();
        }
        if (animation != null) {
            animation.interrupt();
            try {
                animation.join();
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
            animation = null;

            animation = new Thread(new Animator(false));
            animation.start();
        }
    }

    /**
     * @see no.ugland.utransprod.util.CancelableAdaptee#getComponent()
     */
    public final JComponent getComponent() {
        return this;
    }

    /**
     * Interrupts the animation, whatever its state is. You can use it when you
     * need to stop the animation without running the fade out phase. This
     * methods sets the panel invisible at the end.
     */
    public final void interrupt() {
        if (animation != null) {
            animation.interrupt();
            animation = null;

            removeMouseListener(this);
            setVisible(false);
        }
    }

    /**
     * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
     */
    @Override
    public final void paintComponent(final Graphics g) {
        if (started) {
            int width = getWidth();
            int height = getHeight();

            if (width == 0 || height == 0) {
                return;
            }

            @SuppressWarnings("hiding")
            final Ticker tmpTicker = getTicker();
            if (tmpTicker == null) {
                return;
            }

            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHints(hints);

            g2.setColor(new Color(255, 255, 255, (int) (alphaLevel * shield)));
            g2.fillRect(0, 0, getWidth(), getHeight());

            for (int i = 0; i < tmpTicker.bars.length; i++) {
                int channel = 224 - 128 / (i + 1);
                g2.setColor(new Color(channel, channel, channel, alphaLevel));
                g2.fill(tmpTicker.bars[i]);
            }

            double textMaxY = drawTextAt(text, getFont(), g2, width,
                    tmpTicker.maxY, getForeground());

            if (infiniteProgressAdapter != null) {
                infiniteProgressAdapter.paintSubComponents(textMaxY);
            }
        }
    }

    /**
     * Draw text in a Graphics2D.
     * @param text
     *            the text to draw
     * @param font
     *            the font to use
     * @param g2
     *            the graphics context to draw in
     * @param width
     *            the width of the parent, so it can be centered
     * @param aY
     *            the height at which to draw
     * @param foreGround
     *            the foreground color to draw in
     * @return the y value that is the y param + the text height.
     */
    public static double drawTextAt(final String text, final Font font,
            final Graphics2D g2, final int width, final double aY,
            final Color foreGround) {
        double y = aY;
        if (text != null && text.length() > 0) {
            FontRenderContext context = g2.getFontRenderContext();
            TextLayout layout = new TextLayout(text, font, context);
            Rectangle2D bounds = layout.getBounds();
            g2.setColor(foreGround);
            float textX = (float) (width - bounds.getWidth()) / 2;
            y = (float) (y + layout.getLeading() + 2 * layout.getAscent());
            layout.draw(g2, textX, (float) y);
        }
        return y;
    }

    /**
     * Ticker is not built until set bounds is called (or our width and height
     * are > 0).
     * @return null if not ready, or the built ticker
     */
    final Ticker getTicker() {
        if (ticker == null) {
            buildTicker();
        }
        return ticker;
    }

    /**
     * Builds the circular shape and returns the result as an array of
     * <code>Area</code>. Each <code>Area</code> is one of the bars
     * composing the shape.
     */
    private void buildTicker() {
        Area[] areas = new Area[barsCount];
        int width = getWidth();
        int height = getHeight();

        // Sometimes the bounds are set, rebuild the ticker later.
        if (width == 0 || height == 0) {
            return;
        }
        Point2D.Double center = new Point2D.Double((double) width / 2,
                (double) height / 2);
        double fixedAngle = 2.0 * Math.PI / barsCount;
        double maxY = 0.0d;
        for (double i = 0.0; i < barsCount; i++) {
            Area primitive = buildPrimitive();

            AffineTransform toCenter = AffineTransform.getTranslateInstance(
                    center.getX(), center.getY());
            AffineTransform toBorder = AffineTransform.getTranslateInstance(
                    35.0, -6.0);
            AffineTransform toCircle = AffineTransform.getRotateInstance(-i
                    * fixedAngle, center.getX(), center.getY());

            AffineTransform toWheel = new AffineTransform();
            toWheel.concatenate(toCenter);
            toWheel.concatenate(toBorder);

            primitive.transform(toWheel);
            primitive.transform(toCircle);

            areas[(int) i] = primitive;

            Rectangle2D bounds = primitive.getBounds2D();
            if (bounds.getMaxY() > maxY) {
                maxY = bounds.getMaxY();
            }
        }

        ticker = new Ticker();
        ticker.bars = areas;
        ticker.maxY = maxY;
    }

    /**
     * Builds a bar.
     * @return area
     */
    private Area buildPrimitive() {
        Rectangle2D.Double body = new Rectangle2D.Double(6, 0, 30, 12);

        Ellipse2D.Double head = new Ellipse2D.Double(0, 0, 12, 12);
        Ellipse2D.Double tail = new Ellipse2D.Double(30, 0, 12, 12);

        Area tick = new Area(body);
        tick.add(new Area(head));
        tick.add(new Area(tail));

        return tick;
    }

    /**
     * @author atle.brekka
     */
    class Ticker {
        double maxY = 0.0;

        Area[] bars;
    }

    /**
     * Animation thread.
     */
    private class Animator implements Runnable {
        private boolean rampUp = true;

        /**
         * @param aRampUp
         */
        protected Animator(final boolean aRampUp) {
            this.rampUp = aRampUp;
        }

        /**
         * @see java.lang.Runnable#run()
         */
        public void run() {
            @SuppressWarnings("hiding")
            final Ticker tmpTicker = getTicker();
            if (tmpTicker == null) {
                return;
            }

            Point2D.Double center = new Point2D.Double((double) getWidth() / 2,
                    (double) getHeight() / 2);
            double fixedIncrement = 2.0 * Math.PI / barsCount;
            AffineTransform toCircle = AffineTransform.getRotateInstance(
                    fixedIncrement, center.getX(), center.getY());

            long start = System.currentTimeMillis();
            if (rampDelay == 0) {
                if (rampUp) {
                    alphaLevel = 255;
                } else {
                    alphaLevel = 0;
                }

            }

            started = true;
            boolean inRamp = rampUp;

            while (!Thread.interrupted()) {
                if (!inRamp) {
                    for (int i = 0; i < tmpTicker.bars.length; i++) {
                        tmpTicker.bars[i].transform(toCircle);
                    }
                }

                repaint();

                if (rampUp) {
                    if (alphaLevel < 255) {
                        alphaLevel = (int) (255 * (System.currentTimeMillis() - start) / rampDelay);
                        if (alphaLevel >= 255) {
                            alphaLevel = 255;
                            inRamp = false;
                            if (infiniteProgressAdapter != null) {
                                infiniteProgressAdapter.rampUpEnded();
                            }
                        }
                    }
                } else if (alphaLevel > 0) {
                    alphaLevel = (int) (255 - (255 * (System
                            .currentTimeMillis() - start) / rampDelay));
                    if (alphaLevel <= 0) {
                        alphaLevel = 0;
                        break;
                    }
                } else {
                    break;
                }

                try {
                    int sleepTime;
                    if(inRamp){
                        sleepTime=10;
                    }else{
                        sleepTime=(int) (1000 / fps);
                    }
                    Thread.sleep(sleepTime);
                } catch (InterruptedException ie) {
                    break;
                }
                Thread.yield();
            }

            if (!rampUp) {
                started = false;
                repaint();
                setVisible(false);
                removeMouseListener(InfiniteProgressPanel.this);
            }
        }
    }

    /**
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
     */
    public void mouseClicked(final MouseEvent e) {
    }

    /**
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
     */
    public void mousePressed(final MouseEvent e) {
    }

    /**
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    public void mouseReleased(final MouseEvent e) {
    }

    /**
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
     */
    public void mouseEntered(final MouseEvent e) {
    }

    /**
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
     */
    public void mouseExited(final MouseEvent e) {
    }
}
TOP

Related Classes of no.ugland.utransprod.util.InfiniteProgressPanel$Ticker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.