mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5761 - Data Type Manager - Updated the Collapse button to stop any post-filter state restoring
This commit is contained in:
parent
c03417b3f6
commit
b07256a114
4 changed files with 63 additions and 18 deletions
|
@ -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.
|
||||||
|
@ -55,8 +55,9 @@ public class ClearFilterLabel extends GIconLabel {
|
||||||
|
|
||||||
this.textField = textField;
|
this.textField = textField;
|
||||||
|
|
||||||
// pad some to offset from the edge of the text field
|
// pad some to offset from the edge of the text field; the border width is a bit larger to
|
||||||
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
// make it easier to hover over this label. Values were picked through trial-and-error.
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(2, 6, 4, 2));
|
||||||
|
|
||||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
|
||||||
|
@ -84,8 +85,12 @@ public class ClearFilterLabel extends GIconLabel {
|
||||||
});
|
});
|
||||||
|
|
||||||
addMouseListener(new MouseAdapter() {
|
addMouseListener(new MouseAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
// Clear on released instead of clicked to allow for slight movement between the
|
||||||
|
// press and release. If the user moves the most while holding down, the drag
|
||||||
|
// prevents the clicked callback.
|
||||||
clearFilter();
|
clearFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,9 +204,16 @@ public class ClearFilterLabel extends GIconLabel {
|
||||||
Insets textInsets = textField.getInsets();
|
Insets textInsets = textField.getInsets();
|
||||||
Point location = textBounds.getLocation();
|
Point location = textBounds.getLocation();
|
||||||
|
|
||||||
Dimension size = getPreferredSize();
|
// For our placement, use the icon size and some padding to keep the icon visually off of
|
||||||
int half = (textBounds.height - size.height) / 2;
|
// the edges of the text field. (We do not want to use our actual preferred size, as we
|
||||||
int y = textBounds.y + half;
|
// have made our size larger than this padding so it is easier to click us.)
|
||||||
|
int iconHeight = ICON.getIconHeight();
|
||||||
|
int iconWidth = ICON.getIconWidth();
|
||||||
|
int padding = 4;
|
||||||
|
|
||||||
|
Dimension size = new Dimension(iconWidth + padding, iconHeight + padding);
|
||||||
|
int halfHeight = (textBounds.height - size.height) / 2;
|
||||||
|
int y = textBounds.y + halfHeight;
|
||||||
|
|
||||||
int end = location.x + textBounds.width;
|
int end = location.x + textBounds.width;
|
||||||
int x = end - textInsets.right - size.width;
|
int x = end - textInsets.right - size.width;
|
||||||
|
@ -209,7 +221,8 @@ public class ClearFilterLabel extends GIconLabel {
|
||||||
// hide when text is near
|
// hide when text is near
|
||||||
checkForTouchyText(x);
|
checkForTouchyText(x);
|
||||||
|
|
||||||
setBounds(x, y, size.width, size.height);
|
Dimension preferredSize = getPreferredSize();
|
||||||
|
setBounds(x, y, preferredSize.width, preferredSize.height);
|
||||||
|
|
||||||
myParent.validate();
|
myParent.validate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,13 +468,22 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
public void collapseAll(GTreeNode node) {
|
public void collapseAll(GTreeNode node) {
|
||||||
|
|
||||||
runSwingNow(() -> {
|
runSwingNow(() -> {
|
||||||
|
|
||||||
|
if (!isFiltered() && lastFilterTask != null) {
|
||||||
|
// When the user clears the filter, the filter task may be running to restore state.
|
||||||
|
// If the user wishes to collapse nodes, it does not make sense to keep restoring
|
||||||
|
// expanded/selected state. This call allows users to cancel any long running tree
|
||||||
|
// state restoring by executing a collapse action.
|
||||||
|
lastFilterTask.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
node.fireNodeStructureChanged();
|
node.fireNodeStructureChanged();
|
||||||
tree.collapsePath(node.getTreePath());
|
tree.collapsePath(node.getTreePath());
|
||||||
|
|
||||||
boolean nodeIsRoot = node.equals(model.getRoot());
|
boolean nodeIsRoot = node.equals(model.getRoot());
|
||||||
|
|
||||||
if (nodeIsRoot && !tree.isRootAllowedToCollapse()) {
|
if (nodeIsRoot && !tree.isRootAllowedToCollapse()) {
|
||||||
runTask(new GTreeExpandNodeToDepthTask(this, getJTree(), node, 1));
|
runTask(new GTreeExpandNodeToDepthTask(this, node, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1686,7 +1695,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
if (model != null && model.getRoot() != null) {
|
if (model != null && model.getRoot() != null) {
|
||||||
runTask(new GTreeExpandNodeToDepthTask(GTree.this, getJTree(),
|
runTask(new GTreeExpandNodeToDepthTask(GTree.this,
|
||||||
model.getModelRoot(), 1));
|
model.getModelRoot(), 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
import docking.widgets.tree.support.GTreeFilter;
|
||||||
import docking.widgets.tree.tasks.GTreeClearTreeFilterTask;
|
import docking.widgets.tree.tasks.GTreeClearTreeFilterTask;
|
||||||
import docking.widgets.tree.tasks.GTreeExpandAllTask;
|
import docking.widgets.tree.tasks.GTreeExpandAllTask;
|
||||||
|
@ -80,10 +85,30 @@ public class GTreeFilterTask extends GTreeTask {
|
||||||
private void restoreInSameTask(TaskMonitor monitor) {
|
private void restoreInSameTask(TaskMonitor monitor) {
|
||||||
|
|
||||||
GTreeState state = tree.getFilterRestoreState();
|
GTreeState state = tree.getFilterRestoreState();
|
||||||
|
if (isOnlyRootSelected(state)) {
|
||||||
|
// This is a special case that allows the user to signal to not restore the tree state
|
||||||
|
// when the filter is cleared. The tree will normally restore the state to either 1)
|
||||||
|
// the state prior to the filter, or 2) the state the user chose when filtered by
|
||||||
|
// selecting one or more nodes. If the user selects the root, we will use that as a
|
||||||
|
// signal from the user to say they do not want any state to be restored when the filter
|
||||||
|
// is cleared.
|
||||||
|
return;
|
||||||
|
}
|
||||||
GTreeRestoreTreeStateTask restoreTask = new GTreeRestoreTreeStateTask(tree, state);
|
GTreeRestoreTreeStateTask restoreTask = new GTreeRestoreTreeStateTask(tree, state);
|
||||||
restoreTask.run(monitor);
|
restoreTask.run(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isOnlyRootSelected(GTreeState state) {
|
||||||
|
List<TreePath> paths = state.getSelectedPaths();
|
||||||
|
if (paths.size() == 1) {
|
||||||
|
TreePath path = paths.get(0);
|
||||||
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
|
GTreeNode viewRoot = tree.getViewRoot();
|
||||||
|
return Objects.equals(node, viewRoot);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
cancelledProgramatically = true;
|
cancelledProgramatically = true;
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -29,12 +29,10 @@ import ghidra.util.task.TaskMonitor;
|
||||||
public class GTreeExpandNodeToDepthTask extends GTreeTask {
|
public class GTreeExpandNodeToDepthTask extends GTreeTask {
|
||||||
|
|
||||||
private final TreePath[] paths;
|
private final TreePath[] paths;
|
||||||
private final JTree jTree;
|
|
||||||
private final int depth;
|
private final int depth;
|
||||||
|
|
||||||
public GTreeExpandNodeToDepthTask(GTree gTree, JTree jTree, GTreeNode node, int depth) {
|
public GTreeExpandNodeToDepthTask(GTree gTree, GTreeNode node, int depth) {
|
||||||
super(gTree);
|
super(gTree);
|
||||||
this.jTree = jTree;
|
|
||||||
this.paths = new TreePath[] { node.getTreePath() };
|
this.paths = new TreePath[] { node.getTreePath() };
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue