GT-2376: added new task monitor service

This commit is contained in:
adamopolous 2019-04-04 12:36:36 -04:00
parent 538cbc1226
commit f57af0b730
28 changed files with 1363 additions and 558 deletions

View file

@ -67,6 +67,7 @@ public class DialogComponentProvider
protected JButton dismissButton;
private boolean isAlerting;
private JLabel statusLabel;
private JLabel subStatusLabel;
private JPanel statusProgPanel; // contains status panel and progress panel
private Timer showTimer;
private TaskScheduler taskScheduler;
@ -608,6 +609,11 @@ public class DialogComponentProvider
public void setStatusText(String text) {
setStatusText(text, MessageType.INFO);
}
@Override
public void setSubStatusText(String text) {
setSubStatusText(text, MessageType.INFO);
}
/**
* Sets the text in the dialog's status line using the specified message type to control
@ -621,12 +627,24 @@ public class DialogComponentProvider
setStatusText(message, type, false);
}
@Override
public void setSubStatusText(String message, MessageType type) {
setSubStatusText(message, type, false);
}
@Override
public void setStatusText(String message, MessageType type, boolean alert) {
String text = StringUtils.isBlank(message) ? " " : message;
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetStatusText(text, type, alert));
}
@Override
public void setSubStatusText(String message, MessageType type, boolean alert) {
String text = StringUtils.isBlank(message) ? " " : message;
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetSubStatusText(text, type, alert));
}
private void doSetStatusText(String text, MessageType type, boolean alert) {
@ -641,6 +659,20 @@ public class DialogComponentProvider
alertMessage();
}
}
private void doSetSubStatusText(String text, MessageType type, boolean alert) {
SystemUtilities.assertThisIsTheSwingThread(
"Setting text must be performed on the Swing thread");
subStatusLabel.setText(text);
subStatusLabel.setForeground(getStatusColor(type));
updateStatusToolTip();
if (alert) {
alertMessage();
}
}
/**
* Signals for this dialog to visually draw the user's attention to the status text
@ -679,11 +711,13 @@ public class DialogComponentProvider
// normal Swing mechanism may not have yet happened).
mainPanel.validate();
statusLabel.setVisible(false); // disable painting in this dialog so we don't see double
subStatusLabel.setVisible(false);
Animator animator = AnimationUtils.pulseComponent(statusLabel, 1);
animator.addTarget(new TimingTargetAdapter() {
@Override
public void end() {
statusLabel.setVisible(true);
subStatusLabel.setVisible(true);
alertFinishedCallback.call();
isAlerting = false;
}
@ -755,12 +789,7 @@ public class DialogComponentProvider
private void showProgressBar(String localTitle, boolean hasProgress, boolean canCancel) {
taskMonitorComponent.setTaskName(localTitle);
taskMonitorComponent.showProgress(hasProgress);
if (canCancel) {
taskMonitorComponent.showCancelButton(true);
}
else {
taskMonitorComponent.showCancelButton(false);
}
taskMonitorComponent.setCancelButtonVisibility(canCancel);
progressCardLayout.show(statusProgPanel, PROGRESS);
rootPanel.validate();
}
@ -799,18 +828,29 @@ public class DialogComponentProvider
public void clearStatusText() {
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
statusLabel.setText(" ");
subStatusLabel.setText(" ");
updateStatusToolTip();
});
}
/**
* returns the current status in the dialogs status line=
* Returns the current status in the dialogs status line
*
* @return the status text
*/
public String getStatusText() {
return statusLabel.getText();
}
/**
* Returns the secondary status message
*
* @return the secondary status message
*/
public String getSubStatusText() {
return subStatusLabel.getText();
}
protected JLabel getStatusLabel() {
return statusLabel;
}
@ -901,6 +941,14 @@ public class DialogComponentProvider
updateStatusToolTip();
}
});
subStatusLabel = new JLabel();
subStatusLabel.setName("subStatusLabel");
subStatusLabel.setHorizontalAlignment(SwingConstants.CENTER);
subStatusLabel.setForeground(Color.blue);
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(Font.ITALIC));
subStatusLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(11.0f));
// use a strut panel so the size of the message area does not change if we make
// the message label not visible
@ -908,6 +956,7 @@ public class DialogComponentProvider
panel.add(Box.createVerticalStrut(height), BorderLayout.WEST);
panel.add(statusLabel, BorderLayout.CENTER);
panel.add(subStatusLabel, BorderLayout.SOUTH);
return panel;
}

View file

@ -0,0 +1,48 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.task;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import docking.util.AnimatedIcon;
import resources.ResourceManager;
/**
* Panel that displays an animation of the Ghidra dragon chomping bits.
*/
public class ChompingBitsAnimationPanel extends JPanel {
public ChompingBitsAnimationPanel() {
setLayout(new BorderLayout());
List<Icon> iconList = new ArrayList<>();
iconList.add(ResourceManager.loadImage("images/eatbits1.png"));
iconList.add(ResourceManager.loadImage("images/eatbits2.png"));
iconList.add(ResourceManager.loadImage("images/eatbits3.png"));
iconList.add(ResourceManager.loadImage("images/eatbits4.png"));
iconList.add(ResourceManager.loadImage("images/eatbits5.png"));
iconList.add(ResourceManager.loadImage("images/eatbits6.png"));
iconList.add(ResourceManager.loadImage("images/eatbits7.png"));
AnimatedIcon icon = new AnimatedIcon(iconList, 200, 0);
setSize(new Dimension(200, 100));
add(new JLabel(icon));
}
}

View file

@ -0,0 +1,59 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.task;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import docking.util.AnimatedIcon;
import resources.ResourceManager;
/**
* Panel that displays an animation of a spinning hourglass
*/
public class HourglassAnimationPanel extends JPanel {
public HourglassAnimationPanel() {
setLayout(new BorderLayout());
List<Icon> iconList = new ArrayList<>();
iconList.add(ResourceManager.loadImage("images/hourglass24_01.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_02.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_02.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_03.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_03.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_04.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_04.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_05.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_05.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_06.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_06.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_07.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_07.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_08.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_08.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_09.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_10.png"));
iconList.add(ResourceManager.loadImage("images/hourglass24_11.png"));
AnimatedIcon progressIcon = new AnimatedIcon(iconList, 150, 0);
add (new JLabel(progressIcon), BorderLayout.CENTER);
}
}

View file

@ -245,7 +245,7 @@ public class RunManager {
* @param showCancel true means to show the cancel button
*/
public void showCancelButton(boolean showCancel) {
monitor.showCancelButton(showCancel);
monitor.setCancelButtonVisibility(showCancel);
}
/**

View file

@ -0,0 +1,168 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.task;
import java.util.Objects;
import ghidra.util.exception.CancelledException;
/**
* {@link TaskMonitor} that restricts users from being able to update the progress bar. The class
* is initialized with another, fully-featured monitor and forwards all requests to it,
* but squashes calls to methods that are not allowed.
* <p>
* Note: Two instances of this class are deemed equal if they have the same {@link #parentMonitor},
* hence the override of {@link #hashCode()} and {@link #equals(Object)}.
*/
public class SecondaryTaskMonitor implements TaskMonitor {
private TaskMonitor parentMonitor;
/**
* Constructor
*
* @param parentMonitor the fully-functional task monitor this is based off of
*/
public SecondaryTaskMonitor(TaskMonitor parentMonitor) {
this.parentMonitor = parentMonitor;
}
/**
* Overridden to ensure that clients who have this type of monitor will only update the
* secondary message when using this method
*
* @param message the message string to display
*/
@Override
public void setMessage(String message) {
if (parentMonitor instanceof TaskDialog) {
((TaskDialog) parentMonitor).setSecondaryMessage(message);
}
parentMonitor.setMessage(message);
}
@Override
public void setCancelEnabled(boolean enable) {
parentMonitor.setCancelEnabled(enable);
}
@Override
public void setInitialized(boolean init) {
parentMonitor.setInitialized(init);
}
@Override
public boolean isCancelEnabled() {
return parentMonitor.isCancelEnabled();
}
@Override
public boolean isCancelled() {
return parentMonitor.isCancelled();
}
@Override
public synchronized void cancel() {
parentMonitor.cancel();
}
@Override
public synchronized void clearCanceled() {
parentMonitor.clearCanceled();
}
@Override
public void checkCanceled() throws CancelledException {
parentMonitor.checkCanceled();
}
@Override
public void addCancelledListener(CancelledListener listener) {
parentMonitor.addCancelledListener(listener);
}
@Override
public void removeCancelledListener(CancelledListener listener) {
parentMonitor.removeCancelledListener(listener);
}
@Override
public long getMaximum() {
return parentMonitor.getMaximum();
}
@Override
public long getProgress() {
return parentMonitor.getProgress();
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
// squash
}
@Override
public void setProgress(long value) {
// squash
}
@Override
public void initialize(long max) {
// squash
}
@Override
public void setMaximum(long max) {
// squash
}
@Override
public void setIndeterminate(boolean indeterminate) {
// squash
}
@Override
public void incrementProgress(long incrementAmount) {
// squash
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((parentMonitor == null) ? 0 : parentMonitor.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SecondaryTaskMonitor other = (SecondaryTaskMonitor) obj;
if (!Objects.equals(parentMonitor, other.parentMonitor)) {
return false;
}
return true;
}
}

View file

@ -15,20 +15,20 @@
*/
package ghidra.util.task;
import java.awt.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.util.AnimatedIcon;
import docking.widgets.OptionDialog;
import docking.widgets.label.GIconLabel;
import ghidra.util.*;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.timer.GTimer;
import resources.ResourceManager;
/**
* Dialog that is displayed to show activity for a Task that is running outside of the
@ -36,25 +36,48 @@ import resources.ResourceManager;
*/
public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
/** Timer used to give the task a chance to complete */
private static final int SLEEPY_TIME = 10;
/** Amount of time to wait before showing the monitor dialog */
private final static int MAX_DELAY = 200000;
public final static int DEFAULT_WIDTH = 275;
private Timer showTimer;
private TaskMonitorComponent monitorComponent;
private AtomicInteger taskID = new AtomicInteger();
private boolean canCancel;
private Runnable updateMessage;
private Runnable closeDialog;
private Runnable enableCancelButton;
private String newMessage;
private boolean cancelState = true;
private Component centerOnComp;
private Runnable shouldCancelRunnable;
private boolean done;
private boolean taskDone;
private JPanel mainPanel;
private ChompingBitsAnimationPanel chompingBitsPanel;
private TaskMonitorComponent monitorComponent;
/** Creates new TaskDialog
/** Runnable that updates the primary message label in the dialog */
private Runnable updatePrimaryMessageRunnable;
/** Runnable that updates the secondary message label in the dialog */
private Runnable updateSecondaryMessageRunnable;
/** If not null, then the value of the string has yet to be rendered */
private String newPrimaryMessage;
/** If not null, then the value of the string has yet to be rendered */
private String newSecondaryMessage;
/**
* Indicates if this monitor has been initialized for progress updates. If this value
* is set to true, the {@link TaskMonitorService} will not return the monitor to
* another caller (only one client should be able to update progress at a time).
*/
private AtomicBoolean initialized = new AtomicBoolean(false);
private SecondaryTaskMonitor secondaryTaskMonitor;
/**
* Constructor
*
* @param centerOnComp component to be centered over when shown,
* otherwise center over parent. If both centerOnComp and parent
* are null, dialog will be centered on screen.
@ -65,7 +88,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
task.hasProgress());
}
/** Creates a new TaskDialog.
/**
* Constructor
*
* @param task the Task that this dialog will be associated with
*/
public TaskDialog(Task task) {
@ -73,18 +98,20 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
/**
* Construct new TaskDialog.
* Constructor
*
* @param title title for the dialog
* @param canCancel true if the task can be canceled
* @param isModal true if the dialog should be modal
* @param hasProgress true if the dialog should show a progress bar
*/
public TaskDialog(String title, boolean canCancel, boolean isModal, boolean hasProgress) {
this(null, title, isModal, canCancel, true /*hasProgress*/);
this(null, title, isModal, canCancel, hasProgress);
}
/**
* Construct new TaskDialog.
* Constructor
*
* @param centerOnComp component to be centered over when shown, otherwise center over
* parent. If both centerOnComp is null, then the active window will be used
* @param title title for the dialog
@ -99,8 +126,21 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
setup(canCancel, hasProgress);
}
@Override
public boolean isInitialized() {
return initialized.get();
}
@Override
public void setInitialized(boolean init) {
this.initialized.set(init);
}
private void setup(boolean canCancel, boolean hasProgress) {
this.canCancel = canCancel;
monitorComponent = new TaskMonitorComponent(false, false);
chompingBitsPanel = new ChompingBitsAnimationPanel();
setCancelEnabled(canCancel);
setRememberLocation(false);
setRememberSize(false);
setTransient(true);
@ -108,13 +148,18 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
close();
dispose();
};
updateMessage = () -> {
setStatusText(newMessage);
updatePrimaryMessageRunnable = () -> {
setStatusText(newPrimaryMessage);
synchronized (TaskDialog.this) {
newMessage = null;
newPrimaryMessage = null;
}
};
updateSecondaryMessageRunnable = () -> {
setSubStatusText(newSecondaryMessage);
synchronized (TaskDialog.this) {
newSecondaryMessage = null;
}
};
enableCancelButton = () -> TaskDialog.super.setCancelEnabled(cancelState);
shouldCancelRunnable = () -> {
int currentTaskID = taskID.get();
@ -124,9 +169,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
};
monitorComponent = new TaskMonitorComponent(false, false);
mainPanel = new JPanel(new BorderLayout());
addWorkPanel(mainPanel);
if (hasProgress) {
installProgressMonitor();
}
@ -142,6 +187,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
setHelpLocation(new HelpLocation("Tool", "TaskDialog"));
}
/**
* Shows a dialog asking the user if they really, really want to cancel the task
*
* @return true if the task should be cancelled
*/
protected boolean promptToVerifyCancel() {
boolean userSaysYes = OptionDialog.showYesNoDialog(getComponent(), "Cancel?",
"Do you really want to cancel \"" + getTitle() + "\"?") == OptionDialog.OPTION_ONE;
@ -150,34 +200,24 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
/**
* Creates the main work panel for the dialog
* Adds the panel that contains the progress bar to the dialog
*/
private void installProgressMonitor() {
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
mainPanel.removeAll();
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 5, 10));
panel.add(monitorComponent);
mainPanel.add(panel, BorderLayout.NORTH);
mainPanel.add(monitorComponent, BorderLayout.CENTER);
repack();
});
}
/**
* Adds the panel that contains the chomping bits animation to the dialog. This should only be
* called if the dialog has no need to display progress.
*/
private void installActivityDisplay() {
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
mainPanel.removeAll();
JPanel panel = new JPanel(new BorderLayout());
panel.setSize(new Dimension(200, 100));
String[] filenames = { "images/eatbits1.png", "images/eatbits2.png",
"images/eatbits3.png", "images/eatbits4.png", "images/eatbits5.png",
"images/eatbits6.png", "images/eatbits7.png" };
panel.add(
new GIconLabel(new AnimatedIcon(ResourceManager.loadImages(filenames), 200, 0)));
mainPanel.add(panel, BorderLayout.CENTER);
mainPanel.add(chompingBitsPanel, BorderLayout.CENTER);
repack();
});
}
@ -200,7 +240,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
monitorComponent.setShowProgressValue(showProgressValue);
}
/** Sets the percentage done.
/**
* Sets the percentage done
*
* @param param The percentage of the task completed.
*/
@Override
@ -250,62 +292,58 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
monitorComponent.setIndeterminate(indeterminate);
}
/** Called if the user presses the cancel button on
* the dialog
*/
@Override
protected void cancelCallback() {
synchronized (this) {
if (!monitorComponent.isCancelEnabled() || monitorComponent.isCancelled()) {
return;
}
}
SwingUtilities.invokeLater(shouldCancelRunnable);
}
/** Sets the message in the TaskDialog dialog
* @param str The message string to be displayed
*/
@Override
synchronized public void setMessage(String str) {
boolean invoke = (newMessage == null);
newMessage = str;
boolean invoke = (newPrimaryMessage == null);
if (invoke) {
SwingUtilities.invokeLater(updateMessage);
newPrimaryMessage = str;
SwingUtilities.invokeLater(updatePrimaryMessageRunnable);
}
}
/**
* Set the enable state of the Cancel button.
* @param enable the state to set the cancel button.
* Updates the secondary message on the task monitor
*
* @param str the string to update
*/
@Override
public void setCancelEnabled(boolean enable) {
if (canCancel) {
monitorComponent.setCancelEnabled(enable);
SwingUtilities.invokeLater(enableCancelButton);
synchronized public void setSecondaryMessage(String str) {
boolean invoke = (newSecondaryMessage == null);
if (invoke) {
newSecondaryMessage = str;
SwingUtilities.invokeLater(updateSecondaryMessageRunnable);
}
}
@Override
public void setCancelEnabled(boolean enable) {
monitorComponent.setCancelEnabled(enable);
super.setCancelEnabled(enable);
}
@Override
public boolean isCancelEnabled() {
return canCancel && cancelState;
return super.isCancelEnabled();
}
public synchronized void taskProcessed() {
done = true;
taskDone = true;
monitorComponent.notifyChangeListeners();
SwingUtilities.invokeLater(closeDialog);
}
@Override
public synchronized void reset() {
done = false;
taskDone = false;
taskID.incrementAndGet();
}
public synchronized boolean isCompleted() {
return done;
return taskDone;
}
@Override
@ -393,7 +431,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override
public synchronized void cancel() {
if (!canCancel || monitorComponent.isCancelled()) {
if (monitorComponent.isCancelled()) {
return;
}
// Mark as cancelled, must be detected by task which should terminate
@ -432,18 +470,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
@Override
public void addIssueListener(IssueListener listener) {
monitorComponent.addIssueListener(listener);
public synchronized TaskMonitor getSecondaryMonitor() {
if (secondaryTaskMonitor == null) {
secondaryTaskMonitor = new SecondaryTaskMonitor(this);
}
return secondaryTaskMonitor;
}
@Override
public void removeIssueListener(IssueListener listener) {
monitorComponent.removeIssueListener(listener);
}
@Override
public void reportIssue(Issue issue) {
monitorComponent.reportIssue(issue);
}
}

View file

@ -24,75 +24,79 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.*;
import docking.ToolTipManager;
import docking.util.AnimatedIcon;
import docking.widgets.EmptyBorderButton;
import docking.widgets.OptionDialog;
import docking.widgets.label.GDHtmlLabel;
import docking.widgets.label.GIconLabel;
import ghidra.util.Issue;
import ghidra.util.SystemUtilities;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.CancelledException;
import ghidra.util.layout.VerticalLayout;
import resources.Icons;
import resources.ResourceManager;
/**
* Component that contains a progress bar, a progress icon, and a cancel
* button to cancel the task that is associated with this task monitor.
* <p>
* By default the progress bar and progress icon (spinning globe) are visible.
*/
public class TaskMonitorComponent extends JPanel implements TaskMonitor {
private WeakSet<CancelledListener> listeners =
WeakDataStructureFactory.createCopyOnReadWeakSet();
private WeakSet<IssueListener> issueListeners;
private JButton cancelButton;
private JPanel eastButtonPanel;
private JProgressBar progressBar;
private JButton cancelButton;
private JPanel cancelPanel;
private JPanel progressBarPanel;
private JPanel mainContentPanel;
private JPanel progressPanel;
private JPanel activeProgressPanel;
private JLabel imageLabel;
private String progressMessage;
private String taskName;
private JLabel messageLabel;
private volatile boolean isCancelled;
private String message;
private long lastProgress = -1;
private long progress;
private long lastMax = -1;
private long max;
private long lastMaxProgress = -1;
private long maxProgress;
private long scaleFactor = 1;
private Runnable updateProgressPanelRunnable;
private Runnable updateCancelButtonRunnable;
private Runnable updateToolTipRunnable;
private JLabel messageLabel;
private Runnable shouldCancelRunnable;
private boolean showingProgress = true;
private boolean showingIcon = true;
private boolean showingCancelButton = true;
private boolean cancelEnabled = true;
private boolean paintProgressValue = true;
private AtomicBoolean isIndeterminate = new AtomicBoolean(false);
private AtomicInteger taskID = new AtomicInteger();
private Timer updateTimer;
private Runnable shouldCancelRunnable;
private boolean paintProgressValue = true;
private NumberFormat percentFormat = NumberFormat.getPercentInstance();
private long scaleFactor = 1;
/**
* Construct a new TaskMonitorComponent.
* @param l listener that is notified when the task completes or the
* user cancels the task
* Constructor
*/
public TaskMonitorComponent() {
this(true, true);
}
/**
* Constructor
*
* @param includeTextField if true, the dialog can display a status progressMessage with progress details
* @param includeCancelButton if true, a cancel button will be displayed
*/
public TaskMonitorComponent(boolean includeTextField, boolean includeCancelButton) {
updateProgressPanelRunnable = () -> updateProgressPanel();
updateCancelButtonRunnable = () -> updateCancelButton();
@ -113,13 +117,25 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
buildProgressPanel(includeTextField, includeCancelButton);
}
@Override
public void addCancelledListener(CancelledListener mcl) {
listeners.add(mcl);
}
/**
* Reset this monitor so that it can be reused.
*/
public synchronized void reset() {
isCancelled = false;
taskID.incrementAndGet();
@Override
public void removeCancelledListener(CancelledListener mcl) {
listeners.remove(mcl);
}
@Override
public void incrementProgress(long incrementAmount) {
setProgress(progress + incrementAmount);
}
@Override
public long getProgress() {
return progress;
}
@Override
@ -136,7 +152,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
@Override
public synchronized void setMessage(String message) {
this.message = message;
this.progressMessage = message;
startUpdateTimer();
}
@ -148,13 +164,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
progress = value;
startUpdateTimer();
}
private synchronized void startUpdateTimer() {
if (!updateTimer.isRunning()) {
updateTimer.start();
}
}
@Override
public void initialize(long maxValue) {
setMaximum(maxValue);
@ -163,13 +173,13 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
@Override
public void setMaximum(long max) {
this.max = max;
if (progress > this.max) {
this.maxProgress = max;
if (progress > this.maxProgress) {
progress = max;
}
startUpdateTimer();
}
/**
* Sets the <code>indeterminate</code> property of the progress bar,
* which determines whether the progress bar is in determinate
@ -201,17 +211,6 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
});
}
/**
* Returns true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
* been called.
*
* @return true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
* been called.
*/
public boolean isIndeterminate() {
return isIndeterminate.get();
}
@Override
public synchronized void setCancelEnabled(boolean enable) {
if (cancelEnabled != enable) {
@ -243,67 +242,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
isCancelled = false;
}
}
/**
* Set whether the progress bar should be visible.
* @param b true if the progress bar should be visible
*/
public synchronized void showProgress(boolean b) {
if (b != showingProgress) {
showingProgress = b;
SystemUtilities.runSwingLater(updateProgressPanelRunnable);
}
}
/**
* Set the name of the task; the name shows up in the tool tip for
* the cancel button.
* @param name the name of the task
*/
public void setTaskName(String name) {
taskName = name;
SystemUtilities.runSwingLater(updateToolTipRunnable);
}
/**
* Show or not show the cancel button according to the showCancel param.
*/
public void showCancelButton(boolean showCancel) {
if (showCancel == showingCancelButton) {
return;
}
if (showCancel) {
add(eastButtonPanel, BorderLayout.EAST);
}
else {
remove(eastButtonPanel);
}
showingCancelButton = showCancel;
}
/**
* Show or not show the progress icon (spinning globe) according to
* the showIcon param.
*/
public void showProgressIcon(final boolean showIcon) {
if (showIcon == showingIcon) {
return;
}
Runnable r = () -> {
if (showIcon) {
activeProgressPanel.add(imageLabel, BorderLayout.EAST);
}
else {
activeProgressPanel.remove(imageLabel);
}
showingIcon = showIcon;
};
SystemUtilities.runSwingNow(r);
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
this.paintProgressValue = showProgressValue;
@ -312,19 +251,121 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
@Override
public long getMaximum() {
return max;
return maxProgress;
}
/**
* Reset this monitor so that it can be reused
*/
@Override
public synchronized void reset() {
isCancelled = false;
taskID.incrementAndGet();
}
/**
* Returns true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
* been called.
*
* @return true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
* been called.
*/
public boolean isIndeterminate() {
return isIndeterminate.get();
}
/**
* Set whether the progress bar should be visible
*
* @param show true if the progress bar should be visible
*/
public synchronized void showProgress(boolean show) {
if (show != showingProgress) {
showingProgress = show;
SystemUtilities.runSwingLater(updateProgressPanelRunnable);
}
}
/**
* Set the name of the task; the name shows up in the tool tip for
* the cancel button.
*
* @param name the name of the task
*/
public void setTaskName(String name) {
taskName = name;
SystemUtilities.runSwingLater(updateToolTipRunnable);
}
/**
* Set the visibility of the cancel button
*
* @param visible if true, show the cancel button; false otherwise
*/
public void setCancelButtonVisibility(boolean visible) {
if (visible == showingCancelButton) {
return;
}
if (visible) {
add(cancelPanel, BorderLayout.EAST);
}
else {
remove(cancelPanel);
}
repaint();
showingCancelButton = visible;
}
/**
* Sets the visibility of the progress icon
*
* @param visible if true, display the progress icon
*/
public void showProgressIcon(boolean visible) {
if (visible == showingIcon) {
return;
}
Runnable r = () -> {
if (visible) {
mainContentPanel.add(progressPanel, BorderLayout.EAST);
}
else {
mainContentPanel.remove(progressPanel);
}
showingIcon = visible;
};
SystemUtilities.runSwingNow(r);
}
protected void notifyChangeListeners() {
Runnable r = () -> {
for (CancelledListener mcl : listeners) {
mcl.cancelled();
}
};
SwingUtilities.invokeLater(r);
}
private synchronized void startUpdateTimer() {
if (!updateTimer.isRunning()) {
updateTimer.start();
}
}
private synchronized void update() {
if (message != null) {
messageLabel.setText(message);
message = null;
if (progressMessage != null) {
messageLabel.setText(progressMessage);
progressMessage = null;
}
if (max != lastMax) {
setMaxValueInProgressBar(max);
lastMax = max;
if (maxProgress != lastMaxProgress) {
setMaxValueInProgressBar(maxProgress);
lastMaxProgress = maxProgress;
}
if (progress != lastProgress) {
@ -379,10 +420,10 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
private synchronized void updateProgressPanel() {
if (showingProgress) {
progressPanel.add(progressBar, BorderLayout.NORTH);
progressBarPanel.add(progressBar, BorderLayout.NORTH);
}
else {
progressPanel.remove(progressBar);
progressBarPanel.remove(progressBar);
}
}
@ -424,24 +465,24 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
progressBar.setStringPainted(true);
ToolTipManager.sharedInstance().registerComponent(progressBar);
createAnimatedIcon();
progressPanel = new HourglassAnimationPanel();
progressPanel = new JPanel(new VerticalLayout(0));
progressPanel.add(progressBar);
progressBarPanel = new JPanel(new VerticalLayout(0));
progressBarPanel.add(progressBar);
if (includeTextField) {
progressPanel.add(messageLabel);
progressBarPanel.add(messageLabel);
progressBar.setPreferredSize(new Dimension(180, 12));
}
else {
progressBar.setBorderPainted(true);
Dimension size = progressBar.getPreferredSize();
progressPanel.setBorder(BorderFactory.createEmptyBorder(
(imageLabel.getPreferredSize().height - size.height) / 2, 0, 0, 8));
progressBarPanel.setBorder(BorderFactory.createEmptyBorder(
(progressPanel.getPreferredSize().height - size.height) / 2, 0, 0, 8));
}
activeProgressPanel = new JPanel(new BorderLayout());
activeProgressPanel.add(progressPanel, BorderLayout.CENTER);
activeProgressPanel.add(imageLabel, BorderLayout.EAST);
mainContentPanel = new JPanel(new BorderLayout());
mainContentPanel.add(progressBarPanel, BorderLayout.CENTER);
mainContentPanel.add(progressPanel, BorderLayout.EAST);
ImageIcon icon = Icons.STOP_ICON;
cancelButton = new EmptyBorderButton(icon);
@ -452,86 +493,15 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
cancelButton.setFocusable(false);
cancelButton.setRolloverEnabled(true);
add(activeProgressPanel, BorderLayout.CENTER);
add(mainContentPanel, BorderLayout.CENTER);
if (includeCancelButton) {
eastButtonPanel = new JPanel();
eastButtonPanel.setLayout(new BoxLayout(eastButtonPanel, BoxLayout.Y_AXIS));
eastButtonPanel.add(Box.createVerticalGlue());
eastButtonPanel.add(cancelButton);
eastButtonPanel.add(Box.createVerticalGlue());
add(eastButtonPanel, BorderLayout.EAST);
}
}
private void createAnimatedIcon() {
String[] filenames = { "images/hourglass24_01.png", "images/hourglass24_02.png",
"images/hourglass24_02.png", "images/hourglass24_03.png", "images/hourglass24_03.png",
"images/hourglass24_04.png", "images/hourglass24_04.png", "images/hourglass24_05.png",
"images/hourglass24_05.png", "images/hourglass24_06.png", "images/hourglass24_06.png",
"images/hourglass24_07.png", "images/hourglass24_07.png", "images/hourglass24_08.png",
"images/hourglass24_08.png", "images/hourglass24_09.png", "images/hourglass24_10.png",
"images/hourglass24_11.png" };
imageLabel =
new GIconLabel(new AnimatedIcon(ResourceManager.loadImages(filenames), 150, 0));
}
@Override
public void incrementProgress(long incrementAmount) {
setProgress(progress + incrementAmount);
}
@Override
public long getProgress() {
return progress;
}
protected void notifyChangeListeners() {
Runnable r = () -> {
synchronized (listeners) {
for (CancelledListener mcl : listeners) {
mcl.cancelled();
}
}
};
SwingUtilities.invokeLater(r);
}
@Override
public void addCancelledListener(CancelledListener mcl) {
synchronized (listeners) {
listeners.add(mcl);
}
}
@Override
public void removeCancelledListener(CancelledListener mcl) {
synchronized (listeners) {
listeners.remove(mcl);
}
}
@Override
public void addIssueListener(IssueListener listener) {
if (issueListeners == null) {
issueListeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
}
}
@Override
public void removeIssueListener(IssueListener listener) {
if (issueListeners != null) {
issueListeners.remove(listener);
}
}
@Override
public void reportIssue(Issue issue) {
if (issueListeners != null) {
for (IssueListener listener : issueListeners) {
listener.issueReported(issue);
}
cancelPanel = new JPanel();
cancelPanel.setLayout(new BoxLayout(cancelPanel, BoxLayout.Y_AXIS));
cancelPanel.add(Box.createVerticalGlue());
cancelPanel.add(cancelButton);
cancelPanel.add(Box.createVerticalGlue());
add(cancelPanel, BorderLayout.EAST);
}
}
}