diff --git a/openapi/src/com/intellij/openapi/actionSystem/AnAction.java b/openapi/src/com/intellij/openapi/actionSystem/AnAction.java index 1d3f2518c7e9..6accb07a8735 100644 --- a/openapi/src/com/intellij/openapi/actionSystem/AnAction.java +++ b/openapi/src/com/intellij/openapi/actionSystem/AnAction.java @@ -123,6 +123,15 @@ public abstract class AnAction { } } + public final void unregisterCustomShortcutSet(JComponent component){ + if (component != null){ + ArrayList actionList = (ArrayList)component.getClientProperty(ourClientProperty); + if (actionList != null){ + actionList.remove(this); + } + } + } + /** * Copies template presentation and shortcuts set from sourceAction. * diff --git a/source/com/intellij/debugger/ui/breakpoints/BreakpointsConfigurationDialogFactory.java b/source/com/intellij/debugger/ui/breakpoints/BreakpointsConfigurationDialogFactory.java index 95be88fb6018..217529b21821 100644 --- a/source/com/intellij/debugger/ui/breakpoints/BreakpointsConfigurationDialogFactory.java +++ b/source/com/intellij/debugger/ui/breakpoints/BreakpointsConfigurationDialogFactory.java @@ -252,6 +252,7 @@ public class BreakpointsConfigurationDialogFactory { protected void dispose() { apply(); if (myPanel != null) { + myTabbedPane.uninstallKeyboardNavigation(); myLastSelectedTabIndex = myTabbedPane.getSelectedIndex(); myPanel.removeAll(); myPanel = null; diff --git a/source/com/intellij/execution/impl/RunConfigurable.java b/source/com/intellij/execution/impl/RunConfigurable.java index 59aa88920881..da778c87b09e 100644 --- a/source/com/intellij/execution/impl/RunConfigurable.java +++ b/source/com/intellij/execution/impl/RunConfigurable.java @@ -158,6 +158,9 @@ class RunConfigurable extends BaseConfigurable { } public void disposeUIResources() { + if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); + } for (int i = 0; i < myTabs.length; i++) { final ConfigurationTab tab = myTabs[i]; tab.disposeUIResources(); diff --git a/source/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java b/source/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java index 43c83a9f8115..62f91f3fe136 100644 --- a/source/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java +++ b/source/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java @@ -710,6 +710,9 @@ public class AllFileTemplatesConfigurable implements Configurable, ApplicationCo myEditor = null; myEditorComponent = null; } + if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); + } myMainPanel = null; } diff --git a/source/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/source/com/intellij/ide/projectView/impl/ProjectViewImpl.java index e61793067ded..3e54ba53bd30 100644 --- a/source/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/source/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -431,6 +431,9 @@ public final class ProjectViewImpl extends ProjectView implements JDOMExternaliz myProject = null; myStructureViewWrapper.dispose(); myStructureViewWrapper = null; + if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); + } } private JComponent getComponent() { diff --git a/source/com/intellij/ide/util/TreeClassChooserDialog.java b/source/com/intellij/ide/util/TreeClassChooserDialog.java index a8f7ac1f2dcc..98bf1341cfcd 100644 --- a/source/com/intellij/ide/util/TreeClassChooserDialog.java +++ b/source/com/intellij/ide/util/TreeClassChooserDialog.java @@ -343,6 +343,9 @@ public class TreeClassChooserDialog extends DialogWrapper { myBuilder.dispose(); myBuilder = null; } + if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); + } super.dispose(); } diff --git a/source/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/source/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 92f644d30e35..7f856f7556d3 100644 --- a/source/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/source/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -44,7 +44,7 @@ final class EditorTabbedContainer extends TabbedPaneWrapper { } - protected JComponent createTabbedPaneHolder() { + protected TabbedPaneHolder createTabbedPaneHolder() { return new MyTabbedPaneHolder(); } diff --git a/source/com/intellij/openapi/options/ex/ExplorerSettingsEditor.java b/source/com/intellij/openapi/options/ex/ExplorerSettingsEditor.java index f7a5c80821a0..5e31b4037649 100644 --- a/source/com/intellij/openapi/options/ex/ExplorerSettingsEditor.java +++ b/source/com/intellij/openapi/options/ex/ExplorerSettingsEditor.java @@ -156,6 +156,9 @@ public class ExplorerSettingsEditor extends DialogWrapper { } protected final void dispose() { + if (myGroupTabs != null) { + myGroupTabs.uninstallKeyboardNavigation(); + } rememberLastUsedPage(); for (int i = 0; i < myGroups.length; i++) { diff --git a/source/com/intellij/openapi/projectRoots/ui/SdkEditor.java b/source/com/intellij/openapi/projectRoots/ui/SdkEditor.java index 345f5ff46565..a42cbaca6863 100644 --- a/source/com/intellij/openapi/projectRoots/ui/SdkEditor.java +++ b/source/com/intellij/openapi/projectRoots/ui/SdkEditor.java @@ -227,6 +227,9 @@ public class SdkEditor implements Configurable{ } public void disposeUIResources(){ + if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); + } myMainPanel = null; for (Iterator it = myAdditionalDataConfigurables.keySet().iterator(); it.hasNext();) { final SdkType sdkType = (SdkType)it.next(); diff --git a/source/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java b/source/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java index a948d0ab035d..79010337cfaa 100644 --- a/source/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java +++ b/source/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java @@ -183,6 +183,7 @@ public class ModuleEditor { myEditors.clear(); if (myTabbedPane != null) { + myTabbedPane.uninstallKeyboardNavigation(); mySelectedTabName = getSelectedTabName(); myTabbedPane = null; } diff --git a/source/com/intellij/ui/TabbedPaneWrapper.java b/source/com/intellij/ui/TabbedPaneWrapper.java index c0fe126bf19f..88915a143eda 100644 --- a/source/com/intellij/ui/TabbedPaneWrapper.java +++ b/source/com/intellij/ui/TabbedPaneWrapper.java @@ -18,6 +18,8 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Iterator; /** * @author Anton Katilin @@ -26,6 +28,7 @@ import java.lang.reflect.Method; public class TabbedPaneWrapper { protected final TabbedPane myTabbedPane; protected final JComponent myTabbedPaneHolder; + private final java.util.List myRegistrars = new ArrayList(); public TabbedPaneWrapper(){ this(SwingConstants.TOP); @@ -65,7 +68,7 @@ public class TabbedPaneWrapper { myTabbedPane.removeChangeListener(listener); } - protected JComponent createTabbedPaneHolder() { + protected TabbedPaneHolder createTabbedPaneHolder() { return new TabbedPaneHolder(); } @@ -240,9 +243,10 @@ public class TabbedPaneWrapper { /** * Installs tab navigation via IdeActions.ACTION_NEXT_TAB and IdeActions.ACTION_PREVIOUS_TAB shortcuts. + * In order to avoid memory leaks, the uninstallKeyboardNavigation() method should be called when the TabbedPane is not needed anymore */ public final void installKeyboardNavigation(){ - final AnAction nextTabAction=new AnAction() { + final AnAction nextTabAction = new AnAction() { { setEnabledInModalContext(true); } @@ -254,22 +258,26 @@ public class TabbedPaneWrapper { setSelectedIndex(index); } }; + final JComponent component = getComponent(); nextTabAction.registerCustomShortcutSet( ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_TAB).getShortcutSet(), - getComponent() + component ); + myRegistrars.add(new UnregisterCommand(nextTabAction, component)); // make action work in modal dialog box SwingUtilities.invokeLater(new Runnable() { public void run() { - JRootPane rootPane = SwingUtilities.getRootPane(getComponent()); + JRootPane rootPane = SwingUtilities.getRootPane(component); if (rootPane != null) { nextTabAction.registerCustomShortcutSet( - ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_TAB).getShortcutSet(), - rootPane); + ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_TAB).getShortcutSet(), rootPane + ); + myRegistrars.add(new UnregisterCommand(nextTabAction, rootPane)); } } }); - final AnAction previousTabAction=new AnAction() { + + final AnAction previousTabAction = new AnAction() { { setEnabledInModalContext(true); } @@ -283,22 +291,30 @@ public class TabbedPaneWrapper { }; previousTabAction.registerCustomShortcutSet( ActionManager.getInstance().getAction(IdeActions.ACTION_PREVIOUS_TAB).getShortcutSet(), - getComponent() + component ); + myRegistrars.add(new UnregisterCommand(previousTabAction, component)); SwingUtilities.invokeLater(new Runnable() { public void run() { - JRootPane rootPane = SwingUtilities.getRootPane(getComponent()); + JRootPane rootPane = SwingUtilities.getRootPane(component); if (rootPane != null) { previousTabAction.registerCustomShortcutSet( - ActionManager.getInstance().getAction(IdeActions.ACTION_PREVIOUS_TAB).getShortcutSet(), - rootPane + ActionManager.getInstance().getAction(IdeActions.ACTION_PREVIOUS_TAB).getShortcutSet(), rootPane ); + myRegistrars.add(new UnregisterCommand(previousTabAction, rootPane)); } } }); } + public final void uninstallKeyboardNavigation(){ + for (Iterator it = myRegistrars.iterator(); it.hasNext();) { + it.next().unregister(); + } + myRegistrars.clear(); + } + public final String getTitleAt(final int i) { return myTabbedPane.getTitleAt(i); } @@ -555,4 +571,18 @@ public class TabbedPaneWrapper { return requestDefaultFocus(); } } + + private static class UnregisterCommand { + private final AnAction myAction; + private final JComponent myComponent; + + public UnregisterCommand(AnAction action, JComponent component) { + myAction = action; + myComponent = component; + } + + public void unregister() { + myAction.unregisterCustomShortcutSet(myComponent); + } + } } diff --git a/source/com/intellij/ui/content/TabbedPaneContentUI.java b/source/com/intellij/ui/content/TabbedPaneContentUI.java index 03c6b7ccfc97..162233ac33ca 100644 --- a/source/com/intellij/ui/content/TabbedPaneContentUI.java +++ b/source/com/intellij/ui/content/TabbedPaneContentUI.java @@ -236,7 +236,7 @@ public class TabbedPaneContentUI implements ContentUI, PropertyChangeListener { return new MyTabbedPane(tabPlacement); } - protected JComponent createTabbedPaneHolder() { + protected TabbedPaneHolder createTabbedPaneHolder() { return new MyTabbedPaneHolder(); }