mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5826 - Symbol Tree - Fixed flashing due to too many events being
processed
This commit is contained in:
parent
c7aa190b40
commit
95fd25eb58
3 changed files with 57 additions and 13 deletions
|
@ -609,11 +609,13 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
.with(ProgramChangeRecord.class)
|
.with(ProgramChangeRecord.class)
|
||||||
.each(SYMBOL_RENAMED).call(this::processSymbolRenamed)
|
.each(SYMBOL_RENAMED).call(this::processSymbolRenamed)
|
||||||
.each(SYMBOL_DATA_CHANGED, SYMBOL_SCOPE_CHANGED).call(this::processSymbolChanged)
|
.each(SYMBOL_DATA_CHANGED, SYMBOL_SCOPE_CHANGED).call(this::processSymbolChanged)
|
||||||
.each(FUNCTION_CHANGED).call(this::processFunctionChanged)
|
|
||||||
.each(SYMBOL_ADDED).call(this::processSymbolAdded)
|
.each(SYMBOL_ADDED).call(this::processSymbolAdded)
|
||||||
.each(SYMBOL_REMOVED).call(this::processSymbolRemoved)
|
.each(SYMBOL_REMOVED).call(this::processSymbolRemoved)
|
||||||
.each(EXTERNAL_ENTRY_ADDED, EXTERNAL_ENTRY_REMOVED)
|
.each(EXTERNAL_ENTRY_ADDED, EXTERNAL_ENTRY_REMOVED)
|
||||||
.call(this::processExternalEntryChanged)
|
.call(this::processExternalEntryChanged)
|
||||||
|
|
||||||
|
// handle function changes specially so that we can perform coalesce changes
|
||||||
|
.any(FUNCTION_CHANGED).call(this::processAllFunction)
|
||||||
.build();
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
@ -626,10 +628,24 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
symbolRemoved((Symbol) pcr.getObject());
|
symbolRemoved((Symbol) pcr.getObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFunctionChanged(ProgramChangeRecord pcr) {
|
private void processAllFunction(DomainObjectChangedEvent e) {
|
||||||
Function function = (Function) pcr.getObject();
|
|
||||||
Symbol symbol = function.getSymbol();
|
// grab all function records and remove duplicates so that we can make 1 task for all
|
||||||
symbolChanged(symbol);
|
// changes to a given function
|
||||||
|
Set<Symbol> symbols = new HashSet<>();
|
||||||
|
int n = e.numRecords();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
DomainObjectChangeRecord r = e.getChangeRecord(i);
|
||||||
|
if (r instanceof FunctionChangeRecord fcr) {
|
||||||
|
Function f = fcr.getFunction();
|
||||||
|
Symbol s = f.getSymbol();
|
||||||
|
symbols.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Symbol s : symbols) {
|
||||||
|
symbolChanged(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processSymbolChanged(ProgramChangeRecord pcr) {
|
private void processSymbolChanged(ProgramChangeRecord pcr) {
|
||||||
|
@ -851,5 +867,4 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
task.run(monitor);
|
task.run(monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.theme.GColor;
|
import generic.theme.GColor;
|
||||||
import generic.theme.GColorUIResource;
|
import generic.theme.GColorUIResource;
|
||||||
import ghidra.util.HTMLUtilities;
|
|
||||||
|
|
||||||
public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent {
|
public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent {
|
||||||
|
|
||||||
|
@ -71,10 +70,16 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||||
GTreeNode node = (GTreeNode) value;
|
GTreeNode node = (GTreeNode) value;
|
||||||
String text = node.getDisplayText();
|
String text = node.getDisplayText();
|
||||||
setText(text);
|
setText(text);
|
||||||
|
|
||||||
String toolTip = node.getToolTip();
|
String toolTip = node.getToolTip();
|
||||||
setToolTipText(toolTip);
|
setToolTipText(toolTip);
|
||||||
String fromHTML = HTMLUtilities.fromHTML(toolTip);
|
|
||||||
getAccessibleContext().setAccessibleDescription(fromHTML);
|
// Note: attempting to use the tooltip text here by removing the html formatting can be
|
||||||
|
// too slow for large functions. Also, the full preview text for larger symbol tool tips
|
||||||
|
// can be overwhelming for screen readers. The text of the node is a reasonable value
|
||||||
|
// to provide for screen readers.
|
||||||
|
// String fromHTML = HTMLUtilities.fromHTML(toolTip);
|
||||||
|
getAccessibleContext().setAccessibleDescription(node.getDisplayText());
|
||||||
|
|
||||||
Icon icon = getNodeIcon(node, expanded);
|
Icon icon = getNodeIcon(node, expanded);
|
||||||
if (icon == null) {
|
if (icon == null) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.function.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import utilities.util.reflection.ReflectionUtilities;
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
import utility.function.Callback;
|
import utility.function.Callback;
|
||||||
|
import utility.function.Dummy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for creating a compact and efficient {@link DomainObjectListener}s. See
|
* Base class for creating a compact and efficient {@link DomainObjectListener}s. See
|
||||||
|
@ -33,6 +34,7 @@ import utility.function.Callback;
|
||||||
public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObjectChangeRecord, B extends AbstractDomainObjectListenerBuilder<R, B>> {
|
public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObjectChangeRecord, B extends AbstractDomainObjectListenerBuilder<R, B>> {
|
||||||
private String name;
|
private String name;
|
||||||
private BooleanSupplier ignoreCheck;
|
private BooleanSupplier ignoreCheck;
|
||||||
|
private Consumer<DomainObjectChangedEvent> debugConsumer;
|
||||||
private List<EventTrigger> terminateList = new ArrayList<>();
|
private List<EventTrigger> terminateList = new ArrayList<>();
|
||||||
private List<EventTrigger> onAnyList = new ArrayList<>();
|
private List<EventTrigger> onAnyList = new ArrayList<>();
|
||||||
private Map<EventType, TypedRecordConsumer<? extends DomainObjectChangeRecord>> onEachMap =
|
private Map<EventType, TypedRecordConsumer<? extends DomainObjectChangeRecord>> onEachMap =
|
||||||
|
@ -63,6 +65,17 @@ public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObject
|
||||||
|
|
||||||
protected abstract B self();
|
protected abstract B self();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a consumer of events that is intended to be used for clients to add a callback for each
|
||||||
|
* event. This is useful for temporarily inspecting events and adding breakpoints.
|
||||||
|
* @param consumer the consumer to add
|
||||||
|
* @return this builder (for chaining)
|
||||||
|
*/
|
||||||
|
public B debug(Consumer<DomainObjectChangedEvent> consumer) {
|
||||||
|
this.debugConsumer = consumer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a boolean supplier that can be checked to see if the client is in a state where
|
* Sets a boolean supplier that can be checked to see if the client is in a state where
|
||||||
* they don't want events to be processed at this time.
|
* they don't want events to be processed at this time.
|
||||||
|
@ -121,6 +134,7 @@ public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObject
|
||||||
|
|
||||||
BuilderDomainObjectListener listener = new BuilderDomainObjectListener(name);
|
BuilderDomainObjectListener listener = new BuilderDomainObjectListener(name);
|
||||||
listener.setIgnoreCheck(ignoreCheck);
|
listener.setIgnoreCheck(ignoreCheck);
|
||||||
|
listener.setDebugConsumer(debugConsumer);
|
||||||
if (!terminateList.isEmpty()) {
|
if (!terminateList.isEmpty()) {
|
||||||
listener.setTerminateList(terminateList);
|
listener.setTerminateList(terminateList);
|
||||||
}
|
}
|
||||||
|
@ -306,6 +320,7 @@ public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObject
|
||||||
static class BuilderDomainObjectListener implements DomainObjectListener {
|
static class BuilderDomainObjectListener implements DomainObjectListener {
|
||||||
private String name;
|
private String name;
|
||||||
private BooleanSupplier ignoreCheck = () -> false;
|
private BooleanSupplier ignoreCheck = () -> false;
|
||||||
|
private Consumer<DomainObjectChangedEvent> debugConsumer = Dummy.consumer();
|
||||||
private List<EventTrigger> terminateList;
|
private List<EventTrigger> terminateList;
|
||||||
private List<EventTrigger> onAnyList;
|
private List<EventTrigger> onAnyList;
|
||||||
private Map<EventType, TypedRecordConsumer<? extends DomainObjectChangeRecord>> onEachMap;
|
private Map<EventType, TypedRecordConsumer<? extends DomainObjectChangeRecord>> onEachMap;
|
||||||
|
@ -323,6 +338,10 @@ public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObject
|
||||||
this.ignoreCheck = supplier != null ? supplier : () -> false;
|
this.ignoreCheck = supplier != null ? supplier : () -> false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDebugConsumer(Consumer<DomainObjectChangedEvent> debugConsumer) {
|
||||||
|
this.debugConsumer = Dummy.ifNull(debugConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
void setTerminateList(List<EventTrigger> terminatEventList) {
|
void setTerminateList(List<EventTrigger> terminatEventList) {
|
||||||
this.terminateList = terminatEventList;
|
this.terminateList = terminatEventList;
|
||||||
}
|
}
|
||||||
|
@ -339,10 +358,15 @@ public abstract class AbstractDomainObjectListenerBuilder<R extends DomainObject
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
||||||
|
|
||||||
|
// a way for clients to add conditional debug, print statements and breakpoints
|
||||||
|
debugConsumer.accept(event);
|
||||||
|
|
||||||
// check if events are being ignored
|
// check if events are being ignored
|
||||||
if (ignoreCheck.getAsBoolean()) {
|
if (ignoreCheck.getAsBoolean()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for terminating events first
|
// check for terminating events first
|
||||||
if (terminateList != null && processTerminateList(event)) {
|
if (terminateList != null && processTerminateList(event)) {
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue