diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index b6196bdbd6..6300f0e83f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -426,9 +426,18 @@ public class DataTypesProvider extends ComponentProviderAdapter { archiveGTree.addGTreeSelectionListener(e -> { TreePath path = e.getNewLeadSelectionPath(); - DataType dataType = getDataTypeFrom(path); + DataType selectedDt = getDataTypeFrom(path); + DataTypeUrl dtUrl = navigationHistory.getCurrentHistoryItem(); + if (dtUrl != null) { + DataType historyDt = dtUrl.getDataType(plugin); + if (Objects.equals(historyDt, selectedDt)) { + // Don't add an item to the history that is already there. This can happen when + // the user interacts with the navigation buttons. + return; + } + } - dataTypeSelected(e.getEventOrigin(), dataType); + dataTypeSelected(e.getEventOrigin(), selectedDt); }); buildPreviewPane(); @@ -783,8 +792,9 @@ public class DataTypesProvider extends ComponentProviderAdapter { return; } + TreePath treePath = dataTypeNode.getTreePath(); gTree.setSelectedNode(dataTypeNode); - gTree.scrollPathToVisible(dataTypeNode.getTreePath()); + gTree.scrollPathToVisible(treePath); contextChanged(); } diff --git a/Ghidra/Framework/Generic/src/main/java/util/HistoryList.java b/Ghidra/Framework/Generic/src/main/java/util/HistoryList.java index c1796c6255..a229422d9a 100644 --- a/Ghidra/Framework/Generic/src/main/java/util/HistoryList.java +++ b/Ghidra/Framework/Generic/src/main/java/util/HistoryList.java @@ -4,9 +4,9 @@ * 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. @@ -21,6 +21,7 @@ import java.util.function.Consumer; import org.apache.commons.lang3.StringUtils; +import ghidra.util.Msg; import ghidra.util.SystemUtilities; import ghidra.util.datastruct.FixedSizeStack; @@ -38,8 +39,10 @@ import ghidra.util.datastruct.FixedSizeStack; * item is cleared, then client is expected to call {@link #add(Object)} with value of * null. (This is safe to do, regardless of whether null are allowed). When nulls are allowed * and a null value is received, then current item is placed onto the history stack as the - * previous item. This way, when the user presses the back button, the last visible item - * will be activated. + * previous item, thus using the null value as a flag. After using null this way, when the user + * presses the back button, the last visible item will be activated. + * + *

If nulls are not allowed, then any calls to {@link #add(Object)} a null value will be ignored. * *

Note: when nulls are allowed, only a single null value will be stored. Further, * if new, non-null items are added, then the null value is dropped. @@ -191,8 +194,14 @@ public class HistoryList { return; } + doGoToIndex(historyIndex - 1); + } + + private void doGoToIndex(int index) { + historyIndex = index; T leaving = getCurrentHistoryItem(); - T t = historyStack.get(--historyIndex); + T t = historyStack.get(index); + dropNull(); broadcast(t, leaving); } @@ -204,9 +213,21 @@ public class HistoryList { * @param t the item */ public void goBackTo(T t) { - while (!getCurrentHistoryItem().equals(t) && hasPrevious()) { - goBack(); + int index = historyIndex - 1; // assume we want to ignore the current item + while (index >= 0) { + T other = historyStack.get(index); + if (Objects.equals(t, other)) { + break; + } + index--; } + + if (index < 0) { + Msg.error(this, "Item not in backward history: " + t); + return; + } + + doGoToIndex(index); } /** @@ -220,9 +241,7 @@ public class HistoryList { return; } - T leaving = getCurrentHistoryItem(); - T t = historyStack.get(++historyIndex); - broadcast(t, leaving); + doGoToIndex(historyIndex + 1); } /** @@ -232,9 +251,21 @@ public class HistoryList { * @param t the item */ public void goForwardTo(T t) { - while (!getCurrentHistoryItem().equals(t) && hasNext()) { - goForward(); + int index = historyIndex + 1; // assume we want to ignore the current item + while (index < historyStack.size()) { + T other = historyStack.get(index); + if (Objects.equals(t, other)) { + break; + } + index++; } + + if (index < 0) { + Msg.error(this, "Item not in forward history: " + t); + return; + } + + doGoToIndex(index); } /** diff --git a/Ghidra/Framework/Generic/src/test/java/util/HistoryListTest.java b/Ghidra/Framework/Generic/src/test/java/util/HistoryListTest.java index 97fb3900b3..831e0f9fe3 100644 --- a/Ghidra/Framework/Generic/src/test/java/util/HistoryListTest.java +++ b/Ghidra/Framework/Generic/src/test/java/util/HistoryListTest.java @@ -4,9 +4,9 @@ * 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. @@ -16,6 +16,7 @@ package util; import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.*; import java.util.LinkedList; @@ -455,9 +456,10 @@ public class HistoryListTest { // Private Methods //================================================================================================== - private void assertCurrentItem(String item) { - assertThat("History list is not pointing to the expected item", item, - is(historyList.getCurrentHistoryItem())); + private void assertCurrentItem(String expected) { + assertThat("History list is not pointing to the expected item", + historyList.getCurrentHistoryItem(), + is(expected)); } private void assertNotified(String item) {