// 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.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.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
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.JTextField;
import javax.swing.text.JTextComponent;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.gui.HelpAwareOptionPane;
import org.openstreetmap.josm.gui.JMultilineLabel;
import org.openstreetmap.josm.gui.JosmUserIdentityManager;
import org.openstreetmap.josm.gui.help.HelpUtil;
import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
import org.openstreetmap.josm.gui.widgets.BoundingBoxSelectionPanel;
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;
/**
* This panel allows to specify a changeset query
*
*/
public class AdvancedChangesetQueryPanel extends JPanel {
private JCheckBox cbUserRestriction;
private JCheckBox cbOpenAndCloseRestrictions;
private JCheckBox cbTimeRestrictions;
private JCheckBox cbBoundingBoxRestriction;
private UserRestrictionPanel pnlUserRestriction;
private OpenAndCloseStateRestrictionPanel pnlOpenAndCloseRestriction;
private TimeRestrictionPanel pnlTimeRestriction;
private BBoxRestrictionPanel pnlBoundingBoxRestriction;
protected JPanel buildQueryPanel() {
ItemListener stateChangeHandler = new RestrictionGroupStateChangeHandler();
JPanel pnl = new VerticallyScrollablePanel();
pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
pnl.setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
// -- select changesets by a specific user
//
gc.anchor = GridBagConstraints.NORTHWEST;
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(cbUserRestriction = new JCheckBox(), gc);
cbUserRestriction.addItemListener(stateChangeHandler);
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(new JMultilineLabel(tr("Select changesets owned by specific users")),gc);
gc.gridy = 1;
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(pnlUserRestriction = new UserRestrictionPanel(), gc);
// -- restricting the query to open and closed changesets
//
gc.gridy = 2;
gc.gridx = 0;
gc.anchor = GridBagConstraints.NORTHWEST;
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(cbOpenAndCloseRestrictions = new JCheckBox(), gc);
cbOpenAndCloseRestrictions.addItemListener(stateChangeHandler);
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(new JMultilineLabel(tr("Select changesets depending on whether they are open or closed")),gc);
gc.gridy = 3;
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(pnlOpenAndCloseRestriction = new OpenAndCloseStateRestrictionPanel(), gc);
// -- restricting the query to a specific time
//
gc.gridy = 4;
gc.gridx = 0;
gc.anchor = GridBagConstraints.NORTHWEST;
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(cbTimeRestrictions = new JCheckBox(), gc);
cbTimeRestrictions.addItemListener(stateChangeHandler);
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(new JMultilineLabel(tr("Select changesets based on the date/time they have been created or closed")),gc);
gc.gridy = 5;
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(pnlTimeRestriction = new TimeRestrictionPanel(), gc);
// -- restricting the query to a specific bounding box
//
gc.gridy = 6;
gc.gridx = 0;
gc.anchor = GridBagConstraints.NORTHWEST;
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(cbBoundingBoxRestriction = new JCheckBox(), gc);
cbBoundingBoxRestriction.addItemListener(stateChangeHandler);
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(new JMultilineLabel(tr("Select only changesets related to a specific bounding box")),gc);
gc.gridy = 7;
gc.gridx = 1;
gc.weightx = 1.0;
pnl.add(pnlBoundingBoxRestriction = new BBoxRestrictionPanel(), gc);
gc.gridy = 8;
gc.gridx = 0;
gc.gridwidth = 2;
gc.fill =GridBagConstraints.BOTH;
gc.weightx = 1.0;
gc.weighty = 1.0;
pnl.add(new JPanel(), gc);
return pnl;
}
protected void build() {
setLayout(new BorderLayout());
JScrollPane spQueryPanel = new JScrollPane(buildQueryPanel());
add(spQueryPanel, BorderLayout.CENTER);
spQueryPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
spQueryPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
}
public AdvancedChangesetQueryPanel() {
build();
}
public void startUserInput() {
restoreFromSettings();
pnlBoundingBoxRestriction.setVisible(cbBoundingBoxRestriction.isSelected());
pnlOpenAndCloseRestriction.setVisible(cbOpenAndCloseRestrictions.isSelected());
pnlTimeRestriction.setVisible(cbTimeRestrictions.isSelected());
pnlUserRestriction.setVisible(cbUserRestriction.isSelected());
pnlOpenAndCloseRestriction.startUserInput();
pnlUserRestriction.startUserInput();
pnlTimeRestriction.startUserInput();
}
public void displayMessageIfInvalid() {
if (cbUserRestriction.isSelected()) {
if (! pnlUserRestriction.isValidChangesetQuery()) {
pnlUserRestriction.displayMessageIfInvalid();
}
} else if (cbTimeRestrictions.isSelected()) {
if (!pnlTimeRestriction.isValidChangesetQuery()) {
pnlTimeRestriction.displayMessageIfInvalid();
}
} else if (cbBoundingBoxRestriction.isSelected()) {
if (!pnlBoundingBoxRestriction.isValidChangesetQuery()) {
pnlBoundingBoxRestriction.displayMessageIfInvalid();
}
}
}
/**
* Builds the changeset query based on the data entered in the form.
*
* @return the changeset query. null, if the data entered doesn't represent
* a valid changeset query.
*/
public ChangesetQuery buildChangesetQuery() {
ChangesetQuery query = new ChangesetQuery();
if (cbUserRestriction.isSelected()) {
if (! pnlUserRestriction.isValidChangesetQuery())
return null;
pnlUserRestriction.fillInQuery(query);
}
if (cbOpenAndCloseRestrictions.isSelected()) {
// don't have to check whether it's valid. It always is.
pnlOpenAndCloseRestriction.fillInQuery(query);
}
if (cbBoundingBoxRestriction.isSelected()) {
if (!pnlBoundingBoxRestriction.isValidChangesetQuery())
return null;
pnlBoundingBoxRestriction.fillInQuery(query);
}
if (cbTimeRestrictions.isSelected()) {
if (!pnlTimeRestriction.isValidChangesetQuery())
return null;
pnlTimeRestriction.fillInQuery(query);
}
return query;
}
public void rememberSettings() {
Main.pref.put("changeset-query.advanced.user-restrictions", cbUserRestriction.isSelected());
Main.pref.put("changeset-query.advanced.open-restrictions", cbOpenAndCloseRestrictions.isSelected());
Main.pref.put("changeset-query.advanced.time-restrictions", cbTimeRestrictions.isSelected());
Main.pref.put("changeset-query.advanced.bbox-restrictions", cbBoundingBoxRestriction.isSelected());
pnlUserRestriction.rememberSettings();
pnlOpenAndCloseRestriction.rememberSettings();
pnlTimeRestriction.rememberSettings();
}
public void restoreFromSettings() {
cbUserRestriction.setSelected(Main.pref.getBoolean("changeset-query.advanced.user-restrictions", false));
cbOpenAndCloseRestrictions.setSelected(Main.pref.getBoolean("changeset-query.advanced.open-restrictions", false));
cbTimeRestrictions.setSelected(Main.pref.getBoolean("changeset-query.advanced.time-restrictions", false));
cbBoundingBoxRestriction.setSelected(Main.pref.getBoolean("changeset-query.advanced.bbox-restrictions", false));
}
class RestrictionGroupStateChangeHandler implements ItemListener {
protected void userRestrictionStateChanged() {
if (pnlUserRestriction == null) return;
pnlUserRestriction.setVisible(cbUserRestriction.isSelected());
}
protected void openCloseRestrictionStateChanged() {
if (pnlOpenAndCloseRestriction == null) return;
pnlOpenAndCloseRestriction.setVisible(cbOpenAndCloseRestrictions.isSelected());
}
protected void timeRestrictionsStateChanged() {
if (pnlTimeRestriction == null) return;
pnlTimeRestriction.setVisible(cbTimeRestrictions.isSelected());
}
protected void boundingBoxRestrictionChanged() {
if (pnlBoundingBoxRestriction == null) return;
pnlBoundingBoxRestriction.setVisible(cbBoundingBoxRestriction.isSelected());
}
public void itemStateChanged(ItemEvent e) {
if (e.getSource() == cbUserRestriction) {
userRestrictionStateChanged();
} else if (e.getSource() == cbOpenAndCloseRestrictions) {
openCloseRestrictionStateChanged();
} else if (e.getSource() == cbTimeRestrictions) {
timeRestrictionsStateChanged();
} else if (e.getSource() == cbBoundingBoxRestriction) {
boundingBoxRestrictionChanged();
}
validate();
repaint();
}
}
/**
* This is the panel for selecting whether the changeset query should be restricted to
* open or closed changesets
*/
static private class OpenAndCloseStateRestrictionPanel extends JPanel {
private JRadioButton rbOpenOnly;
private JRadioButton rbClosedOnly;
private JRadioButton rbBoth;
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 = new JRadioButton(), 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 = new JRadioButton(), 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 = new JRadioButton(), 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 OpenAndCloseStateRestrictionPanel() {
build();
}
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(v.equals("both"));
rbOpenOnly.setSelected(v.equals("open"));
rbClosedOnly.setSelected(v.equals("closed"));
}
}
/**
* This is the panel for selecting whether the query should be restricted to a specific
* user
*
*/
static private class UserRestrictionPanel extends JPanel {
private ButtonGroup bgUserRestrictions;
private JRadioButton rbRestrictToMyself;
private JRadioButton rbRestrictToUid;
private JRadioButton rbRestrictToUserName;
private JTextField tfUid;
private UidInputFieldValidator valUid;
private JTextField tfUserName;
private UserNameInputValidator valUserName;
private JMultilineLabel lblRestrictedToMyself;
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 = new JTextField(10),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 = new JTextField(10),gc);
SelectAllOnFocusGainedDecorator.decorate(tfUserName);
valUserName = UserNameInputValidator.decorate(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 = new JRadioButton(), gc);
rbRestrictToMyself.addItemListener(userRestrictionChangeHandler);
gc.gridx = 1;
gc.fill = GridBagConstraints.HORIZONTAL;
gc.weightx = 1.0;
add(lblRestrictedToMyself = new JMultilineLabel(tr("Only changesets owned by myself")), gc);
gc.gridx = 0;
gc.gridy = 1;
gc.fill= GridBagConstraints.HORIZONTAL;
gc.weightx = 0.0;
add(rbRestrictToUid = new JRadioButton(), 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 = new JRadioButton(), 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 = new ButtonGroup();
bgUserRestrictions.add(rbRestrictToMyself);
bgUserRestrictions.add(rbRestrictToUid);
bgUserRestrictions.add(rbRestrictToUserName);
}
public UserRestrictionPanel() {
build();
}
public void startUserInput() {
if (JosmUserIdentityManager.getInstance().isAnonymous()) {
lblRestrictedToMyself.setText("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("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 thrown if query is null
* @throws IllegalStateException thrown if one of the available values for query parameters in
* this panel isn't valid
*
*/
public void fillInQuery(ChangesetQuery query) throws IllegalStateException, IllegalArgumentException {
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 (v.equals("mine")) {
JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
if (im.isAnonymous()) {
rbRestrictToUid.setSelected(true);
} else {
rbRestrictToMyself.setSelected(true);
}
} else if (v.equals("uid")) {
rbRestrictToUid.setSelected(true);
} else if (v.equals("username")) {
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 {
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
*/
static private class TimeRestrictionPanel extends JPanel {
private JRadioButton rbClosedAfter;
private JRadioButton rbClosedAfterAndCreatedBefore;
private JTextField tfClosedAfterDate1;
private DateValidator valClosedAfterDate1;
private JTextField tfClosedAfterTime1;
private TimeValidator valClosedAfterTime1;
private JTextField tfClosedAfterDate2;
private DateValidator valClosedAfterDate2;
private JTextField tfClosedAfterTime2;
private TimeValidator valClosedAfterTime2;
private JTextField tfCreatedBeforeDate;
private DateValidator valCreatedBeforeDate;
private JTextField tfCreatedBeforeTime;
private TimeValidator valCreatedBeforeTime;
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 = new JTextField(),gc);
SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate1);
valClosedAfterDate1 = DateValidator.decorate(tfClosedAfterDate1);
tfClosedAfterDate1.setToolTipText(valClosedAfterDate1.getStandardTooltipTextAsHtml());
gc.gridx = 2;
gc.weightx = 0.0;
pnl.add(new JLabel("Time: "),gc);
gc.gridx = 3;
gc.weightx = 0.3;
pnl.add(tfClosedAfterTime1 = new JTextField(),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 = new JTextField(),gc);
SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate2);
valClosedAfterDate2 = DateValidator.decorate(tfClosedAfterDate2);
tfClosedAfterDate2.setToolTipText(valClosedAfterDate2.getStandardTooltipTextAsHtml());
gc.gridx = 3;
gc.weightx = 0.0;
pnl.add(new JLabel("Time:"),gc);
gc.gridx = 4;
gc.weightx = 0.3;
pnl.add(tfClosedAfterTime2 = new JTextField(),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 = new JTextField(),gc);
SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeDate);
valCreatedBeforeDate = DateValidator.decorate(tfCreatedBeforeDate);
tfCreatedBeforeDate.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
gc.gridx = 3;
gc.weightx = 0.0;
pnl.add(new JLabel("Time:"),gc);
gc.gridx = 4;
gc.weightx = 0.3;
pnl.add(tfCreatedBeforeTime = new JTextField(),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 = new JRadioButton(), 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 = new JRadioButton(), 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 TimeRestrictionPanel() {
build();
}
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 {
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) throws IllegalStateException{
if (!isValidChangesetQuery())
throw new IllegalStateException(tr("Cannot build changeset query with time based restrictions. Input is not valid."));
if (rbClosedAfter.isSelected()) {
GregorianCalendar cal = new GregorianCalendar();
Date d1 = valClosedAfterDate1.getDate();
Date d2 = valClosedAfterTime1.getDate();
cal.setTimeInMillis(d1.getTime() + (d2 == null ? 0 : d2.getTime()));
query.closedAfter(cal.getTime());
} else if (rbClosedAfterAndCreatedBefore.isSelected()) {
GregorianCalendar cal = new GregorianCalendar();
Date d1 = valClosedAfterDate2.getDate();
Date d2 = valClosedAfterTime2.getDate();
cal.setTimeInMillis(d1.getTime() + (d2 == null ? 0 : d2.getTime()));
Date d3 = cal.getTime();
d1 = valCreatedBeforeDate.getDate();
d2 = valCreatedBeforeTime.getDate();
cal.setTimeInMillis(d1.getTime() + (d2 == null ? 0 : d2.getTime()));
Date d4 = cal.getTime();
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(v.equals("closed-after"));
rbClosedAfterAndCreatedBefore.setSelected(v.equals("closed-after-created-before"));
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("");
}
}
}
static private class BBoxRestrictionPanel extends BoundingBoxSelectionPanel {
public 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 in a {@see JTextComponent}.
*
*/
static private class UidInputFieldValidator extends AbstractTextComponentValidator {
static public UidInputFieldValidator decorate(JTextComponent tc) {
return new UidInputFieldValidator(tc);
}
public UidInputFieldValidator(JTextComponent tc) {
super(tc);
}
@Override
public boolean isValid() {
return getUid() > 0;
}
@Override
public void validate() {
String value = getComponent().getText();
if (value == null || value.trim().length() == 0) {
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().length() == 0) return 0;
try {
int uid = Integer.parseInt(value.trim());
if (uid > 0) return uid;
return 0;
} catch(NumberFormatException e) {
return 0;
}
}
}
static private class UserNameInputValidator extends AbstractTextComponentValidator {
static public UserNameInputValidator decorate(JTextComponent tc) {
return new UserNameInputValidator(tc);
}
public UserNameInputValidator(JTextComponent tc) {
super(tc);
}
@Override
public boolean isValid() {
return getComponent().getText().trim().length() > 0;
}
@Override
public void validate() {
String value = getComponent().getText();
if (value.trim().length() == 0) {
feedbackInvalid(tr("<html>The current value is not a valid user name.<br>Please enter an non-empty user name.</html>"));
return;
}
feedbackValid(tr("Please enter an non-empty user name"));
}
}
/**
* Validates dates entered as text in in a {@see 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.
*/
static private class DateValidator extends AbstractTextComponentValidator {
static public DateValidator decorate(JTextComponent tc) {
return new DateValidator(tc);
}
public DateValidator(JTextComponent tc) {
super(tc);
}
@Override
public boolean isValid() {
return getDate() != null;
}
public String getStandardTooltipTextAsHtml() {
return "<html>" + getStandardTooltipText() + "</html>";
}
public String getStandardTooltipText() {
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>",
DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()).format(new Date()),
DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(new Date()),
DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format(new Date()),
DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault()).format(new Date())
);
}
@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 Date getDate() {
for (int format: new int[] {DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG, DateFormat.FULL}) {
DateFormat df = DateFormat.getDateInstance(format);
try {
return df.parse(getComponent().getText());
} catch (ParseException e) {
// Try next format
}
}
return null;
}
}
/**
* Validates time values entered as text in in a {@see 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.
*/
static private class TimeValidator extends AbstractTextComponentValidator {
static public TimeValidator decorate(JTextComponent tc) {
return new TimeValidator(tc);
}
public TimeValidator(JTextComponent tc) {
super(tc);
}
@Override
public boolean isValid() {
if (getComponent().getText().trim().length() == 0) return true;
return getDate() != null;
}
public String getStandardTooltipTextAsHtml() {
return "<html>" + getStandardTooltipText() + "</html>";
}
public String getStandardTooltipText() {
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>",
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(new Date()),
DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.getDefault()).format(new Date()),
DateFormat.getTimeInstance(DateFormat.LONG, Locale.getDefault()).format(new Date()),
DateFormat.getTimeInstance(DateFormat.FULL, Locale.getDefault()).format(new Date())
);
}
@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 Date getDate() {
if (getComponent().getText().trim().length() == 0)
return null;
for (int i = 0; i< 4; i++) {
try {
DateFormat df = null;
switch(i) {
case 0: df = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()); break;
case 1: df = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.getDefault()); break;
case 2: df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.getDefault()); break;
case 3: df = DateFormat.getTimeInstance(DateFormat.FULL,Locale.getDefault()); break;
}
Date d = df.parse(getComponent().getText());
return d;
} catch(ParseException e) {
continue;
}
}
return null;
}
}
}