Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 11325)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 11326)
@@ -5,43 +5,19 @@
 
 import java.awt.BorderLayout;
-import java.awt.Color;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
-import java.awt.Insets;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
-import java.time.LocalDate;
-import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.time.format.FormatStyle;
-import java.util.Date;
 
 import javax.swing.BorderFactory;
-import javax.swing.ButtonGroup;
 import javax.swing.JCheckBox;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
-import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
-import javax.swing.text.JTextComponent;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.JosmUserIdentityManager;
-import org.openstreetmap.josm.gui.help.HelpUtil;
-import org.openstreetmap.josm.gui.preferences.server.UserNameValidator;
 import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
-import org.openstreetmap.josm.gui.widgets.BoundingBoxSelectionPanel;
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
-import org.openstreetmap.josm.gui.widgets.JosmTextField;
-import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
 import org.openstreetmap.josm.io.ChangesetQuery;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
 
 /**
@@ -166,4 +142,7 @@
     }
 
+    /**
+     * Initializes HMI for user input.
+     */
     public void startUserInput() {
         restoreFromSettings();
@@ -177,4 +156,7 @@
     }
 
+    /**
+     * Display error message if a field is invalid.
+     */
     public void displayMessageIfInvalid() {
         if (cbUserRestriction.isSelected()) {
@@ -223,4 +205,7 @@
     }
 
+    /**
+     * Remember settings in preferences.
+     */
     public void rememberSettings() {
         Main.pref.put("changeset-query.advanced.user-restrictions", cbUserRestriction.isSelected());
@@ -234,4 +219,7 @@
     }
 
+    /**
+     * Restore settings from preferences.
+     */
     public void restoreFromSettings() {
         cbUserRestriction.setSelected(Main.pref.getBoolean("changeset-query.advanced.user-restrictions", false));
@@ -281,872 +269,3 @@
         }
     }
-
-    /**
-     * This is the panel for selecting whether the changeset query should be restricted to
-     * open or closed changesets
-     */
-    private static class OpenAndCloseStateRestrictionPanel extends JPanel {
-
-        private final JRadioButton rbOpenOnly = new JRadioButton();
-        private final JRadioButton rbClosedOnly = new JRadioButton();
-        private final JRadioButton rbBoth = new JRadioButton();
-
-        OpenAndCloseStateRestrictionPanel() {
-            build();
-        }
-
-        protected void build() {
-            setLayout(new GridBagLayout());
-            setBorder(BorderFactory.createCompoundBorder(
-                    BorderFactory.createEmptyBorder(3, 3, 3, 3),
-                    BorderFactory.createCompoundBorder(
-                            BorderFactory.createLineBorder(Color.GRAY),
-                            BorderFactory.createEmptyBorder(5, 5, 5, 5)
-                    )
-            ));
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.anchor = GridBagConstraints.NORTHWEST;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbOpenOnly, gc);
-
-            gc.gridx = 1;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Query open changesets only")), gc);
-
-            gc.gridy = 1;
-            gc.gridx = 0;
-            gc.weightx = 0.0;
-            add(rbClosedOnly, gc);
-
-            gc.gridx = 1;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Query closed changesets only")), gc);
-
-            gc.gridy = 2;
-            gc.gridx = 0;
-            gc.weightx = 0.0;
-            add(rbBoth, gc);
-
-            gc.gridx = 1;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Query both open and closed changesets")), gc);
-
-            ButtonGroup bgRestrictions = new ButtonGroup();
-            bgRestrictions.add(rbBoth);
-            bgRestrictions.add(rbClosedOnly);
-            bgRestrictions.add(rbOpenOnly);
-        }
-
-        public void startUserInput() {
-            restoreFromSettings();
-        }
-
-        public void fillInQuery(ChangesetQuery query) {
-            if (rbBoth.isSelected()) {
-                query.beingClosed(true);
-                query.beingOpen(true);
-            } else if (rbOpenOnly.isSelected()) {
-                query.beingOpen(true);
-            } else if (rbClosedOnly.isSelected()) {
-                query.beingClosed(true);
-            }
-        }
-
-        public void rememberSettings() {
-            String prefRoot = "changeset-query.advanced.open-restrictions";
-            if (rbBoth.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "both");
-            } else if (rbOpenOnly.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "open");
-            } else if (rbClosedOnly.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "closed");
-            }
-        }
-
-        public void restoreFromSettings() {
-            String prefRoot = "changeset-query.advanced.open-restrictions";
-            String v = Main.pref.get(prefRoot + ".query-type", "open");
-            rbBoth.setSelected("both".equals(v));
-            rbOpenOnly.setSelected("open".equals(v));
-            rbClosedOnly.setSelected("closed".equals(v));
-        }
-    }
-
-    /**
-     * This is the panel for selecting whether the query should be restricted to a specific user
-     */
-    private static class UserRestrictionPanel extends JPanel {
-        private final ButtonGroup bgUserRestrictions = new ButtonGroup();
-        private final JRadioButton rbRestrictToMyself = new JRadioButton();
-        private final JRadioButton rbRestrictToUid = new JRadioButton();
-        private final JRadioButton rbRestrictToUserName = new JRadioButton();
-        private final JosmTextField tfUid = new JosmTextField(10);
-        private transient UidInputFieldValidator valUid;
-        private final JosmTextField tfUserName = new JosmTextField(10);
-        private transient UserNameValidator valUserName;
-        private final JMultilineLabel lblRestrictedToMyself = new JMultilineLabel(tr("Only changesets owned by myself"));
-
-        UserRestrictionPanel() {
-            build();
-        }
-
-        protected JPanel buildUidInputPanel() {
-            JPanel pnl = new JPanel(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("User ID:")), gc);
-
-            gc.gridx = 1;
-            pnl.add(tfUid, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfUid);
-            valUid = UidInputFieldValidator.decorate(tfUid);
-
-            // grab remaining space
-            gc.gridx = 2;
-            gc.weightx = 1.0;
-            pnl.add(new JPanel(), gc);
-            return pnl;
-        }
-
-        protected JPanel buildUserNameInputPanel() {
-            JPanel pnl = new JPanel(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("User name:")), gc);
-
-            gc.gridx = 1;
-            pnl.add(tfUserName, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfUserName);
-            valUserName = new UserNameValidator(tfUserName);
-
-            // grab remaining space
-            gc.gridx = 2;
-            gc.weightx = 1.0;
-            pnl.add(new JPanel(), gc);
-            return pnl;
-        }
-
-        protected void build() {
-            setLayout(new GridBagLayout());
-            setBorder(BorderFactory.createCompoundBorder(
-                    BorderFactory.createEmptyBorder(3, 3, 3, 3),
-                    BorderFactory.createCompoundBorder(
-                            BorderFactory.createLineBorder(Color.GRAY),
-                            BorderFactory.createEmptyBorder(5, 5, 5, 5)
-                    )
-            ));
-
-            ItemListener userRestrictionChangeHandler = new UserRestrictionChangedHandler();
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.anchor = GridBagConstraints.NORTHWEST;
-            gc.gridx = 0;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbRestrictToMyself, gc);
-            rbRestrictToMyself.addItemListener(userRestrictionChangeHandler);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(lblRestrictedToMyself, gc);
-
-            gc.gridx = 0;
-            gc.gridy = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbRestrictToUid, gc);
-            rbRestrictToUid.addItemListener(userRestrictionChangeHandler);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Only changesets owned by the user with the following user ID")), gc);
-
-            gc.gridx = 1;
-            gc.gridy = 2;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(buildUidInputPanel(), gc);
-
-            gc.gridx = 0;
-            gc.gridy = 3;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbRestrictToUserName, gc);
-            rbRestrictToUserName.addItemListener(userRestrictionChangeHandler);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Only changesets owned by the user with the following user name")), gc);
-
-            gc.gridx = 1;
-            gc.gridy = 4;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(buildUserNameInputPanel(), gc);
-
-            bgUserRestrictions.add(rbRestrictToMyself);
-            bgUserRestrictions.add(rbRestrictToUid);
-            bgUserRestrictions.add(rbRestrictToUserName);
-        }
-
-        public void startUserInput() {
-            if (JosmUserIdentityManager.getInstance().isAnonymous()) {
-                lblRestrictedToMyself.setText(tr("Only changesets owned by myself (disabled. JOSM is currently run by an anonymous user)"));
-                rbRestrictToMyself.setEnabled(false);
-                if (rbRestrictToMyself.isSelected()) {
-                    rbRestrictToUid.setSelected(true);
-                }
-            } else {
-                lblRestrictedToMyself.setText(tr("Only changesets owned by myself"));
-                rbRestrictToMyself.setEnabled(true);
-                rbRestrictToMyself.setSelected(true);
-            }
-            restoreFromSettings();
-        }
-
-        /**
-         * Sets the query restrictions on <code>query</code> for changeset owner based
-         * restrictions.
-         *
-         * @param query the query. Must not be null.
-         * @throws IllegalArgumentException if query is null
-         * @throws IllegalStateException if one of the available values for query parameters in
-         * this panel isn't valid
-         */
-        public void fillInQuery(ChangesetQuery query) {
-            CheckParameterUtil.ensureParameterNotNull(query, "query");
-            if (rbRestrictToMyself.isSelected()) {
-                JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
-                if (im.isPartiallyIdentified()) {
-                    query.forUser(im.getUserName());
-                } else if (im.isFullyIdentified()) {
-                    query.forUser(im.getUserId());
-                } else
-                    throw new IllegalStateException(
-                            tr("Cannot restrict changeset query to the current user because the current user is anonymous"));
-            } else if (rbRestrictToUid.isSelected()) {
-                int uid = valUid.getUid();
-                if (uid > 0) {
-                    query.forUser(uid);
-                } else
-                    throw new IllegalStateException(tr("Current value ''{0}'' for user ID is not valid", tfUid.getText()));
-            } else if (rbRestrictToUserName.isSelected()) {
-                if (!valUserName.isValid())
-                    throw new IllegalStateException(
-                            tr("Cannot restrict the changeset query to the user name ''{0}''", tfUserName.getText()));
-                query.forUser(tfUserName.getText());
-            }
-        }
-
-        public boolean isValidChangesetQuery() {
-            if (rbRestrictToUid.isSelected())
-                return valUid.isValid();
-            else if (rbRestrictToUserName.isSelected())
-                return valUserName.isValid();
-            return true;
-        }
-
-        protected void alertInvalidUid() {
-            HelpAwareOptionPane.showOptionDialog(
-                    this,
-                    tr("Please enter a valid user ID"),
-                    tr("Invalid user ID"),
-                    JOptionPane.ERROR_MESSAGE,
-                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserId")
-            );
-        }
-
-        protected void alertInvalidUserName() {
-            HelpAwareOptionPane.showOptionDialog(
-                    this,
-                    tr("Please enter a non-empty user name"),
-                    tr("Invalid user name"),
-                    JOptionPane.ERROR_MESSAGE,
-                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserName")
-            );
-        }
-
-        public void displayMessageIfInvalid() {
-            if (rbRestrictToUid.isSelected()) {
-                if (!valUid.isValid()) {
-                    alertInvalidUid();
-                }
-            } else if (rbRestrictToUserName.isSelected()) {
-                if (!valUserName.isValid()) {
-                    alertInvalidUserName();
-                }
-            }
-        }
-
-        public void rememberSettings() {
-            String prefRoot = "changeset-query.advanced.user-restrictions";
-            if (rbRestrictToMyself.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "mine");
-            } else if (rbRestrictToUid.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "uid");
-            } else if (rbRestrictToUserName.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "username");
-            }
-            Main.pref.put(prefRoot + ".uid", tfUid.getText());
-            Main.pref.put(prefRoot + ".username", tfUserName.getText());
-        }
-
-        public void restoreFromSettings() {
-            String prefRoot = "changeset-query.advanced.user-restrictions";
-            String v = Main.pref.get(prefRoot + ".query-type", "mine");
-            if ("mine".equals(v)) {
-                JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
-                if (im.isAnonymous()) {
-                    rbRestrictToUid.setSelected(true);
-                } else {
-                    rbRestrictToMyself.setSelected(true);
-                }
-            } else if ("uid".equals(v)) {
-                rbRestrictToUid.setSelected(true);
-            } else if ("username".equals(v)) {
-                rbRestrictToUserName.setSelected(true);
-            }
-            tfUid.setText(Main.pref.get(prefRoot + ".uid", ""));
-            if (!valUid.isValid()) {
-                tfUid.setText("");
-            }
-            tfUserName.setText(Main.pref.get(prefRoot + ".username", ""));
-        }
-
-        class UserRestrictionChangedHandler implements ItemListener {
-            @Override
-            public void itemStateChanged(ItemEvent e) {
-                tfUid.setEnabled(rbRestrictToUid.isSelected());
-                tfUserName.setEnabled(rbRestrictToUserName.isSelected());
-                if (rbRestrictToUid.isSelected()) {
-                    tfUid.requestFocusInWindow();
-                } else if (rbRestrictToUserName.isSelected()) {
-                    tfUserName.requestFocusInWindow();
-                }
-            }
-        }
-    }
-
-    /**
-     * This is the panel to apply a time restriction to the changeset query
-     */
-    private static class TimeRestrictionPanel extends JPanel {
-
-        private final JRadioButton rbClosedAfter = new JRadioButton();
-        private final JRadioButton rbClosedAfterAndCreatedBefore = new JRadioButton();
-        private final JosmTextField tfClosedAfterDate1 = new JosmTextField();
-        private transient DateValidator valClosedAfterDate1;
-        private final JosmTextField tfClosedAfterTime1 = new JosmTextField();
-        private transient TimeValidator valClosedAfterTime1;
-        private final JosmTextField tfClosedAfterDate2 = new JosmTextField();
-        private transient DateValidator valClosedAfterDate2;
-        private final JosmTextField tfClosedAfterTime2 = new JosmTextField();
-        private transient TimeValidator valClosedAfterTime2;
-        private final JosmTextField tfCreatedBeforeDate = new JosmTextField();
-        private transient DateValidator valCreatedBeforeDate;
-        private final JosmTextField tfCreatedBeforeTime = new JosmTextField();
-        private transient TimeValidator valCreatedBeforeTime;
-
-        TimeRestrictionPanel() {
-            build();
-        }
-
-        protected JPanel buildClosedAfterInputPanel() {
-            JPanel pnl = new JPanel(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("Date: ")), gc);
-
-            gc.gridx = 1;
-            gc.weightx = 0.7;
-            pnl.add(tfClosedAfterDate1, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate1);
-            valClosedAfterDate1 = DateValidator.decorate(tfClosedAfterDate1);
-            tfClosedAfterDate1.setToolTipText(valClosedAfterDate1.getStandardTooltipTextAsHtml());
-
-            gc.gridx = 2;
-            gc.weightx = 0.0;
-            pnl.add(new JLabel(tr("Time:")), gc);
-
-            gc.gridx = 3;
-            gc.weightx = 0.3;
-            pnl.add(tfClosedAfterTime1, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime1);
-            valClosedAfterTime1 = TimeValidator.decorate(tfClosedAfterTime1);
-            tfClosedAfterTime1.setToolTipText(valClosedAfterTime1.getStandardTooltipTextAsHtml());
-            return pnl;
-        }
-
-        protected JPanel buildClosedAfterAndCreatedBeforeInputPanel() {
-            JPanel pnl = new JPanel(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("Closed after - ")), gc);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("Date:")), gc);
-
-            gc.gridx = 2;
-            gc.weightx = 0.7;
-            pnl.add(tfClosedAfterDate2, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate2);
-            valClosedAfterDate2 = DateValidator.decorate(tfClosedAfterDate2);
-            tfClosedAfterDate2.setToolTipText(valClosedAfterDate2.getStandardTooltipTextAsHtml());
-            gc.gridx = 3;
-            gc.weightx = 0.0;
-            pnl.add(new JLabel(tr("Time:")), gc);
-
-            gc.gridx = 4;
-            gc.weightx = 0.3;
-            pnl.add(tfClosedAfterTime2, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime2);
-            valClosedAfterTime2 = TimeValidator.decorate(tfClosedAfterTime2);
-            tfClosedAfterTime2.setToolTipText(valClosedAfterTime2.getStandardTooltipTextAsHtml());
-
-            gc.gridy = 1;
-            gc.gridx = 0;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("Created before - ")), gc);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            gc.insets = new Insets(0, 0, 0, 3);
-            pnl.add(new JLabel(tr("Date:")), gc);
-
-            gc.gridx = 2;
-            gc.weightx = 0.7;
-            pnl.add(tfCreatedBeforeDate, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeDate);
-            valCreatedBeforeDate = DateValidator.decorate(tfCreatedBeforeDate);
-            tfCreatedBeforeDate.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
-
-            gc.gridx = 3;
-            gc.weightx = 0.0;
-            pnl.add(new JLabel(tr("Time:")), gc);
-
-            gc.gridx = 4;
-            gc.weightx = 0.3;
-            pnl.add(tfCreatedBeforeTime, gc);
-            SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeTime);
-            valCreatedBeforeTime = TimeValidator.decorate(tfCreatedBeforeTime);
-            tfCreatedBeforeTime.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
-
-            return pnl;
-        }
-
-        protected void build() {
-            setLayout(new GridBagLayout());
-            setBorder(BorderFactory.createCompoundBorder(
-                    BorderFactory.createEmptyBorder(3, 3, 3, 3),
-                    BorderFactory.createCompoundBorder(
-                            BorderFactory.createLineBorder(Color.GRAY),
-                            BorderFactory.createEmptyBorder(5, 5, 5, 5)
-                    )
-            ));
-
-            // -- changesets closed after a specific date/time
-            //
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.anchor = GridBagConstraints.NORTHWEST;
-            gc.gridx = 0;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbClosedAfter, gc);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Only changesets closed after the following date/time")), gc);
-
-            gc.gridx = 1;
-            gc.gridy = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(buildClosedAfterInputPanel(), gc);
-
-            // -- changesets closed after a specific date/time and created before a specific date time
-            //
-            gc = new GridBagConstraints();
-            gc.anchor = GridBagConstraints.NORTHWEST;
-            gc.gridy = 2;
-            gc.gridx = 0;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 0.0;
-            add(rbClosedAfterAndCreatedBefore, gc);
-
-            gc.gridx = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(new JMultilineLabel(tr("Only changesets closed after and created before a specific date/time")), gc);
-
-            gc.gridx = 1;
-            gc.gridy = 3;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            add(buildClosedAfterAndCreatedBeforeInputPanel(), gc);
-
-            ButtonGroup bg = new ButtonGroup();
-            bg.add(rbClosedAfter);
-            bg.add(rbClosedAfterAndCreatedBefore);
-
-            ItemListener restrictionChangeHandler = new TimeRestrictionChangedHandler();
-            rbClosedAfter.addItemListener(restrictionChangeHandler);
-            rbClosedAfterAndCreatedBefore.addItemListener(restrictionChangeHandler);
-
-            rbClosedAfter.setSelected(true);
-        }
-
-        public boolean isValidChangesetQuery() {
-            if (rbClosedAfter.isSelected())
-                return valClosedAfterDate1.isValid() && valClosedAfterTime1.isValid();
-            else if (rbClosedAfterAndCreatedBefore.isSelected())
-                return valClosedAfterDate2.isValid() && valClosedAfterTime2.isValid()
-                && valCreatedBeforeDate.isValid() && valCreatedBeforeTime.isValid();
-            // should not happen
-            return true;
-        }
-
-        class TimeRestrictionChangedHandler implements ItemListener {
-            @Override
-            public void itemStateChanged(ItemEvent e) {
-                tfClosedAfterDate1.setEnabled(rbClosedAfter.isSelected());
-                tfClosedAfterTime1.setEnabled(rbClosedAfter.isSelected());
-
-                tfClosedAfterDate2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
-                tfClosedAfterTime2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
-                tfCreatedBeforeDate.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
-                tfCreatedBeforeTime.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
-            }
-        }
-
-        public void startUserInput() {
-            restoreFromSettings();
-        }
-
-        public void fillInQuery(ChangesetQuery query) {
-            if (!isValidChangesetQuery())
-                throw new IllegalStateException(tr("Cannot build changeset query with time based restrictions. Input is not valid."));
-            if (rbClosedAfter.isSelected()) {
-                LocalDate d1 = valClosedAfterDate1.getDate();
-                LocalTime d2 = valClosedAfterTime1.getDate();
-                final Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
-                query.closedAfter(d3);
-            } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
-                LocalDate d1 = valClosedAfterDate2.getDate();
-                LocalTime d2 = valClosedAfterTime2.getDate();
-                Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
-
-                d1 = valCreatedBeforeDate.getDate();
-                d2 = valCreatedBeforeTime.getDate();
-                Date d4 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
-
-                query.closedAfterAndCreatedBefore(d3, d4);
-            }
-        }
-
-        public void displayMessageIfInvalid() {
-            if (isValidChangesetQuery())
-                return;
-            HelpAwareOptionPane.showOptionDialog(
-                    this,
-                    tr(
-                            "<html>Please enter valid date/time values to restrict<br>"
-                            + "the query to a specific time range.</html>"
-                    ),
-                    tr("Invalid date/time values"),
-                    JOptionPane.ERROR_MESSAGE,
-                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidDateTimeValues")
-            );
-        }
-
-        public void rememberSettings() {
-            String prefRoot = "changeset-query.advanced.time-restrictions";
-            if (rbClosedAfter.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "closed-after");
-            } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
-                Main.pref.put(prefRoot + ".query-type", "closed-after-created-before");
-            }
-            Main.pref.put(prefRoot + ".closed-after.date", tfClosedAfterDate1.getText());
-            Main.pref.put(prefRoot + ".closed-after.time", tfClosedAfterTime1.getText());
-            Main.pref.put(prefRoot + ".closed-created.closed.date", tfClosedAfterDate2.getText());
-            Main.pref.put(prefRoot + ".closed-created.closed.time", tfClosedAfterTime2.getText());
-            Main.pref.put(prefRoot + ".closed-created.created.date", tfCreatedBeforeDate.getText());
-            Main.pref.put(prefRoot + ".closed-created.created.time", tfCreatedBeforeTime.getText());
-        }
-
-        public void restoreFromSettings() {
-            String prefRoot = "changeset-query.advanced.open-restrictions";
-            String v = Main.pref.get(prefRoot + ".query-type", "closed-after");
-            rbClosedAfter.setSelected("closed-after".equals(v));
-            rbClosedAfterAndCreatedBefore.setSelected("closed-after-created-before".equals(v));
-            if (!rbClosedAfter.isSelected() && !rbClosedAfterAndCreatedBefore.isSelected()) {
-                rbClosedAfter.setSelected(true);
-            }
-            tfClosedAfterDate1.setText(Main.pref.get(prefRoot + ".closed-after.date", ""));
-            tfClosedAfterTime1.setText(Main.pref.get(prefRoot + ".closed-after.time", ""));
-            tfClosedAfterDate2.setText(Main.pref.get(prefRoot + ".closed-created.closed.date", ""));
-            tfClosedAfterTime2.setText(Main.pref.get(prefRoot + ".closed-created.closed.time", ""));
-            tfCreatedBeforeDate.setText(Main.pref.get(prefRoot + ".closed-created.created.date", ""));
-            tfCreatedBeforeTime.setText(Main.pref.get(prefRoot + ".closed-created.created.time", ""));
-            if (!valClosedAfterDate1.isValid()) {
-                tfClosedAfterDate1.setText("");
-            }
-            if (!valClosedAfterTime1.isValid()) {
-                tfClosedAfterTime1.setText("");
-            }
-            if (!valClosedAfterDate2.isValid()) {
-                tfClosedAfterDate2.setText("");
-            }
-            if (!valClosedAfterTime2.isValid()) {
-                tfClosedAfterTime2.setText("");
-            }
-            if (!valCreatedBeforeDate.isValid()) {
-                tfCreatedBeforeDate.setText("");
-            }
-            if (!valCreatedBeforeTime.isValid()) {
-                tfCreatedBeforeTime.setText("");
-            }
-        }
-    }
-
-    private static class BBoxRestrictionPanel extends BoundingBoxSelectionPanel {
-        BBoxRestrictionPanel() {
-            setBorder(BorderFactory.createCompoundBorder(
-                    BorderFactory.createEmptyBorder(3, 3, 3, 3),
-                    BorderFactory.createCompoundBorder(
-                            BorderFactory.createLineBorder(Color.GRAY),
-                            BorderFactory.createEmptyBorder(5, 5, 5, 5)
-                    )
-            ));
-        }
-
-        public boolean isValidChangesetQuery() {
-            return getBoundingBox() != null;
-        }
-
-        public void fillInQuery(ChangesetQuery query) {
-            if (!isValidChangesetQuery())
-                throw new IllegalStateException(tr("Cannot restrict the changeset query to a specific bounding box. The input is invalid."));
-            query.inBbox(getBoundingBox());
-        }
-
-        public void displayMessageIfInvalid() {
-            if (isValidChangesetQuery())
-                return;
-            HelpAwareOptionPane.showOptionDialog(
-                    this,
-                    tr(
-                            "<html>Please enter valid longitude/latitude values to restrict<br>" +
-                            "the changeset query to a specific bounding box.</html>"
-                    ),
-                    tr("Invalid bounding box"),
-                    JOptionPane.ERROR_MESSAGE,
-                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidBoundingBox")
-            );
-        }
-    }
-
-    /**
-     * Validator for user ids entered in a {@link JTextComponent}.
-     *
-     */
-    private static class UidInputFieldValidator extends AbstractTextComponentValidator {
-        UidInputFieldValidator(JTextComponent tc) {
-            super(tc);
-        }
-
-        public static UidInputFieldValidator decorate(JTextComponent tc) {
-            return new UidInputFieldValidator(tc);
-        }
-
-        @Override
-        public boolean isValid() {
-            return getUid() > 0;
-        }
-
-        @Override
-        public void validate() {
-            String value = getComponent().getText();
-            if (value == null || value.trim().isEmpty()) {
-                feedbackInvalid("");
-                return;
-            }
-            try {
-                int uid = Integer.parseInt(value);
-                if (uid <= 0) {
-                    feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
-                    return;
-                }
-            } catch (NumberFormatException e) {
-                feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
-                return;
-            }
-            feedbackValid(tr("Please enter an integer value > 0"));
-        }
-
-        public int getUid() {
-            String value = getComponent().getText();
-            if (value == null || value.trim().isEmpty()) return 0;
-            try {
-                int uid = Integer.parseInt(value.trim());
-                if (uid > 0)
-                    return uid;
-                return 0;
-            } catch (NumberFormatException e) {
-                return 0;
-            }
-        }
-    }
-
-    /**
-     * Validates dates entered as text in a {@link JTextComponent}. Validates the input
-     * on the fly and gives feedback about whether the date is valid or not.
-     *
-     * Dates can be entered in one of four standard formats defined for the current locale.
-     */
-    private static class DateValidator extends AbstractTextComponentValidator {
-        DateValidator(JTextComponent tc) {
-            super(tc);
-        }
-
-        public static DateValidator decorate(JTextComponent tc) {
-            return new DateValidator(tc);
-        }
-
-        @Override
-        public boolean isValid() {
-            return getDate() != null;
-        }
-
-        public String getStandardTooltipTextAsHtml() {
-            return "<html>" + getStandardTooltipText() + "</html>";
-        }
-
-        public String getStandardTooltipText() {
-            final ZonedDateTime now = ZonedDateTime.now();
-            return tr(
-                    "Please enter a date in the usual format for your locale.<br>"
-                    + "Example: {0}<br>"
-                    + "Example: {1}<br>"
-                    + "Example: {2}<br>"
-                    + "Example: {3}<br>",
-                    DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(now),
-                    DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(now),
-                    DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(now),
-                    DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(now)
-            );
-        }
-
-        @Override
-        public void validate() {
-            if (!isValid()) {
-                String msg = "<html>The current value isn't a valid date.<br>" + getStandardTooltipText()+ "</html>";
-                feedbackInvalid(msg);
-                return;
-            } else {
-                String msg = "<html>" + getStandardTooltipText() + "</html>";
-                feedbackValid(msg);
-            }
-        }
-
-        public LocalDate getDate() {
-            for (final FormatStyle format: FormatStyle.values()) {
-                DateTimeFormatter df = DateTimeFormatter.ofLocalizedDate(format);
-                try {
-                    return LocalDate.parse(getComponent().getText(), df);
-                } catch (DateTimeParseException e) {
-                    // Try next format
-                    Main.trace(e);
-                }
-            }
-            return null;
-        }
-    }
-
-    /**
-     * Validates time values entered as text in a {@link JTextComponent}. Validates the input
-     * on the fly and gives feedback about whether the time value is valid or not.
-     *
-     * Time values can be entered in one of four standard formats defined for the current locale.
-     */
-    private static class TimeValidator extends AbstractTextComponentValidator {
-        TimeValidator(JTextComponent tc) {
-            super(tc);
-        }
-
-        public static TimeValidator decorate(JTextComponent tc) {
-            return new TimeValidator(tc);
-        }
-
-        @Override
-        public boolean isValid() {
-            if (getComponent().getText().trim().isEmpty())
-                return true;
-            return getDate() != null;
-        }
-
-        public String getStandardTooltipTextAsHtml() {
-            return "<html>" + getStandardTooltipText() + "</html>";
-        }
-
-        public String getStandardTooltipText() {
-            final ZonedDateTime now = ZonedDateTime.now();
-            return tr(
-                    "Please enter a valid time in the usual format for your locale.<br>"
-                    + "Example: {0}<br>"
-                    + "Example: {1}<br>"
-                    + "Example: {2}<br>"
-                    + "Example: {3}<br>",
-                    DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(now),
-                    DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).format(now),
-                    DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG).format(now),
-                    DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).format(now)
-            );
-        }
-
-        @Override
-        public void validate() {
-            if (!isValid()) {
-                String msg = "<html>The current value isn't a valid time.<br>" + getStandardTooltipText() + "</html>";
-                feedbackInvalid(msg);
-                return;
-            } else {
-                String msg = "<html>" + getStandardTooltipText() + "</html>";
-                feedbackValid(msg);
-            }
-        }
-
-        public LocalTime getDate() {
-            if (getComponent().getText().trim().isEmpty())
-                return LocalTime.MIDNIGHT;
-
-            for (final FormatStyle format: FormatStyle.values()) {
-                DateTimeFormatter df = DateTimeFormatter.ofLocalizedTime(format);
-                try {
-                    return LocalTime.parse(getComponent().getText(), df);
-                } catch (DateTimeParseException e) {
-                    // Try next format
-                    Main.trace(e);
-                }
-            }
-            return LocalTime.MIDNIGHT;
-        }
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BBoxRestrictionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BBoxRestrictionPanel.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BBoxRestrictionPanel.java	(revision 11326)
@@ -0,0 +1,70 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+
+import javax.swing.BorderFactory;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.BoundingBoxSelectionPanel;
+import org.openstreetmap.josm.io.ChangesetQuery;
+
+/**
+ * This is the panel for selecting whether the query should be restricted to a specific bounding box.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class BBoxRestrictionPanel extends BoundingBoxSelectionPanel implements RestrictionPanel {
+
+    /**
+     * Constructs a new {@code BBoxRestrictionPanel}.
+     */
+    public BBoxRestrictionPanel() {
+        setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEmptyBorder(3, 3, 3, 3),
+                BorderFactory.createCompoundBorder(
+                        BorderFactory.createLineBorder(Color.GRAY),
+                        BorderFactory.createEmptyBorder(5, 5, 5, 5)
+                )
+        ));
+    }
+
+    /**
+     * Determines if the changeset query bbox is valid.
+     * @return {@code true} if the changeset query bbox is defined.
+     */
+    @Override
+    public boolean isValidChangesetQuery() {
+        return getBoundingBox() != null;
+    }
+
+    /**
+     * Sets the query restrictions on <code>query</code> for bbox based restrictions.
+     * @param query query to fill
+     */
+    @Override
+    public void fillInQuery(ChangesetQuery query) {
+        if (!isValidChangesetQuery())
+            throw new IllegalStateException(tr("Cannot restrict the changeset query to a specific bounding box. The input is invalid."));
+        query.inBbox(getBoundingBox());
+    }
+
+    @Override
+    public void displayMessageIfInvalid() {
+        if (isValidChangesetQuery())
+            return;
+        HelpAwareOptionPane.showOptionDialog(
+                this,
+                tr(
+                        "<html>Please enter valid longitude/latitude values to restrict<br>" +
+                        "the changeset query to a specific bounding box.</html>"
+                ),
+                tr("Invalid bounding box"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidBoundingBox")
+        );
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java	(revision 11325)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java	(revision 11326)
@@ -27,6 +27,6 @@
 
 /**
- * This panel presents a list of basic queries for changests.
- *
+ * This panel presents a list of basic queries for changesets.
+ * @since 2689
  */
 public class BasicChangesetQueryPanel extends JPanel {
@@ -145,4 +145,7 @@
     }
 
+    /**
+     * Initializes the panel.
+     */
     public void init() {
         JMultilineLabel lbl = lblQueries.get(BasicQuery.MOST_RECENT_CHANGESETS);
@@ -176,4 +179,7 @@
     }
 
+    /**
+     * Remember settings in preferences.
+     */
     public void rememberInPreferences() {
         BasicQuery q = getSelectedQuery();
@@ -186,4 +192,7 @@
     }
 
+    /**
+     * Restore settings from preferences.
+     */
     public void restoreFromPreferences() {
         BasicQuery q;
@@ -214,4 +223,8 @@
     }
 
+    /**
+     * Builds the changeset query.
+     * @return the changeset query
+     */
     public ChangesetQuery buildChangesetQuery() {
         BasicQuery q = getSelectedQuery();
@@ -247,5 +260,4 @@
     /**
      * Responds to changes in the selected query
-     *
      */
     class SelectQueryHandler implements ItemListener {
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/ChangesetQueryDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/ChangesetQueryDialog.java	(revision 11325)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/ChangesetQueryDialog.java	(revision 11326)
@@ -96,8 +96,15 @@
     }
 
+    /**
+     * Determines if the dialog has been canceled.
+     * @return {@code true} if the dialog has been canceled
+     */
     public boolean isCanceled() {
         return canceled;
     }
 
+    /**
+     * Initializes HMI for user input.
+     */
     public void initForUserInput() {
         pnlBasicChangesetQueries.init();
@@ -108,4 +115,8 @@
     }
 
+    /**
+     * Returns the changeset query.
+     * @return the changeset query
+     */
     public ChangesetQuery getChangesetQuery() {
         if (isCanceled())
@@ -124,4 +135,7 @@
     }
 
+    /**
+     * Initializes HMI for user input.
+     */
     public void startUserInput() {
         pnlUrlBasedQueries.startUserInput();
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/DateValidator.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/DateValidator.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/DateValidator.java	(revision 11326)
@@ -0,0 +1,103 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.FormatStyle;
+
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+
+/**
+ * Validates dates entered as text in a {@link JTextComponent}. Validates the input
+ * on the fly and gives feedback about whether the date is valid or not.
+ *
+ * Dates can be entered in one of four standard formats defined for the current locale.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class DateValidator extends AbstractTextComponentValidator {
+
+    /**
+     * Constructs a new {@code DateValidator} for the given text component.
+     * @param tc text component
+     */
+    public DateValidator(JTextComponent tc) {
+        super(tc);
+    }
+
+    /**
+     * Decorates the given text component.
+     * @param tc text component to decorate
+     * @return new date validator attached to {@code tc}
+     */
+    public static DateValidator decorate(JTextComponent tc) {
+        return new DateValidator(tc);
+    }
+
+    @Override
+    public boolean isValid() {
+        return getDate() != null;
+    }
+
+    /**
+     * Returns the standard tooltip text as HTML.
+     * @return the standard tooltip text as HTML
+     */
+    public String getStandardTooltipTextAsHtml() {
+        return "<html>" + getStandardTooltipText() + "</html>";
+    }
+
+    /**
+     * Returns the standard tooltip text.
+     * @return the standard tooltip text
+     */
+    public String getStandardTooltipText() {
+        final ZonedDateTime now = ZonedDateTime.now();
+        return tr(
+                "Please enter a date in the usual format for your locale.<br>"
+                + "Example: {0}<br>"
+                + "Example: {1}<br>"
+                + "Example: {2}<br>"
+                + "Example: {3}<br>",
+                DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(now),
+                DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(now),
+                DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(now),
+                DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(now)
+        );
+    }
+
+    @Override
+    public void validate() {
+        if (!isValid()) {
+            String msg = "<html>The current value isn't a valid date.<br>" + getStandardTooltipText()+ "</html>";
+            feedbackInvalid(msg);
+            return;
+        } else {
+            String msg = "<html>" + getStandardTooltipText() + "</html>";
+            feedbackValid(msg);
+        }
+    }
+
+    /**
+     * Returns the date.
+     * @return the date
+     */
+    public LocalDate getDate() {
+        for (final FormatStyle format: FormatStyle.values()) {
+            DateTimeFormatter df = DateTimeFormatter.ofLocalizedDate(format);
+            try {
+                return LocalDate.parse(getComponent().getText(), df);
+            } catch (DateTimeParseException e) {
+                // Try next format
+                Main.trace(e);
+            }
+        }
+        return null;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/OpenAndCloseStateRestrictionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/OpenAndCloseStateRestrictionPanel.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/OpenAndCloseStateRestrictionPanel.java	(revision 11326)
@@ -0,0 +1,137 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
+import org.openstreetmap.josm.io.ChangesetQuery;
+
+/**
+ * This is the panel for selecting whether the changeset query should be restricted to
+ * open or closed changesets.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class OpenAndCloseStateRestrictionPanel extends JPanel implements RestrictionPanel {
+
+    private final JRadioButton rbOpenOnly = new JRadioButton();
+    private final JRadioButton rbClosedOnly = new JRadioButton();
+    private final JRadioButton rbBoth = new JRadioButton();
+
+    /**
+     * Constructs a new {@code OpenAndCloseStateRestrictionPanel}.
+     */
+    public OpenAndCloseStateRestrictionPanel() {
+        build();
+    }
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEmptyBorder(3, 3, 3, 3),
+                BorderFactory.createCompoundBorder(
+                        BorderFactory.createLineBorder(Color.GRAY),
+                        BorderFactory.createEmptyBorder(5, 5, 5, 5)
+                )
+        ));
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbOpenOnly, gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Query open changesets only")), gc);
+
+        gc.gridy = 1;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(rbClosedOnly, gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Query closed changesets only")), gc);
+
+        gc.gridy = 2;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(rbBoth, gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Query both open and closed changesets")), gc);
+
+        ButtonGroup bgRestrictions = new ButtonGroup();
+        bgRestrictions.add(rbBoth);
+        bgRestrictions.add(rbClosedOnly);
+        bgRestrictions.add(rbOpenOnly);
+    }
+
+    /**
+     * Initializes HMI for user input.
+     */
+    public void startUserInput() {
+        restoreFromSettings();
+    }
+
+    /**
+     * Sets the query restrictions on <code>query</code> for state based restrictions.
+     * @param query the query to fill
+     */
+    @Override
+    public void fillInQuery(ChangesetQuery query) {
+        if (rbBoth.isSelected()) {
+            query.beingClosed(true);
+            query.beingOpen(true);
+        } else if (rbOpenOnly.isSelected()) {
+            query.beingOpen(true);
+        } else if (rbClosedOnly.isSelected()) {
+            query.beingClosed(true);
+        }
+    }
+
+    /**
+     * Remember settings in preferences.
+     */
+    public void rememberSettings() {
+        String prefRoot = "changeset-query.advanced.open-restrictions";
+        if (rbBoth.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "both");
+        } else if (rbOpenOnly.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "open");
+        } else if (rbClosedOnly.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "closed");
+        }
+    }
+
+    /**
+     * Restore settings from preferences.
+     */
+    public void restoreFromSettings() {
+        String prefRoot = "changeset-query.advanced.open-restrictions";
+        String v = Main.pref.get(prefRoot + ".query-type", "open");
+        rbBoth.setSelected("both".equals(v));
+        rbOpenOnly.setSelected("open".equals(v));
+        rbClosedOnly.setSelected("closed".equals(v));
+    }
+
+    @Override
+    public boolean isValidChangesetQuery() {
+        return true;
+    }
+
+    @Override
+    public void displayMessageIfInvalid() {
+        // Do nothing
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/RestrictionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/RestrictionPanel.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/RestrictionPanel.java	(revision 11326)
@@ -0,0 +1,28 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import org.openstreetmap.josm.io.ChangesetQuery;
+
+/**
+ * Defines a panel to apply a restriction to the changeset query.
+ * @since 11326
+ */
+public interface RestrictionPanel {
+
+    /**
+     * Determines if the changeset query is valid.
+     * @return {@code true} if the changeset query is valid.
+     */
+    boolean isValidChangesetQuery();
+
+    /**
+     * Sets the query restrictions on <code>query</code>.
+     * @param query query to fill
+     */
+    void fillInQuery(ChangesetQuery query);
+
+    /**
+     * Display error message if a field is invalid.
+     */
+    void displayMessageIfInvalid();
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java	(revision 11326)
@@ -0,0 +1,346 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.Date;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.io.ChangesetQuery;
+
+/**
+ * This is the panel to apply a time restriction to the changeset query.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class TimeRestrictionPanel extends JPanel implements RestrictionPanel {
+
+    private final JRadioButton rbClosedAfter = new JRadioButton();
+    private final JRadioButton rbClosedAfterAndCreatedBefore = new JRadioButton();
+    private final JosmTextField tfClosedAfterDate1 = new JosmTextField();
+    private transient DateValidator valClosedAfterDate1;
+    private final JosmTextField tfClosedAfterTime1 = new JosmTextField();
+    private transient TimeValidator valClosedAfterTime1;
+    private final JosmTextField tfClosedAfterDate2 = new JosmTextField();
+    private transient DateValidator valClosedAfterDate2;
+    private final JosmTextField tfClosedAfterTime2 = new JosmTextField();
+    private transient TimeValidator valClosedAfterTime2;
+    private final JosmTextField tfCreatedBeforeDate = new JosmTextField();
+    private transient DateValidator valCreatedBeforeDate;
+    private final JosmTextField tfCreatedBeforeTime = new JosmTextField();
+    private transient TimeValidator valCreatedBeforeTime;
+
+    /**
+     * Constructs a new {@code TimeRestrictionPanel}.
+     */
+    public TimeRestrictionPanel() {
+        build();
+    }
+
+    protected JPanel buildClosedAfterInputPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("Date: ")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 0.7;
+        pnl.add(tfClosedAfterDate1, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate1);
+        valClosedAfterDate1 = DateValidator.decorate(tfClosedAfterDate1);
+        tfClosedAfterDate1.setToolTipText(valClosedAfterDate1.getStandardTooltipTextAsHtml());
+
+        gc.gridx = 2;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Time:")), gc);
+
+        gc.gridx = 3;
+        gc.weightx = 0.3;
+        pnl.add(tfClosedAfterTime1, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime1);
+        valClosedAfterTime1 = TimeValidator.decorate(tfClosedAfterTime1);
+        tfClosedAfterTime1.setToolTipText(valClosedAfterTime1.getStandardTooltipTextAsHtml());
+        return pnl;
+    }
+
+    protected JPanel buildClosedAfterAndCreatedBeforeInputPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("Closed after - ")), gc);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("Date:")), gc);
+
+        gc.gridx = 2;
+        gc.weightx = 0.7;
+        pnl.add(tfClosedAfterDate2, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate2);
+        valClosedAfterDate2 = DateValidator.decorate(tfClosedAfterDate2);
+        tfClosedAfterDate2.setToolTipText(valClosedAfterDate2.getStandardTooltipTextAsHtml());
+        gc.gridx = 3;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Time:")), gc);
+
+        gc.gridx = 4;
+        gc.weightx = 0.3;
+        pnl.add(tfClosedAfterTime2, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime2);
+        valClosedAfterTime2 = TimeValidator.decorate(tfClosedAfterTime2);
+        tfClosedAfterTime2.setToolTipText(valClosedAfterTime2.getStandardTooltipTextAsHtml());
+
+        gc.gridy = 1;
+        gc.gridx = 0;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("Created before - ")), gc);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("Date:")), gc);
+
+        gc.gridx = 2;
+        gc.weightx = 0.7;
+        pnl.add(tfCreatedBeforeDate, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeDate);
+        valCreatedBeforeDate = DateValidator.decorate(tfCreatedBeforeDate);
+        tfCreatedBeforeDate.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
+
+        gc.gridx = 3;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Time:")), gc);
+
+        gc.gridx = 4;
+        gc.weightx = 0.3;
+        pnl.add(tfCreatedBeforeTime, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeTime);
+        valCreatedBeforeTime = TimeValidator.decorate(tfCreatedBeforeTime);
+        tfCreatedBeforeTime.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
+
+        return pnl;
+    }
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEmptyBorder(3, 3, 3, 3),
+                BorderFactory.createCompoundBorder(
+                        BorderFactory.createLineBorder(Color.GRAY),
+                        BorderFactory.createEmptyBorder(5, 5, 5, 5)
+                )
+        ));
+
+        // -- changesets closed after a specific date/time
+        //
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.gridx = 0;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbClosedAfter, gc);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Only changesets closed after the following date/time")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(buildClosedAfterInputPanel(), gc);
+
+        // -- changesets closed after a specific date/time and created before a specific date time
+        //
+        gc = new GridBagConstraints();
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.gridy = 2;
+        gc.gridx = 0;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbClosedAfterAndCreatedBefore, gc);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Only changesets closed after and created before a specific date/time")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(buildClosedAfterAndCreatedBeforeInputPanel(), gc);
+
+        ButtonGroup bg = new ButtonGroup();
+        bg.add(rbClosedAfter);
+        bg.add(rbClosedAfterAndCreatedBefore);
+
+        ItemListener restrictionChangeHandler = new TimeRestrictionChangedHandler();
+        rbClosedAfter.addItemListener(restrictionChangeHandler);
+        rbClosedAfterAndCreatedBefore.addItemListener(restrictionChangeHandler);
+
+        rbClosedAfter.setSelected(true);
+    }
+
+    /**
+     * Determines if the changeset query time information is valid.
+     * @return {@code true} if the changeset query time information is valid.
+     */
+    @Override
+    public boolean isValidChangesetQuery() {
+        if (rbClosedAfter.isSelected())
+            return valClosedAfterDate1.isValid() && valClosedAfterTime1.isValid();
+        else if (rbClosedAfterAndCreatedBefore.isSelected())
+            return valClosedAfterDate2.isValid() && valClosedAfterTime2.isValid()
+            && valCreatedBeforeDate.isValid() && valCreatedBeforeTime.isValid();
+        // should not happen
+        return true;
+    }
+
+    class TimeRestrictionChangedHandler implements ItemListener {
+        @Override
+        public void itemStateChanged(ItemEvent e) {
+            tfClosedAfterDate1.setEnabled(rbClosedAfter.isSelected());
+            tfClosedAfterTime1.setEnabled(rbClosedAfter.isSelected());
+
+            tfClosedAfterDate2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
+            tfClosedAfterTime2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
+            tfCreatedBeforeDate.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
+            tfCreatedBeforeTime.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
+        }
+    }
+
+    /**
+     * Initializes HMI for user input.
+     */
+    public void startUserInput() {
+        restoreFromSettings();
+    }
+
+    /**
+     * Sets the query restrictions on <code>query</code> for time based restrictions.
+     * @param query the query to fill
+     */
+    @Override
+    public void fillInQuery(ChangesetQuery query) {
+        if (!isValidChangesetQuery())
+            throw new IllegalStateException(tr("Cannot build changeset query with time based restrictions. Input is not valid."));
+        if (rbClosedAfter.isSelected()) {
+            LocalDate d1 = valClosedAfterDate1.getDate();
+            LocalTime d2 = valClosedAfterTime1.getTime();
+            final Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
+            query.closedAfter(d3);
+        } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
+            LocalDate d1 = valClosedAfterDate2.getDate();
+            LocalTime d2 = valClosedAfterTime2.getTime();
+            Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
+
+            d1 = valCreatedBeforeDate.getDate();
+            d2 = valCreatedBeforeTime.getTime();
+            Date d4 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
+
+            query.closedAfterAndCreatedBefore(d3, d4);
+        }
+    }
+
+    @Override
+    public void displayMessageIfInvalid() {
+        if (isValidChangesetQuery())
+            return;
+        HelpAwareOptionPane.showOptionDialog(
+                this,
+                tr(
+                        "<html>Please enter valid date/time values to restrict<br>"
+                        + "the query to a specific time range.</html>"
+                ),
+                tr("Invalid date/time values"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidDateTimeValues")
+        );
+    }
+
+    /**
+     * Remember settings in preferences.
+     */
+    public void rememberSettings() {
+        String prefRoot = "changeset-query.advanced.time-restrictions";
+        if (rbClosedAfter.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "closed-after");
+        } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "closed-after-created-before");
+        }
+        Main.pref.put(prefRoot + ".closed-after.date", tfClosedAfterDate1.getText());
+        Main.pref.put(prefRoot + ".closed-after.time", tfClosedAfterTime1.getText());
+        Main.pref.put(prefRoot + ".closed-created.closed.date", tfClosedAfterDate2.getText());
+        Main.pref.put(prefRoot + ".closed-created.closed.time", tfClosedAfterTime2.getText());
+        Main.pref.put(prefRoot + ".closed-created.created.date", tfCreatedBeforeDate.getText());
+        Main.pref.put(prefRoot + ".closed-created.created.time", tfCreatedBeforeTime.getText());
+    }
+
+    /**
+     * Restore settings from preferences.
+     */
+    public void restoreFromSettings() {
+        String prefRoot = "changeset-query.advanced.open-restrictions";
+        String v = Main.pref.get(prefRoot + ".query-type", "closed-after");
+        rbClosedAfter.setSelected("closed-after".equals(v));
+        rbClosedAfterAndCreatedBefore.setSelected("closed-after-created-before".equals(v));
+        if (!rbClosedAfter.isSelected() && !rbClosedAfterAndCreatedBefore.isSelected()) {
+            rbClosedAfter.setSelected(true);
+        }
+        tfClosedAfterDate1.setText(Main.pref.get(prefRoot + ".closed-after.date", ""));
+        tfClosedAfterTime1.setText(Main.pref.get(prefRoot + ".closed-after.time", ""));
+        tfClosedAfterDate2.setText(Main.pref.get(prefRoot + ".closed-created.closed.date", ""));
+        tfClosedAfterTime2.setText(Main.pref.get(prefRoot + ".closed-created.closed.time", ""));
+        tfCreatedBeforeDate.setText(Main.pref.get(prefRoot + ".closed-created.created.date", ""));
+        tfCreatedBeforeTime.setText(Main.pref.get(prefRoot + ".closed-created.created.time", ""));
+        if (!valClosedAfterDate1.isValid()) {
+            tfClosedAfterDate1.setText("");
+        }
+        if (!valClosedAfterTime1.isValid()) {
+            tfClosedAfterTime1.setText("");
+        }
+        if (!valClosedAfterDate2.isValid()) {
+            tfClosedAfterDate2.setText("");
+        }
+        if (!valClosedAfterTime2.isValid()) {
+            tfClosedAfterTime2.setText("");
+        }
+        if (!valCreatedBeforeDate.isValid()) {
+            tfCreatedBeforeDate.setText("");
+        }
+        if (!valCreatedBeforeTime.isValid()) {
+            tfCreatedBeforeTime.setText("");
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeValidator.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeValidator.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeValidator.java	(revision 11326)
@@ -0,0 +1,108 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.FormatStyle;
+
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+
+/**
+ * Validates time values entered as text in a {@link JTextComponent}. Validates the input
+ * on the fly and gives feedback about whether the time value is valid or not.
+ *
+ * Time values can be entered in one of four standard formats defined for the current locale.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class TimeValidator extends AbstractTextComponentValidator {
+
+    /**
+     * Constructs a new {@code TimeValidator} for the given text component.
+     * @param tc text component
+     */
+    public TimeValidator(JTextComponent tc) {
+        super(tc);
+    }
+
+    /**
+     * Decorates the given text component.
+     * @param tc text component to decorate
+     * @return new time validator attached to {@code tc}
+     */
+    public static TimeValidator decorate(JTextComponent tc) {
+        return new TimeValidator(tc);
+    }
+
+    @Override
+    public boolean isValid() {
+        if (getComponent().getText().trim().isEmpty())
+            return true;
+        return getTime() != null;
+    }
+
+    /**
+     * Returns the standard tooltip text as HTML.
+     * @return the standard tooltip text as HTML
+     */
+    public String getStandardTooltipTextAsHtml() {
+        return "<html>" + getStandardTooltipText() + "</html>";
+    }
+
+    /**
+     * Returns the standard tooltip text.
+     * @return the standard tooltip text
+     */
+    public String getStandardTooltipText() {
+        final ZonedDateTime now = ZonedDateTime.now();
+        return tr(
+                "Please enter a valid time in the usual format for your locale.<br>"
+                + "Example: {0}<br>"
+                + "Example: {1}<br>"
+                + "Example: {2}<br>"
+                + "Example: {3}<br>",
+                DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(now),
+                DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).format(now),
+                DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG).format(now),
+                DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).format(now)
+        );
+    }
+
+    @Override
+    public void validate() {
+        if (!isValid()) {
+            String msg = "<html>The current value isn't a valid time.<br>" + getStandardTooltipText() + "</html>";
+            feedbackInvalid(msg);
+            return;
+        } else {
+            String msg = "<html>" + getStandardTooltipText() + "</html>";
+            feedbackValid(msg);
+        }
+    }
+
+    /**
+     * Returns the time.
+     * @return the time
+     */
+    public LocalTime getTime() {
+        if (getComponent().getText().trim().isEmpty())
+            return LocalTime.MIDNIGHT;
+
+        for (final FormatStyle format: FormatStyle.values()) {
+            DateTimeFormatter df = DateTimeFormatter.ofLocalizedTime(format);
+            try {
+                return LocalTime.parse(getComponent().getText(), df);
+            } catch (DateTimeParseException e) {
+                // Try next format
+                Main.trace(e);
+            }
+        }
+        return LocalTime.MIDNIGHT;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UidInputFieldValidator.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UidInputFieldValidator.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UidInputFieldValidator.java	(revision 11326)
@@ -0,0 +1,74 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+
+/**
+ * Validator for user ids entered in a {@link JTextComponent}.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class UidInputFieldValidator extends AbstractTextComponentValidator {
+
+    /**
+     * Constructs a new {@code TimeValidator} for the given text component.
+     * @param tc text component
+     */
+    public UidInputFieldValidator(JTextComponent tc) {
+        super(tc);
+    }
+
+    /**
+     * Decorates the given text component.
+     * @param tc text component to decorate
+     * @return new uid validator attached to {@code tc}
+     */
+    public static UidInputFieldValidator decorate(JTextComponent tc) {
+        return new UidInputFieldValidator(tc);
+    }
+
+    @Override
+    public boolean isValid() {
+        return getUid() > 0;
+    }
+
+    @Override
+    public void validate() {
+        String value = getComponent().getText();
+        if (value == null || value.trim().isEmpty()) {
+            feedbackInvalid("");
+            return;
+        }
+        try {
+            int uid = Integer.parseInt(value);
+            if (uid <= 0) {
+                feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
+                return;
+            }
+        } catch (NumberFormatException e) {
+            feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
+            return;
+        }
+        feedbackValid(tr("Please enter an integer value > 0"));
+    }
+
+    /**
+     * Returns the user identifier.
+     * @return the user identifier
+     */
+    public int getUid() {
+        String value = getComponent().getText();
+        if (value == null || value.trim().isEmpty()) return 0;
+        try {
+            int uid = Integer.parseInt(value.trim());
+            if (uid > 0)
+                return uid;
+            return 0;
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java	(revision 11325)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java	(revision 11326)
@@ -28,4 +28,8 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 
+/**
+ * This panel allows to build a changeset query from an URL.
+ * @since 2689
+ */
 public class UrlBasedQueryPanel extends JPanel {
 
@@ -150,4 +154,7 @@
     }
 
+    /**
+     * Initializes HMI for user input.
+     */
     public void startUserInput() {
         tfUrl.requestFocusInWindow();
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UserRestrictionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UserRestrictionPanel.java	(revision 11326)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UserRestrictionPanel.java	(revision 11326)
@@ -0,0 +1,308 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.changeset.query;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.JosmUserIdentityManager;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.preferences.server.UserNameValidator;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * This is the panel for selecting whether the query should be restricted to a specific user.
+ * @since 11326 (extracted from AdvancedChangesetQueryPanel)
+ */
+public class UserRestrictionPanel extends JPanel implements RestrictionPanel {
+    private final ButtonGroup bgUserRestrictions = new ButtonGroup();
+    private final JRadioButton rbRestrictToMyself = new JRadioButton();
+    private final JRadioButton rbRestrictToUid = new JRadioButton();
+    private final JRadioButton rbRestrictToUserName = new JRadioButton();
+    private final JosmTextField tfUid = new JosmTextField(10);
+    private transient UidInputFieldValidator valUid;
+    private final JosmTextField tfUserName = new JosmTextField(10);
+    private transient UserNameValidator valUserName;
+    private final JMultilineLabel lblRestrictedToMyself = new JMultilineLabel(tr("Only changesets owned by myself"));
+
+    /**
+     * Constructs a new {@code UserRestrictionPanel}.
+     */
+    public UserRestrictionPanel() {
+        build();
+    }
+
+    protected JPanel buildUidInputPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("User ID:")), gc);
+
+        gc.gridx = 1;
+        pnl.add(tfUid, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfUid);
+        valUid = UidInputFieldValidator.decorate(tfUid);
+
+        // grab remaining space
+        gc.gridx = 2;
+        gc.weightx = 1.0;
+        pnl.add(new JPanel(), gc);
+        return pnl;
+    }
+
+    protected JPanel buildUserNameInputPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0, 0, 0, 3);
+        pnl.add(new JLabel(tr("User name:")), gc);
+
+        gc.gridx = 1;
+        pnl.add(tfUserName, gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfUserName);
+        valUserName = new UserNameValidator(tfUserName);
+
+        // grab remaining space
+        gc.gridx = 2;
+        gc.weightx = 1.0;
+        pnl.add(new JPanel(), gc);
+        return pnl;
+    }
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEmptyBorder(3, 3, 3, 3),
+                BorderFactory.createCompoundBorder(
+                        BorderFactory.createLineBorder(Color.GRAY),
+                        BorderFactory.createEmptyBorder(5, 5, 5, 5)
+                )
+        ));
+
+        ItemListener userRestrictionChangeHandler = new UserRestrictionChangedHandler();
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.gridx = 0;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbRestrictToMyself, gc);
+        rbRestrictToMyself.addItemListener(userRestrictionChangeHandler);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(lblRestrictedToMyself, gc);
+
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbRestrictToUid, gc);
+        rbRestrictToUid.addItemListener(userRestrictionChangeHandler);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Only changesets owned by the user with the following user ID")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 2;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(buildUidInputPanel(), gc);
+
+        gc.gridx = 0;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        add(rbRestrictToUserName, gc);
+        rbRestrictToUserName.addItemListener(userRestrictionChangeHandler);
+
+        gc.gridx = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(new JMultilineLabel(tr("Only changesets owned by the user with the following user name")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 4;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(buildUserNameInputPanel(), gc);
+
+        bgUserRestrictions.add(rbRestrictToMyself);
+        bgUserRestrictions.add(rbRestrictToUid);
+        bgUserRestrictions.add(rbRestrictToUserName);
+    }
+
+    /**
+     * Initializes HMI for user input.
+     */
+    public void startUserInput() {
+        if (JosmUserIdentityManager.getInstance().isAnonymous()) {
+            lblRestrictedToMyself.setText(tr("Only changesets owned by myself (disabled. JOSM is currently run by an anonymous user)"));
+            rbRestrictToMyself.setEnabled(false);
+            if (rbRestrictToMyself.isSelected()) {
+                rbRestrictToUid.setSelected(true);
+            }
+        } else {
+            lblRestrictedToMyself.setText(tr("Only changesets owned by myself"));
+            rbRestrictToMyself.setEnabled(true);
+            rbRestrictToMyself.setSelected(true);
+        }
+        restoreFromSettings();
+    }
+
+    /**
+     * Sets the query restrictions on <code>query</code> for changeset owner based restrictions.
+     *
+     * @param query the query. Must not be null.
+     * @throws IllegalArgumentException if query is null
+     * @throws IllegalStateException if one of the available values for query parameters in this panel isn't valid
+     */
+    @Override
+    public void fillInQuery(ChangesetQuery query) {
+        CheckParameterUtil.ensureParameterNotNull(query, "query");
+        if (rbRestrictToMyself.isSelected()) {
+            JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
+            if (im.isPartiallyIdentified()) {
+                query.forUser(im.getUserName());
+            } else if (im.isFullyIdentified()) {
+                query.forUser(im.getUserId());
+            } else
+                throw new IllegalStateException(
+                        tr("Cannot restrict changeset query to the current user because the current user is anonymous"));
+        } else if (rbRestrictToUid.isSelected()) {
+            int uid = valUid.getUid();
+            if (uid > 0) {
+                query.forUser(uid);
+            } else
+                throw new IllegalStateException(tr("Current value ''{0}'' for user ID is not valid", tfUid.getText()));
+        } else if (rbRestrictToUserName.isSelected()) {
+            if (!valUserName.isValid())
+                throw new IllegalStateException(
+                        tr("Cannot restrict the changeset query to the user name ''{0}''", tfUserName.getText()));
+            query.forUser(tfUserName.getText());
+        }
+    }
+
+    /**
+     * Determines if the changeset query time information is valid.
+     * @return {@code true} if the changeset query time information is valid.
+     */
+    @Override
+    public boolean isValidChangesetQuery() {
+        if (rbRestrictToUid.isSelected())
+            return valUid.isValid();
+        else if (rbRestrictToUserName.isSelected())
+            return valUserName.isValid();
+        return true;
+    }
+
+    protected void alertInvalidUid() {
+        HelpAwareOptionPane.showOptionDialog(
+                this,
+                tr("Please enter a valid user ID"),
+                tr("Invalid user ID"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserId")
+        );
+    }
+
+    protected void alertInvalidUserName() {
+        HelpAwareOptionPane.showOptionDialog(
+                this,
+                tr("Please enter a non-empty user name"),
+                tr("Invalid user name"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserName")
+        );
+    }
+
+    @Override
+    public void displayMessageIfInvalid() {
+        if (rbRestrictToUid.isSelected()) {
+            if (!valUid.isValid()) {
+                alertInvalidUid();
+            }
+        } else if (rbRestrictToUserName.isSelected()) {
+            if (!valUserName.isValid()) {
+                alertInvalidUserName();
+            }
+        }
+    }
+
+    /**
+     * Remember settings in preferences.
+     */
+    public void rememberSettings() {
+        String prefRoot = "changeset-query.advanced.user-restrictions";
+        if (rbRestrictToMyself.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "mine");
+        } else if (rbRestrictToUid.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "uid");
+        } else if (rbRestrictToUserName.isSelected()) {
+            Main.pref.put(prefRoot + ".query-type", "username");
+        }
+        Main.pref.put(prefRoot + ".uid", tfUid.getText());
+        Main.pref.put(prefRoot + ".username", tfUserName.getText());
+    }
+
+    /**
+     * Restore settings from preferences.
+     */
+    public void restoreFromSettings() {
+        String prefRoot = "changeset-query.advanced.user-restrictions";
+        String v = Main.pref.get(prefRoot + ".query-type", "mine");
+        if ("mine".equals(v)) {
+            JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
+            if (im.isAnonymous()) {
+                rbRestrictToUid.setSelected(true);
+            } else {
+                rbRestrictToMyself.setSelected(true);
+            }
+        } else if ("uid".equals(v)) {
+            rbRestrictToUid.setSelected(true);
+        } else if ("username".equals(v)) {
+            rbRestrictToUserName.setSelected(true);
+        }
+        tfUid.setText(Main.pref.get(prefRoot + ".uid", ""));
+        if (!valUid.isValid()) {
+            tfUid.setText("");
+        }
+        tfUserName.setText(Main.pref.get(prefRoot + ".username", ""));
+    }
+
+    class UserRestrictionChangedHandler implements ItemListener {
+        @Override
+        public void itemStateChanged(ItemEvent e) {
+            tfUid.setEnabled(rbRestrictToUid.isSelected());
+            tfUserName.setEnabled(rbRestrictToUserName.isSelected());
+            if (rbRestrictToUid.isSelected()) {
+                tfUid.requestFocusInWindow();
+            } else if (rbRestrictToUserName.isSelected()) {
+                tfUserName.requestFocusInWindow();
+            }
+        }
+    }
+}
