diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java index 39aa86c54a..944730eac8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java @@ -83,7 +83,7 @@ public class ProgramTreePlugin extends ProgramPlugin private final static Icon NAVIGATION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON; - private HashMap providerMap;// map of view providers, key is the name + private Map providerMap;// map of view providers, key is the name private GoToService goToService; private ViewManagerService viewManagerService; private ProgramTreeActionManager actionManager; @@ -202,9 +202,7 @@ public class ProgramTreePlugin extends ProgramPlugin */ @Override public void dispose() { - Iterator iter = providerMap.keySet().iterator(); - while (iter.hasNext()) { - String treeName = iter.next(); + for (String treeName : providerMap.keySet()) { TreeViewProvider provider = providerMap.get(treeName); deregisterService(ViewProviderService.class, provider); provider.dispose(); @@ -251,10 +249,8 @@ public class ProgramTreePlugin extends ProgramPlugin viewProvider.writeDataState(saveState); saveState.putInt(NUMBER_OF_VIEWS, providerMap.size()); - Iterator iter = providerMap.keySet().iterator(); int idx = 0; - while (iter.hasNext()) { - String treeName = iter.next(); + for (String treeName : providerMap.keySet()) { saveState.putString(TREE_NAME + "-" + idx, treeName); TreeViewProvider provider = providerMap.get(treeName); provider.writeDataState(saveState); @@ -269,7 +265,6 @@ public class ProgramTreePlugin extends ProgramPlugin */ @Override public void readDataState(SaveState saveState) { - viewProvider.readDataState(saveState); int numberOfViews = saveState.getInt(NUMBER_OF_VIEWS, 0); @@ -313,6 +308,23 @@ public class ProgramTreePlugin extends ProgramPlugin } selectionToggleAction.setSelected(saveState.getBoolean(TOGGLE_STATE, true)); + + // + // At this point, all tree views have been restored. The low level components have cache + // that needs to get updated. We want to maintain the order of the tree views so that the + // UI does not move around on the user. Use the view names as they are stored in the + // program to provide a consistent order. + // + List list = new ArrayList<>(); + String[] orderedTreeNames = currentProgram.getListing().getTreeNames(); + for (String treeName : orderedTreeNames) { + TreeViewProvider provider = providerMap.get(treeName); + list.add(provider); + } + + viewProvider.treeViewsRestored(list); + viewProvider.readDataState(saveState); + } @Override @@ -355,10 +367,7 @@ public class ProgramTreePlugin extends ProgramPlugin private void removeStaleProviders(ArrayList providerList) { HashMap map = new HashMap<>(providerMap); - // remove views from the map that are not in the providerList - Iterator iter = map.keySet().iterator(); - while (iter.hasNext()) { - String treeName = iter.next(); + for (String treeName : map.keySet()) { TreeViewProvider provider = map.get(treeName); if (!providerList.contains(provider)) { deregisterService(ViewProviderService.class, provider); @@ -610,9 +619,7 @@ public class ProgramTreePlugin extends ProgramPlugin * fragment was moved; update all the view maps. */ void fragmentMoved() { - Iterator iter = providerMap.keySet().iterator(); - while (iter.hasNext()) { - String treeName = iter.next(); + for (String treeName : providerMap.keySet()) { TreeViewProvider provider = providerMap.get(treeName); provider.notifyListeners(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java index cb0952ca32..956f25539b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java @@ -70,6 +70,11 @@ class TreeViewProvider implements ViewProviderService { }); } + @Override + public String toString() { + return treePanel.getTreeName(); + } + @Override public JComponent getViewComponent() { return treePanel; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java index b863f60d21..58d28a1989 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java @@ -17,13 +17,13 @@ package ghidra.app.plugin.core.programtree; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.Collection; import javax.swing.JComponent; import docking.*; import ghidra.app.context.ProgramActionContext; import ghidra.app.services.ViewManagerService; -import ghidra.framework.model.DomainObject; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; @@ -35,7 +35,6 @@ import ghidra.util.HelpLocation; public class ViewManagerComponentProvider extends ComponentProviderAdapter implements ViewManagerService, ViewChangeListener { - private static final String OLD_NAME = "ProgramTreePlugin"; private static final String NAME = "Program Tree"; public static final String CURRENT_VIEW = "Current Viewname"; @@ -79,14 +78,12 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter viewPanel.addView(service); String viewName = service.getViewName(); if (viewName.equals(restoredViewName)) { - // state is being restored, so set the current view now + viewPanel.setCurrentView(restoredViewName); restoredViewName = null; - viewPanel.setCurrentView(viewName); } else if (viewPanel.getNumberOfViews() == 1) { - viewName = viewPanel.getCurrentViewName(); - // we only have one view, so force view map events to go out + viewName = viewPanel.getCurrentViewName(); viewPanel.setCurrentView(viewName); } } @@ -117,8 +114,7 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter @Override public void viewChanged(AddressSetView addrSet) { - for (int i = 0; i < listeners.size(); i++) { - ViewChangeListener l = listeners.get(i); + for (ViewChangeListener l : listeners) { l.viewChanged(addrSet); } } @@ -145,23 +141,15 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter } void readDataState(SaveState saveState) { - if (saveState != null) { - restoredViewName = saveState.getString(CURRENT_VIEW, null); - if (viewPanel.setCurrentView(restoredViewName)) { - restoredViewName = null; // have the view - } - // else wait for serviceAdded to restore the view... + String savedCurrentView = saveState.getString(CURRENT_VIEW, null); + if (!viewPanel.setCurrentView(savedCurrentView)) { + // the view to has not yet been added from a call to serviceAdded(); save for later + restoredViewName = savedCurrentView; } } - Object getUndoRedoState(DomainObject domainObject) { - SaveState saveState = new SaveState(); - writeDataState(saveState); - return saveState; - } - - void restoreUndoRedoState(DomainObject domainObject, Object state) { - readDataState((SaveState) state); + void treeViewsRestored(Collection treeViews) { + viewPanel.treeViewsRestored(treeViews); } /** @@ -222,5 +210,4 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter public void setCurrentProgram(Program program) { currentProgram = program; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java index e2f1d8c20d..6a15324a38 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java @@ -17,21 +17,23 @@ package ghidra.app.plugin.core.programtree; import java.awt.*; import java.awt.event.MouseEvent; +import java.util.Collection; import java.util.HashMap; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import org.apache.commons.lang3.StringUtils; + import docking.ActionContext; -import docking.EditListener; import docking.action.DockingAction; import docking.action.MenuData; +import docking.widgets.OptionDialog; import docking.widgets.tabbedpane.DockingTabRenderer; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSetView; import ghidra.program.util.ProgramLocation; -import ghidra.util.Msg; import ghidra.util.exception.AssertException; /** @@ -349,6 +351,15 @@ class ViewPanel extends JPanel implements ChangeListener { setPreferredSize(new Dimension(200, 300)); } + void treeViewsRestored(Collection treeViews) { + + map.clear(); + + for (TreeViewProvider treeProvider : treeViews) { + addView(treeProvider); + } + } + /** * If the panel is active, then set the current view to be active and all * others to be inactive. @@ -448,52 +459,24 @@ class ViewPanel extends JPanel implements ChangeListener { */ private void renameView() { ViewProviderService vps = getCurrentViewProvider(); - int tabIndex = tabbedPane.getSelectedIndex(); String oldName = vps.getViewName(); - Rectangle rect = tabbedPane.getBoundsAt(tabIndex); - tool.showEditWindow(oldName, tabbedPane, rect, new RenameListener(vps, tabIndex)); - } + String newName = + OptionDialog.showInputSingleLineDialog(tabbedPane, "Rename Tab", "New name:", oldName); -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class RenameListener implements EditListener { - - private ViewProviderService vps; - private int tabIndex; - - RenameListener(ViewProviderService vps, int tabIndex) { - this.vps = vps; - this.tabIndex = tabIndex; + if (StringUtils.isBlank(newName)) { + return; } - @Override - public void editCompleted(String newName) { - - if (newName.length() == 0) { - - Msg.showError(getClass(), null, "Invalid Name", "Please enter a valid name."); - - String oldName = vps.getViewName(); - Rectangle rect = tabbedPane.getBoundsAt(tabIndex); - tool.showEditWindow(oldName, tabbedPane, rect, this); - return; + if (!newName.equals(oldName)) { + if (vps.viewRenamed(newName)) { + int selectedIndex = tabbedPane.getSelectedIndex(); + tabbedPane.setTitleAt(selectedIndex, newName); + DockingTabRenderer renderer = + (DockingTabRenderer) tabbedPane.getTabComponentAt(selectedIndex); + renderer.setTitle(newName, newName); + map.remove(oldName); + map.put(newName, vps); } - - String oldName = vps.getViewName(); - if (!newName.equals(oldName)) { - if (vps.viewRenamed(newName)) { - int selectedIndex = tabbedPane.getSelectedIndex(); - tabbedPane.setTitleAt(selectedIndex, newName); - DockingTabRenderer renderer = - (DockingTabRenderer) tabbedPane.getTabComponentAt(selectedIndex); - renderer.setTitle(newName, newName); - map.remove(oldName); - map.put(newName, vps); - } - } - } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java index 5b6dfdea7b..29a8f5ee64 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java @@ -17,8 +17,8 @@ package ghidra.app.plugin.core.programtree; import static org.junit.Assert.*; -import java.awt.*; -import java.awt.event.ActionListener; +import java.awt.Component; +import java.awt.Container; import java.util.concurrent.atomic.AtomicReference; import javax.swing.*; @@ -26,8 +26,8 @@ import javax.swing.*; import org.junit.*; import docking.DefaultActionContext; -import docking.EditWindow; import docking.action.DockingActionIf; +import docking.widgets.dialogs.InputDialog; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.services.ProgramManager; import ghidra.app.services.ViewManagerService; @@ -254,6 +254,9 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testDeleteView() throws Exception { + + env.showTool(); + // delete the "Tree Two" view setCurrentViewProvider("Tree Two"); @@ -359,47 +362,35 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView())); } - // NOTE: this test has been commented out because it fails consistently due to timing errors. - // However, this test will almost always run successfully after the first time it is run. So, - // this test can be uncommented and run to test the functionality of view renaming when - // changes are made. - public void dontTestRenameView() throws Exception { + @Test + public void testRenameView() throws Exception { env.showTool(); - - final DockingActionIf renameAction = getAction(plugin, "Rename Tree View"); - waitForTasks(); - waitForSwing(); setCurrentViewProvider(DEFAULT_TREE_NAME); - SwingUtilities - .invokeAndWait(() -> renameAction.actionPerformed(new DefaultActionContext())); - EditWindow editWindow = findEditWindow(tool.getToolFrame()); - assertNotNull(editWindow); + DockingActionIf renameAction = getAction(plugin, "Rename Tree View"); + performAction(renameAction, false); - final JTextField textField = (JTextField) getInstanceField("textField", editWindow); - SwingUtilities.invokeAndWait(() -> { - textField.setText("My Tree"); - ActionListener[] listeners = textField.getActionListeners(); - listeners[0].actionPerformed(null); - }); + InputDialog dialog = waitForDialogComponent(InputDialog.class); + dialog.setValue("My Tree"); + pressButtonByText(dialog, "OK"); + waitForProgram(program); - program.flushEvents(); - - ViewProviderService vps = provider.getCurrentViewProvider(); + ViewProviderService vps = runSwing(() -> provider.getCurrentViewProvider()); assertEquals("My Tree", vps.getViewName()); assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME)); assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView())); assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView())); undo(program); - vps = provider.getCurrentViewProvider(); + vps = runSwing(() -> provider.getCurrentViewProvider()); assertEquals(DEFAULT_TREE_NAME, vps.getViewName()); assertNotNull(program.getListing().getRootModule(DEFAULT_TREE_NAME)); redo(program); - vps = provider.getCurrentViewProvider(); + provider.getCurrentViewProvider(); + vps = runSwing(() -> provider.getCurrentViewProvider()); assertEquals("My Tree", vps.getViewName()); assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME)); } @@ -409,25 +400,19 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { env.showTool(); - final DockingActionIf renameAction = getAction(plugin, "Rename Tree View"); - waitForTasks(); waitForSwing(); setCurrentViewProvider(DEFAULT_TREE_NAME); - SwingUtilities - .invokeAndWait(() -> renameAction.actionPerformed(new DefaultActionContext())); - EditWindow editWindow = findEditWindow(tool.getToolFrame()); - assertNotNull(editWindow); - final JTextField textField = (JTextField) getInstanceField("textField", editWindow); - SwingUtilities.invokeAndWait(() -> { - textField.requestFocus(); - textField.setText("Main Tree"); - ActionListener[] listeners = textField.getActionListeners(); - listeners[0].actionPerformed(null); - }); - program.flushEvents(); + DockingActionIf renameAction = getAction(plugin, "Rename Tree View"); + performAction(renameAction, false); + + InputDialog dialog = waitForDialogComponent(InputDialog.class); + dialog.setValue("Main Tree"); + pressButtonByText(dialog, "OK"); + waitForProgram(program); + ViewProviderService vps = provider.getCurrentViewProvider(); assertEquals(DEFAULT_TREE_NAME, vps.getViewName()); assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView())); @@ -466,16 +451,6 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { return null; } - private EditWindow findEditWindow(Window window) { - Window[] w = window.getOwnedWindows(); - for (Window element : w) { - if (element instanceof EditWindow) { - return (EditWindow) element; - } - } - return null; - } - private void findTabbedPane() { Component[] comp = viewPanel.getComponents(); for (Component element : comp) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 87ac93e9e6..950f784cd0 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -29,7 +29,8 @@ import javax.swing.*; import org.apache.commons.collections4.map.LazyMap; import org.jdom.Element; -import docking.action.*; +import docking.action.ActionContextProvider; +import docking.action.DockingActionIf; import docking.actions.*; import docking.widgets.PasswordDialog; import generic.util.WindowUtilities; @@ -104,7 +105,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private boolean isDocking; private boolean hasStatusBar; - private EditWindow editWindow; private boolean windowsOnTop; private Window lastActiveWindow; @@ -190,18 +190,14 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return null; } - Iterator iter = instances.iterator(); - while (iter.hasNext()) { - DockingWindowManager winMgr = iter.next(); + for (DockingWindowManager winMgr : instances) { if (winMgr.root.getFrame() == win) { return winMgr; } List detachedWindows = winMgr.root.getDetachedWindows(); List safeAccessCopy = new LinkedList<>(detachedWindows); - Iterator windowIterator = safeAccessCopy.iterator(); - while (windowIterator.hasNext()) { - DetachedWindowNode dw = windowIterator.next(); + for (DetachedWindowNode dw : safeAccessCopy) { if (dw.getWindow() == win) { return winMgr; } @@ -1386,7 +1382,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return null; } - private void updateFocus(final ComponentPlaceholder placeholder) { + private void updateFocus(ComponentPlaceholder placeholder) { if (placeholder == null) { return; } @@ -1398,29 +1394,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder // our application isn't focused--don't do anything return; } - placeholder.requestFocus(); }); } - /** - * Display an text edit box on top of the specified component. - * - * @param defaultText initial text to be displayed in edit box - * @param c component over which the edit box will be placed - * @param r specifies the bounds of the edit box relative to the component. The height is - * ignored. The default text field height is used as the preferred height. - * @param listener when the edit is complete, this listener is notified with the new text. The - * edit box is dismissed prior to notifying the listener. - */ - public void showEditWindow(String defaultText, Component c, Rectangle r, - EditListener listener) { - if (editWindow == null) { - editWindow = new EditWindow(this); - } - editWindow.show(defaultText, c, r, listener); - } - void restoreFocusOwner(String focusOwner, String focusName) { if (focusOwner == null) { // nothing to restore @@ -1510,20 +1487,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder setNextFocusPlaceholder(null); } - /** - * Clears the docking window manager's notion of the active provider. This is used - * when a component that is not contained within a dockable component gets focus - * (e.g., JTabbedPanes for stacked components). - */ - private void deactivateFocusedComponent() { - if (focusedPlaceholder != null) { - focusedPlaceholder.setSelected(false); - focusedPlaceholder = null; - } - // also clear any pending focus transfers - setNextFocusPlaceholder(null); - } - /** * Invoked by associated docking windows when they become active or inactive * @@ -1571,7 +1534,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder // adjust the focus if no component within the window has focus Component newFocusComponent = (Component) evt.getNewValue(); - if (newFocusComponent == null) { return; // we'll get called again with the correct value } @@ -1582,13 +1544,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return; } - if (!ensureDockableComponentContainsFocusOwner(newFocusComponent, dockableComponent)) { - // This implies we have made a call that will change the focus, which means - // will be back here again or we are in some special case and we do not want to - // do any more focus work + if (SwingUtilities.isDescendingFrom(newFocusComponent, dockableComponent)) { + updateDockingWindowStateForNewFocusOwner(newFocusComponent, dockableComponent); return; } + // The new Java focus owner is not part of our DockableComponent hierarchy. See if we need + // to change the focus to a component that is. + ensureAllowedFocusOwner(newFocusComponent, dockableComponent); + } + + private void updateDockingWindowStateForNewFocusOwner(Component newFocusComponent, + DockableComponent dockableComponent) { + ComponentPlaceholder placeholder = dockableComponent.getComponentWindowingPlaceholder(); if (placeholder == null) { return; // it's been disposed @@ -1604,38 +1572,28 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder Swing.runLater(() -> setFocusedComponent(placeholder)); } - private boolean ensureDockableComponentContainsFocusOwner(Component newFocusComponent, + private void ensureAllowedFocusOwner(Component newFocusComponent, DockableComponent dockableComponent) { - if (isFocusComponentInEditingWindow(newFocusComponent)) { - return false; + if (nextFocusedPlaceholder != null) { + // We have a new pending focus request for a DockableComponent, so nothing to do. + return; + } + + // We allow JTabbedPanes, as that is the component we use to stack components and users need + // to be able to select and activate tabs when using the keyboard focus traversal. + if (newFocusComponent instanceof JTabbedPane) { + if (focusedPlaceholder != null) { + focusedPlaceholder.setSelected(false); // update the header to not be focused + focusedPlaceholder = null; + } + return; } // Transfer focus to one of our component providers when a component gets focus that is // not contained in a dockable component provider. This keeps unexpected components // from getting focus as the user navigates the application from the keyboard. - if (!SwingUtilities.isDescendingFrom(newFocusComponent, dockableComponent)) { - - // We make an exception for JTabbedPane as that is the component we use to stack - // components and users need to be able to select and activate tabs when using the - // keyboard focus traversal - if (newFocusComponent instanceof JTabbedPane) { - deactivateFocusedComponent(); - return false; - } - - dockableComponent.requestFocus(); - return false; - } - return true; - } - - private boolean isFocusComponentInEditingWindow(Component newFocusComponent) { - if (editWindow == null) { - return false; - } - - return SwingUtilities.isDescendingFrom(newFocusComponent, editWindow); + dockableComponent.requestFocus(); } private DockableComponent getDockableComponentForFocusOwner(Window window, @@ -1668,9 +1626,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder if (comp instanceof DockableComponent) { return (DockableComponent) comp; } - if (comp instanceof EditWindow) { - return getDockableComponent(((EditWindow) comp).getAssociatedComponent()); - } comp = comp.getParent(); } @@ -1962,7 +1917,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder /* Note: Which window should be the parent of the dialog when the user does not specify? - + Some use cases; a dialog is shown from: 1) A toolbar action 2) A component provider's code @@ -1970,7 +1925,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder 4) A background thread 5) The help window 6) A modal password dialog appears over the splash screen - + It seems like the parent should be the active window for 1-2. Case 3 should probably use the window of the dialog provider. Case 4 should probably use the main tool frame, since the user may be @@ -1978,12 +1933,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder active window, we can default to the tool's frame. Case 5 should use the help window. Case 6 should use the splash screen as the parent. - + We have not yet solidified how we should parent. This documentation is meant to move us towards clarity as we find Use Cases that don't make sense. (Once we finalize our understanding, we should update the javadoc to list exactly where the given Dialog Component will be shown.) - + Use Case A -The user presses an action on a toolbar from a window on screen 1, while the main tool frame is on screen 2. We want the popup window to appear on screen @@ -2002,12 +1957,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder E -A long-running API shows a non-modal progress dialog. This API then shows a results dialog which is also non-modal. We do not want to parent the new dialog to the original dialog, since it is a progress dialog that will go away. - - + + For now, the easiest mental model to use is to always prefer the active non-transient window so that a dialog will appear in the user's view. If we find a case where this is not desired, then document it here. - + */ DockingWindowManager dwm = getActiveInstance(); @@ -2280,9 +2235,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder if (includeMain) { winList.add(root.getMainWindow()); } - Iterator it = root.getDetachedWindows().iterator(); - while (it.hasNext()) { - DetachedWindowNode node = it.next(); + for (DetachedWindowNode node : root.getDetachedWindows()) { Window win = node.getWindow(); if (win != null) { winList.add(win); @@ -2293,9 +2246,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder void iconify() { List winList = getWindows(false); - Iterator it = winList.iterator(); - while (it.hasNext()) { - Window w = it.next(); + for (Window w : winList) { if (w instanceof Frame) { w.setVisible(false); } @@ -2304,9 +2255,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder void deIconify() { List winList = getWindows(false); - Iterator it = winList.iterator(); - while (it.hasNext()) { - Window w = it.next(); + for (Window w : winList) { if (w instanceof Frame) { w.setVisible(true); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/EditWindow.java b/Ghidra/Framework/Docking/src/main/java/docking/EditWindow.java deleted file mode 100644 index 72ab3a6130..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/EditWindow.java +++ /dev/null @@ -1,187 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking; - -import java.awt.*; -import java.awt.event.*; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import generic.theme.GThemeDefaults.Colors; - -/** - * A re-usable floating text edit window. - */ -public class EditWindow extends JWindow { - - private DockingWindowManager mgr; - private JTextField textField; - private boolean active = false; - private Component comp; - private Rectangle rect; - private EditListener listener; - - private AssociatedComponentListener compListener = new AssociatedComponentListener(); - - EditWindow(DockingWindowManager mgr) { - super(mgr.getRootFrame()); - this.mgr = mgr; - create(); - } - - Component getAssociatedComponent() { - return comp; - } - - @Override - public boolean isActive() { - return active; - } - - @Override - public void setVisible(boolean state) { - - active = state; - super.setVisible(state); - - if (!state) { - if (comp != null) { - comp.removeComponentListener(compListener); - if (comp instanceof JTabbedPane) { - ((JTabbedPane) comp).removeChangeListener(compListener); - } - Frame frame = mgr.getRootFrame(); - frame.removeComponentListener(compListener); - comp = null; - listener = null; - } - } - } - - void close() { - setVisible(false); - dispose(); - } - - void show(String defaultText, Component c, Rectangle r, EditListener editListener) { - - if (comp != null) { - setVisible(false); - } - - if (c == null || !c.isVisible()) { - return; - } - - this.comp = c; - this.rect = r; - this.listener = editListener; - - comp.addComponentListener(compListener); - - if (comp instanceof JTabbedPane) { - ((JTabbedPane) comp).addChangeListener(compListener); - } - - Frame frame = mgr.getRootFrame(); - frame.addComponentListener(compListener); - - setLocation(); - - textField.setText(defaultText != null ? defaultText : ""); - Dimension d = textField.getPreferredSize(); - textField.setPreferredSize(new Dimension(rect.width, d.height)); - pack(); - - setVisible(true); - - toFront(); - textField.requestFocus(); - textField.selectAll(); - } - - private void setLocation() { - Point p = comp.getLocationOnScreen(); - setLocation(p.x + rect.x + 3, p.y + rect.y); - } - - private void create() { - textField = new JTextField(" "); - JPanel panel = new JPanel(new BorderLayout()); - panel.setBackground(Colors.BACKGROUND); - panel.add(textField, BorderLayout.CENTER); - - textField.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - close(); - } - } - }); - textField.addFocusListener(new FocusAdapter() { - @Override - public void focusLost(FocusEvent e) { - if (!e.isTemporary()) { - close(); - } - } - }); - textField.addActionListener(e -> { - if (listener != null) { - String text = textField.getText(); - EditListener l = listener; - close(); - l.editCompleted(text); - } - }); - - getContentPane().add(panel, BorderLayout.CENTER); - } - - private class AssociatedComponentListener implements ComponentListener, ChangeListener { - - @Override - public void componentHidden(ComponentEvent e) { - close(); - } - - @Override - public void componentResized(ComponentEvent e) { - close(); - } - - @Override - public void componentShown(ComponentEvent e) { - // stub - } - - @Override - public void componentMoved(ComponentEvent e) { - if (comp != null && comp.isVisible()) { - setLocation(); - } - } - - @Override - public void stateChanged(ChangeEvent e) { - close(); - } - } - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tab/GTabPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tab/GTabPanel.java index aca4e560d9..56328dde0b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tab/GTabPanel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tab/GTabPanel.java @@ -262,7 +262,7 @@ public class GTabPanel extends JPanel { * @return a list of all tab values that are not visible */ public List getHiddenTabs() { - Set hiddenValues = new LinkedHashSet(allValues); + Set hiddenValues = new LinkedHashSet<>(allValues); hiddenValues.removeAll(getVisibleTabs()); return new ArrayList<>(hiddenValues); } @@ -405,7 +405,7 @@ public class GTabPanel extends JPanel { return; } JComponent c = hasHiddenTabs() ? hiddenValuesControl : allTabs.get(allTabs.size() - 1); - tabList = new TabListPopup(this, c, tabTypeName); + tabList = new TabListPopup<>(this, c, tabTypeName); tabList.setVisible(true); } @@ -492,16 +492,19 @@ public class GTabPanel extends JPanel { closeTabList(); setBorder(null); if (!shouldShowTabs()) { + setFocusable(false); revalidate(); repaint(); return; } + + setFocusable(true); setBorder(new GTabPanelBorder()); GTab selectedTab = null; int availableWidth = getPanelWidth(); if (selectedValue != null) { - selectedTab = new GTab(this, selectedValue, true); + selectedTab = new GTab<>(this, selectedValue, true); availableWidth -= getTabWidth(selectedTab); } createNonSelectedTabsForWidth(availableWidth); @@ -579,7 +582,7 @@ public class GTabPanel extends JPanel { if (value == selectedValue) { continue; } - GTab tab = new GTab(this, value, false); + GTab tab = new GTab<>(this, value, false); int tabWidth = getTabWidth(tab); if (tabWidth > availableWidth) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java index 5ad7f2541d..4a0f47d91e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java @@ -1464,22 +1464,6 @@ public abstract class PluginTool extends AbstractDockingTool { eventMgr.removeEventListener(className); } - /** - * Display an text edit box on top of the specified component. - * @param defaultText initial text to be displayed in edit box - * @param comp component over which the edit box will be placed - * @param rect specifies the bounds of the edit box relative to the - * component. The height is ignored. The default text field height - * is used as the preferred height. - * @param listener when the edit is complete, this listener is notified - * with the new text. The edit box is dismissed prior to notifying - * the listener. - */ - public void showEditWindow(String defaultText, Component comp, Rectangle rect, - EditListener listener) { - winMgr.showEditWindow(defaultText, comp, rect, listener); - } - /** * Cancel the current task in the tool. */