ca.sqlpower.architect.swingui.ArchitectStatusBar.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.architect.swingui.ArchitectStatusBar.java

Source

/*
 * Copyright (c) 2010, SQL Power Group Inc.
 *
 * This file is part of SQL Power Architect.
 *
 * SQL Power Architect is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SQL Power Architect 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

package ca.sqlpower.architect.swingui;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.concurrent.GuardedBy;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

import org.apache.log4j.Logger;

import ca.sqlpower.architect.ArchitectStatusInformation;
import ca.sqlpower.util.MonitorableImpl;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

/**
 * This class contains a status bar that appears at the bottom of the
 * ArchitectFrame. The status bar is handy for updating the user on changes that
 * are helpful for the user to know but is not so important that they need to be
 * given a pop-up.
 */
public class ArchitectStatusBar implements ArchitectStatusInformation {

    private static final Logger logger = Logger.getLogger(ArchitectStatusBar.class);

    private static final ImageIcon PROGRESS_BAR_ICON = new ImageIcon(
            ArchitectStatusBar.class.getResource("/icons/progressBar.png"));

    /**
     * This progress bar updates its UI immediately by painting directly to its
     * graphics object on changes to its settings to keep the user informed of
     * the current progress of the operation even if the foreground thread that
     * normally does the painting is blocked waiting for server operations.
     * <p>
     * Methods in this class are synchronized as the progress bar can be updated
     * from multiple threads.
     */
    private class ArchitectStatusProgressBar extends MonitorableImpl {

        /**
         * This is the starting x position on the progress bar where this
         * progress bar specifically can start to paint.
         */
        private int x = 0;

        /**
         * This is the width of the progress bar this specific progress bar
         * can update on.
         */
        private int width = 0;

        @Override
        public synchronized void setJobSize(Integer jobSize) {
            super.setJobSize(jobSize);
            paint();
        }

        @Override
        public synchronized void setMessage(String message) {
            super.setMessage(message);
            paint();
        }

        @Override
        public synchronized void setProgress(int progress) {
            super.setProgress(progress);
            paint();
            if (progress >= getJobSize()) {
                removeProgressBar(this);
            }
        }

        @Override
        public synchronized void incrementProgress() {
            super.incrementProgress();
            paint();
            if (getProgress() >= getJobSize()) {
                removeProgressBar(this);
            }
        }

        @Override
        public synchronized void setCancelled(boolean cancelled) {
            super.setCancelled(cancelled);
            paint();
            removeProgressBar(this);
        }

        @Override
        public synchronized void setFinished(boolean finished) {
            super.setFinished(finished);
            paint();
            removeProgressBar(this);
        }

        private synchronized void paint() {
            Graphics g = progressBarPanel.getGraphics();
            progressBarPanel.setDoubleBuffered(true);

            if (!isCancelled() && !isFinished() && getJobSize() != null && getProgress() < getJobSize()) {
                BufferedImage buffer = new BufferedImage(getWidth(), progressBarPanel.getHeight(),
                        BufferedImage.TYPE_INT_ARGB);
                Graphics bufferG = buffer.getGraphics();
                bufferG = progressBarPanel.getGraphics();

                bufferG.setColor(progressBarPanel.getBackground());
                bufferG.fillRect(getX(), 0, getWidth(), progressBarPanel.getHeight());
                bufferG.drawImage(PROGRESS_BAR_ICON.getImage(), getX(), 0,
                        (int) (getWidth() * getProgress() / getJobSize()), progressBarPanel.getHeight(), null);

                bufferG.setColor(Color.BLACK);
                if (getMessage() != null) {

                    Font font = g.getFont();
                    int fontSize = font.getSize();
                    FontMetrics fm = g.getFontMetrics();
                    while (fm.getHeight() > progressBarPanel.getHeight() && fontSize > 0) {
                        font = font.deriveFont((float) (fontSize - 1));
                        g.setFont(font);
                        fm = g.getFontMetrics();
                        fontSize--;
                    }

                    int fontX = (int) ((getWidth() / 2) - (fm.getStringBounds(getMessage(), g).getWidth() / 2))
                            + getX();
                    int fontY = (progressBarPanel.getHeight() / 2) + (fm.getHeight() / 2);
                    bufferG.drawString(getMessage(), fontX, fontY);
                }
                g.drawImage(buffer, getWidth(), progressBarPanel.getHeight(), null);
            } else {
                g.setColor(progressBarPanel.getBackground());
                g.fillRect(getX(), 0, getWidth(), progressBarPanel.getHeight());
            }
        }

        public synchronized void setX(int x) {
            this.x = x;
            paint();
        }

        public synchronized int getX() {
            return x;
        }

        public synchronized void setWidth(int width) {
            this.width = width;
            paint();
        }

        public synchronized int getWidth() {
            return width;
        }

    }

    /**
     * The actual panel that appears at the bottom of the ArchitectFrame.
     * Interesting things like status text and progress bars can be entered
     * here.
     */
    private final JPanel statusBar = new JPanel();

    /**
     * Different {@link ArchitectStatusBar} classes will be given this progress
     * bar and a section of it that they can repaint for a progress bar. This
     * allows the progress to be given to the user even if the EDT thread is
     * busy. If we tried to add in other panels and work with them the panel
     * would not be added until after the current work of the EDT was completed
     * which may be too late.
     */
    private final JPanel progressBarPanel = new JPanel();

    /**
     * The existing progress bars in this status bar.
     */
    @GuardedBy("this")
    private final List<ArchitectStatusProgressBar> progressBars = new ArrayList<ArchitectStatusProgressBar>();

    public ArchitectStatusBar() {
        DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout("fill:pref:grow"), statusBar);
        builder.append(progressBarPanel);
    }

    public JPanel getStatusBar() {
        return statusBar;
    }

    @Override
    public MonitorableImpl createProgressMonitor() {
        ArchitectStatusProgressBar newBar = new ArchitectStatusProgressBar();
        synchronized (this) {
            progressBars.add(newBar);
        }
        resizeProgressBars();
        return newBar;
    }

    /**
     * Call to clear a progress bar off of the status bar.
     */
    private void removeProgressBar(ArchitectStatusProgressBar bar) {
        synchronized (this) {
            progressBars.remove(bar);
        }
        resizeProgressBars();
    }

    private void resizeProgressBars() {
        Graphics g = progressBarPanel.getGraphics();
        g.setColor(progressBarPanel.getBackground());
        g.fillRect(0, 0, progressBarPanel.getWidth(), progressBarPanel.getHeight());
        synchronized (this) {
            if (progressBars.isEmpty())
                return;
            int newWidth = progressBarPanel.getWidth() / progressBars.size();
            int x = 0;
            for (ArchitectStatusProgressBar bar : progressBars) {
                bar.setX(x);
                bar.setWidth(newWidth);
                x += newWidth;
            }
        }
    }

}