mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-1-dragonmacher-focus-fix--SQUASHED'
This commit is contained in:
commit
2a83263d73
9 changed files with 136 additions and 430 deletions
|
@ -83,7 +83,7 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
|
|
||||||
private final static Icon NAVIGATION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
|
private final static Icon NAVIGATION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
|
||||||
|
|
||||||
private HashMap<String, TreeViewProvider> providerMap;// map of view providers, key is the name
|
private Map<String, TreeViewProvider> providerMap;// map of view providers, key is the name
|
||||||
private GoToService goToService;
|
private GoToService goToService;
|
||||||
private ViewManagerService viewManagerService;
|
private ViewManagerService viewManagerService;
|
||||||
private ProgramTreeActionManager actionManager;
|
private ProgramTreeActionManager actionManager;
|
||||||
|
@ -202,9 +202,7 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
Iterator<String> iter = providerMap.keySet().iterator();
|
for (String treeName : providerMap.keySet()) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
String treeName = iter.next();
|
|
||||||
TreeViewProvider provider = providerMap.get(treeName);
|
TreeViewProvider provider = providerMap.get(treeName);
|
||||||
deregisterService(ViewProviderService.class, provider);
|
deregisterService(ViewProviderService.class, provider);
|
||||||
provider.dispose();
|
provider.dispose();
|
||||||
|
@ -251,10 +249,8 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
viewProvider.writeDataState(saveState);
|
viewProvider.writeDataState(saveState);
|
||||||
|
|
||||||
saveState.putInt(NUMBER_OF_VIEWS, providerMap.size());
|
saveState.putInt(NUMBER_OF_VIEWS, providerMap.size());
|
||||||
Iterator<String> iter = providerMap.keySet().iterator();
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
while (iter.hasNext()) {
|
for (String treeName : providerMap.keySet()) {
|
||||||
String treeName = iter.next();
|
|
||||||
saveState.putString(TREE_NAME + "-" + idx, treeName);
|
saveState.putString(TREE_NAME + "-" + idx, treeName);
|
||||||
TreeViewProvider provider = providerMap.get(treeName);
|
TreeViewProvider provider = providerMap.get(treeName);
|
||||||
provider.writeDataState(saveState);
|
provider.writeDataState(saveState);
|
||||||
|
@ -269,7 +265,6 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void readDataState(SaveState saveState) {
|
public void readDataState(SaveState saveState) {
|
||||||
viewProvider.readDataState(saveState);
|
|
||||||
|
|
||||||
int numberOfViews = saveState.getInt(NUMBER_OF_VIEWS, 0);
|
int numberOfViews = saveState.getInt(NUMBER_OF_VIEWS, 0);
|
||||||
|
|
||||||
|
@ -313,6 +308,23 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionToggleAction.setSelected(saveState.getBoolean(TOGGLE_STATE, true));
|
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<TreeViewProvider> 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
|
@Override
|
||||||
|
@ -355,10 +367,7 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
private void removeStaleProviders(ArrayList<TreeViewProvider> providerList) {
|
private void removeStaleProviders(ArrayList<TreeViewProvider> providerList) {
|
||||||
HashMap<String, TreeViewProvider> map = new HashMap<>(providerMap);
|
HashMap<String, TreeViewProvider> map = new HashMap<>(providerMap);
|
||||||
|
|
||||||
// remove views from the map that are not in the providerList
|
for (String treeName : map.keySet()) {
|
||||||
Iterator<String> iter = map.keySet().iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
String treeName = iter.next();
|
|
||||||
TreeViewProvider provider = map.get(treeName);
|
TreeViewProvider provider = map.get(treeName);
|
||||||
if (!providerList.contains(provider)) {
|
if (!providerList.contains(provider)) {
|
||||||
deregisterService(ViewProviderService.class, provider);
|
deregisterService(ViewProviderService.class, provider);
|
||||||
|
@ -610,9 +619,7 @@ public class ProgramTreePlugin extends ProgramPlugin
|
||||||
* fragment was moved; update all the view maps.
|
* fragment was moved; update all the view maps.
|
||||||
*/
|
*/
|
||||||
void fragmentMoved() {
|
void fragmentMoved() {
|
||||||
Iterator<String> iter = providerMap.keySet().iterator();
|
for (String treeName : providerMap.keySet()) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
String treeName = iter.next();
|
|
||||||
TreeViewProvider provider = providerMap.get(treeName);
|
TreeViewProvider provider = providerMap.get(treeName);
|
||||||
provider.notifyListeners();
|
provider.notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,11 @@ class TreeViewProvider implements ViewProviderService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return treePanel.getTreeName();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JComponent getViewComponent() {
|
public JComponent getViewComponent() {
|
||||||
return treePanel;
|
return treePanel;
|
||||||
|
|
|
@ -17,13 +17,13 @@ package ghidra.app.plugin.core.programtree;
|
||||||
|
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.services.ViewManagerService;
|
import ghidra.app.services.ViewManagerService;
|
||||||
import ghidra.framework.model.DomainObject;
|
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
@ -35,7 +35,6 @@ import ghidra.util.HelpLocation;
|
||||||
public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
||||||
implements ViewManagerService, ViewChangeListener {
|
implements ViewManagerService, ViewChangeListener {
|
||||||
|
|
||||||
private static final String OLD_NAME = "ProgramTreePlugin";
|
|
||||||
private static final String NAME = "Program Tree";
|
private static final String NAME = "Program Tree";
|
||||||
|
|
||||||
public static final String CURRENT_VIEW = "Current Viewname";
|
public static final String CURRENT_VIEW = "Current Viewname";
|
||||||
|
@ -79,14 +78,12 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
||||||
viewPanel.addView(service);
|
viewPanel.addView(service);
|
||||||
String viewName = service.getViewName();
|
String viewName = service.getViewName();
|
||||||
if (viewName.equals(restoredViewName)) {
|
if (viewName.equals(restoredViewName)) {
|
||||||
// state is being restored, so set the current view now
|
viewPanel.setCurrentView(restoredViewName);
|
||||||
restoredViewName = null;
|
restoredViewName = null;
|
||||||
viewPanel.setCurrentView(viewName);
|
|
||||||
}
|
}
|
||||||
else if (viewPanel.getNumberOfViews() == 1) {
|
else if (viewPanel.getNumberOfViews() == 1) {
|
||||||
viewName = viewPanel.getCurrentViewName();
|
|
||||||
|
|
||||||
// we only have one view, so force view map events to go out
|
// we only have one view, so force view map events to go out
|
||||||
|
viewName = viewPanel.getCurrentViewName();
|
||||||
viewPanel.setCurrentView(viewName);
|
viewPanel.setCurrentView(viewName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,8 +114,7 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void viewChanged(AddressSetView addrSet) {
|
public void viewChanged(AddressSetView addrSet) {
|
||||||
for (int i = 0; i < listeners.size(); i++) {
|
for (ViewChangeListener l : listeners) {
|
||||||
ViewChangeListener l = listeners.get(i);
|
|
||||||
l.viewChanged(addrSet);
|
l.viewChanged(addrSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,23 +141,15 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
void readDataState(SaveState saveState) {
|
void readDataState(SaveState saveState) {
|
||||||
if (saveState != null) {
|
String savedCurrentView = saveState.getString(CURRENT_VIEW, null);
|
||||||
restoredViewName = saveState.getString(CURRENT_VIEW, null);
|
if (!viewPanel.setCurrentView(savedCurrentView)) {
|
||||||
if (viewPanel.setCurrentView(restoredViewName)) {
|
// the view to has not yet been added from a call to serviceAdded(); save for later
|
||||||
restoredViewName = null; // have the view
|
restoredViewName = savedCurrentView;
|
||||||
}
|
|
||||||
// else wait for serviceAdded to restore the view...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object getUndoRedoState(DomainObject domainObject) {
|
void treeViewsRestored(Collection<TreeViewProvider> treeViews) {
|
||||||
SaveState saveState = new SaveState();
|
viewPanel.treeViewsRestored(treeViews);
|
||||||
writeDataState(saveState);
|
|
||||||
return saveState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void restoreUndoRedoState(DomainObject domainObject, Object state) {
|
|
||||||
readDataState((SaveState) state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,5 +210,4 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
|
||||||
public void setCurrentProgram(Program program) {
|
public void setCurrentProgram(Program program) {
|
||||||
currentProgram = program;
|
currentProgram = program;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,21 +17,23 @@ package ghidra.app.plugin.core.programtree;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.EditListener;
|
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tabbedpane.DockingTabRenderer;
|
import docking.widgets.tabbedpane.DockingTabRenderer;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -349,6 +351,15 @@ class ViewPanel extends JPanel implements ChangeListener {
|
||||||
setPreferredSize(new Dimension(200, 300));
|
setPreferredSize(new Dimension(200, 300));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void treeViewsRestored(Collection<TreeViewProvider> treeViews) {
|
||||||
|
|
||||||
|
map.clear();
|
||||||
|
|
||||||
|
for (TreeViewProvider treeProvider : treeViews) {
|
||||||
|
addView(treeProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the panel is active, then set the current view to be active and all
|
* If the panel is active, then set the current view to be active and all
|
||||||
* others to be inactive.
|
* others to be inactive.
|
||||||
|
@ -448,52 +459,24 @@ class ViewPanel extends JPanel implements ChangeListener {
|
||||||
*/
|
*/
|
||||||
private void renameView() {
|
private void renameView() {
|
||||||
ViewProviderService vps = getCurrentViewProvider();
|
ViewProviderService vps = getCurrentViewProvider();
|
||||||
int tabIndex = tabbedPane.getSelectedIndex();
|
|
||||||
String oldName = vps.getViewName();
|
String oldName = vps.getViewName();
|
||||||
Rectangle rect = tabbedPane.getBoundsAt(tabIndex);
|
String newName =
|
||||||
tool.showEditWindow(oldName, tabbedPane, rect, new RenameListener(vps, tabIndex));
|
OptionDialog.showInputSingleLineDialog(tabbedPane, "Rename Tab", "New name:", oldName);
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
if (StringUtils.isBlank(newName)) {
|
||||||
// Inner Classes
|
return;
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
private class RenameListener implements EditListener {
|
|
||||||
|
|
||||||
private ViewProviderService vps;
|
|
||||||
private int tabIndex;
|
|
||||||
|
|
||||||
RenameListener(ViewProviderService vps, int tabIndex) {
|
|
||||||
this.vps = vps;
|
|
||||||
this.tabIndex = tabIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (!newName.equals(oldName)) {
|
||||||
public void editCompleted(String newName) {
|
if (vps.viewRenamed(newName)) {
|
||||||
|
int selectedIndex = tabbedPane.getSelectedIndex();
|
||||||
if (newName.length() == 0) {
|
tabbedPane.setTitleAt(selectedIndex, newName);
|
||||||
|
DockingTabRenderer renderer =
|
||||||
Msg.showError(getClass(), null, "Invalid Name", "Please enter a valid name.");
|
(DockingTabRenderer) tabbedPane.getTabComponentAt(selectedIndex);
|
||||||
|
renderer.setTitle(newName, newName);
|
||||||
String oldName = vps.getViewName();
|
map.remove(oldName);
|
||||||
Rectangle rect = tabbedPane.getBoundsAt(tabIndex);
|
map.put(newName, vps);
|
||||||
tool.showEditWindow(oldName, tabbedPane, rect, this);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ package ghidra.app.plugin.core.programtree;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.Component;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.Container;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -26,8 +26,8 @@ import javax.swing.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.DefaultActionContext;
|
import docking.DefaultActionContext;
|
||||||
import docking.EditWindow;
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.widgets.dialogs.InputDialog;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.services.ViewManagerService;
|
import ghidra.app.services.ViewManagerService;
|
||||||
|
@ -254,6 +254,9 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteView() throws Exception {
|
public void testDeleteView() throws Exception {
|
||||||
|
|
||||||
|
env.showTool();
|
||||||
|
|
||||||
// delete the "Tree Two" view
|
// delete the "Tree Two" view
|
||||||
setCurrentViewProvider("Tree Two");
|
setCurrentViewProvider("Tree Two");
|
||||||
|
|
||||||
|
@ -359,47 +362,35 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView()));
|
assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this test has been commented out because it fails consistently due to timing errors.
|
@Test
|
||||||
// However, this test will almost always run successfully after the first time it is run. So,
|
public void testRenameView() throws Exception {
|
||||||
// this test can be uncommented and run to test the functionality of view renaming when
|
|
||||||
// changes are made.
|
|
||||||
public void dontTestRenameView() throws Exception {
|
|
||||||
env.showTool();
|
env.showTool();
|
||||||
|
|
||||||
final DockingActionIf renameAction = getAction(plugin, "Rename Tree View");
|
|
||||||
|
|
||||||
waitForTasks();
|
waitForTasks();
|
||||||
waitForSwing();
|
|
||||||
|
|
||||||
setCurrentViewProvider(DEFAULT_TREE_NAME);
|
setCurrentViewProvider(DEFAULT_TREE_NAME);
|
||||||
SwingUtilities
|
|
||||||
.invokeAndWait(() -> renameAction.actionPerformed(new DefaultActionContext()));
|
|
||||||
|
|
||||||
EditWindow editWindow = findEditWindow(tool.getToolFrame());
|
DockingActionIf renameAction = getAction(plugin, "Rename Tree View");
|
||||||
assertNotNull(editWindow);
|
performAction(renameAction, false);
|
||||||
|
|
||||||
final JTextField textField = (JTextField) getInstanceField("textField", editWindow);
|
InputDialog dialog = waitForDialogComponent(InputDialog.class);
|
||||||
SwingUtilities.invokeAndWait(() -> {
|
dialog.setValue("My Tree");
|
||||||
textField.setText("My Tree");
|
pressButtonByText(dialog, "OK");
|
||||||
ActionListener[] listeners = textField.getActionListeners();
|
waitForProgram(program);
|
||||||
listeners[0].actionPerformed(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
program.flushEvents();
|
ViewProviderService vps = runSwing(() -> provider.getCurrentViewProvider());
|
||||||
|
|
||||||
ViewProviderService vps = provider.getCurrentViewProvider();
|
|
||||||
assertEquals("My Tree", vps.getViewName());
|
assertEquals("My Tree", vps.getViewName());
|
||||||
assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
||||||
assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView()));
|
assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView()));
|
||||||
assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView()));
|
assertTrue(provider.getCurrentView().hasSameAddresses(vps.getCurrentView()));
|
||||||
|
|
||||||
undo(program);
|
undo(program);
|
||||||
vps = provider.getCurrentViewProvider();
|
vps = runSwing(() -> provider.getCurrentViewProvider());
|
||||||
assertEquals(DEFAULT_TREE_NAME, vps.getViewName());
|
assertEquals(DEFAULT_TREE_NAME, vps.getViewName());
|
||||||
assertNotNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
assertNotNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
||||||
|
|
||||||
redo(program);
|
redo(program);
|
||||||
vps = provider.getCurrentViewProvider();
|
provider.getCurrentViewProvider();
|
||||||
|
vps = runSwing(() -> provider.getCurrentViewProvider());
|
||||||
assertEquals("My Tree", vps.getViewName());
|
assertEquals("My Tree", vps.getViewName());
|
||||||
assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
assertNull(program.getListing().getRootModule(DEFAULT_TREE_NAME));
|
||||||
}
|
}
|
||||||
|
@ -409,25 +400,19 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
env.showTool();
|
env.showTool();
|
||||||
|
|
||||||
final DockingActionIf renameAction = getAction(plugin, "Rename Tree View");
|
|
||||||
|
|
||||||
waitForTasks();
|
waitForTasks();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
setCurrentViewProvider(DEFAULT_TREE_NAME);
|
setCurrentViewProvider(DEFAULT_TREE_NAME);
|
||||||
SwingUtilities
|
|
||||||
.invokeAndWait(() -> renameAction.actionPerformed(new DefaultActionContext()));
|
|
||||||
|
|
||||||
EditWindow editWindow = findEditWindow(tool.getToolFrame());
|
DockingActionIf renameAction = getAction(plugin, "Rename Tree View");
|
||||||
assertNotNull(editWindow);
|
performAction(renameAction, false);
|
||||||
final JTextField textField = (JTextField) getInstanceField("textField", editWindow);
|
|
||||||
SwingUtilities.invokeAndWait(() -> {
|
InputDialog dialog = waitForDialogComponent(InputDialog.class);
|
||||||
textField.requestFocus();
|
dialog.setValue("Main Tree");
|
||||||
textField.setText("Main Tree");
|
pressButtonByText(dialog, "OK");
|
||||||
ActionListener[] listeners = textField.getActionListeners();
|
waitForProgram(program);
|
||||||
listeners[0].actionPerformed(null);
|
|
||||||
});
|
|
||||||
program.flushEvents();
|
|
||||||
ViewProviderService vps = provider.getCurrentViewProvider();
|
ViewProviderService vps = provider.getCurrentViewProvider();
|
||||||
assertEquals(DEFAULT_TREE_NAME, vps.getViewName());
|
assertEquals(DEFAULT_TREE_NAME, vps.getViewName());
|
||||||
assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView()));
|
assertTrue(provider.getCurrentView().hasSameAddresses(cb.getView()));
|
||||||
|
@ -466,16 +451,6 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
return null;
|
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() {
|
private void findTabbedPane() {
|
||||||
Component[] comp = viewPanel.getComponents();
|
Component[] comp = viewPanel.getComponents();
|
||||||
for (Component element : comp) {
|
for (Component element : comp) {
|
||||||
|
|
|
@ -29,7 +29,8 @@ import javax.swing.*;
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.action.*;
|
import docking.action.ActionContextProvider;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
import docking.actions.*;
|
import docking.actions.*;
|
||||||
import docking.widgets.PasswordDialog;
|
import docking.widgets.PasswordDialog;
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
|
@ -104,7 +105,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
private boolean isDocking;
|
private boolean isDocking;
|
||||||
private boolean hasStatusBar;
|
private boolean hasStatusBar;
|
||||||
|
|
||||||
private EditWindow editWindow;
|
|
||||||
private boolean windowsOnTop;
|
private boolean windowsOnTop;
|
||||||
|
|
||||||
private Window lastActiveWindow;
|
private Window lastActiveWindow;
|
||||||
|
@ -190,18 +190,14 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<DockingWindowManager> iter = instances.iterator();
|
for (DockingWindowManager winMgr : instances) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
DockingWindowManager winMgr = iter.next();
|
|
||||||
if (winMgr.root.getFrame() == win) {
|
if (winMgr.root.getFrame() == win) {
|
||||||
return winMgr;
|
return winMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DetachedWindowNode> detachedWindows = winMgr.root.getDetachedWindows();
|
List<DetachedWindowNode> detachedWindows = winMgr.root.getDetachedWindows();
|
||||||
List<DetachedWindowNode> safeAccessCopy = new LinkedList<>(detachedWindows);
|
List<DetachedWindowNode> safeAccessCopy = new LinkedList<>(detachedWindows);
|
||||||
Iterator<DetachedWindowNode> windowIterator = safeAccessCopy.iterator();
|
for (DetachedWindowNode dw : safeAccessCopy) {
|
||||||
while (windowIterator.hasNext()) {
|
|
||||||
DetachedWindowNode dw = windowIterator.next();
|
|
||||||
if (dw.getWindow() == win) {
|
if (dw.getWindow() == win) {
|
||||||
return winMgr;
|
return winMgr;
|
||||||
}
|
}
|
||||||
|
@ -1386,7 +1382,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFocus(final ComponentPlaceholder placeholder) {
|
private void updateFocus(ComponentPlaceholder placeholder) {
|
||||||
if (placeholder == null) {
|
if (placeholder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1398,29 +1394,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
// our application isn't focused--don't do anything
|
// our application isn't focused--don't do anything
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
placeholder.requestFocus();
|
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) {
|
void restoreFocusOwner(String focusOwner, String focusName) {
|
||||||
if (focusOwner == null) {
|
if (focusOwner == null) {
|
||||||
// nothing to restore
|
// nothing to restore
|
||||||
|
@ -1510,20 +1487,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
setNextFocusPlaceholder(null);
|
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
|
* 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
|
// adjust the focus if no component within the window has focus
|
||||||
Component newFocusComponent = (Component) evt.getNewValue();
|
Component newFocusComponent = (Component) evt.getNewValue();
|
||||||
|
|
||||||
if (newFocusComponent == null) {
|
if (newFocusComponent == null) {
|
||||||
return; // we'll get called again with the correct value
|
return; // we'll get called again with the correct value
|
||||||
}
|
}
|
||||||
|
@ -1582,13 +1544,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ensureDockableComponentContainsFocusOwner(newFocusComponent, dockableComponent)) {
|
if (SwingUtilities.isDescendingFrom(newFocusComponent, dockableComponent)) {
|
||||||
// This implies we have made a call that will change the focus, which means
|
updateDockingWindowStateForNewFocusOwner(newFocusComponent, dockableComponent);
|
||||||
// will be back here again or we are in some special case and we do not want to
|
|
||||||
// do any more focus work
|
|
||||||
return;
|
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();
|
ComponentPlaceholder placeholder = dockableComponent.getComponentWindowingPlaceholder();
|
||||||
if (placeholder == null) {
|
if (placeholder == null) {
|
||||||
return; // it's been disposed
|
return; // it's been disposed
|
||||||
|
@ -1604,38 +1572,28 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
Swing.runLater(() -> setFocusedComponent(placeholder));
|
Swing.runLater(() -> setFocusedComponent(placeholder));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean ensureDockableComponentContainsFocusOwner(Component newFocusComponent,
|
private void ensureAllowedFocusOwner(Component newFocusComponent,
|
||||||
DockableComponent dockableComponent) {
|
DockableComponent dockableComponent) {
|
||||||
|
|
||||||
if (isFocusComponentInEditingWindow(newFocusComponent)) {
|
if (nextFocusedPlaceholder != null) {
|
||||||
return false;
|
// 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
|
// 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
|
// not contained in a dockable component provider. This keeps unexpected components
|
||||||
// from getting focus as the user navigates the application from the keyboard.
|
// from getting focus as the user navigates the application from the keyboard.
|
||||||
if (!SwingUtilities.isDescendingFrom(newFocusComponent, dockableComponent)) {
|
dockableComponent.requestFocus();
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockableComponent getDockableComponentForFocusOwner(Window window,
|
private DockableComponent getDockableComponentForFocusOwner(Window window,
|
||||||
|
@ -1668,9 +1626,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
if (comp instanceof DockableComponent) {
|
if (comp instanceof DockableComponent) {
|
||||||
return (DockableComponent) comp;
|
return (DockableComponent) comp;
|
||||||
}
|
}
|
||||||
if (comp instanceof EditWindow) {
|
|
||||||
return getDockableComponent(((EditWindow) comp).getAssociatedComponent());
|
|
||||||
}
|
|
||||||
comp = comp.getParent();
|
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?
|
Note: Which window should be the parent of the dialog when the user does not specify?
|
||||||
|
|
||||||
Some use cases; a dialog is shown from:
|
Some use cases; a dialog is shown from:
|
||||||
1) A toolbar action
|
1) A toolbar action
|
||||||
2) A component provider's code
|
2) A component provider's code
|
||||||
|
@ -1970,7 +1925,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
4) A background thread
|
4) A background thread
|
||||||
5) The help window
|
5) The help window
|
||||||
6) A modal password dialog appears over the splash screen
|
6) A modal password dialog appears over the splash screen
|
||||||
|
|
||||||
It seems like the parent should be the active window for 1-2.
|
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 3 should probably use the window of the dialog provider.
|
||||||
Case 4 should probably use the main tool frame, since the user may be
|
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.
|
active window, we can default to the tool's frame.
|
||||||
Case 5 should use the help window.
|
Case 5 should use the help window.
|
||||||
Case 6 should use the splash screen as the parent.
|
Case 6 should use the splash screen as the parent.
|
||||||
|
|
||||||
We have not yet solidified how we should parent. This documentation is meant to
|
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
|
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
|
finalize our understanding, we should update the javadoc to list exactly where
|
||||||
the given Dialog Component will be shown.)
|
the given Dialog Component will be shown.)
|
||||||
|
|
||||||
Use Case
|
Use Case
|
||||||
A -The user presses an action on a toolbar from a window on screen 1, while the
|
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
|
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
|
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
|
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.
|
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
|
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
|
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.
|
not desired, then document it here.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DockingWindowManager dwm = getActiveInstance();
|
DockingWindowManager dwm = getActiveInstance();
|
||||||
|
@ -2280,9 +2235,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
if (includeMain) {
|
if (includeMain) {
|
||||||
winList.add(root.getMainWindow());
|
winList.add(root.getMainWindow());
|
||||||
}
|
}
|
||||||
Iterator<DetachedWindowNode> it = root.getDetachedWindows().iterator();
|
for (DetachedWindowNode node : root.getDetachedWindows()) {
|
||||||
while (it.hasNext()) {
|
|
||||||
DetachedWindowNode node = it.next();
|
|
||||||
Window win = node.getWindow();
|
Window win = node.getWindow();
|
||||||
if (win != null) {
|
if (win != null) {
|
||||||
winList.add(win);
|
winList.add(win);
|
||||||
|
@ -2293,9 +2246,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
|
|
||||||
void iconify() {
|
void iconify() {
|
||||||
List<Window> winList = getWindows(false);
|
List<Window> winList = getWindows(false);
|
||||||
Iterator<Window> it = winList.iterator();
|
for (Window w : winList) {
|
||||||
while (it.hasNext()) {
|
|
||||||
Window w = it.next();
|
|
||||||
if (w instanceof Frame) {
|
if (w instanceof Frame) {
|
||||||
w.setVisible(false);
|
w.setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -2304,9 +2255,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
|
|
||||||
void deIconify() {
|
void deIconify() {
|
||||||
List<Window> winList = getWindows(false);
|
List<Window> winList = getWindows(false);
|
||||||
Iterator<Window> it = winList.iterator();
|
for (Window w : winList) {
|
||||||
while (it.hasNext()) {
|
|
||||||
Window w = it.next();
|
|
||||||
if (w instanceof Frame) {
|
if (w instanceof Frame) {
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -262,7 +262,7 @@ public class GTabPanel<T> extends JPanel {
|
||||||
* @return a list of all tab values that are not visible
|
* @return a list of all tab values that are not visible
|
||||||
*/
|
*/
|
||||||
public List<T> getHiddenTabs() {
|
public List<T> getHiddenTabs() {
|
||||||
Set<T> hiddenValues = new LinkedHashSet<T>(allValues);
|
Set<T> hiddenValues = new LinkedHashSet<>(allValues);
|
||||||
hiddenValues.removeAll(getVisibleTabs());
|
hiddenValues.removeAll(getVisibleTabs());
|
||||||
return new ArrayList<>(hiddenValues);
|
return new ArrayList<>(hiddenValues);
|
||||||
}
|
}
|
||||||
|
@ -405,7 +405,7 @@ public class GTabPanel<T> extends JPanel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JComponent c = hasHiddenTabs() ? hiddenValuesControl : allTabs.get(allTabs.size() - 1);
|
JComponent c = hasHiddenTabs() ? hiddenValuesControl : allTabs.get(allTabs.size() - 1);
|
||||||
tabList = new TabListPopup<T>(this, c, tabTypeName);
|
tabList = new TabListPopup<>(this, c, tabTypeName);
|
||||||
tabList.setVisible(true);
|
tabList.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,16 +492,19 @@ public class GTabPanel<T> extends JPanel {
|
||||||
closeTabList();
|
closeTabList();
|
||||||
setBorder(null);
|
setBorder(null);
|
||||||
if (!shouldShowTabs()) {
|
if (!shouldShowTabs()) {
|
||||||
|
setFocusable(false);
|
||||||
revalidate();
|
revalidate();
|
||||||
repaint();
|
repaint();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
setBorder(new GTabPanelBorder());
|
setBorder(new GTabPanelBorder());
|
||||||
|
|
||||||
GTab<T> selectedTab = null;
|
GTab<T> selectedTab = null;
|
||||||
int availableWidth = getPanelWidth();
|
int availableWidth = getPanelWidth();
|
||||||
if (selectedValue != null) {
|
if (selectedValue != null) {
|
||||||
selectedTab = new GTab<T>(this, selectedValue, true);
|
selectedTab = new GTab<>(this, selectedValue, true);
|
||||||
availableWidth -= getTabWidth(selectedTab);
|
availableWidth -= getTabWidth(selectedTab);
|
||||||
}
|
}
|
||||||
createNonSelectedTabsForWidth(availableWidth);
|
createNonSelectedTabsForWidth(availableWidth);
|
||||||
|
@ -579,7 +582,7 @@ public class GTabPanel<T> extends JPanel {
|
||||||
if (value == selectedValue) {
|
if (value == selectedValue) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GTab<T> tab = new GTab<T>(this, value, false);
|
GTab<T> tab = new GTab<>(this, value, false);
|
||||||
|
|
||||||
int tabWidth = getTabWidth(tab);
|
int tabWidth = getTabWidth(tab);
|
||||||
if (tabWidth > availableWidth) {
|
if (tabWidth > availableWidth) {
|
||||||
|
|
|
@ -1464,22 +1464,6 @@ public abstract class PluginTool extends AbstractDockingTool {
|
||||||
eventMgr.removeEventListener(className);
|
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.
|
* Cancel the current task in the tool.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue