mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-07-07 19:49:54 +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.program.util.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class SymbolTreeProvider extends ComponentProviderAdapter {
|
public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
|
@ -76,7 +75,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
*/
|
*/
|
||||||
private List<GTreeTask> bufferedTasks = new ArrayList<>();
|
private List<GTreeTask> bufferedTasks = new ArrayList<>();
|
||||||
private SwingUpdateManager domainChangeUpdateManager = new SwingUpdateManager(1000,
|
private SwingUpdateManager domainChangeUpdateManager = new SwingUpdateManager(1000,
|
||||||
SwingUpdateManager.DEFAULT_MAX_DELAY, "Symbol Tree Provider", () -> {
|
AbstractSwingUpdateManager.DEFAULT_MAX_DELAY, "Symbol Tree Provider", () -> {
|
||||||
|
|
||||||
if (bufferedTasks.isEmpty()) {
|
if (bufferedTasks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -167,7 +166,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
|
||||||
// This code serves to perform navigation in the case that the selection handler
|
// This code serves to perform navigation in the case that the selection handler
|
||||||
// above does not, as is the case when the node is already selected. This code
|
// above does not, as is the case when the node is already selected. This code
|
||||||
// will get called on the mouse release, whereas the selection handler gets called
|
// will get called on the mouse release, whereas the selection handler gets called
|
||||||
// on the mouse pressed.
|
// on the mouse pressed.
|
||||||
// For now, just attempt to perform the goto. It may get called twice, but this
|
// For now, just attempt to perform the goto. It may get called twice, but this
|
||||||
|
@ -230,12 +229,12 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
String createGroup = "0Create";
|
String createGroup = "0Create";
|
||||||
int createGroupIndex = 0;
|
int createGroupIndex = 0;
|
||||||
DockingAction createNamespaceAction = new CreateNamespaceAction(plugin, createGroup,
|
DockingAction createNamespaceAction =
|
||||||
Integer.toString(createGroupIndex++));
|
new CreateNamespaceAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||||
DockingAction createClassAction = new CreateClassAction(plugin, createGroup,
|
DockingAction createClassAction =
|
||||||
Integer.toString(createGroupIndex++));
|
new CreateClassAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||||
DockingAction convertToClassAction = new ConvertToClassAction(plugin, createGroup,
|
DockingAction convertToClassAction =
|
||||||
Integer.toString(createGroupIndex++));
|
new ConvertToClassAction(plugin, createGroup, Integer.toString(createGroupIndex++));
|
||||||
|
|
||||||
DockingAction renameAction = new RenameAction(plugin);
|
DockingAction renameAction = new RenameAction(plugin);
|
||||||
DockingAction cutAction = new CutAction(plugin, this);
|
DockingAction cutAction = new CutAction(plugin, this);
|
||||||
|
@ -414,6 +413,11 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rebuildTree() {
|
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();
|
SymbolTreeRootNode node = (SymbolTreeRootNode) tree.getModelRoot();
|
||||||
node.setChildren(null);
|
node.setChildren(null);
|
||||||
tree.refilterLater();
|
tree.refilterLater();
|
||||||
|
@ -434,9 +438,8 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
private void addTask(GTreeTask task) {
|
private void addTask(GTreeTask task) {
|
||||||
// Note: if we want to call this method from off the Swing thread, then we have to
|
// 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.
|
// synchronize on the list that we are adding to here.
|
||||||
Swing.assertSwingThread(
|
Swing.assertSwingThread("Adding tasks must be done on the Swing thread," +
|
||||||
"Adding tasks must be done on the Swing thread," +
|
"since they are put into a list that is processed on the Swing thread. ");
|
||||||
"since they are put into a list that is processed on the Swing thread. ");
|
|
||||||
|
|
||||||
bufferedTasks.add(task);
|
bufferedTasks.add(task);
|
||||||
domainChangeUpdateManager.update();
|
domainChangeUpdateManager.update();
|
||||||
|
|
|
@ -19,21 +19,35 @@ import java.awt.Component;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.UndoRedoKeeper;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
|
||||||
class SymbolEditor extends DefaultCellEditor {
|
class SymbolEditor extends DefaultCellEditor {
|
||||||
|
|
||||||
private JTextField symbolField = null;
|
private JTextField symbolField = null;
|
||||||
|
private UndoRedoKeeper undoRedoKeeper;
|
||||||
|
|
||||||
SymbolEditor() {
|
SymbolEditor() {
|
||||||
super(new JTextField());
|
super(new JTextField());
|
||||||
symbolField = (JTextField) super.getComponent();
|
symbolField = (JTextField) super.getComponent();
|
||||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||||
|
undoRedoKeeper = DockingUtils.installUndoRedo(symbolField);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCellEditorValue() {
|
public boolean stopCellEditing() {
|
||||||
return symbolField.getText().trim();
|
if (super.stopCellEditing()) {
|
||||||
|
undoRedoKeeper.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelCellEditing() {
|
||||||
|
super.cancelCellEditing();
|
||||||
|
undoRedoKeeper.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -57,11 +57,11 @@ import resources.ResourceManager;
|
||||||
* When using a UI component that is HTML enabled, care must be used when constructing the text
|
* When using a UI component that is HTML enabled, care must be used when constructing the text
|
||||||
* that is being rendered.
|
* that is being rendered.
|
||||||
* <p>
|
* <p>
|
||||||
* During string-building or concatenation, appending a non-literal string value (ie.
|
* During string-building or concatenation, appending a non-literal string value (ie.
|
||||||
* {@code "Hello " + getFoo();} ), the non-literal string value should be escaped using
|
* {@code "Hello " + getFoo();} ), the non-literal string value should be escaped using
|
||||||
* {@link HTMLUtilities#escapeHTML(String)} (ie. {@code "Hello " + HTMLUtilities.escapeHTML(getFoo());}.
|
* {@link HTMLUtilities#escapeHTML(String)} (ie. {@code "Hello " + HTMLUtilities.escapeHTML(getFoo());}.
|
||||||
* <p>
|
* <p>
|
||||||
* Of course, there are exceptions to every rule, and if the string value can be definitely be
|
* Of course, there are exceptions to every rule, and if the string value can be definitely be
|
||||||
* traced to its source and there are no user-supplied origins, the HTML escaping can be skipped.
|
* traced to its source and there are no user-supplied origins, the HTML escaping can be skipped.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: just using a UI component that is HTML enabled does not mean that it will treat its
|
* Note: just using a UI component that is HTML enabled does not mean that it will treat its
|
||||||
|
@ -70,9 +70,9 @@ import resources.ResourceManager;
|
||||||
* If you fail to do this, the escaped substrings will look wrong because any '<' and '>' chars
|
* If you fail to do this, the escaped substrings will look wrong because any '<' and '>' chars
|
||||||
* (and others) in the substring will be mangled when rendered in plain-text mode.
|
* (and others) in the substring will be mangled when rendered in plain-text mode.
|
||||||
* <p>
|
* <p>
|
||||||
* When working with plain text, try to avoid allowing a user supplied string being the first
|
* When working with plain text, try to avoid allowing a user supplied string being the first
|
||||||
* value of text that could be fed to a UI component. This will prevent the possibly hostile
|
* value of text that could be fed to a UI component. This will prevent the possibly hostile
|
||||||
* string from having a leading HTML start tag.
|
* string from having a leading HTML start tag.
|
||||||
* (ie. when displaying an error to the user about a bad file, don't put the filename
|
* (ie. when displaying an error to the user about a bad file, don't put the filename
|
||||||
* value at the start of the string, but instead put a quote or some other delimiter to prevent
|
* value at the start of the string, but instead put a quote or some other delimiter to prevent
|
||||||
* html mode).
|
* html mode).
|
||||||
|
@ -101,10 +101,10 @@ public class DockingUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A version the control key modifiers that is based upon the pre-Java 9 {@link InputEvent}
|
* A version the control key modifiers that is based upon the pre-Java 9 {@link InputEvent}
|
||||||
* usage. This mask is here for those clients that cannot be upgraded, such as those with
|
* usage. This mask is here for those clients that cannot be upgraded, such as those with
|
||||||
* dependencies on 3rd-party libraries that still use the old mask style.
|
* dependencies on 3rd-party libraries that still use the old mask style.
|
||||||
*
|
*
|
||||||
* @deprecated use instead {@link #CONTROL_KEY_MODIFIER_MASK}
|
* @deprecated use instead {@link #CONTROL_KEY_MODIFIER_MASK}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final int CONTROL_KEY_MODIFIER_MASK_DEPRECATED =
|
public static final int CONTROL_KEY_MODIFIER_MASK_DEPRECATED =
|
||||||
|
@ -149,8 +149,8 @@ public class DockingUtils {
|
||||||
/**
|
/**
|
||||||
* Checks if the mouseEvent has the "control" key down. On windows, this is actually
|
* Checks if the mouseEvent has the "control" key down. On windows, this is actually
|
||||||
* the <code>control</code> key. On Mac, it is the <code>command</code> key.
|
* the <code>control</code> key. On Mac, it is the <code>command</code> key.
|
||||||
*
|
*
|
||||||
* @param mouseEvent the event to check
|
* @param mouseEvent the event to check
|
||||||
* @return true if the control key is pressed
|
* @return true if the control key is pressed
|
||||||
*/
|
*/
|
||||||
public static boolean isControlModifier(MouseEvent mouseEvent) {
|
public static boolean isControlModifier(MouseEvent mouseEvent) {
|
||||||
|
@ -162,8 +162,8 @@ public class DockingUtils {
|
||||||
/**
|
/**
|
||||||
* Checks if the mouseEvent has the "control" key down. On windows, this is actually
|
* Checks if the mouseEvent has the "control" key down. On windows, this is actually
|
||||||
* the <code>control</code> key. On Mac, it is the <code>command</code> key.
|
* the <code>control</code> key. On Mac, it is the <code>command</code> key.
|
||||||
*
|
*
|
||||||
* @param keyEvent the event to check
|
* @param keyEvent the event to check
|
||||||
* @return true if the control key is pressed
|
* @return true if the control key is pressed
|
||||||
*/
|
*/
|
||||||
public static boolean isControlModifier(KeyEvent keyEvent) {
|
public static boolean isControlModifier(KeyEvent keyEvent) {
|
||||||
|
@ -172,10 +172,18 @@ public class DockingUtils {
|
||||||
return (modifiers & osSpecificMask) == osSpecificMask;
|
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) {
|
public static UndoRedoKeeper installUndoRedo(JTextComponent textComponent) {
|
||||||
|
|
||||||
Document document = textComponent.getDocument();
|
Document document = textComponent.getDocument();
|
||||||
|
|
||||||
final UndoRedoKeeper undoRedoKeeper = new UndoRedoKeeper();
|
final UndoRedoKeeper undoRedoKeeper = new UndoRedoKeeper();
|
||||||
document.addUndoableEditListener(e -> {
|
document.addUndoableEditListener(e -> {
|
||||||
UndoableEdit edit = e.getEdit();
|
UndoableEdit edit = e.getEdit();
|
||||||
|
@ -189,9 +197,11 @@ public class DockingUtils {
|
||||||
KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
|
KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
|
||||||
if (REDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
if (REDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
||||||
undoRedoKeeper.redo();
|
undoRedoKeeper.redo();
|
||||||
|
e.consume(); // consume to prevent other listeners from processing a second time
|
||||||
}
|
}
|
||||||
else if (UNDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
else if (UNDO_KEYSTROKE.equals(keyStrokeForEvent)) {
|
||||||
undoRedoKeeper.undo();
|
undoRedoKeeper.undo();
|
||||||
|
e.consume(); // consume to prevent other listeners from processing a second time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -226,7 +236,7 @@ public class DockingUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform some operation on a component and all of its descendants, recursively
|
* Perform some operation on a component and all of its descendants, recursively
|
||||||
*
|
*
|
||||||
* This traverses the swing/awt component tree starting at the given container and descends
|
* This traverses the swing/awt component tree starting at the given container and descends
|
||||||
* recursively through all containers. Any time a component of type (or subclass of type) is
|
* recursively through all containers. Any time a component of type (or subclass of type) is
|
||||||
* found, the given callback is executed on it. If order is
|
* found, the given callback is executed on it. If order is
|
||||||
|
@ -234,7 +244,7 @@ public class DockingUtils {
|
||||||
* the children of a container before executing the callback on the container itself; if
|
* the children of a container before executing the callback on the container itself; if
|
||||||
* {@link TreeTraversalOrder#PARENT_FIRST}, then the traversal will execute the callback on the
|
* {@link TreeTraversalOrder#PARENT_FIRST}, then the traversal will execute the callback on the
|
||||||
* container before descending.
|
* container before descending.
|
||||||
*
|
*
|
||||||
* The callback must return one of three result values. In normal circumstances, it should
|
* The callback must return one of three result values. In normal circumstances, it should
|
||||||
* return {@link TreeTraversalResult#CONTINUE}, allowing traversal to continue to the next
|
* return {@link TreeTraversalResult#CONTINUE}, allowing traversal to continue to the next
|
||||||
* element. If the callback wishes to terminate traversal "successfully," e.g., because it
|
* element. If the callback wishes to terminate traversal "successfully," e.g., because it
|
||||||
|
@ -242,14 +252,14 @@ public class DockingUtils {
|
||||||
* {@link TreeTraversalResult#FINISH}. If an error occurs during traversal, then it should
|
* {@link TreeTraversalResult#FINISH}. If an error occurs during traversal, then it should
|
||||||
* either return {@link TreeTraversalResult#TERMINATE} or throw an appropriate exception to
|
* either return {@link TreeTraversalResult#TERMINATE} or throw an appropriate exception to
|
||||||
* terminate traversal "unsuccessfully."
|
* terminate traversal "unsuccessfully."
|
||||||
*
|
*
|
||||||
* This method will also return a value of {@link TreeTraversalResult} indicating how traversal
|
* This method will also return a value of {@link TreeTraversalResult} indicating how traversal
|
||||||
* terminated. If {@link TreeTraversalResult#CONTINUE}, then every element in the subtree was
|
* terminated. If {@link TreeTraversalResult#CONTINUE}, then every element in the subtree was
|
||||||
* visited, and traversal was successful. If {@link TreeTraversalResult#FINISH}, then some
|
* visited, and traversal was successful. If {@link TreeTraversalResult#FINISH}, then some
|
||||||
* elements may have been omitted, but traversal was still successful. If
|
* elements may have been omitted, but traversal was still successful. If
|
||||||
* {@link TreeTraversalResult#TERMINATE}, then some elements may have been omitted, and
|
* {@link TreeTraversalResult#TERMINATE}, then some elements may have been omitted, and
|
||||||
* traversal was not successful.
|
* traversal was not successful.
|
||||||
*
|
*
|
||||||
* @param start the "root" container of the subtree on which to operate
|
* @param start the "root" container of the subtree on which to operate
|
||||||
* @param type the type of components on which to operate
|
* @param type the type of components on which to operate
|
||||||
* @param order whether to operation on children or parents first
|
* @param order whether to operation on children or parents first
|
||||||
|
@ -288,9 +298,9 @@ public class DockingUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform some operation on a component and all of its descendents, recursively.
|
* Perform some operation on a component and all of its descendents, recursively.
|
||||||
*
|
*
|
||||||
* This applies the operation to all components in the tree, children first.
|
* This applies the operation to all components in the tree, children first.
|
||||||
*
|
*
|
||||||
* @param start the "root" container of the subtree on which to operate
|
* @param start the "root" container of the subtree on which to operate
|
||||||
* @param cb the callback to perform the actual operation
|
* @param cb the callback to perform the actual operation
|
||||||
* @return a result indicating whether or not traversal completed successfully
|
* @return a result indicating whether or not traversal completed successfully
|
||||||
|
@ -306,18 +316,18 @@ public class DockingUtils {
|
||||||
* to be painted.
|
* to be painted.
|
||||||
* <p>
|
* <p>
|
||||||
* <u>Notes</u>
|
* <u>Notes</u>
|
||||||
* Historically, to make a component transparent you would call
|
* Historically, to make a component transparent you would call
|
||||||
* {@link JComponent#setOpaque(boolean)} with a <code>false</code> value. However, it turns out
|
* {@link JComponent#setOpaque(boolean)} with a <code>false</code> value. However, it turns out
|
||||||
* that the definition and the implementation of this method are at odds. <code>setOpaque(false)</code>
|
* that the definition and the implementation of this method are at odds. <code>setOpaque(false)</code>
|
||||||
* is meant to signal that some part of the component is transparent, so the parent component
|
* is meant to signal that some part of the component is transparent, so the parent component
|
||||||
* needs to be painted. Most LaFs implemented this by not painting the background of the
|
* needs to be painted. Most LaFs implemented this by not painting the background of the
|
||||||
* component, but used the parent's color instead. The Nimbus LaF actually honors the
|
* component, but used the parent's color instead. The Nimbus LaF actually honors the
|
||||||
* contract of <code>setOpaque()</code>, which has the effect of painting the components
|
* contract of <code>setOpaque()</code>, which has the effect of painting the components
|
||||||
* background by default.
|
* background by default.
|
||||||
* <p>
|
* <p>
|
||||||
* This method allows components to achieve transparency when they used to
|
* This method allows components to achieve transparency when they used to
|
||||||
* rely on <code>setOpaque(false)</code>.
|
* rely on <code>setOpaque(false)</code>.
|
||||||
*
|
*
|
||||||
* @param c the component to be made transparent
|
* @param c the component to be made transparent
|
||||||
*/
|
*/
|
||||||
public static void setTransparent(JComponent c) {
|
public static void setTransparent(JComponent c) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import javax.swing.*;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||||
import ghidra.util.datastruct.WeakSet;
|
import ghidra.util.datastruct.WeakSet;
|
||||||
|
@ -63,10 +64,10 @@ public class FilterTextField extends JPanel {
|
||||||
private WeakSet<Callback> enterListeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
private WeakSet<Callback> enterListeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this text field with the given component. <code>component</code> may be null, but
|
* Constructs this text field with the given component. <code>component</code> may be null, but
|
||||||
* then this field will be unable to flash in response to focus events (see the header
|
* then this field will be unable to flash in response to focus events (see the header
|
||||||
* documentation).
|
* documentation).
|
||||||
*
|
*
|
||||||
* @param component The component needed to listen for focus changes, may be null.
|
* @param component The component needed to listen for focus changes, may be null.
|
||||||
*/
|
*/
|
||||||
public FilterTextField(Component component) {
|
public FilterTextField(Component component) {
|
||||||
|
@ -74,7 +75,7 @@ public class FilterTextField extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this text field with the given component and the preferred visible column
|
* Constructs this text field with the given component and the preferred visible column
|
||||||
* width. <code>component</code> may be null, but then this field will be able to flash in
|
* width. <code>component</code> may be null, but then this field will be able to flash in
|
||||||
* response to focus events (see the header documentation).
|
* response to focus events (see the header documentation).
|
||||||
* @param component The component needed to listen for focus changes, may be null.
|
* @param component The component needed to listen for focus changes, may be null.
|
||||||
|
@ -120,6 +121,8 @@ public class FilterTextField extends JPanel {
|
||||||
});
|
});
|
||||||
|
|
||||||
add(layeredPane, BorderLayout.NORTH);
|
add(layeredPane, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
DockingUtils.installUndoRedo(textField);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyEnterPressed() {
|
private void notifyEnterPressed() {
|
||||||
|
@ -158,11 +161,11 @@ public class FilterTextField extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will signal to the users if a filter is currently applied (has text). For
|
* This method will signal to the users if a filter is currently applied (has text). For
|
||||||
* example, the default implementation will 'flash' the filter by changing its background
|
* example, the default implementation will 'flash' the filter by changing its background
|
||||||
* color multiple times.
|
* color multiple times.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: this method will not perform the alert if the minimum time between alerts
|
* Note: this method will not perform the alert if the minimum time between alerts
|
||||||
* has not passed. To force the alter to take place, call {@link #alert(boolean)} with a
|
* has not passed. To force the alter to take place, call {@link #alert(boolean)} with a
|
||||||
* value of <code>true</code>.
|
* value of <code>true</code>.
|
||||||
*/
|
*/
|
||||||
|
@ -241,11 +244,11 @@ public class FilterTextField extends JPanel {
|
||||||
/**
|
/**
|
||||||
* Adds the listener to this filter field that will be called when the user presses the
|
* Adds the listener to this filter field that will be called when the user presses the
|
||||||
* enter key.
|
* enter key.
|
||||||
*
|
*
|
||||||
* <P>Note: this listener cannot be anonymous, as the underlying storage mechanism may be
|
* <P>Note: this listener cannot be anonymous, as the underlying storage mechanism may be
|
||||||
* using a weak data structure. This means that you will need to store the listener in
|
* using a weak data structure. This means that you will need to store the listener in
|
||||||
* a field inside of your class.
|
* a field inside of your class.
|
||||||
*
|
*
|
||||||
* @param callback the listener
|
* @param callback the listener
|
||||||
*/
|
*/
|
||||||
public void addEnterListener(Callback callback) {
|
public void addEnterListener(Callback callback) {
|
||||||
|
@ -257,13 +260,13 @@ public class FilterTextField extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the filter listener to this filter field that will be called when the filter
|
* Adds the filter listener to this filter field that will be called when the filter
|
||||||
* contents change.
|
* contents change.
|
||||||
*
|
*
|
||||||
* <P>Note: this listener cannot be anonymous, as the underlying storage mechanism may be
|
* <P>Note: this listener cannot be anonymous, as the underlying storage mechanism may be
|
||||||
* using a weak data structure. This means that you will need to store the listener in
|
* using a weak data structure. This means that you will need to store the listener in
|
||||||
* a field inside of your class.
|
* a field inside of your class.
|
||||||
*
|
*
|
||||||
* @param l the listener
|
* @param l the listener
|
||||||
*/
|
*/
|
||||||
public void addFilterListener(FilterListener l) {
|
public void addFilterListener(FilterListener l) {
|
||||||
|
@ -298,7 +301,7 @@ public class FilterTextField extends JPanel {
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Package Methods (these make testing easier)
|
// Package Methods (these make testing easier)
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/*package*/ void doSetBackground(Color c) {
|
/*package*/ void doSetBackground(Color c) {
|
||||||
textField.setBackground(c);
|
textField.setBackground(c);
|
||||||
|
@ -352,7 +355,7 @@ public class FilterTextField extends JPanel {
|
||||||
|
|
||||||
private void updateFilterButton(boolean showFilter) {
|
private void updateFilterButton(boolean showFilter) {
|
||||||
|
|
||||||
// Note: this must be run on the Swing thread. When the filter button shows itself,
|
// Note: this must be run on the Swing thread. When the filter button shows itself,
|
||||||
// it requires an AWT lock. If called from a non-Swing thread, deadlocks!
|
// it requires an AWT lock. If called from a non-Swing thread, deadlocks!
|
||||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||||
if (showFilter) {
|
if (showFilter) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 javax.swing.*;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.UndoRedoKeeper;
|
||||||
|
|
||||||
public class GTableTextCellEditor extends DefaultCellEditor {
|
public class GTableTextCellEditor extends DefaultCellEditor {
|
||||||
private static final Object TABLE_FOCUS_CELL_HIGHLIGHT_BORDER =
|
private static final Object TABLE_FOCUS_CELL_HIGHLIGHT_BORDER =
|
||||||
"Table.focusCellHighlightBorder";
|
"Table.focusCellHighlightBorder";
|
||||||
|
|
||||||
|
private UndoRedoKeeper undoRedoKeeper;
|
||||||
|
|
||||||
public GTableTextCellEditor(JTextField textField) {
|
public GTableTextCellEditor(JTextField textField) {
|
||||||
super(textField);
|
super(textField);
|
||||||
setClickCountToStart(2);
|
setClickCountToStart(2);
|
||||||
|
|
||||||
textField.setBorder(UIManager.getBorder(TABLE_FOCUS_CELL_HIGHLIGHT_BORDER));
|
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;
|
return saveState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns an HTML representation of this constraint set in a tabular form. It will be used
|
* 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()}
|
* inside the HTML representation of the entire filter. See {@link ColumnBasedTableFilter#getHtmlRepresentation()}
|
||||||
* for a description of the table format.
|
* 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.
|
* This expects the UI to have been constructed.
|
||||||
*
|
*
|
||||||
* @see #getValue()
|
* @see #getValue()
|
||||||
* @return
|
* @return the value
|
||||||
*/
|
*/
|
||||||
protected abstract ColumnConstraint<T> getValueFromComponent();
|
protected abstract ColumnConstraint<T> getValueFromComponent();
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
import docking.widgets.DropDownTextField;
|
import docking.widgets.DropDownTextField;
|
||||||
import docking.widgets.DropDownTextFieldDataModel;
|
import docking.widgets.DropDownTextFieldDataModel;
|
||||||
import docking.widgets.list.GListCellRenderer;
|
import docking.widgets.list.GListCellRenderer;
|
||||||
|
@ -61,6 +62,7 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
||||||
textField = new DropDownTextField<>(autocompleter, 100);
|
textField = new DropDownTextField<>(autocompleter, 100);
|
||||||
textField.setIgnoreEnterKeyPress(true);
|
textField.setIgnoreEnterKeyPress(true);
|
||||||
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
||||||
|
DockingUtils.installUndoRedo(textField);
|
||||||
panel.add(textField, BorderLayout.NORTH);
|
panel.add(textField, BorderLayout.NORTH);
|
||||||
textField.addActionListener(e -> textField.closeDropDownWindow());
|
textField.addActionListener(e -> textField.closeDropDownWindow());
|
||||||
|
|
||||||
|
@ -139,9 +141,8 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
searchText = searchText.trim();
|
searchText = searchText.trim();
|
||||||
lastConstraint =
|
lastConstraint = (StringColumnConstraint) currentConstraint
|
||||||
(StringColumnConstraint) currentConstraint.parseConstraintValue(searchText,
|
.parseConstraintValue(searchText, columnDataSource.getTableDataSource());
|
||||||
columnDataSource.getTableDataSource());
|
|
||||||
|
|
||||||
// Use a Collator to support languages other than English.
|
// Use a Collator to support languages other than English.
|
||||||
Collator collator = Collator.getInstance();
|
Collator collator = Collator.getInstance();
|
||||||
|
@ -222,8 +223,7 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE
|
||||||
// escape all unescaped '\' and '$' chars, as Match.appendReplacement() will treat
|
// escape all unescaped '\' and '$' chars, as Match.appendReplacement() will treat
|
||||||
// them as regex characters
|
// them as regex characters
|
||||||
String quoted = Matcher.quoteReplacement(group);
|
String quoted = Matcher.quoteReplacement(group);
|
||||||
String replacement =
|
String replacement = HTMLUtilities.colorString(color, HTMLUtilities.bold(quoted));
|
||||||
HTMLUtilities.colorString(color, HTMLUtilities.bold(quoted));
|
|
||||||
matcher.appendReplacement(sb, replacement);
|
matcher.appendReplacement(sb, replacement);
|
||||||
}
|
}
|
||||||
matcher.appendTail(sb);
|
matcher.appendTail(sb);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
import docking.widgets.label.GDHtmlLabel;
|
import docking.widgets.label.GDHtmlLabel;
|
||||||
import docking.widgets.table.constraint.ColumnConstraint;
|
import docking.widgets.table.constraint.ColumnConstraint;
|
||||||
import docking.widgets.table.constraint.StringColumnConstraint;
|
import docking.widgets.table.constraint.StringColumnConstraint;
|
||||||
|
@ -50,6 +51,8 @@ public class StringConstraintEditor extends AbstractColumnConstraintEditor<Strin
|
||||||
textField = new JTextField();
|
textField = new JTextField();
|
||||||
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
textField.getDocument().addUndoableEditListener(e -> valueChanged());
|
||||||
|
|
||||||
|
DockingUtils.installUndoRedo(textField);
|
||||||
|
|
||||||
panel.add(textField, BorderLayout.CENTER);
|
panel.add(textField, BorderLayout.CENTER);
|
||||||
|
|
||||||
infoLabel = new GDHtmlLabel("abc"); // temporary text in the label so that it sizes properly
|
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
|
@Override
|
||||||
protected void updateInfoMessage(boolean isValid) {
|
protected void updateInfoMessage(boolean isValid) {
|
||||||
// uses   to presever the labels height.
|
// uses   to preserve the label's height
|
||||||
String status = formatStatus(isValid ? " " : errorMessage, true);
|
String status = formatStatus(isValid ? " " : errorMessage, true);
|
||||||
infoLabel.setText(status);
|
infoLabel.setText(status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.USER_GENERATED;
|
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
|
||||||
import static ghidra.util.SystemUtilities.runSwingNow;
|
import static ghidra.util.SystemUtilities.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.dnd.Autoscroll;
|
import java.awt.dnd.Autoscroll;
|
||||||
|
@ -186,7 +186,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns tree event notifications on/off
|
* Turns tree event notifications on/off
|
||||||
*
|
*
|
||||||
* @param b true to enable events, false to disable events
|
* @param b true to enable events, false to disable events
|
||||||
*/
|
*/
|
||||||
public void setEventsEnabled(boolean b) {
|
public void setEventsEnabled(boolean b) {
|
||||||
|
@ -320,7 +320,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Note: </b>See the usage note at the header of this class concerning how tree state is used
|
* <b>Note: </b>See the usage note at the header of this class concerning how tree state is used
|
||||||
* relative to the <code>equals()</code> method.
|
* relative to the <code>equals()</code> method.
|
||||||
*
|
*
|
||||||
* @return the saved state
|
* @return the saved state
|
||||||
*/
|
*/
|
||||||
public GTreeState getTreeState() {
|
public GTreeState getTreeState() {
|
||||||
|
@ -538,7 +538,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the model for this tree
|
* Returns the model for this tree
|
||||||
*
|
*
|
||||||
* @return the model for this tree
|
* @return the model for this tree
|
||||||
*/
|
*/
|
||||||
public GTreeModel getModel() {
|
public GTreeModel getModel() {
|
||||||
|
@ -554,7 +554,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current viewport position of the scrollable tree.
|
* Returns the current viewport position of the scrollable tree.
|
||||||
*
|
*
|
||||||
* @return the current viewport position of the scrollable tree.
|
* @return the current viewport position of the scrollable tree.
|
||||||
*/
|
*/
|
||||||
public Point getViewPosition() {
|
public Point getViewPosition() {
|
||||||
|
@ -825,7 +825,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* and always contains all the nodes regardless of any filter being applied. If a filter is
|
* and always contains all the nodes regardless of any filter being applied. If a filter is
|
||||||
* applied to the tree, then this is not the actual root node being displayed by the
|
* applied to the tree, then this is not the actual root node being displayed by the
|
||||||
* {@link JTree}.
|
* {@link JTree}.
|
||||||
*
|
*
|
||||||
* @return the root node as provided by the client.
|
* @return the root node as provided by the client.
|
||||||
*/
|
*/
|
||||||
public GTreeNode getModelRoot() {
|
public GTreeNode getModelRoot() {
|
||||||
|
@ -837,7 +837,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* are no filters applied, then this will be the same as the model root (See
|
* are no filters applied, then this will be the same as the model root (See
|
||||||
* {@link #getModelRoot()}). If a filter is applied, then this will be a clone of the model root
|
* {@link #getModelRoot()}). If a filter is applied, then this will be a clone of the model root
|
||||||
* that contains clones of all nodes matching the filter.
|
* that contains clones of all nodes matching the filter.
|
||||||
*
|
*
|
||||||
* @return the root node currently being display by the {@link JTree}
|
* @return the root node currently being display by the {@link JTree}
|
||||||
*/
|
*/
|
||||||
public GTreeNode getViewRoot() {
|
public GTreeNode getViewRoot() {
|
||||||
|
@ -846,7 +846,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is useful for debugging tree problems. Don't know where else to put it.
|
* This method is useful for debugging tree problems. Don't know where else to put it.
|
||||||
*
|
*
|
||||||
* @param out the output writer
|
* @param out the output writer
|
||||||
* @param name use this to indicate what tree event occurred ("node inserted" "node removed",
|
* @param name use this to indicate what tree event occurred ("node inserted" "node removed",
|
||||||
* etc.)
|
* etc.)
|
||||||
|
@ -1009,6 +1009,11 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
// Waits for the given model node, passing it to the consumer when available
|
// Waits for the given model node, passing it to the consumer when available
|
||||||
private void getModelNode(GTreeNode parent, String childName, Consumer<GTreeNode> consumer) {
|
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;
|
int expireMs = 3000;
|
||||||
Supplier<GTreeNode> supplier = () -> {
|
Supplier<GTreeNode> supplier = () -> {
|
||||||
GTreeNode modelParent = getModelNode(parent);
|
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
|
// Waits for the given view node, passing it to the consumer when available
|
||||||
private void getViewNode(GTreeNode parent, String childName, Consumer<GTreeNode> consumer) {
|
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;
|
int expireMs = 3000;
|
||||||
Supplier<GTreeNode> supplier = () -> {
|
Supplier<GTreeNode> supplier = () -> {
|
||||||
GTreeNode viewParent = getViewNode(parent);
|
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
|
// ensure we operate on the model node which will always have the given child not the view
|
||||||
// node, which may have its child filtered
|
// node, which may have its child filtered
|
||||||
GTreeNode modelParent = getModelNode(parent);
|
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 -> {
|
getModelNode(modelParent, childName, newModelChild -> {
|
||||||
// force the filter to accept the new node
|
// force the filter to accept the new node
|
||||||
ignoreFilter(newModelChild);
|
ignoreFilter(newModelChild);
|
||||||
|
@ -1252,7 +1267,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
/**
|
/**
|
||||||
* Re-filters the tree if the newNode should be included in the current filter results. If the
|
* Re-filters the tree if the newNode should be included in the current filter results. If the
|
||||||
* new node doesn't match the filter, there is no need to refilter the tree.
|
* new node doesn't match the filter, there is no need to refilter the tree.
|
||||||
*
|
*
|
||||||
* @param newNode the node that may cause the tree to refilter.
|
* @param newNode the node that may cause the tree to refilter.
|
||||||
*/
|
*/
|
||||||
public void refilterLater(GTreeNode newNode) {
|
public void refilterLater(GTreeNode newNode) {
|
||||||
|
@ -1300,7 +1315,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* Used to run simple GTree tasks that can be expressed as a {@link MonitoredRunnable} (or a
|
* Used to run simple GTree tasks that can be expressed as a {@link MonitoredRunnable} (or a
|
||||||
* lambda taking a {@link TaskMonitor}).
|
* lambda taking a {@link TaskMonitor}).
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param runnableTask {@link TaskMonitor} to watch and update with progress.
|
* @param runnableTask {@link TaskMonitor} to watch and update with progress.
|
||||||
*/
|
*/
|
||||||
public void runTask(MonitoredRunnable runnableTask) {
|
public void runTask(MonitoredRunnable runnableTask) {
|
||||||
|
@ -1324,6 +1339,10 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
tree.stopEditing();
|
tree.stopEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cancelEditing() {
|
||||||
|
tree.cancelEditing();
|
||||||
|
}
|
||||||
|
|
||||||
public void setNodeEditable(GTreeNode child) {
|
public void setNodeEditable(GTreeNode child) {
|
||||||
// for now only subclasses of GTree will set a node editable.
|
// for now only subclasses of GTree will set a node editable.
|
||||||
}
|
}
|
||||||
|
@ -1522,7 +1541,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* Calling setSelectedPaths on GTree queues the selection for after any currently scheduled
|
* Calling setSelectedPaths on GTree queues the selection for after any currently scheduled
|
||||||
* tasks. This method sets the selected path immediately and does not wait for for scheduled
|
* tasks. This method sets the selected path immediately and does not wait for for scheduled
|
||||||
* tasks.
|
* tasks.
|
||||||
*
|
*
|
||||||
* @param path the path to select.
|
* @param path the path to select.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 java.awt.Component;
|
||||||
|
|
||||||
import javax.swing.JTree;
|
import javax.swing.*;
|
||||||
import javax.swing.tree.DefaultTreeCellEditor;
|
import javax.swing.tree.DefaultTreeCellEditor;
|
||||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.UndoRedoKeeper;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
|
||||||
public class GTreeCellEditor extends DefaultTreeCellEditor {
|
public class GTreeCellEditor extends DefaultTreeCellEditor {
|
||||||
|
|
||||||
|
private UndoRedoKeeper undoRedoKeeper;
|
||||||
|
|
||||||
public GTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
|
public GTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
|
||||||
super(tree, renderer);
|
super(tree, renderer);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getTreeCellEditorComponent(JTree jTree, Object value,
|
|
||||||
boolean isSelected, boolean expanded, boolean leaf, int row) {
|
|
||||||
|
|
||||||
GTreeNode node = (GTreeNode)value;
|
if (realEditor instanceof DefaultCellEditor) {
|
||||||
|
Component c = ((DefaultCellEditor) realEditor).getComponent();
|
||||||
|
if (c instanceof JTextField) {
|
||||||
|
JTextField tf = (JTextField) c;
|
||||||
|
undoRedoKeeper = DockingUtils.installUndoRedo(tf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopCellEditing() {
|
||||||
|
if (super.stopCellEditing()) {
|
||||||
|
clearUndoRedo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()) {
|
if (node.isLeaf()) {
|
||||||
renderer.setLeafIcon(node.getIcon(expanded));
|
renderer.setLeafIcon(node.getIcon(expanded));
|
||||||
}
|
}
|
||||||
|
@ -41,8 +74,8 @@ public class GTreeCellEditor extends DefaultTreeCellEditor {
|
||||||
renderer.setOpenIcon(node.getIcon(true));
|
renderer.setOpenIcon(node.getIcon(true));
|
||||||
renderer.setClosedIcon(node.getIcon(false));
|
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 New Issue
Block a user