// 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(""); } } }