mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-04 01:15:18 +00:00
Merge remote-tracking branch 'origin/GP-2186-dragonmacher-dt-editing-npe--SQUASHED'
This commit is contained in:
commit
c4c363b6b4
|
@ -45,8 +45,7 @@ import ghidra.program.model.symbol.*;
|
|||
import ghidra.program.util.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||
|
@ -76,7 +75,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
*/
|
||||
private List<GTreeTask> bufferedTasks = new ArrayList<>();
|
||||
private SwingUpdateManager domainChangeUpdateManager = new SwingUpdateManager(1000,
|
||||
SwingUpdateManager.DEFAULT_MAX_DELAY, "Symbol Tree Provider", () -> {
|
||||
AbstractSwingUpdateManager.DEFAULT_MAX_DELAY, "Symbol Tree Provider", () -> {
|
||||
|
||||
if (bufferedTasks.isEmpty()) {
|
||||
return;
|
||||
|
@ -230,12 +229,12 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
|
||||
String createGroup = "0Create";
|
||||
int createGroupIndex = 0;
|
||||
DockingAction createNamespaceAction = new CreateNamespaceAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
DockingAction createClassAction = new CreateClassAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
DockingAction convertToClassAction = new ConvertToClassAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
DockingAction createNamespaceAction =
|
||||
new CreateNamespaceAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||
DockingAction createClassAction =
|
||||
new CreateClassAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||
DockingAction convertToClassAction =
|
||||
new ConvertToClassAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||
|
||||
DockingAction renameAction = new RenameAction(plugin);
|
||||
DockingAction cutAction = new CutAction(plugin, this);
|
||||
|
@ -414,6 +413,11 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private void rebuildTree() {
|
||||
|
||||
// If we do not cancel the edit here, then an open edits will instead be committed. It
|
||||
// seems safer to cancel an edit rather than to commit it without asking.
|
||||
tree.cancelEditing();
|
||||
|
||||
SymbolTreeRootNode node = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
node.setChildren(null);
|
||||
tree.refilterLater();
|
||||
|
@ -434,9 +438,8 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
private void addTask(GTreeTask task) {
|
||||
// Note: if we want to call this method from off the Swing thread, then we have to
|
||||
// synchronize on the list that we are adding to here.
|
||||
Swing.assertSwingThread(
|
||||
"Adding tasks must be done on the Swing thread," +
|
||||
"since they are put into a list that is processed on the Swing thread. ");
|
||||
Swing.assertSwingThread("Adding tasks must be done on the Swing thread," +
|
||||
"since they are put into a list that is processed on the Swing thread. ");
|
||||
|
||||
bufferedTasks.add(task);
|
||||
domainChangeUpdateManager.update();
|
||||
|
|
|
@ -19,21 +19,35 @@ import java.awt.Component;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.UndoRedoKeeper;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
class SymbolEditor extends DefaultCellEditor {
|
||||
|
||||
private JTextField symbolField = null;
|
||||
private UndoRedoKeeper undoRedoKeeper;
|
||||
|
||||
SymbolEditor() {
|
||||
super(new JTextField());
|
||||
symbolField = (JTextField) super.getComponent();
|
||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||
undoRedoKeeper = DockingUtils.installUndoRedo(symbolField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return symbolField.getText().trim();
|
||||
public boolean stopCellEditing() {
|
||||
if (super.stopCellEditing()) {
|
||||
undoRedoKeeper.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCellEditing() {
|
||||
super.cancelCellEditing();
|
||||
undoRedoKeeper.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -172,10 +172,18 @@ public class DockingUtils {
|
|||
return (modifiers & osSpecificMask) == osSpecificMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs key binding support for undo/redo operations on the given text component.
|
||||
*
|
||||
* <p>Note: the edits are tracked by adding a listener to the document of the given text
|
||||
* component. If that document is changed, then undo/redo will stop working.
|
||||
*
|
||||
* @param textComponent the text component
|
||||
* @return the object that allows the client to track the undo/redo state
|
||||
*/
|
||||
public static UndoRedoKeeper installUndoRedo(JTextComponent textComponent) {
|
||||
|
||||
Document document = textComponent.getDocument();
|
||||
|
||||
final UndoRedoKeeper undoRedoKeeper = new UndoRedoKeeper();
|
||||
document.addUndoableEditListener(e -> {
|
||||
UndoableEdit edit = e.getEdit();
|
||||
|
@ -189,9 +197,11 @@ public class DockingUtils {
|
|||
KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
|
||||
if (REDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
||||
undoRedoKeeper.redo();
|
||||
e.consume(); // consume to prevent other listeners from processing a second time
|
||||
}
|
||||
else if (UNDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
||||
undoRedoKeeper.undo();
|
||||
e.consume(); // consume to prevent other listeners from processing a second time
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.swing.*;
|
|||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
@ -120,6 +121,8 @@ public class FilterTextField extends JPanel {
|
|||
});
|
||||
|
||||
add(layeredPane, BorderLayout.NORTH);
|
||||
|
||||
DockingUtils.installUndoRedo(textField);
|
||||
}
|
||||
|
||||
private void notifyEnterPressed() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,14 +17,36 @@ package docking.widgets.table;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.UndoRedoKeeper;
|
||||
|
||||
public class GTableTextCellEditor extends DefaultCellEditor {
|
||||
private static final Object TABLE_FOCUS_CELL_HIGHLIGHT_BORDER =
|
||||
"Table.focusCellHighlightBorder";
|
||||
|
||||
private UndoRedoKeeper undoRedoKeeper;
|
||||
|
||||
public GTableTextCellEditor(JTextField textField) {
|
||||
super(textField);
|
||||
setClickCountToStart(2);
|
||||
|
||||
textField.setBorder(UIManager.getBorder(TABLE_FOCUS_CELL_HIGHLIGHT_BORDER));
|
||||
|
||||
undoRedoKeeper = DockingUtils.installUndoRedo(textField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopCellEditing() {
|
||||
if (super.stopCellEditing()) {
|
||||
undoRedoKeeper.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCellEditing() {
|
||||
super.cancelCellEditing();
|
||||
undoRedoKeeper.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ public class ColumnConstraintSet<R, T> {
|
|||
return saveState;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns an HTML representation of this constraint set in a tabular form. It will be used
|
||||
* inside the HTML representation of the entire filter. See {@link ColumnBasedTableFilter#getHtmlRepresentation()}
|
||||
* for a description of the table format.
|
||||
|
|
|
@ -62,7 +62,7 @@ public abstract class AbstractColumnConstraintEditor<T> implements ColumnConstra
|
|||
* This expects the UI to have been constructed.
|
||||
*
|
||||
* @see #getValue()
|
||||
* @return
|
||||
* @return the value
|
||||
*/
|
||||
protected abstract ColumnConstraint<T> getValueFromComponent();
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.*;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.widgets.DropDownTextField;
|
||||
import docking.widgets.DropDownTextFieldDataModel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
|
@ -61,6 +62,7 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
|||
textField = new DropDownTextField<>(autocompleter, 100);
|
||||
textField.setIgnoreEnterKeyPress(true);
|
||||
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
||||
DockingUtils.installUndoRedo(textField);
|
||||
panel.add(textField, BorderLayout.NORTH);
|
||||
textField.addActionListener(e -> textField.closeDropDownWindow());
|
||||
|
||||
|
@ -139,9 +141,8 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
|||
return Collections.emptyList();
|
||||
}
|
||||
searchText = searchText.trim();
|
||||
lastConstraint =
|
||||
(StringColumnConstraint) currentConstraint.parseConstraintValue(searchText,
|
||||
columnDataSource.getTableDataSource());
|
||||
lastConstraint = (StringColumnConstraint) currentConstraint
|
||||
.parseConstraintValue(searchText, columnDataSource.getTableDataSource());
|
||||
|
||||
// Use a Collator to support languages other than English.
|
||||
Collator collator = Collator.getInstance();
|
||||
|
@ -222,8 +223,7 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
|||
// escape all unescaped '\' and '$' chars, as Match.appendReplacement() will treat
|
||||
// them as regex characters
|
||||
String quoted = Matcher.quoteReplacement(group);
|
||||
String replacement =
|
||||
HTMLUtilities.colorString(color, HTMLUtilities.bold(quoted));
|
||||
String replacement = HTMLUtilities.colorString(color, HTMLUtilities.bold(quoted));
|
||||
matcher.appendReplacement(sb, replacement);
|
||||
}
|
||||
matcher.appendTail(sb);
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.*;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import docking.widgets.table.constraint.ColumnConstraint;
|
||||
import docking.widgets.table.constraint.StringColumnConstraint;
|
||||
|
@ -50,6 +51,8 @@ public class StringConstraintEditor extends AbstractColumnConstraintEditor<Strin
|
|||
textField = new JTextField();
|
||||
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
||||
|
||||
DockingUtils.installUndoRedo(textField);
|
||||
|
||||
panel.add(textField, BorderLayout.CENTER);
|
||||
|
||||
infoLabel = new GDHtmlLabel("abc"); // temporary text in the label so that it sizes properly
|
||||
|
@ -77,7 +80,7 @@ public class StringConstraintEditor extends AbstractColumnConstraintEditor<Strin
|
|||
|
||||
@Override
|
||||
protected void updateInfoMessage(boolean isValid) {
|
||||
// uses   to presever the labels height.
|
||||
// uses   to preserve the label's height
|
||||
String status = formatStatus(isValid ? " " : errorMessage, true);
|
||||
infoLabel.setText(status);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.tree;
|
||||
|
||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.USER_GENERATED;
|
||||
import static ghidra.util.SystemUtilities.runSwingNow;
|
||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
|
||||
import static ghidra.util.SystemUtilities.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.dnd.Autoscroll;
|
||||
|
@ -1009,6 +1009,11 @@ public class GTree extends JPanel implements BusyListener {
|
|||
// Waits for the given model node, passing it to the consumer when available
|
||||
private void getModelNode(GTreeNode parent, String childName, Consumer<GTreeNode> consumer) {
|
||||
|
||||
// check for null here to preserve the stack, as the code below is asynchronous
|
||||
Objects.requireNonNull(parent);
|
||||
Objects.requireNonNull(childName);
|
||||
Objects.requireNonNull(consumer);
|
||||
|
||||
int expireMs = 3000;
|
||||
Supplier<GTreeNode> supplier = () -> {
|
||||
GTreeNode modelParent = getModelNode(parent);
|
||||
|
@ -1023,6 +1028,11 @@ public class GTree extends JPanel implements BusyListener {
|
|||
// Waits for the given view node, passing it to the consumer when available
|
||||
private void getViewNode(GTreeNode parent, String childName, Consumer<GTreeNode> consumer) {
|
||||
|
||||
// check for null here to preserve the stack, as the code below is asynchronous
|
||||
Objects.requireNonNull(parent);
|
||||
Objects.requireNonNull(childName);
|
||||
Objects.requireNonNull(consumer);
|
||||
|
||||
int expireMs = 3000;
|
||||
Supplier<GTreeNode> supplier = () -> {
|
||||
GTreeNode viewParent = getViewNode(parent);
|
||||
|
@ -1081,6 +1091,11 @@ public class GTree extends JPanel implements BusyListener {
|
|||
// ensure we operate on the model node which will always have the given child not the view
|
||||
// node, which may have its child filtered
|
||||
GTreeNode modelParent = getModelNode(parent);
|
||||
if (modelParent == null) {
|
||||
Msg.error(this, "Attempted to show a node with an invalid parent.\n\tParent: " +
|
||||
parent + "\n\tchild: " + childName);
|
||||
return;
|
||||
}
|
||||
getModelNode(modelParent, childName, newModelChild -> {
|
||||
// force the filter to accept the new node
|
||||
ignoreFilter(newModelChild);
|
||||
|
@ -1324,6 +1339,10 @@ public class GTree extends JPanel implements BusyListener {
|
|||
tree.stopEditing();
|
||||
}
|
||||
|
||||
public void cancelEditing() {
|
||||
tree.cancelEditing();
|
||||
}
|
||||
|
||||
public void setNodeEditable(GTreeNode child) {
|
||||
// for now only subclasses of GTree will set a node editable.
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,22 +17,56 @@ package docking.widgets.tree.support;
|
|||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultTreeCellEditor;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.UndoRedoKeeper;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
|
||||
public class GTreeCellEditor extends DefaultTreeCellEditor {
|
||||
|
||||
private UndoRedoKeeper undoRedoKeeper;
|
||||
|
||||
public GTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
|
||||
super(tree, renderer);
|
||||
|
||||
if (realEditor instanceof DefaultCellEditor) {
|
||||
Component c = ((DefaultCellEditor) realEditor).getComponent();
|
||||
if (c instanceof JTextField) {
|
||||
JTextField tf = (JTextField) c;
|
||||
undoRedoKeeper = DockingUtils.installUndoRedo(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellEditorComponent(JTree jTree, Object value,
|
||||
boolean isSelected, boolean expanded, boolean leaf, int row) {
|
||||
public boolean stopCellEditing() {
|
||||
if (super.stopCellEditing()) {
|
||||
clearUndoRedo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode)value;
|
||||
@Override
|
||||
public void cancelCellEditing() {
|
||||
super.cancelCellEditing();
|
||||
clearUndoRedo();
|
||||
}
|
||||
|
||||
private void clearUndoRedo() {
|
||||
if (undoRedoKeeper != null) {
|
||||
undoRedoKeeper.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellEditorComponent(JTree jTree, Object value, boolean isSelected,
|
||||
boolean expanded, boolean leaf, int row) {
|
||||
|
||||
GTreeNode node = (GTreeNode) value;
|
||||
if (node.isLeaf()) {
|
||||
renderer.setLeafIcon(node.getIcon(expanded));
|
||||
}
|
||||
|
@ -41,8 +74,8 @@ public class GTreeCellEditor extends DefaultTreeCellEditor {
|
|||
renderer.setOpenIcon(node.getIcon(true));
|
||||
renderer.setClosedIcon(node.getIcon(false));
|
||||
}
|
||||
return super.getTreeCellEditorComponent(jTree, value, isSelected, expanded,
|
||||
leaf, row);
|
||||
|
||||
return super.getTreeCellEditorComponent(jTree, value, isSelected, expanded, leaf, row);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue