mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-2861 - Task Monitor - removed recently added Task Monitor Service due
to conceptual issues; fixed bug in TaskLauncher that caused deadlock; updated ImporterUtilities usage of TaskLauncher to trigger task dialog
This commit is contained in:
parent
2108a5ed4c
commit
a94c6efe68
21 changed files with 413 additions and 1452 deletions
|
@ -67,7 +67,6 @@ 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;
|
||||
|
@ -609,11 +608,6 @@ 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
|
||||
|
@ -627,24 +621,12 @@ 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) {
|
||||
|
||||
|
@ -659,14 +641,12 @@ 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) {
|
||||
|
@ -711,13 +691,11 @@ 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;
|
||||
}
|
||||
|
@ -828,7 +806,6 @@ public class DialogComponentProvider
|
|||
public void clearStatusText() {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
statusLabel.setText(" ");
|
||||
subStatusLabel.setText(" ");
|
||||
updateStatusToolTip();
|
||||
});
|
||||
}
|
||||
|
@ -842,15 +819,6 @@ public class DialogComponentProvider
|
|||
return statusLabel.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the secondary status message
|
||||
*
|
||||
* @return the secondary status message
|
||||
*/
|
||||
public String getSubStatusText() {
|
||||
return subStatusLabel.getText();
|
||||
}
|
||||
|
||||
protected JLabel getStatusLabel() {
|
||||
return statusLabel;
|
||||
}
|
||||
|
@ -941,23 +909,13 @@ 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(0, 5, 3, 5));
|
||||
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(9.0f));
|
||||
|
||||
// use a strut panel so the size of the message area does not change if we make
|
||||
// the message label not visible
|
||||
int height =
|
||||
statusLabel.getPreferredSize().height + subStatusLabel.getPreferredSize().height + 5;
|
||||
int height = statusLabel.getPreferredSize().height;
|
||||
|
||||
panel.add(Box.createVerticalStrut(height), BorderLayout.WEST);
|
||||
panel.add(statusLabel, BorderLayout.CENTER);
|
||||
panel.add(subStatusLabel, BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
/* ###
|
||||
* 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);
|
||||
return;
|
||||
}
|
||||
parentMonitor.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
parentMonitor.setCancelEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitialized(boolean init) {
|
||||
parentMonitor.setInitialized(init);
|
||||
}
|
||||
|
||||
/**
|
||||
* Secondary monitors should not be able to reset progress or revert back
|
||||
* to an uninitialized state; hence the override.
|
||||
*/
|
||||
@Override
|
||||
public void finished() {
|
||||
synchronized (this) {
|
||||
setMessage("");
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -42,6 +42,17 @@ import util.CollectionUtils;
|
|||
* .setStatusTextAlignment(SwingConstants.LEADING)
|
||||
* .launchModal();
|
||||
* </pre>
|
||||
*
|
||||
* Or,
|
||||
*
|
||||
* <pre>
|
||||
* TaskBuilder.withRunnable(monitor -> doWork(parameter, monitor))
|
||||
* .setTitle("Task Title")
|
||||
* .setHasProgress(true)
|
||||
* .setCanCancel(true)
|
||||
* .setStatusTextAlignment(SwingConstants.LEADING)
|
||||
* .launchModal();
|
||||
* </pre>
|
||||
*/
|
||||
public class TaskBuilder {
|
||||
|
||||
|
@ -57,7 +68,25 @@ public class TaskBuilder {
|
|||
private int statusTextAlignment = SwingConstants.CENTER;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* A convenience method to start a builder using the given runnable. After calling this
|
||||
* method you are still required to call {@link #setTitle(String)}.
|
||||
*
|
||||
* <p>This method allows for a more attractive fluent API usage than does the constructor
|
||||
* (see the javadoc header).
|
||||
*
|
||||
* @param r the runnable
|
||||
* @return this builder
|
||||
*/
|
||||
public static TaskBuilder withRunnable(MonitoredRunnable r) {
|
||||
return new TaskBuilder(r);
|
||||
}
|
||||
|
||||
private TaskBuilder(MonitoredRunnable r) {
|
||||
this.runnable = Objects.requireNonNull(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param title the required title for your task. This will appear as the title of the
|
||||
* task dialog
|
||||
|
@ -68,6 +97,18 @@ public class TaskBuilder {
|
|||
this.runnable = Objects.requireNonNull(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of this task. The title must be set before calling any of the
|
||||
* <code>launch</code> methods.
|
||||
*
|
||||
* @param title the title
|
||||
* @return this builder
|
||||
*/
|
||||
public TaskBuilder setTitle(String title) {
|
||||
this.title = Objects.requireNonNull(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this task reports progress. The default is <tt>true</tt>.
|
||||
*
|
||||
|
@ -149,6 +190,8 @@ public class TaskBuilder {
|
|||
* Launches the task built by this builder, using a blocking modal dialog.
|
||||
*/
|
||||
public void launchModal() {
|
||||
validate();
|
||||
|
||||
boolean isModal = true;
|
||||
Task t = new TaskBuilderTask(isModal);
|
||||
int delay = getDelay(launchDelay, isModal);
|
||||
|
@ -160,6 +203,8 @@ public class TaskBuilder {
|
|||
* @return the launcher that launched the task
|
||||
*/
|
||||
public TaskLauncher launchNonModal() {
|
||||
validate();
|
||||
|
||||
boolean isModal = false;
|
||||
Task t = new TaskBuilderTask(isModal);
|
||||
int delay = getDelay(launchDelay, isModal);
|
||||
|
@ -167,6 +212,12 @@ public class TaskBuilder {
|
|||
return launcher;
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (title == null) {
|
||||
throw new NullPointerException("Task title cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
private static int getDelay(int userDelay, boolean isModal) {
|
||||
if (userDelay >= 0) {
|
||||
return userDelay;
|
||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.util.task;
|
|||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -38,10 +37,10 @@ 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;
|
||||
|
@ -57,24 +56,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
/** 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
|
||||
*
|
||||
|
@ -108,7 +92,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
public TaskDialog(String title, boolean canCancel, boolean isModal, boolean hasProgress) {
|
||||
this(null, title, isModal, canCancel, hasProgress);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
@ -126,20 +110,10 @@ 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) {
|
||||
monitorComponent = new TaskMonitorComponent(false, false);
|
||||
chompingBitsPanel = new ChompingBitsAnimationPanel();
|
||||
|
||||
|
||||
setCancelEnabled(canCancel);
|
||||
setRememberLocation(false);
|
||||
setRememberSize(false);
|
||||
|
@ -154,12 +128,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
newPrimaryMessage = null;
|
||||
}
|
||||
};
|
||||
updateSecondaryMessageRunnable = () -> {
|
||||
setSubStatusText(newSecondaryMessage);
|
||||
synchronized (TaskDialog.this) {
|
||||
newSecondaryMessage = null;
|
||||
}
|
||||
};
|
||||
shouldCancelRunnable = () -> {
|
||||
int currentTaskID = taskID.get();
|
||||
|
||||
|
@ -171,7 +139,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
|
||||
mainPanel = new JPanel(new BorderLayout());
|
||||
addWorkPanel(mainPanel);
|
||||
|
||||
|
||||
if (hasProgress) {
|
||||
installProgressMonitor();
|
||||
}
|
||||
|
@ -306,19 +274,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the secondary message on the task monitor
|
||||
*
|
||||
* @param str the string to update
|
||||
*/
|
||||
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);
|
||||
|
@ -386,7 +341,6 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
// only to show a progress dialog if enough time has elapsed.
|
||||
//
|
||||
GTimer.scheduleRunnable(delay, () -> {
|
||||
|
||||
if (isCompleted()) {
|
||||
return;
|
||||
}
|
||||
|
@ -467,12 +421,4 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
public void removeCancelledListener(CancelledListener listener) {
|
||||
monitorComponent.removeCancelledListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized TaskMonitor getSecondaryMonitor() {
|
||||
if (secondaryTaskMonitor == null) {
|
||||
secondaryTaskMonitor = new SecondaryTaskMonitor(this);
|
||||
}
|
||||
return secondaryTaskMonitor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import java.awt.Component;
|
|||
import javax.swing.SwingUtilities;
|
||||
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TaskUtilities;
|
||||
|
||||
/**
|
||||
* Class to initiate a Task in a new Thread, and to show a progress dialog that indicates
|
||||
|
@ -285,16 +286,8 @@ public class TaskLauncher {
|
|||
|
||||
private TaskDialog buildTaskDialog(Component comp, int dialogWidth) {
|
||||
|
||||
//
|
||||
// This class may be used by background threads. Make sure that our GUI creation is
|
||||
// on the Swing thread to prevent exceptions while painting (as seen when using the
|
||||
// Nimbus Look and Feel).
|
||||
//
|
||||
|
||||
SystemUtilities.runSwingNow(() -> {
|
||||
taskDialog = createTaskDialog(comp);
|
||||
taskDialog.setMinimumSize(dialogWidth, 0);
|
||||
});
|
||||
taskDialog = createTaskDialog(comp);
|
||||
taskDialog.setMinimumSize(dialogWidth, 0);
|
||||
|
||||
if (task.isInterruptible() || task.isForgettable()) {
|
||||
taskDialog.addCancelledListener(monitorChangeListener);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue