mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 21:21:56 +00:00
Moved setLayout into a task to fix a race condition when popping up the
cancel dialog
This commit is contained in:
parent
ba80c729ec
commit
1c145dd781
@ -64,6 +64,7 @@ import ghidra.graph.job.GraphJobRunner;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import resources.Icons;
|
||||
|
||||
@ -128,10 +129,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
* provides graph displays for supplied graphs
|
||||
*/
|
||||
private final DefaultGraphDisplayProvider graphDisplayProvider;
|
||||
/**
|
||||
* a 'busy' dialog to show while the layout algorithm is working
|
||||
*/
|
||||
private LayoutWorkingDialog layoutWorkingDialog;
|
||||
/**
|
||||
* the vertex that has been nominated to be 'focused' in the graph display and listing
|
||||
*/
|
||||
@ -359,20 +356,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
.onActionStateChanged((s, t) -> layoutChanged(s.getName()))
|
||||
.addStates(getLayoutActionStates())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
// show a 'busy' dialog while the layout algorithm is computing vertex locations
|
||||
viewer.getVisualizationModel()
|
||||
.getLayoutModel()
|
||||
.getLayoutStateChangeSupport()
|
||||
.addLayoutStateChangeListener(
|
||||
evt -> {
|
||||
if (evt.active) {
|
||||
Swing.runLater(this::showLayoutWorking);
|
||||
}
|
||||
else {
|
||||
Swing.runLater(this::hideLayoutWorking);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createPopupActions() {
|
||||
@ -569,7 +552,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
*/
|
||||
private void layoutChanged(String layoutName) {
|
||||
if (layoutTransitionManager != null) {
|
||||
layoutTransitionManager.setLayout(layoutName);
|
||||
new TaskLauncher(new SetLayoutTask(viewer, layoutTransitionManager, layoutName), null,
|
||||
1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,27 +571,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
componentProvider.getTool().showDialog(filterDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* show the 'busy' dialog indicating that the layout algorithm is working
|
||||
*/
|
||||
protected void showLayoutWorking() {
|
||||
if (this.layoutWorkingDialog != null) {
|
||||
layoutWorkingDialog.close();
|
||||
}
|
||||
this.layoutWorkingDialog =
|
||||
new LayoutWorkingDialog(viewer.getVisualizationModel().getLayoutAlgorithm());
|
||||
componentProvider.getTool().showDialog(layoutWorkingDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* hide the 'busy' dialog for the layout algorithm work
|
||||
*/
|
||||
protected void hideLayoutWorking() {
|
||||
if (this.layoutWorkingDialog != null) {
|
||||
layoutWorkingDialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add or remove the satellite viewer
|
||||
* @param context information about the event
|
||||
|
@ -1,57 +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.graph.visualization;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import ghidra.service.graph.AttributedVertex;
|
||||
|
||||
/**
|
||||
* Extends DialogComponentProvider to make a dialog with buttons to show that the
|
||||
* layout arrangement algorithm is busy
|
||||
*/
|
||||
public class LayoutWorkingDialog extends DialogComponentProvider {
|
||||
|
||||
public LayoutWorkingDialog(LayoutAlgorithm<AttributedVertex> layoutAlgorithm) {
|
||||
super("Working....", false);
|
||||
super.addWorkPanel(createPanel(layoutAlgorithm));
|
||||
setRememberSize(false);
|
||||
addDismissButton();
|
||||
setDefaultButton(dismissButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a layout-formatted JComponent holding 2 vertical lists
|
||||
* of buttons, one list for vertex filter buttons and one list for
|
||||
* edge filter buttons. Each list has a border and title.
|
||||
* @return a formatted JComponent (container)
|
||||
*/
|
||||
JComponent createPanel(LayoutAlgorithm<AttributedVertex> layoutAlgorithm) {
|
||||
JProgressBar progressBar = new JProgressBar();
|
||||
progressBar.setIndeterminate(true);
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(progressBar, BorderLayout.CENTER);
|
||||
panel.add(new JLabel("Please wait......."), BorderLayout.NORTH);
|
||||
addCancelButton();
|
||||
cancelButton.addActionListener(evt -> layoutAlgorithm.cancel());
|
||||
return panel;
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* 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.graph.visualization;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.jungrapht.visualization.VisualizationModel;
|
||||
import org.jungrapht.visualization.VisualizationViewer;
|
||||
import org.jungrapht.visualization.layout.event.LayoutStateChange.*;
|
||||
import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||
|
||||
import ghidra.service.graph.AttributedEdge;
|
||||
import ghidra.service.graph.AttributedVertex;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
/**
|
||||
* Task to change the layout of the graph
|
||||
*/
|
||||
public class SetLayoutTask extends Task {
|
||||
|
||||
private LayoutTransitionManager layoutTransitionManager;
|
||||
private String layoutName;
|
||||
private VisualizationViewer<AttributedVertex, AttributedEdge> viewer;
|
||||
private CountDownLatch taskDone = new CountDownLatch(1);
|
||||
|
||||
public SetLayoutTask(VisualizationViewer<AttributedVertex, AttributedEdge> viewer,
|
||||
LayoutTransitionManager layoutTransitionManager, String layoutName) {
|
||||
super("Changing Graph Layout to " + layoutName, true, false, true, false);
|
||||
this.viewer = viewer;
|
||||
this.layoutTransitionManager = layoutTransitionManager;
|
||||
this.layoutName = layoutName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
// add a callback for when/if the user cancels the layout, use a variable cause
|
||||
// monitor uses a weak listener list and it would othewise get garbage collected.
|
||||
CancelledListener cancelListener = this::taskCancelled;
|
||||
monitor.addCancelledListener(cancelListener);
|
||||
|
||||
// add a listener so we are notified when the layout starts and ends
|
||||
VisualizationModel<AttributedVertex, AttributedEdge> model = viewer.getVisualizationModel();
|
||||
LayoutModel<AttributedVertex> layoutModel = model.getLayoutModel();
|
||||
Support support = layoutModel.getLayoutStateChangeSupport();
|
||||
Listener listener = this::layoutStateChanged;
|
||||
support.addLayoutStateChangeListener(listener);
|
||||
|
||||
// start the layout - needs to be done on swing thread to prevent issues and intermediate
|
||||
// paints - should be changed in the future to not require it to be on the swing thread.
|
||||
Swing.runNow(() -> layoutTransitionManager.setLayout(layoutName));
|
||||
|
||||
// some of the layouts are done on the calling thread and some aren't. If they are on
|
||||
// the calling thread, then by now, we already got the "done" callback and the "taskDone"
|
||||
// countdown latch has been triggered and won't wait. If, however, the layout has been
|
||||
// diverted to another thread, we want to wait until the layout is completed
|
||||
// There are two ways the latch will be triggered, the layout is completed or the user
|
||||
// cancles the layout.
|
||||
try {
|
||||
taskDone.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
model.getLayoutAlgorithm().cancel();
|
||||
}
|
||||
|
||||
// clean up the listeners
|
||||
support.removeLayoutStateChangeListener(listener);
|
||||
monitor.removeCancelledListener(cancelListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notfication when the layout algorithm starts and stops.
|
||||
* @param e the event. If the event.active is true, then the
|
||||
* algorithm is starting, if false, the algorithm is done.
|
||||
*/
|
||||
private void layoutStateChanged(Event e) {
|
||||
if (!e.active) {
|
||||
// algorithm is done, release the latch
|
||||
taskDone.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback if the user cancels the layout
|
||||
*/
|
||||
private void taskCancelled() {
|
||||
// release the latch and tell the layout algorithm to cancel.
|
||||
taskDone.countDown();
|
||||
viewer.getVisualizationModel().getLayoutAlgorithm().cancel();
|
||||
}
|
||||
|
||||
}
|
@ -273,6 +273,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
* @return true if {@link #setIndeterminate(boolean)} with a value of <code>true</code> has
|
||||
* been called.
|
||||
*/
|
||||
@Override
|
||||
public boolean isIndeterminate() {
|
||||
return isIndeterminate.get();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user