alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (AquaProgressBarUI.java)

This example Java source code file (AquaProgressBarUI.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

adjusttimer, ancestorevent, animator, aquaprogressbarui, awt, bean, dimension, event, fontmetrics, graphics, graphics2d, gui, jcomponent, minimum_delay, plaf, point, rectangle, sizevariant, string, swing

The AquaProgressBarUI.java Java example source code

/*
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.apple.laf;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.beans.*;

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

import sun.swing.SwingUtilities2;

import apple.laf.JRSUIStateFactory;
import apple.laf.JRSUIConstants.*;
import apple.laf.JRSUIState.ValueState;

import com.apple.laf.AquaUtilControlSize.*;
import com.apple.laf.AquaUtils.RecyclableSingleton;

public class AquaProgressBarUI extends ProgressBarUI implements ChangeListener, PropertyChangeListener, AncestorListener, Sizeable {
    private static final boolean ADJUSTTIMER = true;

    protected static final RecyclableSingleton<SizeDescriptor> sizeDescriptor = new RecyclableSingleton() {
        @Override
        protected SizeDescriptor getInstance() {
            return new SizeDescriptor(new SizeVariant(146, 20)) {
                public SizeVariant deriveSmall(final SizeVariant v) { v.alterMinSize(0, -6); return super.deriveSmall(v); }
            };
        }
    };
    static SizeDescriptor getSizeDescriptor() {
        return sizeDescriptor.get();
    }

    protected Size sizeVariant = Size.REGULAR;

    protected Color selectionForeground;

    private Animator animator;
    protected boolean isAnimating;
    protected boolean isCircular;

    protected final AquaPainter<ValueState> painter = AquaPainter.create(JRSUIStateFactory.getProgressBar());

    protected JProgressBar progressBar;

    public static ComponentUI createUI(final JComponent x) {
        return new AquaProgressBarUI();
    }

    protected AquaProgressBarUI() { }

    public void installUI(final JComponent c) {
        progressBar = (JProgressBar)c;
        installDefaults();
        installListeners();
    }

    public void uninstallUI(final JComponent c) {
        uninstallDefaults();
        uninstallListeners();
        stopAnimationTimer();
        progressBar = null;
    }

    protected void installDefaults() {
        progressBar.setOpaque(false);
        LookAndFeel.installBorder(progressBar, "ProgressBar.border");
        LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background", "ProgressBar.foreground", "ProgressBar.font");
        selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
    }

    protected void uninstallDefaults() {
        LookAndFeel.uninstallBorder(progressBar);
    }

    protected void installListeners() {
        progressBar.addChangeListener(this); // Listen for changes in the progress bar's data
        progressBar.addPropertyChangeListener(this); // Listen for changes between determinate and indeterminate state
        progressBar.addAncestorListener(this);
        AquaUtilControlSize.addSizePropertyListener(progressBar);
    }

    protected void uninstallListeners() {
        AquaUtilControlSize.removeSizePropertyListener(progressBar);
        progressBar.removeAncestorListener(this);
        progressBar.removePropertyChangeListener(this);
        progressBar.removeChangeListener(this);
    }

    public void stateChanged(final ChangeEvent e) {
        progressBar.repaint();
    }

    public void propertyChange(final PropertyChangeEvent e) {
        final String prop = e.getPropertyName();
        if ("indeterminate".equals(prop)) {
            if (!progressBar.isIndeterminate()) return;
            stopAnimationTimer();
            // start the animation thread
            startAnimationTimer();
        }

        if ("JProgressBar.style".equals(prop)) {
            isCircular = "circular".equalsIgnoreCase(e.getNewValue() + "");
            progressBar.repaint();
        }
    }

    // listen for Ancestor events to stop our timer when we are no longer visible
    // <rdar://problem/5405035> JProgressBar: UI in Aqua look and feel causes memory leaks
    public void ancestorRemoved(final AncestorEvent e) {
        stopAnimationTimer();
    }

    public void ancestorAdded(final AncestorEvent e) {
        if (!progressBar.isIndeterminate()) return;
        startAnimationTimer();
    }

    public void ancestorMoved(final AncestorEvent e) { }

    public void paint(final Graphics g, final JComponent c) {
        revalidateAnimationTimers(); // revalidate to turn on/off timers when values change

        painter.state.set(getState(c));
        painter.state.set(isHorizontal() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
        painter.state.set(isAnimating ? Animating.YES : Animating.NO);

        if (progressBar.isIndeterminate()) {
            if (isCircular) {
                painter.state.set(Widget.PROGRESS_SPINNER);
                painter.paint(g, c, 2, 2, 16, 16);
                return;
            }

            painter.state.set(Widget.PROGRESS_INDETERMINATE_BAR);
            paint(g);
            return;
        }

        painter.state.set(Widget.PROGRESS_BAR);
        painter.state.setValue(checkValue(progressBar.getPercentComplete()));
        paint(g);
    }

    static double checkValue(final double value) {
        return Double.isNaN(value) ? 0 : value;
    }

    protected void paint(final Graphics g) {
        // this is questionable. We may want the insets to mean something different.
        final Insets i = progressBar.getInsets();
        final int width = progressBar.getWidth() - (i.right + i.left);
        final int height = progressBar.getHeight() - (i.bottom + i.top);

        painter.paint(g, progressBar, i.left, i.top, width, height);

        if (progressBar.isStringPainted() && !progressBar.isIndeterminate()) {
            paintString(g, i.left, i.top, width, height);
        }
    }

    protected State getState(final JComponent c) {
        if (!c.isEnabled()) return State.INACTIVE;
        if (!AquaFocusHandler.isActive(c)) return State.INACTIVE;
        return State.ACTIVE;
    }

    protected void paintString(final Graphics g, final int x, final int y, final int width, final int height) {
        if (!(g instanceof Graphics2D)) return;

        final Graphics2D g2 = (Graphics2D)g;
        final String progressString = progressBar.getString();
        g2.setFont(progressBar.getFont());
        final Point renderLocation = getStringPlacement(g2, progressString, x, y, width, height);
        final Rectangle oldClip = g2.getClipBounds();

        if (isHorizontal()) {
            g2.setColor(selectionForeground);
            SwingUtilities2.drawString(progressBar, g2, progressString, renderLocation.x, renderLocation.y);
        } else { // VERTICAL
            // We rotate it -90 degrees, then translate it down since we are going to be bottom up.
            final AffineTransform savedAT = g2.getTransform();
            g2.transform(AffineTransform.getRotateInstance(0.0f - (Math.PI / 2.0f), 0, 0));
            g2.translate(-progressBar.getHeight(), 0);

            // 0,0 is now the bottom left of the viewable area, so we just draw our image at
            // the render location since that calculation knows about rotation.
            g2.setColor(selectionForeground);
            SwingUtilities2.drawString(progressBar, g2, progressString, renderLocation.x, renderLocation.y);

            g2.setTransform(savedAT);
        }

        g2.setClip(oldClip);
    }

    /**
     * Designate the place where the progress string will be painted. This implementation places it at the center of the
     * progress bar (in both x and y). Override this if you want to right, left, top, or bottom align the progress
     * string or if you need to nudge it around for any reason.
     */
    protected Point getStringPlacement(final Graphics g, final String progressString, int x, int y, int width, int height) {
        final FontMetrics fontSizer = progressBar.getFontMetrics(progressBar.getFont());
        final int stringWidth = fontSizer.stringWidth(progressString);

        if (!isHorizontal()) {
            // Calculate the location for the rotated text in real component coordinates.
            // swapping x & y and width & height
            final int oldH = height;
            height = width;
            width = oldH;

            final int oldX = x;
            x = y;
            y = oldX;
        }

        return new Point(x + Math.round(width / 2 - stringWidth / 2), y + ((height + fontSizer.getAscent() - fontSizer.getLeading() - fontSizer.getDescent()) / 2) - 1);
    }

    static Dimension getCircularPreferredSize() {
        return new Dimension(20, 20);
    }

    public Dimension getPreferredSize(final JComponent c) {
        if (isCircular) {
            return getCircularPreferredSize();
        }

        final FontMetrics metrics = progressBar.getFontMetrics(progressBar.getFont());

        final Dimension size = isHorizontal() ? getPreferredHorizontalSize(metrics) : getPreferredVerticalSize(metrics);
        final Insets insets = progressBar.getInsets();

        size.width += insets.left + insets.right;
        size.height += insets.top + insets.bottom;
        return size;
    }

    protected Dimension getPreferredHorizontalSize(final FontMetrics metrics) {
        final SizeVariant variant = getSizeDescriptor().get(sizeVariant);
        final Dimension size = new Dimension(variant.w, variant.h);
        if (!progressBar.isStringPainted()) return size;

        // Ensure that the progress string will fit
        final String progString = progressBar.getString();
        final int stringWidth = metrics.stringWidth(progString);
        if (stringWidth > size.width) {
            size.width = stringWidth;
        }

        // This uses both Height and Descent to be sure that
        // there is more than enough room in the progress bar
        // for everything.
        // This does have a strange dependency on
        // getStringPlacememnt() in a funny way.
        final int stringHeight = metrics.getHeight() + metrics.getDescent();
        if (stringHeight > size.height) {
            size.height = stringHeight;
        }
        return size;
    }

    protected Dimension getPreferredVerticalSize(final FontMetrics metrics) {
        final SizeVariant variant = getSizeDescriptor().get(sizeVariant);
        final Dimension size = new Dimension(variant.h, variant.w);
        if (!progressBar.isStringPainted()) return size;

        // Ensure that the progress string will fit.
        final String progString = progressBar.getString();
        final int stringHeight = metrics.getHeight() + metrics.getDescent();
        if (stringHeight > size.width) {
            size.width = stringHeight;
        }

        // This is also for completeness.
        final int stringWidth = metrics.stringWidth(progString);
        if (stringWidth > size.height) {
            size.height = stringWidth;
        }
        return size;
    }

    public Dimension getMinimumSize(final JComponent c) {
        if (isCircular) {
            return getCircularPreferredSize();
        }

        final Dimension pref = getPreferredSize(progressBar);

        // The Minimum size for this component is 10.
        // The rationale here is that there should be at least one pixel per 10 percent.
        if (isHorizontal()) {
            pref.width = 10;
        } else {
            pref.height = 10;
        }

        return pref;
    }

    public Dimension getMaximumSize(final JComponent c) {
        if (isCircular) {
            return getCircularPreferredSize();
        }

        final Dimension pref = getPreferredSize(progressBar);

        if (isHorizontal()) {
            pref.width = Short.MAX_VALUE;
        } else {
            pref.height = Short.MAX_VALUE;
        }

        return pref;
    }

    public void applySizeFor(final JComponent c, final Size size) {
        painter.state.set(sizeVariant = size == Size.MINI ? Size.SMALL : sizeVariant); // CUI doesn't support mini progress bars right now
    }

    protected void startAnimationTimer() {
        if (animator == null) animator = new Animator();
        animator.start();
        isAnimating = true;
    }

    protected void stopAnimationTimer() {
        if (animator != null) animator.stop();
        isAnimating = false;
    }

    private final Rectangle fUpdateArea = new Rectangle(0, 0, 0, 0);
    private final Dimension fLastSize = new Dimension(0, 0);
    protected Rectangle getRepaintRect() {
        int height = progressBar.getHeight();
        int width = progressBar.getWidth();

        if (isCircular) {
            return new Rectangle(20, 20);
        }

        if (fLastSize.height == height && fLastSize.width == width) {
            return fUpdateArea;
        }

        int x = 0;
        int y = 0;
        fLastSize.height = height;
        fLastSize.width = width;

        final int maxHeight = getMaxProgressBarHeight();

        if (isHorizontal()) {
            final int excessHeight = height - maxHeight;
            y += excessHeight / 2;
            height = maxHeight;
        } else {
            final int excessHeight = width - maxHeight;
            x += excessHeight / 2;
            width = maxHeight;
        }

        fUpdateArea.setBounds(x, y, width, height);

        return fUpdateArea;
    }

    protected int getMaxProgressBarHeight() {
        return getSizeDescriptor().get(sizeVariant).h;
    }

    protected boolean isHorizontal() {
        return progressBar.getOrientation() == SwingConstants.HORIZONTAL;
    }

    protected void revalidateAnimationTimers() {
        if (progressBar.isIndeterminate()) return;

        if (!isAnimating) {
            startAnimationTimer(); // only starts if supposed to!
            return;
        }

        final BoundedRangeModel model = progressBar.getModel();
        final double currentValue = model.getValue();
        if ((currentValue == model.getMaximum()) || (currentValue == model.getMinimum())) {
            stopAnimationTimer();
        }
    }

    protected void repaint() {
        final Rectangle repaintRect = getRepaintRect();
        if (repaintRect == null) {
            progressBar.repaint();
            return;
        }

        progressBar.repaint(repaintRect);
    }

    protected class Animator implements ActionListener {
        private static final int MINIMUM_DELAY = 5;
        private Timer timer;
        private long previousDelay; // used to tune the repaint interval
        private long lastCall; // the last time actionPerformed was called
        private int repaintInterval;

        public Animator() {
            repaintInterval = UIManager.getInt("ProgressBar.repaintInterval");

            // Make sure repaintInterval is reasonable.
            if (repaintInterval <= 0) repaintInterval = 100;
        }

        protected void start() {
            previousDelay = repaintInterval;
            lastCall = 0;

            if (timer == null) {
                timer = new Timer(repaintInterval, this);
            } else {
                timer.setDelay(repaintInterval);
            }

            if (ADJUSTTIMER) {
                timer.setRepeats(false);
                timer.setCoalesce(false);
            }

            timer.start();
        }

        protected void stop() {
            timer.stop();
        }

        public void actionPerformed(final ActionEvent e) {
            if (!ADJUSTTIMER) {
                repaint();
                return;
            }

            final long time = System.currentTimeMillis();

            if (lastCall > 0) {
                // adjust nextDelay
                int nextDelay = (int)(previousDelay - time + lastCall + repaintInterval);
                if (nextDelay < MINIMUM_DELAY) {
                    nextDelay = MINIMUM_DELAY;
                }

                timer.setInitialDelay(nextDelay);
                previousDelay = nextDelay;
            }

            timer.start();
            lastCall = time;

            repaint();
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java AquaProgressBarUI.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.