001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset.query; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Color; 007import java.awt.GridBagConstraints; 008import java.awt.GridBagLayout; 009import java.awt.Insets; 010import java.awt.event.ItemEvent; 011import java.awt.event.ItemListener; 012import java.time.Instant; 013import java.time.LocalDate; 014import java.time.LocalTime; 015import java.time.ZoneId; 016 017import javax.swing.BorderFactory; 018import javax.swing.ButtonGroup; 019import javax.swing.JLabel; 020import javax.swing.JOptionPane; 021import javax.swing.JPanel; 022import javax.swing.JRadioButton; 023 024import org.openstreetmap.josm.gui.HelpAwareOptionPane; 025import org.openstreetmap.josm.gui.help.HelpUtil; 026import org.openstreetmap.josm.gui.widgets.JosmTextField; 027import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator; 028import org.openstreetmap.josm.io.ChangesetQuery; 029import org.openstreetmap.josm.spi.preferences.Config; 030import org.openstreetmap.josm.tools.GBC; 031 032/** 033 * This is the panel to apply a time restriction to the changeset query. 034 * @since 11326 (extracted from AdvancedChangesetQueryPanel) 035 */ 036public class TimeRestrictionPanel extends JPanel implements RestrictionPanel { 037 038 private final JRadioButton rbClosedAfter = new JRadioButton( 039 tr("Only changesets closed after the following date/time")); 040 private final JRadioButton rbClosedAfterAndCreatedBefore = new JRadioButton( 041 tr("Only changesets closed after and created before a specific date/time")); 042 private final JosmTextField tfClosedAfterDate1 = new JosmTextField(); 043 private transient DateValidator valClosedAfterDate1; 044 private final JosmTextField tfClosedAfterTime1 = new JosmTextField(); 045 private transient TimeValidator valClosedAfterTime1; 046 private final JosmTextField tfClosedAfterDate2 = new JosmTextField(); 047 private transient DateValidator valClosedAfterDate2; 048 private final JosmTextField tfClosedAfterTime2 = new JosmTextField(); 049 private transient TimeValidator valClosedAfterTime2; 050 private final JosmTextField tfCreatedBeforeDate = new JosmTextField(); 051 private transient DateValidator valCreatedBeforeDate; 052 private final JosmTextField tfCreatedBeforeTime = new JosmTextField(); 053 private transient TimeValidator valCreatedBeforeTime; 054 055 /** 056 * Constructs a new {@code TimeRestrictionPanel}. 057 */ 058 public TimeRestrictionPanel() { 059 build(); 060 } 061 062 protected JPanel buildClosedAfterInputPanel() { 063 JPanel pnl = new JPanel(new GridBagLayout()); 064 GridBagConstraints gc = new GridBagConstraints(); 065 gc.fill = GridBagConstraints.HORIZONTAL; 066 gc.weightx = 0.0; 067 gc.insets = new Insets(0, 0, 0, 3); 068 pnl.add(new JLabel(tr("Date: ")), gc); 069 070 gc.gridx = 1; 071 gc.weightx = 0.7; 072 pnl.add(tfClosedAfterDate1, gc); 073 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate1); 074 valClosedAfterDate1 = DateValidator.decorate(tfClosedAfterDate1); 075 tfClosedAfterDate1.setToolTipText(valClosedAfterDate1.getStandardTooltipTextAsHtml()); 076 077 gc.gridx = 2; 078 gc.weightx = 0.0; 079 pnl.add(new JLabel(tr("Time:")), gc); 080 081 gc.gridx = 3; 082 gc.weightx = 0.3; 083 pnl.add(tfClosedAfterTime1, gc); 084 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime1); 085 valClosedAfterTime1 = TimeValidator.decorate(tfClosedAfterTime1); 086 tfClosedAfterTime1.setToolTipText(valClosedAfterTime1.getStandardTooltipTextAsHtml()); 087 return pnl; 088 } 089 090 protected JPanel buildClosedAfterAndCreatedBeforeInputPanel() { 091 JPanel pnl = new JPanel(new GridBagLayout()); 092 GridBagConstraints gc = new GridBagConstraints(); 093 gc.fill = GridBagConstraints.HORIZONTAL; 094 gc.weightx = 0.0; 095 gc.insets = new Insets(0, 0, 0, 3); 096 pnl.add(new JLabel(tr("Closed after - ")), gc); 097 098 gc.gridx = 1; 099 gc.fill = GridBagConstraints.HORIZONTAL; 100 gc.weightx = 0.0; 101 gc.insets = new Insets(0, 0, 0, 3); 102 pnl.add(new JLabel(tr("Date:")), gc); 103 104 gc.gridx = 2; 105 gc.weightx = 0.7; 106 pnl.add(tfClosedAfterDate2, gc); 107 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate2); 108 valClosedAfterDate2 = DateValidator.decorate(tfClosedAfterDate2); 109 tfClosedAfterDate2.setToolTipText(valClosedAfterDate2.getStandardTooltipTextAsHtml()); 110 gc.gridx = 3; 111 gc.weightx = 0.0; 112 pnl.add(new JLabel(tr("Time:")), gc); 113 114 gc.gridx = 4; 115 gc.weightx = 0.3; 116 pnl.add(tfClosedAfterTime2, gc); 117 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime2); 118 valClosedAfterTime2 = TimeValidator.decorate(tfClosedAfterTime2); 119 tfClosedAfterTime2.setToolTipText(valClosedAfterTime2.getStandardTooltipTextAsHtml()); 120 121 gc.gridy = 1; 122 gc.gridx = 0; 123 gc.fill = GridBagConstraints.HORIZONTAL; 124 gc.weightx = 0.0; 125 gc.insets = new Insets(0, 0, 0, 3); 126 pnl.add(new JLabel(tr("Created before - ")), gc); 127 128 gc.gridx = 1; 129 gc.fill = GridBagConstraints.HORIZONTAL; 130 gc.weightx = 0.0; 131 gc.insets = new Insets(0, 0, 0, 3); 132 pnl.add(new JLabel(tr("Date:")), gc); 133 134 gc.gridx = 2; 135 gc.weightx = 0.7; 136 pnl.add(tfCreatedBeforeDate, gc); 137 SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeDate); 138 valCreatedBeforeDate = DateValidator.decorate(tfCreatedBeforeDate); 139 tfCreatedBeforeDate.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml()); 140 141 gc.gridx = 3; 142 gc.weightx = 0.0; 143 pnl.add(new JLabel(tr("Time:")), gc); 144 145 gc.gridx = 4; 146 gc.weightx = 0.3; 147 pnl.add(tfCreatedBeforeTime, gc); 148 SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeTime); 149 valCreatedBeforeTime = TimeValidator.decorate(tfCreatedBeforeTime); 150 tfCreatedBeforeTime.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml()); 151 152 return pnl; 153 } 154 155 protected void build() { 156 setLayout(new GridBagLayout()); 157 setBorder(BorderFactory.createCompoundBorder( 158 BorderFactory.createEmptyBorder(3, 3, 3, 3), 159 BorderFactory.createCompoundBorder( 160 BorderFactory.createLineBorder(Color.GRAY), 161 BorderFactory.createEmptyBorder(5, 5, 5, 5) 162 ) 163 )); 164 165 // -- changesets closed after a specific date/time 166 // 167 GridBagConstraints gc = GBC.eol().fill(GridBagConstraints.HORIZONTAL); 168 add(rbClosedAfter, gc); 169 add(buildClosedAfterInputPanel(), gc); 170 171 // -- changesets closed after a specific date/time and created before a specific date time 172 // 173 add(rbClosedAfterAndCreatedBefore, gc); 174 add(buildClosedAfterAndCreatedBeforeInputPanel(), gc); 175 176 ButtonGroup bg = new ButtonGroup(); 177 bg.add(rbClosedAfter); 178 bg.add(rbClosedAfterAndCreatedBefore); 179 180 ItemListener restrictionChangeHandler = new TimeRestrictionChangedHandler(); 181 rbClosedAfter.addItemListener(restrictionChangeHandler); 182 rbClosedAfterAndCreatedBefore.addItemListener(restrictionChangeHandler); 183 184 rbClosedAfter.setSelected(true); 185 } 186 187 /** 188 * Determines if the changeset query time information is valid. 189 * @return {@code true} if the changeset query time information is valid. 190 */ 191 @Override 192 public boolean isValidChangesetQuery() { 193 if (rbClosedAfter.isSelected()) 194 return valClosedAfterDate1.isValid() && valClosedAfterTime1.isValid(); 195 else if (rbClosedAfterAndCreatedBefore.isSelected()) 196 return valClosedAfterDate2.isValid() && valClosedAfterTime2.isValid() 197 && valCreatedBeforeDate.isValid() && valCreatedBeforeTime.isValid(); 198 // should not happen 199 return true; 200 } 201 202 class TimeRestrictionChangedHandler implements ItemListener { 203 @Override 204 public void itemStateChanged(ItemEvent e) { 205 tfClosedAfterDate1.setEnabled(rbClosedAfter.isSelected()); 206 tfClosedAfterTime1.setEnabled(rbClosedAfter.isSelected()); 207 208 tfClosedAfterDate2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected()); 209 tfClosedAfterTime2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected()); 210 tfCreatedBeforeDate.setEnabled(rbClosedAfterAndCreatedBefore.isSelected()); 211 tfCreatedBeforeTime.setEnabled(rbClosedAfterAndCreatedBefore.isSelected()); 212 } 213 } 214 215 /** 216 * Initializes HMI for user input. 217 */ 218 public void startUserInput() { 219 restoreFromSettings(); 220 } 221 222 /** 223 * Sets the query restrictions on <code>query</code> for time based restrictions. 224 * @param query the query to fill 225 */ 226 @Override 227 public void fillInQuery(ChangesetQuery query) { 228 if (!isValidChangesetQuery()) 229 throw new IllegalStateException(tr("Cannot build changeset query with time based restrictions. Input is not valid.")); 230 if (rbClosedAfter.isSelected()) { 231 LocalDate d1 = valClosedAfterDate1.getDate(); 232 LocalTime d2 = valClosedAfterTime1.getTime(); 233 final Instant d3 = d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant(); 234 query.closedAfter(d3); 235 } else if (rbClosedAfterAndCreatedBefore.isSelected()) { 236 LocalDate d1 = valClosedAfterDate2.getDate(); 237 LocalTime d2 = valClosedAfterTime2.getTime(); 238 Instant d3 = d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant(); 239 240 d1 = valCreatedBeforeDate.getDate(); 241 d2 = valCreatedBeforeTime.getTime(); 242 Instant d4 = d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant(); 243 244 query.closedAfterAndCreatedBefore(d3, d4); 245 } 246 } 247 248 @Override 249 public void displayMessageIfInvalid() { 250 if (isValidChangesetQuery()) 251 return; 252 HelpAwareOptionPane.showOptionDialog( 253 this, 254 tr( 255 "<html>Please enter valid date/time values to restrict<br>" 256 + "the query to a specific time range.</html>" 257 ), 258 tr("Invalid date/time values"), 259 JOptionPane.ERROR_MESSAGE, 260 HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidDateTimeValues") 261 ); 262 } 263 264 /** 265 * Remember settings in preferences. 266 */ 267 public void rememberSettings() { 268 String prefRoot = "changeset-query.advanced.time-restrictions"; 269 if (rbClosedAfter.isSelected()) { 270 Config.getPref().put(prefRoot + ".query-type", "closed-after"); 271 } else if (rbClosedAfterAndCreatedBefore.isSelected()) { 272 Config.getPref().put(prefRoot + ".query-type", "closed-after-created-before"); 273 } 274 Config.getPref().put(prefRoot + ".closed-after.date", tfClosedAfterDate1.getText()); 275 Config.getPref().put(prefRoot + ".closed-after.time", tfClosedAfterTime1.getText()); 276 Config.getPref().put(prefRoot + ".closed-created.closed.date", tfClosedAfterDate2.getText()); 277 Config.getPref().put(prefRoot + ".closed-created.closed.time", tfClosedAfterTime2.getText()); 278 Config.getPref().put(prefRoot + ".closed-created.created.date", tfCreatedBeforeDate.getText()); 279 Config.getPref().put(prefRoot + ".closed-created.created.time", tfCreatedBeforeTime.getText()); 280 } 281 282 /** 283 * Restore settings from preferences. 284 */ 285 public void restoreFromSettings() { 286 String prefRoot = "changeset-query.advanced.open-restrictions"; 287 String v = Config.getPref().get(prefRoot + ".query-type", "closed-after"); 288 rbClosedAfter.setSelected("closed-after".equals(v)); 289 rbClosedAfterAndCreatedBefore.setSelected("closed-after-created-before".equals(v)); 290 if (!rbClosedAfter.isSelected() && !rbClosedAfterAndCreatedBefore.isSelected()) { 291 rbClosedAfter.setSelected(true); 292 } 293 tfClosedAfterDate1.setText(Config.getPref().get(prefRoot + ".closed-after.date", "")); 294 tfClosedAfterTime1.setText(Config.getPref().get(prefRoot + ".closed-after.time", "")); 295 tfClosedAfterDate2.setText(Config.getPref().get(prefRoot + ".closed-created.closed.date", "")); 296 tfClosedAfterTime2.setText(Config.getPref().get(prefRoot + ".closed-created.closed.time", "")); 297 tfCreatedBeforeDate.setText(Config.getPref().get(prefRoot + ".closed-created.created.date", "")); 298 tfCreatedBeforeTime.setText(Config.getPref().get(prefRoot + ".closed-created.created.time", "")); 299 if (!valClosedAfterDate1.isValid()) { 300 tfClosedAfterDate1.setText(""); 301 } 302 if (!valClosedAfterTime1.isValid()) { 303 tfClosedAfterTime1.setText(""); 304 } 305 if (!valClosedAfterDate2.isValid()) { 306 tfClosedAfterDate2.setText(""); 307 } 308 if (!valClosedAfterTime2.isValid()) { 309 tfClosedAfterTime2.setText(""); 310 } 311 if (!valCreatedBeforeDate.isValid()) { 312 tfCreatedBeforeDate.setText(""); 313 } 314 if (!valCreatedBeforeTime.isValid()) { 315 tfCreatedBeforeTime.setText(""); 316 } 317 } 318}