/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program 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.
* This program 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 com.bc.ceres.swing.progress;
import com.bc.ceres.swing.SwingHelper;
import com.jidesoft.swing.JideButton;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.FilteredImageSource;
import java.awt.image.RGBImageFilter;
import java.util.EventObject;
/**
* A utility class very similar to {@link javax.swing.ProgressMonitor} but with the following extensions:
* <ul>
* <li>It is cancelable.</li>
* <li>It has a {@link Dialog.ModalityType dialog modality type}.</li>
* </ul>
*/
public class ProgressDialog {
public static final Color SELECTED_BORDER_COLOR = new Color(8, 36, 107);
private static final Color SELECTED_BACKGROUND_COLOR = new Color(130, 146, 185);
private static final Color ROLLOVER_BACKGROUND_COLOR = new Color(181, 190, 214);
private static final int BUTTON_MIN_SIZE = 16;
private Component parentComponent;
private String title;
private JComponent messageComponent;
private JComponent extensibleMessageComponent;
int notExtendedDialogHeight;
private String note;
private int minimum;
private int maximum;
private boolean cancelable;
private Dialog.ModalityType modalityType;
private JDialog dialog;
private JLabel noteLabel;
private JProgressBar progressBar;
public JButton cancelButton;
private boolean canceled = false;
private boolean placeExtensibleMessageAboveProgressBar;
private boolean placeMessageAboveProgressBar;
/**
* Constructs a graphic object that shows progress, typically by filling
* in a rectangular bar as the process nears completion.
*
* @param parentComponent the parent component for the dialog box
*/
public ProgressDialog(Component parentComponent) {
this.parentComponent = parentComponent;
this.minimum = 0;
this.maximum = 100;
this.title = UIManager.getString("ProgressMonitor.progressText");
this.note = "";
this.cancelable = true;
this.modalityType = Dialog.ModalityType.MODELESS;
}
public Component getParentComponent() {
return parentComponent;
}
public JComponent getMessageComponent() {
return messageComponent;
}
public void setMessageComponent(JComponent messageComponent) {
setMessageComponent(messageComponent, true);
}
public void setMessageComponent(JComponent messageComponent, boolean placeMessageAboveProgressBar) {
this.messageComponent = messageComponent;
this.placeMessageAboveProgressBar = placeMessageAboveProgressBar;
}
public JComponent getExtensibleMessageComponent() {
return extensibleMessageComponent;
}
public void setExtensibleMessageComponent(JComponent extensibleMessageComponent,
boolean placeExtensibleMessageAboveProgressBar) {
this.extensibleMessageComponent = extensibleMessageComponent;
this.placeExtensibleMessageAboveProgressBar = placeExtensibleMessageAboveProgressBar;
}
public Dialog.ModalityType getModalityType() {
return modalityType;
}
public void setModalityType(Dialog.ModalityType modalityType) {
this.modalityType = modalityType;
}
public boolean isCancelable() {
return cancelable;
}
public void setCancelable(boolean cancelable) {
if (cancelButton != null) {
cancelButton.setEnabled(cancelable && !canceled);
}
this.cancelable = cancelable;
}
/**
* Indicate the progress of the operation being monitored.
* If the specified value is >= the maximum, the progress
* monitor is closed.
*
* @param progress an int specifying the current value, between the
* maximum and minimum specified for this component
* @see #setMinimum
* @see #setMaximum
* @see #close
*/
public void setProgress(int progress) {
if (progress >= maximum) {
close();
return;
}
if (progressBar != null) {
if (progressBar.isIndeterminate()) {
progressBar.setIndeterminate(false);
}
progressBar.setValue(progress);
}
}
/**
* Sets the canceled state if this dialog is cancelable.
* Note that this method will not automatically close the dialog.
*/
public void cancel() {
if (isCancelable()) {
canceled = true;
setCancelable(false);
}
}
/**
* Indicate that the operation is complete. This happens automatically
* when the value set by {@link #setProgress} is >= {@link #getMaximum maximum} , but it may be called
* earlier if the operation ends early.
*/
public void close() {
if (dialog != null) {
dialog.setVisible(false);
dialog.dispose();
dialog = null;
progressBar = null;
noteLabel = null;
cancelButton = null;
}
}
/**
* Returns the minimum value -- the lower end of the progress value.
*
* @return an int representing the minimum value
* @see #setMinimum
*/
public int getMinimum() {
return minimum;
}
/**
* Specifies the minimum value.
*
* @param minimum an int specifying the minimum value
* @see #getMinimum
*/
public void setMinimum(int minimum) {
if (progressBar != null) {
progressBar.setMinimum(minimum);
}
this.minimum = minimum;
}
/**
* Returns the maximum value -- the higher end of the progress value.
*
* @return an int representing the maximum value
* @see #setMaximum
*/
public int getMaximum() {
return maximum;
}
/**
* Specifies the maximum value.
*
* @param maximum an int specifying the maximum value
* @see #getMaximum
*/
public void setMaximum(int maximum) {
if (progressBar != null) {
progressBar.setMaximum(maximum);
}
this.maximum = maximum;
}
/**
* @return {@code true} if the user hit the 'Cancel' button in the progress dialog.
*/
public boolean isCanceled() {
return canceled;
}
/**
* Specifies the message that is displayed.
*
* @param title a String specifying the message to display
* @see #getTitle
*/
public void setTitle(String title) {
if (dialog != null) {
dialog.setTitle(title);
}
this.title = title;
}
/**
* Specifies the message that is displayed.
*
* @return a String specifying the message to display
* @see #setTitle
*/
public String getTitle() {
return this.title;
}
/**
* Specifies the additional note that is displayed along with the
* progress message. Used, for example, to show which file the
* is currently being copied during a multiple-file copy.
*
* @param note a String specifying the note to display
* @see #getNote
*/
public void setNote(String note) {
if (noteLabel != null) {
noteLabel.setText(note);
}
this.note = note;
}
/**
* Specifies the additional note that is displayed along with the
* progress message.
*
* @return a String specifying the note to display
* @see #setNote
*/
public String getNote() {
return this.note;
}
/////////////////////////////////////////////////////////////////////////////////////
public void show() {
if (dialog != null) {
dialog.setVisible(true);
return;
}
Window parentWindow = null;
if (parentComponent != null) {
parentWindow = SwingUtilities.getWindowAncestor(parentComponent);
}
progressBar = new JProgressBar();
progressBar.setMinimum(minimum);
progressBar.setMaximum(maximum);
progressBar.setIndeterminate(true);
Dimension preferredSize = progressBar.getPreferredSize();
preferredSize.width = Math.max(300, preferredSize.width);
progressBar.setPreferredSize(preferredSize);
noteLabel = new JLabel(note);
JPanel messagePanel = new JPanel(new BorderLayout(4, 4));
if (messageComponent != null) {
messagePanel.add(messageComponent, BorderLayout.CENTER);
}
messagePanel.add(noteLabel, BorderLayout.SOUTH);
JPanel progressPanel = new JPanel(new BorderLayout(4, 4));
progressPanel.add(progressBar, BorderLayout.SOUTH);
JPanel extensibleMessagePanel = new JPanel(new BorderLayout(4, 4));
if (extensibleMessageComponent != null) {
extensibleMessageComponent.setVisible(false);
final ImageIcon[] icons = new ImageIcon[]{
new ImageIcon(getClass().getResource("icons/PanelUp12.png")),
new ImageIcon(getClass().getResource("icons/PanelDown12.png"))};
final ImageIcon[] rolloverIcons = new ImageIcon[]{
createRolloverIcon(icons[0]),
createRolloverIcon(icons[1]),
};
final JLabel extendLabel = new JLabel();
final String moreText = "More";
final String lessText = "Less";
final JideButton extendButton = new JideButton(icons[1]);
configure(extendButton);
extendLabel.setText(moreText);
extendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (extensibleMessageComponent.isVisible()) {
extensibleMessageComponent.setVisible(false);
extendButton.setIcon(icons[1]);
extendButton.setRolloverIcon(rolloverIcons[1]);
extendLabel.setText(moreText);
Dimension size = dialog.getSize();
dialog.setSize(size.width, notExtendedDialogHeight);
dialog.pack();
} else {
extensibleMessageComponent.setVisible(true);
extendButton.setIcon(icons[0]);
extendButton.setRolloverIcon(rolloverIcons[0]);
extendLabel.setText(lessText);
Dimension size = dialog.getSize();
dialog.setSize(size.width,
Math.max(notExtendedDialogHeight + 200, size.height));
}
}
});
JPanel extendPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
extendPanel.add(extendButton);
extendPanel.add(extendLabel);
extensibleMessagePanel.add(extendPanel, BorderLayout.NORTH);
extensibleMessagePanel.add(extensibleMessageComponent, BorderLayout.CENTER);
}
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
if (cancelable) {
this.cancelButton = new JButton(UIManager.getString("OptionPane.cancelButtonText"));
this.cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cancel();
}
});
buttonPanel.add(cancelButton);
}
GridBagLayout gbl = new GridBagLayout();
JPanel contentPanel = new JPanel(gbl);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.weightx = 1;
gbc.insets = new Insets(4, 4, 4, 4);
if (messageComponent != null) {
gbc.gridy = 3;
if (placeMessageAboveProgressBar) {
gbc.gridy = 0;
}
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
gbl.addLayoutComponent(messagePanel, gbc);
contentPanel.add(messagePanel);
}
if (extensibleMessageComponent != null) {
gbc.gridy = 4;
if (placeExtensibleMessageAboveProgressBar) {
gbc.gridy = 1;
}
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
gbl.addLayoutComponent(extensibleMessagePanel, gbc);
contentPanel.add(extensibleMessagePanel);
}
gbc.gridy = 2;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbl.addLayoutComponent(progressBar, gbc);
contentPanel.add(progressBar);
if (cancelable) {
gbc.gridy = 5;
gbl.addLayoutComponent(buttonPanel, gbc);
contentPanel.add(buttonPanel);
}
dialog = new JDialog(parentWindow, title, modalityType);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
cancel();
}
});
dialog.setContentPane(contentPanel);
dialog.pack();
SwingHelper.centerComponent(dialog, parentWindow);
notExtendedDialogHeight = dialog.getHeight();
dialog.setVisible(true);
}
private void configure(AbstractButton button) {
Icon icon = button.getIcon();
final int space = 3;
Dimension prefSize = new Dimension(Math.max(icon.getIconWidth(), BUTTON_MIN_SIZE) + space,
Math.max(icon.getIconHeight(), BUTTON_MIN_SIZE) + space);
Dimension minSize = new Dimension(Math.max(icon.getIconWidth(), BUTTON_MIN_SIZE),
Math.max(icon.getIconHeight(), BUTTON_MIN_SIZE));
Dimension maxSize = new Dimension(Math.max(icon.getIconWidth(), BUTTON_MIN_SIZE) + space,
Math.max(icon.getIconHeight(), BUTTON_MIN_SIZE) + space);
button.setPreferredSize(prefSize);
button.setMaximumSize(maxSize);
button.setMinimumSize(minSize);
}
public static ImageIcon createRolloverIcon(ImageIcon imageIcon) {
return new ImageIcon(createRolloverImage(imageIcon.getImage()));
}
private static Image createRolloverImage(Image image) {
return Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(),
new BrightBlueFilter()));
}
private static class BrightBlueFilter extends RGBImageFilter {
public BrightBlueFilter() {
canFilterIndexColorModel = true;
}
@Override
public int filterRGB(int x, int y, int rgb) {
int a = (rgb & 0xff000000) >> 24;
int r = (rgb & 0x00ff0000) >> 16;
int g = (rgb & 0x0000ff00) >> 8;
int b = rgb & 0x000000ff;
int i = (r + g + b) / 3;
r = g = i;
b = 255;
return a << 24 | r << 16 | g << 8 | b;
}
}
}