Index: trunk/src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 14977)
@@ -16,4 +16,5 @@
 import java.nio.file.InvalidPathException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -44,6 +45,8 @@
 import org.openstreetmap.josm.spi.preferences.AbstractPreferences;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.spi.preferences.DefaultPreferenceChangeEvent;
 import org.openstreetmap.josm.spi.preferences.IBaseDirectories;
 import org.openstreetmap.josm.spi.preferences.ListSetting;
+import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
 import org.openstreetmap.josm.spi.preferences.Setting;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -51,4 +54,5 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.PlatformManager;
+import org.openstreetmap.josm.tools.ReflectionUtils;
 import org.openstreetmap.josm.tools.Utils;
 import org.xml.sax.SAXException;
@@ -120,4 +124,12 @@
 
     /**
+     * Preferences classes calling directly the method {@link #putSetting(String, Setting)}.
+     * This collection allows us to exclude them when searching the business class who set a preference.
+     * The found class is used as event source when notifying event listeners.
+     */
+    private static final Collection<Class<?>> preferencesClasses = Arrays.asList(
+            Preferences.class, PreferencesUtils.class, AbstractPreferences.class);
+
+    /**
      * Constructs a new {@code Preferences}.
      */
@@ -216,6 +228,6 @@
 
     protected void firePreferenceChanged(String key, Setting<?> oldValue, Setting<?> newValue) {
-        final org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent evt =
-                new org.openstreetmap.josm.spi.preferences.DefaultPreferenceChangeEvent(key, oldValue, newValue);
+        final PreferenceChangeEvent evt =
+                new DefaultPreferenceChangeEvent(ReflectionUtils.findCallerClass(preferencesClasses), key, oldValue, newValue);
         listeners.fireEvent(listener -> listener.preferenceChanged(evt));
 
Index: trunk/src/org/openstreetmap/josm/data/StructUtils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/StructUtils.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/data/StructUtils.java	(revision 14977)
@@ -33,5 +33,5 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.MultiMap;
-import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.ReflectionUtils;
 
 /**
@@ -161,5 +161,5 @@
             }
             try {
-                Utils.setObjectsAccessible(f);
+                ReflectionUtils.setObjectsAccessible(f);
                 Object fieldValue = f.get(struct);
                 Object defaultFieldValue = f.get(structPrototype);
@@ -215,5 +215,5 @@
                 continue;
             }
-            Utils.setObjectsAccessible(f);
+            ReflectionUtils.setObjectsAccessible(f);
             if (f.getType() == Boolean.class || f.getType() == boolean.class) {
                 value = Boolean.valueOf(keyValue.getValue());
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14977)
@@ -158,4 +158,5 @@
 import org.openstreetmap.josm.tools.PlatformHookWindows;
 import org.openstreetmap.josm.tools.PlatformManager;
+import org.openstreetmap.josm.tools.ReflectionUtils;
 import org.openstreetmap.josm.tools.Shortcut;
 import org.openstreetmap.josm.tools.Utils;
@@ -991,5 +992,5 @@
             try {
                 Field field = Toolkit.class.getDeclaredField("resources");
-                Utils.setObjectsAccessible(field);
+                ReflectionUtils.setObjectsAccessible(field);
                 field.set(null, ResourceBundle.getBundle("sun.awt.resources.awt"));
             } catch (ReflectiveOperationException | RuntimeException e) { // NOPMD
Index: trunk/src/org/openstreetmap/josm/gui/MapStatus.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapStatus.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/MapStatus.java	(revision 14977)
@@ -253,5 +253,5 @@
             tr("The name of the object at the mouse pointer."),
             getNameLabelCharacterCount(MainApplication.getMainFrame()), PROP_BACKGROUND_COLOR.get());
-    private final JosmTextField helpText = new JosmTextField();
+    private final JosmTextField helpText = new JosmTextField(null, null, 0, false);
     private final JProgressBar progressBar = new JProgressBar();
     private final transient ComponentAdapter mvComponentAdapter;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 14977)
@@ -57,10 +57,10 @@
 
     // CHECKSTYLE.OFF: SingleSpaceSeparator
-    private final JosmTextField tfID        = new JosmTextField(10);
+    private final JosmTextField tfID        = new JosmTextField(null, null, 10, false);
     private final JosmTextArea  taComment   = new JosmTextArea(5, 40);
-    private final JosmTextField tfOpen      = new JosmTextField(10);
-    private final JosmTextField tfUser      = new JosmTextField("");
-    private final JosmTextField tfCreatedOn = new JosmTextField(20);
-    private final JosmTextField tfClosedOn  = new JosmTextField(20);
+    private final JosmTextField tfOpen      = new JosmTextField(null, null, 10, false);
+    private final JosmTextField tfUser      = new JosmTextField(null, "", 0);
+    private final JosmTextField tfCreatedOn = new JosmTextField(null, null, 20, false);
+    private final JosmTextField tfClosedOn  = new JosmTextField(null, null, 20, false);
 
     private final OpenChangesetPopupMenuAction   actOpenChangesetPopupMenu   = new OpenChangesetPopupMenuAction();
Index: trunk/src/org/openstreetmap/josm/gui/io/BasicUploadSettingsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/BasicUploadSettingsPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/io/BasicUploadSettingsPanel.java	(revision 14977)
@@ -17,4 +17,5 @@
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
@@ -33,5 +34,4 @@
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Utils;
@@ -78,7 +78,5 @@
         hcbUploadComment.setToolTipText(tr("Enter an upload comment"));
         hcbUploadComment.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH);
-        List<String> cmtHistory = new LinkedList<>(Config.getPref().getList(HISTORY_KEY, new LinkedList<String>()));
-        Collections.reverse(cmtHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
-        hcbUploadComment.setPossibleItems(cmtHistory);
+        populateHistoryComboBox(hcbUploadComment, HISTORY_KEY, new LinkedList<String>());
         CommentModelListener commentModelListener = new CommentModelListener(hcbUploadComment, changesetCommentModel);
         hcbUploadComment.getEditor().addActionListener(commentModelListener);
@@ -92,6 +90,5 @@
                 final String source = MainApplication.getMap().mapView.getLayerInformationForSourceTag();
                 hcbUploadSource.setText(Utils.shortenString(source, Changeset.MAX_CHANGESET_TAG_LENGTH));
-                // Fix #9965
-                changesetSourceModel.setComment(hcbUploadSource.getText());
+                changesetSourceModel.setComment(hcbUploadSource.getText()); // Fix #9965
             }
         });
@@ -100,7 +97,5 @@
         hcbUploadSource.setToolTipText(tr("Enter a source"));
         hcbUploadSource.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH);
-        List<String> sourceHistory = new LinkedList<>(Config.getPref().getList(SOURCE_HISTORY_KEY, getDefaultSources()));
-        Collections.reverse(sourceHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
-        hcbUploadSource.setPossibleItems(sourceHistory);
+        populateHistoryComboBox(hcbUploadSource, SOURCE_HISTORY_KEY, getDefaultSources());
         CommentModelListener sourceModelListener = new CommentModelListener(hcbUploadSource, changesetSourceModel);
         hcbUploadSource.getEditor().addActionListener(sourceModelListener);
@@ -108,4 +103,27 @@
         pnl.add(hcbUploadSource, GBC.eol().fill(GBC.HORIZONTAL));
         return pnl;
+    }
+
+    /**
+     * Refreshes contents of upload history combo boxes from preferences.
+     */
+    protected void refreshHistoryComboBoxes() {
+        populateHistoryComboBox(hcbUploadComment, HISTORY_KEY, new LinkedList<String>());
+        populateHistoryComboBox(hcbUploadSource, SOURCE_HISTORY_KEY, getDefaultSources());
+    }
+
+    private static void populateHistoryComboBox(HistoryComboBox hcb, String historyKey, List<String> defaultValues) {
+        List<String> cmtHistory = new LinkedList<>(Config.getPref().getList(historyKey, defaultValues));
+        Collections.reverse(cmtHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
+        hcb.setPossibleItems(cmtHistory);
+        hcb.discardAllUndoableEdits();
+    }
+
+    /**
+     * Discards undoable edits of upload history combo boxes.
+     */
+    protected void discardAllUndoableEdits() {
+        hcbUploadComment.discardAllUndoableEdits();
+        hcbUploadSource.discardAllUndoableEdits();
     }
 
@@ -133,15 +151,12 @@
      * @param changesetSourceModel the model for the changeset source. Must not be null.
      * @param changesetReviewModel the model for the changeset review. Must not be null.
-     * @throws IllegalArgumentException if {@code changesetCommentModel} is null
+     * @throws NullPointerException if a model is null
      * @since 12719 (signature)
      */
     public BasicUploadSettingsPanel(ChangesetCommentModel changesetCommentModel, ChangesetCommentModel changesetSourceModel,
             ChangesetReviewModel changesetReviewModel) {
-        CheckParameterUtil.ensureParameterNotNull(changesetCommentModel, "changesetCommentModel");
-        CheckParameterUtil.ensureParameterNotNull(changesetSourceModel, "changesetSourceModel");
-        CheckParameterUtil.ensureParameterNotNull(changesetReviewModel, "changesetReviewModel");
-        this.changesetCommentModel = changesetCommentModel;
-        this.changesetSourceModel = changesetSourceModel;
-        this.changesetReviewModel = changesetReviewModel;
+        this.changesetCommentModel = Objects.requireNonNull(changesetCommentModel, "changesetCommentModel");
+        this.changesetSourceModel = Objects.requireNonNull(changesetSourceModel, "changesetSourceModel");
+        this.changesetReviewModel = Objects.requireNonNull(changesetReviewModel, "changesetReviewModel");
         changesetCommentModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadComment));
         changesetSourceModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadSource));
@@ -167,5 +182,5 @@
         hcbUploadComment.addCurrentItemToHistory();
         Config.getPref().putList(HISTORY_KEY, hcbUploadComment.getHistory());
-        Config.getPref().putInt(HISTORY_LAST_USED_KEY, (int) (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+        Config.getPref().putLong(HISTORY_LAST_USED_KEY, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
         // store the history of sources
         hcbUploadSource.addCurrentItemToHistory();
@@ -197,6 +212,25 @@
     }
 
+    /**
+     * Returns the panel that displays a summary of data the user is about to upload.
+     * @return the upload parameter summary panel
+     */
     public UploadParameterSummaryPanel getUploadParameterSummaryPanel() {
         return pnlUploadParameterSummary;
+    }
+
+    /**
+     * Forces update of comment/source model if matching text field is focused.
+     * @since 14977
+     */
+    public void forceUpdateActiveField() {
+        updateModelIfFocused(hcbUploadComment, changesetCommentModel);
+        updateModelIfFocused(hcbUploadSource, changesetSourceModel);
+    }
+
+    private static void updateModelIfFocused(HistoryComboBox hcb, ChangesetCommentModel changesetModel) {
+        if (hcb.getEditorComponent().hasFocus()) {
+            changesetModel.setComment(hcb.getText());
+        }
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 14977)
@@ -33,15 +33,13 @@
 
 /**
- * ChangesetManagementPanel allows to configure changeset to be used in the next
- * upload.
+ * ChangesetManagementPanel allows to configure changeset to be used in the next upload.
  *
  * It is displayed as one of the configuration panels in the {@link UploadDialog}.
  *
- * ChangesetManagementPanel is a source for {@link java.beans.PropertyChangeEvent}s. Clients can listen
- * to
+ * ChangesetManagementPanel is a source for {@link java.beans.PropertyChangeEvent}s. Clients can listen to
  * <ul>
  *   <li>{@link #SELECTED_CHANGESET_PROP}  - the new value in the property change event is
  *   the changeset selected by the user. The value is null if the user didn't select a
- *   a changeset or if he chosed to use a new changeset.</li>
+ *   a changeset or if he chose to use a new changeset.</li>
  *   <li> {@link #CLOSE_CHANGESET_AFTER_UPLOAD} - the new value is a boolean value indicating
  *   whether the changeset should be closed after the next upload</li>
Index: trunk/src/org/openstreetmap/josm/gui/io/IUploadDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/IUploadDialog.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/io/IUploadDialog.java	(revision 14977)
@@ -66,3 +66,9 @@
      */
     void handleIllegalChunkSize();
+
+    /**
+     * Forces update of comment/source model if matching text field is active.
+     * @since 14977
+     */
+    void forceUpdateActiveField();
 }
Index: trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java	(revision 14977)
@@ -4,4 +4,5 @@
 import java.awt.BorderLayout;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 
@@ -17,5 +18,4 @@
 import org.openstreetmap.josm.gui.tagging.TagModel;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
 
 /**
@@ -38,15 +38,12 @@
      * @param changesetSourceModel the changeset source model. Must not be null.
      * @param changesetReviewModel the model for the changeset review. Must not be null.
-     * @throws IllegalArgumentException if {@code changesetCommentModel} is null
+     * @throws NullPointerException if a model is null
      * @since 12719 (signature)
      */
     public TagSettingsPanel(ChangesetCommentModel changesetCommentModel, ChangesetCommentModel changesetSourceModel,
             ChangesetReviewModel changesetReviewModel) {
-        CheckParameterUtil.ensureParameterNotNull(changesetCommentModel, "changesetCommentModel");
-        CheckParameterUtil.ensureParameterNotNull(changesetSourceModel, "changesetSourceModel");
-        CheckParameterUtil.ensureParameterNotNull(changesetReviewModel, "changesetReviewModel");
-        this.changesetCommentModel = changesetCommentModel;
-        this.changesetSourceModel = changesetSourceModel;
-        this.changesetReviewModel = changesetReviewModel;
+        this.changesetCommentModel = Objects.requireNonNull(changesetCommentModel, "changesetCommentModel");
+        this.changesetSourceModel = Objects.requireNonNull(changesetSourceModel, "changesetSourceModel");
+        this.changesetReviewModel = Objects.requireNonNull(changesetReviewModel, "changesetReviewModel");
         changesetCommentModel.addChangeListener(new ChangesetCommentChangeListener("comment", "hashtags"));
         changesetSourceModel.addChangeListener(new ChangesetCommentChangeListener("source"));
Index: trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 14977)
@@ -283,9 +283,27 @@
      */
     public void setChangesetTags(DataSet dataSet) {
+        setChangesetTags(dataSet, false);
+    }
+
+    /**
+     * Sets the tags for this upload based on (later items overwrite earlier ones):
+     * <ul>
+     * <li>previous "source" and "comment" input</li>
+     * <li>the tags set in the dataset (see {@link DataSet#getChangeSetTags()})</li>
+     * <li>the tags from the selected open changeset</li>
+     * <li>the JOSM user agent (see {@link Version#getAgentString(boolean)})</li>
+     * </ul>
+     *
+     * @param dataSet to obtain the tags set in the dataset
+     * @param keepSourceComment if {@code true}, keep upload {@code source} and {@code comment} current values from models
+     */
+    private void setChangesetTags(DataSet dataSet, boolean keepSourceComment) {
         final Map<String, String> tags = new HashMap<>();
 
         // obtain from previous input
-        tags.put("source", getLastChangesetSourceFromHistory());
-        tags.put("comment", getLastChangesetCommentFromHistory());
+        if (!keepSourceComment) {
+            tags.put("source", getLastChangesetSourceFromHistory());
+            tags.put("comment", getLastChangesetCommentFromHistory());
+        }
 
         // obtain from dataset
@@ -318,6 +336,13 @@
         }
 
+        // ignore source/comment to keep current values from models ?
+        if (keepSourceComment) {
+            tags.put("source", changesetSourceModel.getComment());
+            tags.put("comment", changesetCommentModel.getComment());
+        }
+
         pnlTagSettings.initFromTags(tags);
         pnlTagSettings.tableChanged(null);
+        pnlBasicUploadSettings.discardAllUndoableEdits();
     }
 
@@ -519,4 +544,7 @@
         @Override
         public void actionPerformed(ActionEvent e) {
+            // force update of model in case dialog is closed before focus lost event, see #17452
+            dialog.forceUpdateActiveField();
+
             if (isUploadCommentTooShort(dialog.getUploadComment()) && warnUploadComment()) {
                 // abort for missing comment
@@ -620,5 +648,5 @@
         if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
             Changeset cs = (Changeset) evt.getNewValue();
-            setChangesetTags(dataSet);
+            setChangesetTags(dataSet, cs == null); // keep comment/source of first tab for new changesets 
             if (cs == null) {
                 tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
@@ -634,7 +662,22 @@
     @Override
     public void preferenceChanged(PreferenceChangeEvent e) {
-        if (e.getKey() == null || !"osm-server.url".equals(e.getKey()))
-            return;
-        final Setting<?> newValue = e.getNewValue();
+        if (e.getKey() != null
+                && e.getSource() != getClass()
+                && e.getSource() != BasicUploadSettingsPanel.class) {
+            switch (e.getKey()) {
+                case "osm-server.url":
+                    osmServerUrlChanged(e.getNewValue());
+                    break;
+                case BasicUploadSettingsPanel.HISTORY_KEY:
+                case BasicUploadSettingsPanel.SOURCE_HISTORY_KEY:
+                    pnlBasicUploadSettings.refreshHistoryComboBoxes();
+                    break;
+                default:
+                    return;
+            }
+        }
+    }
+
+    private void osmServerUrlChanged(Setting<?> newValue) {
         final String url;
         if (newValue == null || newValue.getValue() == null) {
@@ -648,11 +691,10 @@
     private static String getLastChangesetTagFromHistory(String historyKey, List<String> def) {
         Collection<String> history = Config.getPref().getList(historyKey, def);
-        int age = (int) (System.currentTimeMillis() / 1000 - Config.getPref().getInt(BasicUploadSettingsPanel.HISTORY_LAST_USED_KEY, 0));
-        if (history != null && age < Config.getPref().getLong(BasicUploadSettingsPanel.HISTORY_MAX_AGE_KEY, TimeUnit.HOURS.toMillis(4))
+        long age = System.currentTimeMillis() / 1000 - Config.getPref().getLong(BasicUploadSettingsPanel.HISTORY_LAST_USED_KEY, 0);
+        if (age < Config.getPref().getLong(BasicUploadSettingsPanel.HISTORY_MAX_AGE_KEY, TimeUnit.HOURS.toSeconds(4))
                 && !history.isEmpty()) {
             return history.iterator().next();
-        } else {
-            return null;
-        }
+        }
+        return null;
     }
 
@@ -661,5 +703,5 @@
      * @return the last changeset comment from history
      */
-    public String getLastChangesetCommentFromHistory() {
+    public static String getLastChangesetCommentFromHistory() {
         return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.HISTORY_KEY, new ArrayList<String>());
     }
@@ -669,5 +711,5 @@
      * @return the last changeset source from history
      */
-    public String getLastChangesetSourceFromHistory() {
+    public static String getLastChangesetSourceFromHistory() {
         return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.SOURCE_HISTORY_KEY, BasicUploadSettingsPanel.getDefaultSources());
     }
@@ -695,4 +737,11 @@
     }
 
+    @Override
+    public void forceUpdateActiveField() {
+        if (tpConfigPanels.getSelectedComponent() == pnlBasicUploadSettings) {
+            pnlBasicUploadSettings.forceUpdateActiveField();
+        }
+    }
+
     /**
      * Clean dialog state and release resources.
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java	(revision 14977)
@@ -22,6 +22,6 @@
 public class AccessTokenInfoPanel extends JPanel {
 
-    private final JosmTextField tfAccessTokenKey = new JosmTextField();
-    private final JosmTextField tfAccessTokenSecret = new JosmTextField();
+    private final JosmTextField tfAccessTokenKey = new JosmTextField(null, null, 0, false);
+    private final JosmTextField tfAccessTokenSecret = new JosmTextField(null, null, 0, false);
     private final JCheckBox cbSaveAccessTokenInPreferences = new JCheckBox(tr("Save Access Token in preferences"));
 
Index: trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java	(revision 14977)
@@ -211,5 +211,5 @@
     private class RetrieveAccessTokenPanel extends JPanel {
 
-        private final JosmTextField tfAuthoriseUrl = new JosmTextField();
+        private final JosmTextField tfAuthoriseUrl = new JosmTextField(null, null, 0, false);
 
         /**
Index: trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 14977)
@@ -212,6 +212,6 @@
      */
     private class AlreadyAuthorisedPanel extends JPanel {
-        private final JosmTextField tfAccessTokenKey = new JosmTextField();
-        private final JosmTextField tfAccessTokenSecret = new JosmTextField();
+        private final JosmTextField tfAccessTokenKey = new JosmTextField(null, null, 0, false);
+        private final JosmTextField tfAccessTokenSecret = new JosmTextField(null, null, 0, false);
 
         /**
Index: trunk/src/org/openstreetmap/josm/gui/widgets/JosmComboBox.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/JosmComboBox.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/JosmComboBox.java	(revision 14977)
@@ -34,4 +34,6 @@
  */
 public class JosmComboBox<E> extends JComboBox<E> {
+
+    private final ContextMenuHandler handler = new ContextMenuHandler();
 
     /**
@@ -165,4 +167,8 @@
 
     protected final void init(E prototype) {
+        init(prototype, true);
+    }
+
+    protected final void init(E prototype, boolean registerPropertyChangeListener) {
         if (prototype != null) {
             setPrototypeDisplayValue(prototype);
@@ -186,7 +192,8 @@
         }
         // Handle text contextual menus for editable comboboxes
-        ContextMenuHandler handler = new ContextMenuHandler();
-        addPropertyChangeListener("editable", handler);
-        addPropertyChangeListener("editor", handler);
+        if (registerPropertyChangeListener) {
+            addPropertyChangeListener("editable", handler);
+            addPropertyChangeListener("editor", handler);
+        }
     }
 
@@ -232,4 +239,10 @@
         }
 
+        private void discardAllUndoableEdits() {
+            if (launcher != null) {
+                launcher.discardAllUndoableEdits();
+            }
+        }
+
         @Override
         public void mousePressed(MouseEvent e) {
@@ -250,10 +263,19 @@
 
     /**
-     * Reinitializes this {@link JosmComboBox} to the specified values. This may needed if a custom renderer is used.
+     * Reinitializes this {@link JosmComboBox} to the specified values. This may be needed if a custom renderer is used.
      * @param values The values displayed in the combo box.
      * @since 5558
      */
     public final void reinitialize(Collection<E> values) {
-        init(findPrototypeDisplayValue(values));
+        init(findPrototypeDisplayValue(values), false);
+        discardAllUndoableEdits();
+    }
+
+    /**
+     * Empties the internal undo manager, if any.
+     * @since 14977
+     */
+    public final void discardAllUndoableEdits() {
+        handler.discardAllUndoableEdits();
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/widgets/JosmImageView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/JosmImageView.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/JosmImageView.java	(revision 14977)
@@ -17,5 +17,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.ReflectionUtils;
 
 /**
@@ -48,5 +48,5 @@
         widthField = getDeclaredField("width");
         heightField = getDeclaredField("height");
-        Utils.setObjectsAccessible(imageField, stateField, widthField, heightField);
+        ReflectionUtils.setObjectsAccessible(imageField, stateField, widthField, heightField);
     }
 
@@ -108,5 +108,5 @@
             // And update the size params
             Method updateImageSize = ImageView.class.getDeclaredMethod("updateImageSize");
-            Utils.setObjectsAccessible(updateImageSize);
+            ReflectionUtils.setObjectsAccessible(updateImageSize);
             updateImageSize.invoke(this);
         } finally {
@@ -136,5 +136,5 @@
             } else {
                 Method loadImage = ImageView.class.getDeclaredMethod("loadImage");
-                Utils.setObjectsAccessible(loadImage);
+                ReflectionUtils.setObjectsAccessible(loadImage);
                 loadImage.invoke(this);
             }
Index: trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/JosmTextField.java	(revision 14977)
@@ -146,4 +146,12 @@
     public final void setHint(String hint) {
         this.hint = hint;
+    }
+
+    /**
+     * Empties the internal undo manager.
+     * @since 14977
+     */
+    public final void discardAllUndoableEdits() {
+        launcher.discardAllUndoableEdits();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuLauncher.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuLauncher.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/PopupMenuLauncher.java	(revision 14977)
@@ -155,3 +155,13 @@
         return menu;
     }
+
+    /**
+     * Empties the internal undo manager, if any.
+     * @since 14977
+     */
+    public void discardAllUndoableEdits() {
+        if (menu instanceof TextContextualPopupMenu) {
+            ((TextContextualPopupMenu) menu).discardAllUndoableEdits();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/widgets/TextContextualPopupMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/TextContextualPopupMenu.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/TextContextualPopupMenu.java	(revision 14977)
@@ -61,6 +61,5 @@
     protected final transient UndoableEditListener undoEditListener = e -> {
         undo.addEdit(e.getEdit());
-        undoAction.updateUndoState();
-        redoAction.updateRedoState();
+        updateUndoRedoState();
     };
 
@@ -77,4 +76,9 @@
     protected TextContextualPopupMenu() {
         // Restricts visibility
+    }
+
+    private void updateUndoRedoState() {
+        undoAction.updateUndoState();
+        redoAction.updateRedoState();
     }
 
@@ -90,13 +94,6 @@
         if (component != null && !isAttached()) {
             this.component = component;
-            this.undoRedo = undoRedo;
             if (undoRedo && component.isEditable()) {
-                component.getDocument().addUndoableEditListener(undoEditListener);
-                if (!GraphicsEnvironment.isHeadless()) {
-                    component.getInputMap().put(
-                            KeyStroke.getKeyStroke(KeyEvent.VK_Z, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()), undoAction);
-                    component.getInputMap().put(
-                            KeyStroke.getKeyStroke(KeyEvent.VK_Y, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()), redoAction);
-                }
+                enableUndoRedo();
             }
             addMenuEntries();
@@ -104,4 +101,30 @@
         }
         return this;
+    }
+
+    private void enableUndoRedo() {
+        if (!undoRedo) {
+            component.getDocument().addUndoableEditListener(undoEditListener);
+            if (!GraphicsEnvironment.isHeadless()) {
+                component.getInputMap().put(
+                        KeyStroke.getKeyStroke(KeyEvent.VK_Z, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()), undoAction);
+                component.getInputMap().put(
+                        KeyStroke.getKeyStroke(KeyEvent.VK_Y, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()), redoAction);
+            }
+            undoRedo = true;
+        }
+    }
+
+    private void disableUndoRedo() {
+        if (undoRedo) {
+            if (!GraphicsEnvironment.isHeadless()) {
+                component.getInputMap().remove(
+                        KeyStroke.getKeyStroke(KeyEvent.VK_Z, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()));
+                component.getInputMap().remove(
+                        KeyStroke.getKeyStroke(KeyEvent.VK_Y, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()));
+            }
+            component.getDocument().removeUndoableEditListener(undoEditListener);
+            undoRedo = false;
+        }
     }
 
@@ -134,5 +157,5 @@
             removeAll();
             if (undoRedo) {
-                component.getDocument().removeUndoableEditListener(undoEditListener);
+                disableUndoRedo();
             }
             component = null;
@@ -166,4 +189,13 @@
             component.removeMouseListener(launcher);
         }
+    }
+
+    /**
+     * Empties the internal undo manager.
+     * @since 14977
+     */
+    public void discardAllUndoableEdits() {
+        undo.discardAllEdits();
+        updateUndoRedoState();
     }
 
Index: trunk/src/org/openstreetmap/josm/spi/preferences/DefaultPreferenceChangeEvent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/spi/preferences/DefaultPreferenceChangeEvent.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/spi/preferences/DefaultPreferenceChangeEvent.java	(revision 14977)
@@ -1,4 +1,6 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.spi.preferences;
+
+import java.util.EventObject;
 
 /**
@@ -6,14 +8,28 @@
  * @since 12881
  */
-public class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
-    
+public class DefaultPreferenceChangeEvent extends EventObject implements PreferenceChangeEvent {
+
     private final String key;
     private final Setting<?> oldValue;
     private final Setting<?> newValue;
 
-    public DefaultPreferenceChangeEvent(String key, Setting<?> oldValue, Setting<?> newValue) {
+    /**
+     * Constructs a new {@code DefaultPreferenceChangeEvent}.
+     * @param source the class source of this event
+     * @param key preference key
+     * @param oldValue preference old value
+     * @param newValue preference new value
+     * @since 14977
+     */
+    public DefaultPreferenceChangeEvent(Class<?> source, String key, Setting<?> oldValue, Setting<?> newValue) {
+        super(source);
         this.key = key;
         this.oldValue = oldValue;
         this.newValue = newValue;
+    }
+
+    @Override
+    public Class<?> getSource() {
+        return (Class<?>) super.getSource();
     }
 
@@ -32,4 +48,4 @@
         return newValue;
     }
-    
+
 }
Index: trunk/src/org/openstreetmap/josm/spi/preferences/PreferenceChangeEvent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/spi/preferences/PreferenceChangeEvent.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/spi/preferences/PreferenceChangeEvent.java	(revision 14977)
@@ -7,4 +7,11 @@
  */
 public interface PreferenceChangeEvent {
+
+    /**
+     * Returns the class source of this event.
+     * @return The class source of this event
+     * @since 14977
+     */
+    Class<?> getSource();
 
     /**
@@ -25,4 +32,3 @@
      */
     Setting<?> getNewValue();
-    
 }
Index: trunk/src/org/openstreetmap/josm/tools/ReflectionUtils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ReflectionUtils.java	(revision 14977)
+++ trunk/src/org/openstreetmap/josm/tools/ReflectionUtils.java	(revision 14977)
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.lang.reflect.AccessibleObject;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.function.Function;
+
+/**
+ * Reflection utilities.
+ * @since 14977
+ */
+public final class ReflectionUtils {
+
+    private ReflectionUtils() {
+        // Hide default constructor for utils classes
+    }
+
+    /**
+     * Sets {@code AccessibleObject}(s) accessible.
+     * @param objects objects
+     * @see AccessibleObject#setAccessible
+     */
+    public static void setObjectsAccessible(final AccessibleObject... objects) {
+        if (objects != null && objects.length > 0) {
+            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+                for (AccessibleObject o : objects) {
+                    if (o != null) {
+                        o.setAccessible(true);
+                    }
+                }
+                return null;
+            });
+        }
+    }
+
+    /**
+     * To use from a method to know which class called it.
+     * @param exclusions classes to exclude from the search. Can be null
+     * @return the first calling class not present in {@code exclusions}
+     */
+    public static Class<?> findCallerClass(Collection<Class<?>> exclusions) {
+        return findCaller(x -> {
+            try {
+                return Class.forName(x.getClassName());
+            } catch (ClassNotFoundException e) {
+                Logging.error(e);
+                return null;
+            }
+        }, exclusions);
+    }
+
+    private static <T extends Object> T findCaller(Function<StackTraceElement, T> getter, Collection<T> exclusions) {
+        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+        for (int i = 3; i < stack.length; i++) {
+            T t = getter.apply(stack[i]);
+            if (exclusions == null || !exclusions.contains(t)) {
+                return t;
+            }
+        }
+        return null;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 14977)
@@ -30,8 +30,6 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
-import java.security.AccessController;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.PrivilegedAction;
 import java.text.Bidi;
 import java.text.DateFormat;
@@ -1559,17 +1557,10 @@
      * @param objects objects
      * @see AccessibleObject#setAccessible
+     * @deprecated Use {@link ReflectionUtils#setObjectsAccessible(AccessibleObject...)}
      * @since 10223
      */
+    @Deprecated
     public static void setObjectsAccessible(final AccessibleObject... objects) {
-        if (objects != null && objects.length > 0) {
-            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
-                for (AccessibleObject o : objects) {
-                    if (o != null) {
-                        o.setAccessible(true);
-                    }
-                }
-                return null;
-            });
-        }
+        ReflectionUtils.setObjectsAccessible(objects);
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/WinRegistry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/WinRegistry.java	(revision 14976)
+++ trunk/src/org/openstreetmap/josm/tools/WinRegistry.java	(revision 14977)
@@ -63,5 +63,5 @@
         regQueryInfoKey = getDeclaredMethod("WindowsRegQueryInfoKey1", int.class);
         regEnumKeyEx = getDeclaredMethod("WindowsRegEnumKeyEx", int.class, int.class, int.class);
-        Utils.setObjectsAccessible(regOpenKey, regCloseKey, regQueryValueEx, regEnumValue, regQueryInfoKey, regEnumKeyEx);
+        ReflectionUtils.setObjectsAccessible(regOpenKey, regCloseKey, regQueryValueEx, regEnumValue, regQueryInfoKey, regEnumKeyEx);
     }
 
