mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02: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
4 changed files with 110 additions and 97 deletions
|
@ -64,6 +64,7 @@ import ghidra.graph.job.GraphJobRunner;
|
||||||
import ghidra.service.graph.*;
|
import ghidra.service.graph.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskLauncher;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
|
|
||||||
|
@ -128,10 +129,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
* provides graph displays for supplied graphs
|
* provides graph displays for supplied graphs
|
||||||
*/
|
*/
|
||||||
private final DefaultGraphDisplayProvider graphDisplayProvider;
|
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
|
* 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()))
|
.onActionStateChanged((s, t) -> layoutChanged(s.getName()))
|
||||||
.addStates(getLayoutActionStates())
|
.addStates(getLayoutActionStates())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.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() {
|
private void createPopupActions() {
|
||||||
|
@ -569,7 +552,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
*/
|
*/
|
||||||
private void layoutChanged(String layoutName) {
|
private void layoutChanged(String layoutName) {
|
||||||
if (layoutTransitionManager != null) {
|
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);
|
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
|
* add or remove the satellite viewer
|
||||||
* @param context information about the event
|
* @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
|
* @return true if {@link #setIndeterminate(boolean)} with a value of <code>true</code> has
|
||||||
* been called.
|
* been called.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean isIndeterminate() {
|
public boolean isIndeterminate() {
|
||||||
return isIndeterminate.get();
|
return isIndeterminate.get();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue