Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(revision 14463)
@@ -7,5 +7,4 @@
 import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
@@ -469,16 +468,5 @@
         }
         Config.getPref().removePreferenceChangeListener(this);
-        destroyComponents(this, false);
-    }
-
-    private static void destroyComponents(Component component, boolean destroyItself) {
-        if (component instanceof Container) {
-            for (Component c: ((Container) component).getComponents()) {
-                destroyComponents(c, true);
-            }
-        }
-        if (destroyItself && component instanceof Destroyable) {
-            ((Destroyable) component).destroy();
-        }
+        GuiHelper.destroyComponents(this, false);
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/history/CoordinateInfoViewer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/CoordinateInfoViewer.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/CoordinateInfoViewer.java	(revision 14463)
@@ -28,4 +28,5 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.Pair;
 
@@ -35,12 +36,6 @@
  * @since 2243
  */
-public class CoordinateInfoViewer extends JPanel {
-
-    /** the model */
-    private transient HistoryBrowserModel model;
-    /** the common info panel for the history node in role REFERENCE_POINT_IN_TIME */
-    private VersionInfoPanel referenceInfoPanel;
-    /** the common info panel for the history node in role CURRENT_POINT_IN_TIME */
-    private VersionInfoPanel currentInfoPanel;
+public class CoordinateInfoViewer extends HistoryBrowserPanel {
+
     /** the info panel for coordinates for the node in role REFERENCE_POINT_IN_TIME */
     private LatLonViewer referenceLatLonViewer;
@@ -53,5 +48,4 @@
 
     protected void build() {
-        setLayout(new GridBagLayout());
         GridBagConstraints gc = new GridBagConstraints();
 
@@ -133,11 +127,7 @@
     }
 
+    @Override
     protected void unregisterAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.removeChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.removeChangeListener(referenceInfoPanel);
-        }
+        super.unregisterAsChangeListener(model);
         if (currentLatLonViewer != null) {
             model.removeChangeListener(currentLatLonViewer);
@@ -154,11 +144,7 @@
     }
 
+    @Override
     protected void registerAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.addChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.addChangeListener(referenceInfoPanel);
-        }
+        super.registerAsChangeListener(model);
         if (currentLatLonViewer != null) {
             model.addChangeListener(currentLatLonViewer);
@@ -175,17 +161,10 @@
     }
 
-    /**
-     * Sets the model for this viewer
-     *
-     * @param model the model.
-     */
-    public void setModel(HistoryBrowserModel model) {
-        if (this.model != null) {
-            unregisterAsChangeListener(model);
-        }
-        this.model = model;
-        if (this.model != null) {
-            registerAsChangeListener(model);
-        }
+    @Override
+    public void destroy() {
+        super.destroy();
+        referenceLatLonViewer.destroy();
+        currentLatLonViewer.destroy();
+        distanceViewer.destroy();
     }
 
@@ -244,5 +223,5 @@
      * A UI widgets which displays the Lan/Lon-coordinates of a {@link HistoryNode}.
      */
-    private static class LatLonViewer extends JPanel implements ChangeListener {
+    private static class LatLonViewer extends JPanel implements ChangeListener, Destroyable {
 
         private final JosmTextArea lblLat = newTextArea();
@@ -252,5 +231,4 @@
 
         protected void build() {
-            setLayout(new GridBagLayout());
             setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
             GridBagConstraints gc = new GridBagConstraints();
@@ -294,4 +272,5 @@
          */
         LatLonViewer(HistoryBrowserModel model, PointInTimeType role) {
+            super(new GridBagLayout());
             this.updater = new Updater(model, role);
             this.modifiedColor = PointInTimeType.CURRENT_POINT_IN_TIME == role
@@ -329,4 +308,10 @@
         public void stateChanged(ChangeEvent e) {
             refresh();
+        }
+
+        @Override
+        public void destroy() {
+            lblLat.destroy();
+            lblLon.destroy();
         }
     }
@@ -374,5 +359,5 @@
     }
 
-    private static class DistanceViewer extends JPanel implements ChangeListener {
+    private static class DistanceViewer extends JPanel implements ChangeListener, Destroyable {
 
         private final JosmTextArea lblDistance = newTextArea();
@@ -380,10 +365,10 @@
 
         DistanceViewer(HistoryBrowserModel model) {
-            this.updater = new Updater(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
+            super(new GridBagLayout());
+            updater = new Updater(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
             build();
         }
 
         protected void build() {
-            setLayout(new GridBagLayout());
             setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
             GridBagConstraints gc = new GridBagConstraints();
@@ -432,4 +417,9 @@
             refresh();
         }
+
+        @Override
+        public void destroy() {
+            lblDistance.destroy();
+        }
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java	(revision 14463)
@@ -15,11 +15,12 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.history.History;
+import org.openstreetmap.josm.tools.Destroyable;
 
 /**
  * HistoryBrowser is an UI component which displays history information about an {@link OsmPrimitive}.
  *
- *
+ * @since 1709
  */
-public class HistoryBrowser extends JPanel {
+public class HistoryBrowser extends JPanel implements Destroyable {
 
     /** the model */
@@ -106,5 +107,4 @@
     }
 
-
     /**
      * populates the browser with the history of a specific {@link OsmPrimitive}
@@ -149,3 +149,12 @@
         return model;
     }
+
+    @Override
+    public void destroy() {
+        model.unlinkAsListener();
+        for (Destroyable component : new Destroyable[] {
+                tagInfoViewer, nodeListViewer, relationMemberListViewer, coordinateInfoViewer}) {
+            component.destroy();
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java	(revision 14463)
@@ -126,5 +126,7 @@
     /**
      * Removes this history browser model as listener for data change and layer change events.
-     */
+     * @deprecated not needeed anymore, job is done in {@link #dispose}
+     */
+    @Deprecated
     public void unlinkAsListener() {
         getHistoryBrowser().getModel().unlinkAsListener();
@@ -160,6 +162,4 @@
 
         void run() {
-            getHistoryBrowser().getModel().unlinkAsListener();
-            HistoryDataSet.getInstance().removeHistoryDataSetListener(HistoryBrowserDialog.this);
             HistoryBrowserDialogManager.getInstance().hide(HistoryBrowserDialog.this);
         }
@@ -202,3 +202,10 @@
         return browser;
     }
+
+    @Override
+    public void dispose() {
+        HistoryDataSet.getInstance().removeHistoryDataSetListener(HistoryBrowserDialog.this);
+        GuiHelper.destroyComponents(this, false);
+        super.dispose();
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 14463)
@@ -153,5 +153,4 @@
         dialogs.addAll(this.dialogs.values());
         for (HistoryBrowserDialog dialog: dialogs) {
-            dialog.unlinkAsListener();
             hide(dialog);
         }
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserPanel.java	(revision 14463)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserPanel.java	(revision 14463)
@@ -0,0 +1,83 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.history;
+
+import java.awt.GridBagLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Destroyable;
+
+/**
+ * Superclass of history browsing panels, backed by an {@link HistoryBrowserModel}.
+ * @since 14463
+ */
+public abstract class HistoryBrowserPanel extends JPanel implements Destroyable {
+
+    /** the model */
+    protected transient HistoryBrowserModel model;
+    /** the common info panel for the history object in role REFERENCE_POINT_IN_TIME */
+    protected VersionInfoPanel referenceInfoPanel;
+    /** the common info panel for the history object in role CURRENT_POINT_IN_TIME */
+    protected VersionInfoPanel currentInfoPanel;
+
+    private final List<JosmAction> josmActions = new ArrayList<>();
+
+    protected HistoryBrowserPanel() {
+        super(new GridBagLayout());
+    }
+
+    protected void registerAsChangeListener(HistoryBrowserModel model) {
+        if (currentInfoPanel != null) {
+            model.addChangeListener(currentInfoPanel);
+        }
+        if (referenceInfoPanel != null) {
+            model.addChangeListener(referenceInfoPanel);
+        }
+    }
+
+    protected void unregisterAsChangeListener(HistoryBrowserModel model) {
+        if (currentInfoPanel != null) {
+            model.removeChangeListener(currentInfoPanel);
+        }
+        if (referenceInfoPanel != null) {
+            model.removeChangeListener(referenceInfoPanel);
+        }
+    }
+
+    /**
+     * Sets the history browsing model for this viewer.
+     *
+     * @param model the history browsing model
+     */
+    protected final void setModel(HistoryBrowserModel model) {
+        if (this.model != null) {
+            unregisterAsChangeListener(this.model);
+        }
+        this.model = model;
+        if (this.model != null) {
+            registerAsChangeListener(model);
+        }
+    }
+
+    protected final <T extends AbstractAction> T trackJosmAction(T action) {
+        if (action instanceof JosmAction) {
+            josmActions.add((JosmAction) action);
+        }
+        return action;
+    }
+
+    @Override
+    public void destroy() {
+        setModel(null);
+        if (referenceInfoPanel != null)
+            referenceInfoPanel.destroy();
+        if (currentInfoPanel != null)
+            currentInfoPanel.destroy();
+        josmActions.forEach(JosmAction::destroy);
+        josmActions.clear();
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryViewerPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryViewerPanel.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryViewerPanel.java	(revision 14463)
@@ -3,8 +3,6 @@
 
 import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
 import java.awt.Insets;
 
-import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
@@ -16,9 +14,6 @@
  * @since 6207
  */
-public abstract class HistoryViewerPanel extends JPanel {
+public abstract class HistoryViewerPanel extends HistoryBrowserPanel {
 
-    protected transient HistoryBrowserModel model;
-    protected VersionInfoPanel referenceInfoPanel;
-    protected VersionInfoPanel currentInfoPanel;
     protected transient AdjustmentSynchronizer adjustmentSynchronizer;
     protected transient SelectionSynchronizer selectionSynchronizer;
@@ -35,36 +30,4 @@
     }
 
-    /**
-     * Sets the history browsing model.
-     * @param model The history browsing model
-     */
-    public final void setModel(HistoryBrowserModel model) {
-        if (this.model != null) {
-            unregisterAsChangeListener(model);
-        }
-        this.model = model;
-        if (this.model != null) {
-            registerAsChangeListener(model);
-        }
-    }
-
-    protected final void unregisterAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.removeChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.removeChangeListener(referenceInfoPanel);
-        }
-    }
-
-    protected final void registerAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.addChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.addChangeListener(referenceInfoPanel);
-        }
-    }
-
     protected abstract JTable buildReferenceTable();
 
@@ -72,5 +35,4 @@
 
     private void build() {
-        setLayout(new GridBagLayout());
         GridBagConstraints gc = new GridBagConstraints();
 
Index: /trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java	(revision 14463)
@@ -4,7 +4,4 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
 import java.awt.Point;
 import java.awt.event.ActionEvent;
@@ -13,5 +10,4 @@
 
 import javax.swing.AbstractAction;
-import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JScrollPane;
@@ -32,5 +28,4 @@
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
 import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.util.AdjustmentSynchronizer;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
@@ -47,12 +42,7 @@
  * @since 1709
  */
-public class NodeListViewer extends JPanel {
-
-    private transient HistoryBrowserModel model;
-    private VersionInfoPanel referenceInfoPanel;
-    private VersionInfoPanel currentInfoPanel;
-    private transient AdjustmentSynchronizer adjustmentSynchronizer;
-    private transient SelectionSynchronizer selectionSynchronizer;
-    private NodeListPopupMenu popupMenu;
+public class NodeListViewer extends HistoryViewerPanel {
+
+    private final NodeListPopupMenu popupMenu = new NodeListPopupMenu();
 
     /**
@@ -61,9 +51,8 @@
      */
     public NodeListViewer(HistoryBrowserModel model) {
-        setModel(model);
-        build();
-    }
-
-    protected JScrollPane embeddInScrollPane(JTable table) {
+        super(model);
+    }
+
+    protected JScrollPane embedInScrollPane(JTable table) {
         JScrollPane pane = new JScrollPane(table);
         adjustmentSynchronizer.participateInSynchronizedScrolling(pane.getVerticalScrollBar());
@@ -71,5 +60,6 @@
     }
 
-    protected JTable buildReferenceNodeListTable() {
+    @Override
+    protected JTable buildReferenceTable() {
         final DiffTableModel tableModel = model.getNodeListTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
         final NodeListTableColumnModel columnModel = new NodeListTableColumnModel();
@@ -84,5 +74,6 @@
     }
 
-    protected JTable buildCurrentNodeListTable() {
+    @Override
+    protected JTable buildCurrentTable() {
         final DiffTableModel tableModel = model.getNodeListTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
         final NodeListTableColumnModel columnModel = new NodeListTableColumnModel();
@@ -97,91 +88,4 @@
     }
 
-    protected void build() {
-        setLayout(new GridBagLayout());
-        GridBagConstraints gc = new GridBagConstraints();
-
-        // ---------------------------
-        gc.gridx = 0;
-        gc.gridy = 0;
-        gc.gridwidth = 1;
-        gc.gridheight = 1;
-        gc.weightx = 0.5;
-        gc.weighty = 0.0;
-        gc.insets = new Insets(5, 5, 5, 0);
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.anchor = GridBagConstraints.FIRST_LINE_START;
-        referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
-        add(referenceInfoPanel, gc);
-
-        gc.gridx = 1;
-        gc.gridy = 0;
-        gc.gridwidth = 1;
-        gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 0.5;
-        gc.weighty = 0.0;
-        gc.anchor = GridBagConstraints.FIRST_LINE_START;
-        currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
-        add(currentInfoPanel, gc);
-
-        adjustmentSynchronizer = new AdjustmentSynchronizer();
-        selectionSynchronizer = new SelectionSynchronizer();
-
-        popupMenu = new NodeListPopupMenu();
-
-        // ---------------------------
-        gc.gridx = 0;
-        gc.gridy = 1;
-        gc.gridwidth = 1;
-        gc.gridheight = 1;
-        gc.weightx = 0.5;
-        gc.weighty = 1.0;
-        gc.fill = GridBagConstraints.BOTH;
-        gc.anchor = GridBagConstraints.NORTHWEST;
-        add(embeddInScrollPane(buildReferenceNodeListTable()), gc);
-
-        gc.gridx = 1;
-        gc.gridy = 1;
-        gc.gridwidth = 1;
-        gc.gridheight = 1;
-        gc.weightx = 0.5;
-        gc.weighty = 1.0;
-        gc.fill = GridBagConstraints.BOTH;
-        gc.anchor = GridBagConstraints.NORTHWEST;
-        add(embeddInScrollPane(buildCurrentNodeListTable()), gc);
-    }
-
-    protected void unregisterAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.removeChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.removeChangeListener(referenceInfoPanel);
-        }
-    }
-
-    protected void registerAsChangeListener(HistoryBrowserModel model) {
-        if (currentInfoPanel != null) {
-            model.addChangeListener(currentInfoPanel);
-        }
-        if (referenceInfoPanel != null) {
-            model.addChangeListener(referenceInfoPanel);
-        }
-    }
-
-    /**
-     * Sets the history browser model.
-     * @param model the history browser model
-     */
-    public void setModel(HistoryBrowserModel model) {
-        if (this.model != null) {
-            unregisterAsChangeListener(model);
-        }
-        this.model = model;
-        if (this.model != null) {
-            registerAsChangeListener(model);
-        }
-    }
-
     static final class ReversedChangeListener implements TableModelListener {
         private final NodeListTableColumnModel columnModel;
Index: /trunk/src/org/openstreetmap/josm/gui/history/RelationMemberListViewer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/RelationMemberListViewer.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/RelationMemberListViewer.java	(revision 14463)
@@ -17,5 +17,5 @@
  *   <li>on the right, it displays the list of relation members for the version at {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li>
  * </ul>
- *
+ * @since 1709
  */
 public class RelationMemberListViewer extends HistoryViewerPanel {
Index: /trunk/src/org/openstreetmap/josm/gui/history/TagInfoViewer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/TagInfoViewer.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/TagInfoViewer.java	(revision 14463)
@@ -91,10 +91,10 @@
         Supplier<Collection<? extends Tagged>> objectSp = () -> Arrays.asList(histoSp.get());
 
-        tagMenu.add(new CopyValueAction(table, tagKeyFn, objectSp));
-        tagMenu.add(new CopyKeyValueAction(table, tagKeyFn, objectSp));
-        tagMenu.add(new CopyAllKeyValueAction(table, tagKeyFn, objectSp));
+        tagMenu.add(trackJosmAction(new CopyValueAction(table, tagKeyFn, objectSp)));
+        tagMenu.add(trackJosmAction(new CopyKeyValueAction(table, tagKeyFn, objectSp)));
+        tagMenu.add(trackJosmAction(new CopyAllKeyValueAction(table, tagKeyFn, objectSp)));
         tagMenu.addSeparator();
-        tagMenu.add(new HelpAction(table, tagKeyFn, tagValuesFn, null, null));
-        tagMenu.add(new TaginfoAction(table, tagKeyFn, tagValuesFn, null, null));
+        tagMenu.add(trackJosmAction(new HelpAction(table, tagKeyFn, tagValuesFn, null, null)));
+        tagMenu.add(trackJosmAction(new TaginfoAction(table, tagKeyFn, tagValuesFn, null, null)));
 
         table.addMouseListener(new PopupMenuLauncher(tagMenu));
Index: /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 14463)
@@ -41,4 +41,5 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -51,5 +52,5 @@
  * @since 1709
  */
-public class VersionInfoPanel extends JPanel implements ChangeListener {
+public class VersionInfoPanel extends JPanel implements ChangeListener, Destroyable {
     private final PointInTimeType pointInTimeType;
     private final transient HistoryBrowserModel model;
@@ -339,3 +340,8 @@
         }
     }
+
+    @Override
+    public void destroy() {
+        model.removeChangeListener(this);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java	(revision 14463)
@@ -58,4 +58,5 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageOverlay;
@@ -646,3 +647,20 @@
         }
     }
+
+    /**
+     * Destroys recursively all {@link Destroyable} components of a given container, and optionnally the container itself.
+     * @param component the component to destroy
+     * @param destroyItself whether to destroy the component itself
+     * @since 14463
+     */
+    public static void destroyComponents(Component component, boolean destroyItself) {
+        if (component instanceof Container) {
+            for (Component c: ((Container) component).getComponents()) {
+                destroyComponents(c, true);
+            }
+        }
+        if (destroyItself && component instanceof Destroyable) {
+            ((Destroyable) component).destroy();
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/JosmEditorPane.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/JosmEditorPane.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/JosmEditorPane.java	(revision 14463)
@@ -16,4 +16,5 @@
 
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.LanguageInfo;
@@ -24,5 +25,7 @@
  * @since 5886
  */
-public class JosmEditorPane extends JEditorPane {
+public class JosmEditorPane extends JEditorPane implements Destroyable {
+
+    private final PopupMenuLauncher launcher;
 
     /**
@@ -31,5 +34,5 @@
      */
     public JosmEditorPane() {
-        TextContextualPopupMenu.enableMenuFor(this, true);
+        launcher = TextContextualPopupMenu.enableMenuFor(this, true);
     }
 
@@ -137,3 +140,8 @@
         );
     }
+
+    @Override
+    public void destroy() {
+        TextContextualPopupMenu.disableMenuFor(this, launcher);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java	(revision 14463)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.Destroyable;
 
 /**
@@ -16,5 +17,7 @@
  * @since 5886
  */
-public class JosmTextArea extends JTextArea implements FocusListener {
+public class JosmTextArea extends JTextArea implements Destroyable, FocusListener {
+
+    private final PopupMenuLauncher launcher;
 
     /**
@@ -88,5 +91,5 @@
     public JosmTextArea(Document doc, String text, int rows, int columns) {
         super(doc, text, rows, columns);
-        TextContextualPopupMenu.enableMenuFor(this, true);
+        launcher = TextContextualPopupMenu.enableMenuFor(this, true);
         addFocusListener(this);
     }
@@ -119,3 +122,9 @@
         }
     }
+
+    @Override
+    public void destroy() {
+        removeFocusListener(this);
+        TextContextualPopupMenu.disableMenuFor(this, launcher);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java	(revision 14462)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java	(revision 14463)
@@ -16,4 +16,5 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.Destroyable;
 
 /**
@@ -26,6 +27,7 @@
  * @since 5886
  */
-public class JosmTextField extends JTextField implements FocusListener {
+public class JosmTextField extends JTextField implements Destroyable, FocusListener {
 
+    private final PopupMenuLauncher launcher;
     private String hint;
 
@@ -69,5 +71,5 @@
     public JosmTextField(Document doc, String text, int columns, boolean undoRedo) {
         super(doc, text, columns);
-        TextContextualPopupMenu.enableMenuFor(this, undoRedo);
+        launcher = TextContextualPopupMenu.enableMenuFor(this, undoRedo);
         // Fix minimum size when columns are specified
         if (columns > 0) {
@@ -184,3 +186,9 @@
         repaint();
     }
+
+    @Override
+    public void destroy() {
+        removeFocusListener(this);
+        TextContextualPopupMenu.disableMenuFor(this, launcher);
+    }
 }
