Package org.rioproject.tools.ui.progresspanel

Source Code of org.rioproject.tools.ui.progresspanel.SingleComponentInfiniteProgress

/*
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rioproject.tools.ui.progresspanel;

import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.*;
import java.awt.image.BufferedImage;

/**
* A InfiniteProgressPanel-like component, but more efficient. This is the
* preferred class to use unless you need the total control over the appearance
* that InfiniteProgressPanel gives you.
*
* <p>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 as a <i>glass pane</i> in the window
* performing the long operation.
*
* <p>Calling setVisible(true) makes
* the component visible and starts the animation. Calling setVisible(false)
* halts the animation and makes the component invisible. 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>setVisible()</code>, method.
*
* <p>This version of the infinite progress panel does not display any fade
* in/out when
* the animation is started/stopped.
*
*/
public class SingleComponentInfiniteProgress extends JComponent
    implements ActionListener {
    private static final double UNSCALED_BAR_SIZE = 45d;

    public static final int DEFAULT_NUMBER_OF_BARS = 12;
    public static final int DEFAULT_FPS = 10;
    public static final double NO_AUTOMATIC_RESIZING = -1d;
    public static final double NO_MAX_BAR_SIZE = -1d;

    private int numBars;
    private double dScale = 1.2d;
    private double resizeRatio = NO_AUTOMATIC_RESIZING;
    private double maxBarSize = 64d;
    private double minBarSize = 4;
    private boolean useBackBuffer;
    private String text;

    private MouseAdapter mouseAdapter = new MouseAdapter() {
    };
    private MouseMotionAdapter mouseMotionAdapter = new MouseMotionAdapter() {
    };
    private KeyAdapter keyAdapter = new KeyAdapter() {
    };
    private ComponentAdapter componentAdapter = new ComponentAdapter() {
        public void componentResized(final ComponentEvent e) {
            if (useBackBuffer) {
                setOpaque(false);
                imageBuf = null;
                iterate = 3;
            }
        }
    };

    private BufferedImage imageBuf = null;
    private Area[] bars;
    private Rectangle barsBounds = null;
    private Rectangle barsScreenBounds = null;
    private AffineTransform centerAndScaleTransform = null;
    private Timer timer;
    private Color[] colors = null;
    private int colorOffset = 0;
    private boolean tempHide = false;

    public SingleComponentInfiniteProgress() {
        this(true);
    }

    public SingleComponentInfiniteProgress(boolean i_bUseBackBuffer) {
        this(i_bUseBackBuffer, DEFAULT_NUMBER_OF_BARS);
    }

    public SingleComponentInfiniteProgress(int numBars) {
        this(true, numBars, DEFAULT_FPS, NO_AUTOMATIC_RESIZING, NO_MAX_BAR_SIZE);
    }

    public SingleComponentInfiniteProgress(boolean i_bUseBackBuffer,
                                           int numBars) {
        this(i_bUseBackBuffer,
             numBars,
             DEFAULT_FPS,
             NO_AUTOMATIC_RESIZING,
             NO_MAX_BAR_SIZE);
    }

    public SingleComponentInfiniteProgress(boolean i_bUseBackBuffer,
                                           int numBars,
                                           int fps,
                                           double resizeRatio,
                                           double maxBarSize) {
        this.useBackBuffer = i_bUseBackBuffer;
        this.numBars = numBars;
        this.resizeRatio = resizeRatio;
        this.maxBarSize = maxBarSize;

        this.timer = new Timer(1000 / fps, this);

        colors = new Color[numBars * 2];
        // build bars
        bars = buildTicker(numBars);
        // calculate bars bounding rectangle
        barsBounds = new Rectangle();
        for (Area bar : bars) {
            barsBounds = barsBounds.union(bar.getBounds());
        }
        // create colors
        for (int i = 0; i < bars.length; i++) {
            int channel = 224 - 128 / (i + 1);
            colors[i] = new Color(channel, channel, channel);
            colors[numBars + i] = colors[i];
        }
        // set cursor
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

        // set opaque
        setOpaque(useBackBuffer);
    }

    /**
     * Show/Hide the pane, starting and stopping the animation as you go
     */
    public void setVisible(boolean i_bIsVisible) {
        final boolean old = isVisible();
        setOpaque(false);
        // capture
        if (i_bIsVisible) {
            if (useBackBuffer) {
                // add window resize listener
                Window w = SwingUtilities.getWindowAncestor(this);
                if (w != null) {
                    w.addComponentListener(componentAdapter);
                } else {
                    addAncestorListener(new AncestorListener() {
                        public void ancestorAdded(AncestorEvent event) {
                            Window w = SwingUtilities.getWindowAncestor(
                                SingleComponentInfiniteProgress.this);
                            if (w != null) {
                                w.addComponentListener(componentAdapter);
                            }
                        }

                        public void ancestorRemoved(AncestorEvent event) {
                        }

                        public void ancestorMoved(AncestorEvent event) {
                        }
                    });
                }
                iterate = 3;
            }
            // capture events
            addMouseListener(mouseAdapter);
            addMouseMotionListener(mouseMotionAdapter);
            addKeyListener(keyAdapter);
            timer.start();
        } else {
            // stop anim
            timer.stop();
            /// free back buffer
            imageBuf = null;
            // stop capturing events
            removeMouseListener(mouseAdapter);
            removeMouseMotionListener(mouseMotionAdapter);
            removeKeyListener(keyAdapter);
            // remove window resize listener
            Window oWindow = SwingUtilities.getWindowAncestor(this);
            if (oWindow != null) {
                oWindow.removeComponentListener(componentAdapter);
            }
        }
        super.setVisible(i_bIsVisible);
        firePropertyChange("running", old, i_bIsVisible);
    }

    /**
     * Recalc bars based on changes in size
     */
    public void setBounds(int x, int y, int width, int height) {
        super.setBounds(x, y, width, height);
        recalcSize();
    }

    @Override
    public void setSize(int i, int i1) {
        super.setSize(i, i1);
        recalcSize();
    }

    private void recalcSize() {
        calcBarsForBounds(getWidth(), getHeight(), true);
        // Now, see if the text fits...
        final double maxY = getTextMaxY(getText(),
                                        getFont(),
                                        (Graphics2D) getGraphics(),
                                        barsScreenBounds.getMaxY());
        final int bottom = getY() + getHeight();
        if (maxY >= bottom) {
            final int neededSpace = (int) Math.ceil(maxY) - bottom;
            calcBarsForBounds(getWidth(),
                              Math.max(1,
                                       getHeight() - neededSpace),
                              false);
        }

    }

    /**
     * paint background dimed and bars over top
     */
    protected void paintComponent(Graphics g) {
        if (!tempHide) {
            paintBars(g, true);
        }
    }

    public void start() {
        setVisible(true);
    }

    public void stop() {
        setVisible(false);
    }

    public void setText(String text) {
        final String old = this.text;
        this.text = text;
        repaint();
        firePropertyChange("text", old, text);
    }

    public JComponent getComponent() {
        return this;
    }

    //
    // METHODS FROM INTERFACE ActionListener
    //

    int iterate;  //we draw use transparency to draw a number of iterations before making a snapshot

    /**
     * Called to animate the rotation of the bar's colors
     */
    public void actionPerformed(ActionEvent e) {
        // rotate colors
        if (colorOffset == (numBars - 1)) {
            colorOffset = 0;
        } else {
            colorOffset++;
        }
        // repaint
        if (barsScreenBounds != null) {
            repaint(barsScreenBounds);
        } else {
            repaint();
        }
        if (useBackBuffer && imageBuf == null) {
            if (iterate < 0) {
                try {
                    makeSnapshot();
                    setOpaque(true);
                } catch (AWTException e1) {
                    e1.printStackTrace();
                }
            } else {
                iterate--;
            }
        }
    }

    public String getText() {
        return text;
    }

    public double getResizeRatio() {
        return resizeRatio;
    }

    public void setResizeRatio(final double resizeRatio) {
        final double old = this.resizeRatio;
        this.resizeRatio = resizeRatio;
        setBounds(getBounds());
        repaint();
        firePropertyChange("resizeRatio", old, resizeRatio);
    }

    public double getMaxBarSize() {
        return maxBarSize;
    }

    public void setMaxBarSize(final double maxBarSize) {
        final double old = this.maxBarSize;
        this.maxBarSize = maxBarSize;
        setBounds(getBounds());
        repaint();
        firePropertyChange("maxBarSize", old, maxBarSize);
    }

    public int getNumBars() {
        return numBars;
    }

    public boolean getUseBackBuffer() {
        return useBackBuffer;
    }

    public boolean isRunning() {
        return isVisible();
    }

    private void makeSnapshot() throws AWTException {
        final Rectangle bounds = getBounds();
        final Point upperLeft = new Point(bounds.x, bounds.y);
        SwingUtilities.convertPointToScreen(upperLeft, this);
        final Rectangle screenRect = new Rectangle(upperLeft.x,
                                                   upperLeft.y,
                                                   bounds.width,
                                                   bounds.height);
        Insets insets = getInsets();
        screenRect.x += insets.left;
        screenRect.y += insets.top;
        screenRect.width -= insets.left + insets.right;
        screenRect.height -= insets.top + insets.bottom;
        // capture window contents
        imageBuf = new Robot().createScreenCapture(screenRect);
        //no need to fade because we are allready using an image that is showing through
    }

    protected void calcBarsForBounds(final int width,
                                     final int height,
                                     final boolean honorMinBarSize) {
        // update centering transform
        centerAndScaleTransform = new AffineTransform();
        centerAndScaleTransform.translate((double) width / 2d,
                                          (double) height / 2d);

        double scale = dScale;

        if (resizeRatio != NO_AUTOMATIC_RESIZING) {
            final int minSpace = Math.min(width, height);
            scale = (minSpace * resizeRatio) / UNSCALED_BAR_SIZE;
            if (maxBarSize != NO_MAX_BAR_SIZE &&
                (UNSCALED_BAR_SIZE * scale) >= maxBarSize) {
                scale = maxBarSize / UNSCALED_BAR_SIZE;
            }
            if (honorMinBarSize && (UNSCALED_BAR_SIZE * scale < minBarSize)) {
                scale = minBarSize / UNSCALED_BAR_SIZE;
            }
        }

        centerAndScaleTransform.scale(scale, scale);

        calcNewBarsBounds();
    }

    private void calcNewBarsBounds() {
        if (barsBounds != null) {
            Area oBounds = new Area(barsBounds);
            oBounds.transform(centerAndScaleTransform);
            barsScreenBounds = oBounds.getBounds();
        }
    }

    protected double paintBars(final Graphics g,
                               final boolean paintBackground) {
        Rectangle oClip = g.getClipBounds();
        if (paintBackground) {
            if (imageBuf != null) {
                // draw background image
                g.drawImage(imageBuf,
                            oClip.x,
                            oClip.y,
                            oClip.x + oClip.width,
                            oClip.y + oClip.height,
                            oClip.x,
                            oClip.y,
                            oClip.x + oClip.width,
                            oClip.y + oClip.height,
                            null);
                g.drawImage(imageBuf, 0, 0, null);
            } else {
                g.setColor(new Color(255, 255, 255, 180));
                g.fillRect(oClip.x, oClip.y, oClip.width, oClip.height);
            }
        }
        // move to center
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.transform(centerAndScaleTransform);
        // draw ticker
        for (int i = 0; i < bars.length; i++) {
            g2.setColor(colors[i + colorOffset]);
            g2.fill(bars[i]);
        }

        // NOTE: we pass in g and not g2 so that the transformation is not
        // applied.

        // NOTE: this will not contain the size of the sub components, since the
        // paintSubComponents(...) method does not provide this information, and
        // I don't feel like patching the adapter since I don't use it right
        // now. :)

        return drawTextAt(text,
                          getFont(),
                          (Graphics2D) g,
                          getWidth(),
                          barsScreenBounds.getMaxY(),
                          getForeground());
    }

    /**
     * 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 y 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(String text, Font font, Graphics2D g2,
                                    int width, final double y, Color foreGround) {
        final TextLayout layout = getTextLayout(text, font, g2);
        if (layout != null) {
            Rectangle2D bounds = layout.getBounds();
            g2.setColor(foreGround);
            float textX = (float) (width - bounds.getWidth()) / 2;
            float horizontal = (float) (y + layout.getLeading() + 2 * layout.getAscent());
            layout.draw(g2, textX, horizontal);
            return y + bounds.getHeight();
        } else {
            return 0d;
        }
    }

    public static double getTextMaxY(final String text, final Font font,
                                     final Graphics2D g2, final double y) {
        final TextLayout layout = getTextLayout(text, font, g2);
        if (layout != null) {
            return y + layout.getLeading() + (2 * layout.getAscent()) +
                   layout.getBounds().getHeight();
        } else {
            return 0d;
        }
    }

    private static TextLayout getTextLayout(final String text, final Font font,
                                            final Graphics2D g2) {
        if (text != null && text.length() > 0) {
            FontRenderContext context = g2.getFontRenderContext();
            return new TextLayout(text, font, context);
        } else {
            return null;
        }
    }


    /*
     * 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 static Area[] buildTicker(int i_iBarCount) {
        Area[] ticker = new Area[i_iBarCount];
        Point2D.Double center = new Point2D.Double(0, 0);
        double fixedAngle = 2.0 * Math.PI / ((double) i_iBarCount);

        for (double i = 0.0; i < (double) i_iBarCount; i++) {
            Area primitive = buildPrimitive();

            AffineTransform toCenter = AffineTransform.getTranslateInstance(
                center.getX(), center.getY());
            AffineTransform toBorder = AffineTransform.getTranslateInstance(
                UNSCALED_BAR_SIZE,
                -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);

            ticker[(int) i] = primitive;
        }

        return ticker;
    }

    /*
     * Builds a bar.
     */
    private static 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;
    }
}
TOP

Related Classes of org.rioproject.tools.ui.progresspanel.SingleComponentInfiniteProgress

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.