source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java@ 11035

Last change on this file since 11035 was 11035, checked in by simon04, 8 years ago

see #13376 - Replace Calendar usages with Java 8 Date API

  • Property svn:eol-style set to native
File size: 45.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.changeset.query;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Color;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
10import java.awt.Insets;
11import java.awt.event.ItemEvent;
12import java.awt.event.ItemListener;
13import java.time.LocalDate;
14import java.time.LocalTime;
15import java.time.ZoneId;
16import java.time.ZonedDateTime;
17import java.time.format.DateTimeFormatter;
18import java.time.format.DateTimeParseException;
19import java.time.format.FormatStyle;
20import java.util.Date;
21
22import javax.swing.BorderFactory;
23import javax.swing.ButtonGroup;
24import javax.swing.JCheckBox;
25import javax.swing.JLabel;
26import javax.swing.JOptionPane;
27import javax.swing.JPanel;
28import javax.swing.JRadioButton;
29import javax.swing.JScrollPane;
30import javax.swing.text.JTextComponent;
31
32import org.openstreetmap.josm.Main;
33import org.openstreetmap.josm.gui.HelpAwareOptionPane;
34import org.openstreetmap.josm.gui.JosmUserIdentityManager;
35import org.openstreetmap.josm.gui.help.HelpUtil;
36import org.openstreetmap.josm.gui.preferences.server.UserNameValidator;
37import org.openstreetmap.josm.gui.util.GuiHelper;
38import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
39import org.openstreetmap.josm.gui.widgets.BoundingBoxSelectionPanel;
40import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
41import org.openstreetmap.josm.gui.widgets.JosmTextField;
42import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
43import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
44import org.openstreetmap.josm.io.ChangesetQuery;
45import org.openstreetmap.josm.tools.CheckParameterUtil;
46
47/**
48 * This panel allows to specify a changeset query
49 * @since 2689
50 */
51public class AdvancedChangesetQueryPanel extends JPanel {
52
53 private final JCheckBox cbUserRestriction = new JCheckBox();
54 private final JCheckBox cbOpenAndCloseRestrictions = new JCheckBox();
55 private final JCheckBox cbTimeRestrictions = new JCheckBox();
56 private final JCheckBox cbBoundingBoxRestriction = new JCheckBox();
57 private final UserRestrictionPanel pnlUserRestriction = new UserRestrictionPanel();
58 private final OpenAndCloseStateRestrictionPanel pnlOpenAndCloseRestriction = new OpenAndCloseStateRestrictionPanel();
59 private final TimeRestrictionPanel pnlTimeRestriction = new TimeRestrictionPanel();
60 private final BBoxRestrictionPanel pnlBoundingBoxRestriction = new BBoxRestrictionPanel();
61
62 /**
63 * Constructs a new {@code AdvancedChangesetQueryPanel}.
64 */
65 public AdvancedChangesetQueryPanel() {
66 build();
67 }
68
69 protected JPanel buildQueryPanel() {
70 ItemListener stateChangeHandler = new RestrictionGroupStateChangeHandler();
71 JPanel pnl = new VerticallyScrollablePanel(new GridBagLayout());
72 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
73 GridBagConstraints gc = new GridBagConstraints();
74
75 // -- select changesets by a specific user
76 //
77 gc.anchor = GridBagConstraints.NORTHWEST;
78 gc.weightx = 0.0;
79 gc.fill = GridBagConstraints.HORIZONTAL;
80 pnl.add(cbUserRestriction, gc);
81 cbUserRestriction.addItemListener(stateChangeHandler);
82
83 gc.gridx = 1;
84 gc.weightx = 1.0;
85 pnl.add(new JMultilineLabel(tr("Select changesets owned by specific users")), gc);
86
87 gc.gridy = 1;
88 gc.gridx = 1;
89 gc.weightx = 1.0;
90 pnl.add(pnlUserRestriction, gc);
91
92 // -- restricting the query to open and closed changesets
93 //
94 gc.gridy = 2;
95 gc.gridx = 0;
96 gc.anchor = GridBagConstraints.NORTHWEST;
97 gc.weightx = 0.0;
98 gc.fill = GridBagConstraints.HORIZONTAL;
99 pnl.add(cbOpenAndCloseRestrictions, gc);
100 cbOpenAndCloseRestrictions.addItemListener(stateChangeHandler);
101
102 gc.gridx = 1;
103 gc.weightx = 1.0;
104 pnl.add(new JMultilineLabel(tr("Select changesets depending on whether they are open or closed")), gc);
105
106 gc.gridy = 3;
107 gc.gridx = 1;
108 gc.weightx = 1.0;
109 pnl.add(pnlOpenAndCloseRestriction, gc);
110
111 // -- restricting the query to a specific time
112 //
113 gc.gridy = 4;
114 gc.gridx = 0;
115 gc.anchor = GridBagConstraints.NORTHWEST;
116 gc.weightx = 0.0;
117 gc.fill = GridBagConstraints.HORIZONTAL;
118 pnl.add(cbTimeRestrictions, gc);
119 cbTimeRestrictions.addItemListener(stateChangeHandler);
120
121 gc.gridx = 1;
122 gc.weightx = 1.0;
123 pnl.add(new JMultilineLabel(tr("Select changesets based on the date/time they have been created or closed")), gc);
124
125 gc.gridy = 5;
126 gc.gridx = 1;
127 gc.weightx = 1.0;
128 pnl.add(pnlTimeRestriction, gc);
129
130
131 // -- restricting the query to a specific bounding box
132 //
133 gc.gridy = 6;
134 gc.gridx = 0;
135 gc.anchor = GridBagConstraints.NORTHWEST;
136 gc.weightx = 0.0;
137 gc.fill = GridBagConstraints.HORIZONTAL;
138 pnl.add(cbBoundingBoxRestriction, gc);
139 cbBoundingBoxRestriction.addItemListener(stateChangeHandler);
140
141 gc.gridx = 1;
142 gc.weightx = 1.0;
143 pnl.add(new JMultilineLabel(tr("Select only changesets related to a specific bounding box")), gc);
144
145 gc.gridy = 7;
146 gc.gridx = 1;
147 gc.weightx = 1.0;
148 pnl.add(pnlBoundingBoxRestriction, gc);
149
150
151 gc.gridy = 8;
152 gc.gridx = 0;
153 gc.gridwidth = 2;
154 gc.fill = GridBagConstraints.BOTH;
155 gc.weightx = 1.0;
156 gc.weighty = 1.0;
157 pnl.add(new JPanel(), gc);
158
159 return pnl;
160 }
161
162 protected final void build() {
163 setLayout(new BorderLayout());
164 JScrollPane spQueryPanel = GuiHelper.embedInVerticalScrollPane(buildQueryPanel());
165 add(spQueryPanel, BorderLayout.CENTER);
166 }
167
168 public void startUserInput() {
169 restoreFromSettings();
170 pnlBoundingBoxRestriction.setVisible(cbBoundingBoxRestriction.isSelected());
171 pnlOpenAndCloseRestriction.setVisible(cbOpenAndCloseRestrictions.isSelected());
172 pnlTimeRestriction.setVisible(cbTimeRestrictions.isSelected());
173 pnlUserRestriction.setVisible(cbUserRestriction.isSelected());
174 pnlOpenAndCloseRestriction.startUserInput();
175 pnlUserRestriction.startUserInput();
176 pnlTimeRestriction.startUserInput();
177 }
178
179 public void displayMessageIfInvalid() {
180 if (cbUserRestriction.isSelected()) {
181 if (!pnlUserRestriction.isValidChangesetQuery()) {
182 pnlUserRestriction.displayMessageIfInvalid();
183 }
184 } else if (cbTimeRestrictions.isSelected()) {
185 if (!pnlTimeRestriction.isValidChangesetQuery()) {
186 pnlTimeRestriction.displayMessageIfInvalid();
187 }
188 } else if (cbBoundingBoxRestriction.isSelected()) {
189 if (!pnlBoundingBoxRestriction.isValidChangesetQuery()) {
190 pnlBoundingBoxRestriction.displayMessageIfInvalid();
191 }
192 }
193 }
194
195 /**
196 * Builds the changeset query based on the data entered in the form.
197 *
198 * @return the changeset query. null, if the data entered doesn't represent
199 * a valid changeset query.
200 */
201 public ChangesetQuery buildChangesetQuery() {
202 ChangesetQuery query = new ChangesetQuery();
203 if (cbUserRestriction.isSelected()) {
204 if (!pnlUserRestriction.isValidChangesetQuery())
205 return null;
206 pnlUserRestriction.fillInQuery(query);
207 }
208 if (cbOpenAndCloseRestrictions.isSelected()) {
209 // don't have to check whether it's valid. It always is.
210 pnlOpenAndCloseRestriction.fillInQuery(query);
211 }
212 if (cbBoundingBoxRestriction.isSelected()) {
213 if (!pnlBoundingBoxRestriction.isValidChangesetQuery())
214 return null;
215 pnlBoundingBoxRestriction.fillInQuery(query);
216 }
217 if (cbTimeRestrictions.isSelected()) {
218 if (!pnlTimeRestriction.isValidChangesetQuery())
219 return null;
220 pnlTimeRestriction.fillInQuery(query);
221 }
222 return query;
223 }
224
225 public void rememberSettings() {
226 Main.pref.put("changeset-query.advanced.user-restrictions", cbUserRestriction.isSelected());
227 Main.pref.put("changeset-query.advanced.open-restrictions", cbOpenAndCloseRestrictions.isSelected());
228 Main.pref.put("changeset-query.advanced.time-restrictions", cbTimeRestrictions.isSelected());
229 Main.pref.put("changeset-query.advanced.bbox-restrictions", cbBoundingBoxRestriction.isSelected());
230
231 pnlUserRestriction.rememberSettings();
232 pnlOpenAndCloseRestriction.rememberSettings();
233 pnlTimeRestriction.rememberSettings();
234 }
235
236 public void restoreFromSettings() {
237 cbUserRestriction.setSelected(Main.pref.getBoolean("changeset-query.advanced.user-restrictions", false));
238 cbOpenAndCloseRestrictions.setSelected(Main.pref.getBoolean("changeset-query.advanced.open-restrictions", false));
239 cbTimeRestrictions.setSelected(Main.pref.getBoolean("changeset-query.advanced.time-restrictions", false));
240 cbBoundingBoxRestriction.setSelected(Main.pref.getBoolean("changeset-query.advanced.bbox-restrictions", false));
241 }
242
243 class RestrictionGroupStateChangeHandler implements ItemListener {
244 protected void userRestrictionStateChanged() {
245 if (pnlUserRestriction == null)
246 return;
247 pnlUserRestriction.setVisible(cbUserRestriction.isSelected());
248 }
249
250 protected void openCloseRestrictionStateChanged() {
251 if (pnlOpenAndCloseRestriction == null)
252 return;
253 pnlOpenAndCloseRestriction.setVisible(cbOpenAndCloseRestrictions.isSelected());
254 }
255
256 protected void timeRestrictionsStateChanged() {
257 if (pnlTimeRestriction == null)
258 return;
259 pnlTimeRestriction.setVisible(cbTimeRestrictions.isSelected());
260 }
261
262 protected void boundingBoxRestrictionChanged() {
263 if (pnlBoundingBoxRestriction == null)
264 return;
265 pnlBoundingBoxRestriction.setVisible(cbBoundingBoxRestriction.isSelected());
266 }
267
268 @Override
269 public void itemStateChanged(ItemEvent e) {
270 if (e.getSource() == cbUserRestriction) {
271 userRestrictionStateChanged();
272 } else if (e.getSource() == cbOpenAndCloseRestrictions) {
273 openCloseRestrictionStateChanged();
274 } else if (e.getSource() == cbTimeRestrictions) {
275 timeRestrictionsStateChanged();
276 } else if (e.getSource() == cbBoundingBoxRestriction) {
277 boundingBoxRestrictionChanged();
278 }
279 validate();
280 repaint();
281 }
282 }
283
284 /**
285 * This is the panel for selecting whether the changeset query should be restricted to
286 * open or closed changesets
287 */
288 private static class OpenAndCloseStateRestrictionPanel extends JPanel {
289
290 private final JRadioButton rbOpenOnly = new JRadioButton();
291 private final JRadioButton rbClosedOnly = new JRadioButton();
292 private final JRadioButton rbBoth = new JRadioButton();
293
294 OpenAndCloseStateRestrictionPanel() {
295 build();
296 }
297
298 protected void build() {
299 setLayout(new GridBagLayout());
300 setBorder(BorderFactory.createCompoundBorder(
301 BorderFactory.createEmptyBorder(3, 3, 3, 3),
302 BorderFactory.createCompoundBorder(
303 BorderFactory.createLineBorder(Color.GRAY),
304 BorderFactory.createEmptyBorder(5, 5, 5, 5)
305 )
306 ));
307 GridBagConstraints gc = new GridBagConstraints();
308 gc.anchor = GridBagConstraints.NORTHWEST;
309 gc.fill = GridBagConstraints.HORIZONTAL;
310 gc.weightx = 0.0;
311 add(rbOpenOnly, gc);
312
313 gc.gridx = 1;
314 gc.weightx = 1.0;
315 add(new JMultilineLabel(tr("Query open changesets only")), gc);
316
317 gc.gridy = 1;
318 gc.gridx = 0;
319 gc.weightx = 0.0;
320 add(rbClosedOnly, gc);
321
322 gc.gridx = 1;
323 gc.weightx = 1.0;
324 add(new JMultilineLabel(tr("Query closed changesets only")), gc);
325
326 gc.gridy = 2;
327 gc.gridx = 0;
328 gc.weightx = 0.0;
329 add(rbBoth, gc);
330
331 gc.gridx = 1;
332 gc.weightx = 1.0;
333 add(new JMultilineLabel(tr("Query both open and closed changesets")), gc);
334
335 ButtonGroup bgRestrictions = new ButtonGroup();
336 bgRestrictions.add(rbBoth);
337 bgRestrictions.add(rbClosedOnly);
338 bgRestrictions.add(rbOpenOnly);
339 }
340
341 public void startUserInput() {
342 restoreFromSettings();
343 }
344
345 public void fillInQuery(ChangesetQuery query) {
346 if (rbBoth.isSelected()) {
347 query.beingClosed(true);
348 query.beingOpen(true);
349 } else if (rbOpenOnly.isSelected()) {
350 query.beingOpen(true);
351 } else if (rbClosedOnly.isSelected()) {
352 query.beingClosed(true);
353 }
354 }
355
356 public void rememberSettings() {
357 String prefRoot = "changeset-query.advanced.open-restrictions";
358 if (rbBoth.isSelected()) {
359 Main.pref.put(prefRoot + ".query-type", "both");
360 } else if (rbOpenOnly.isSelected()) {
361 Main.pref.put(prefRoot + ".query-type", "open");
362 } else if (rbClosedOnly.isSelected()) {
363 Main.pref.put(prefRoot + ".query-type", "closed");
364 }
365 }
366
367 public void restoreFromSettings() {
368 String prefRoot = "changeset-query.advanced.open-restrictions";
369 String v = Main.pref.get(prefRoot + ".query-type", "open");
370 rbBoth.setSelected("both".equals(v));
371 rbOpenOnly.setSelected("open".equals(v));
372 rbClosedOnly.setSelected("closed".equals(v));
373 }
374 }
375
376 /**
377 * This is the panel for selecting whether the query should be restricted to a specific user
378 */
379 private static class UserRestrictionPanel extends JPanel {
380 private final ButtonGroup bgUserRestrictions = new ButtonGroup();
381 private final JRadioButton rbRestrictToMyself = new JRadioButton();
382 private final JRadioButton rbRestrictToUid = new JRadioButton();
383 private final JRadioButton rbRestrictToUserName = new JRadioButton();
384 private final JosmTextField tfUid = new JosmTextField(10);
385 private transient UidInputFieldValidator valUid;
386 private final JosmTextField tfUserName = new JosmTextField(10);
387 private transient UserNameValidator valUserName;
388 private final JMultilineLabel lblRestrictedToMyself = new JMultilineLabel(tr("Only changesets owned by myself"));
389
390 UserRestrictionPanel() {
391 build();
392 }
393
394 protected JPanel buildUidInputPanel() {
395 JPanel pnl = new JPanel(new GridBagLayout());
396 GridBagConstraints gc = new GridBagConstraints();
397 gc.fill = GridBagConstraints.HORIZONTAL;
398 gc.weightx = 0.0;
399 gc.insets = new Insets(0, 0, 0, 3);
400 pnl.add(new JLabel(tr("User ID:")), gc);
401
402 gc.gridx = 1;
403 pnl.add(tfUid, gc);
404 SelectAllOnFocusGainedDecorator.decorate(tfUid);
405 valUid = UidInputFieldValidator.decorate(tfUid);
406
407 // grab remaining space
408 gc.gridx = 2;
409 gc.weightx = 1.0;
410 pnl.add(new JPanel(), gc);
411 return pnl;
412 }
413
414 protected JPanel buildUserNameInputPanel() {
415 JPanel pnl = new JPanel(new GridBagLayout());
416 GridBagConstraints gc = new GridBagConstraints();
417 gc.fill = GridBagConstraints.HORIZONTAL;
418 gc.weightx = 0.0;
419 gc.insets = new Insets(0, 0, 0, 3);
420 pnl.add(new JLabel(tr("User name:")), gc);
421
422 gc.gridx = 1;
423 pnl.add(tfUserName, gc);
424 SelectAllOnFocusGainedDecorator.decorate(tfUserName);
425 valUserName = new UserNameValidator(tfUserName);
426
427 // grab remaining space
428 gc.gridx = 2;
429 gc.weightx = 1.0;
430 pnl.add(new JPanel(), gc);
431 return pnl;
432 }
433
434 protected void build() {
435 setLayout(new GridBagLayout());
436 setBorder(BorderFactory.createCompoundBorder(
437 BorderFactory.createEmptyBorder(3, 3, 3, 3),
438 BorderFactory.createCompoundBorder(
439 BorderFactory.createLineBorder(Color.GRAY),
440 BorderFactory.createEmptyBorder(5, 5, 5, 5)
441 )
442 ));
443
444 ItemListener userRestrictionChangeHandler = new UserRestrictionChangedHandler();
445 GridBagConstraints gc = new GridBagConstraints();
446 gc.anchor = GridBagConstraints.NORTHWEST;
447 gc.gridx = 0;
448 gc.fill = GridBagConstraints.HORIZONTAL;
449 gc.weightx = 0.0;
450 add(rbRestrictToMyself, gc);
451 rbRestrictToMyself.addItemListener(userRestrictionChangeHandler);
452
453 gc.gridx = 1;
454 gc.fill = GridBagConstraints.HORIZONTAL;
455 gc.weightx = 1.0;
456 add(lblRestrictedToMyself, gc);
457
458 gc.gridx = 0;
459 gc.gridy = 1;
460 gc.fill = GridBagConstraints.HORIZONTAL;
461 gc.weightx = 0.0;
462 add(rbRestrictToUid, gc);
463 rbRestrictToUid.addItemListener(userRestrictionChangeHandler);
464
465 gc.gridx = 1;
466 gc.fill = GridBagConstraints.HORIZONTAL;
467 gc.weightx = 1.0;
468 add(new JMultilineLabel(tr("Only changesets owned by the user with the following user ID")), gc);
469
470 gc.gridx = 1;
471 gc.gridy = 2;
472 gc.fill = GridBagConstraints.HORIZONTAL;
473 gc.weightx = 1.0;
474 add(buildUidInputPanel(), gc);
475
476 gc.gridx = 0;
477 gc.gridy = 3;
478 gc.fill = GridBagConstraints.HORIZONTAL;
479 gc.weightx = 0.0;
480 add(rbRestrictToUserName, gc);
481 rbRestrictToUserName.addItemListener(userRestrictionChangeHandler);
482
483 gc.gridx = 1;
484 gc.fill = GridBagConstraints.HORIZONTAL;
485 gc.weightx = 1.0;
486 add(new JMultilineLabel(tr("Only changesets owned by the user with the following user name")), gc);
487
488 gc.gridx = 1;
489 gc.gridy = 4;
490 gc.fill = GridBagConstraints.HORIZONTAL;
491 gc.weightx = 1.0;
492 add(buildUserNameInputPanel(), gc);
493
494 bgUserRestrictions.add(rbRestrictToMyself);
495 bgUserRestrictions.add(rbRestrictToUid);
496 bgUserRestrictions.add(rbRestrictToUserName);
497 }
498
499 public void startUserInput() {
500 if (JosmUserIdentityManager.getInstance().isAnonymous()) {
501 lblRestrictedToMyself.setText(tr("Only changesets owned by myself (disabled. JOSM is currently run by an anonymous user)"));
502 rbRestrictToMyself.setEnabled(false);
503 if (rbRestrictToMyself.isSelected()) {
504 rbRestrictToUid.setSelected(true);
505 }
506 } else {
507 lblRestrictedToMyself.setText(tr("Only changesets owned by myself"));
508 rbRestrictToMyself.setEnabled(true);
509 rbRestrictToMyself.setSelected(true);
510 }
511 restoreFromSettings();
512 }
513
514 /**
515 * Sets the query restrictions on <code>query</code> for changeset owner based
516 * restrictions.
517 *
518 * @param query the query. Must not be null.
519 * @throws IllegalArgumentException if query is null
520 * @throws IllegalStateException if one of the available values for query parameters in
521 * this panel isn't valid
522 */
523 public void fillInQuery(ChangesetQuery query) {
524 CheckParameterUtil.ensureParameterNotNull(query, "query");
525 if (rbRestrictToMyself.isSelected()) {
526 JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
527 if (im.isPartiallyIdentified()) {
528 query.forUser(im.getUserName());
529 } else if (im.isFullyIdentified()) {
530 query.forUser(im.getUserId());
531 } else
532 throw new IllegalStateException(
533 tr("Cannot restrict changeset query to the current user because the current user is anonymous"));
534 } else if (rbRestrictToUid.isSelected()) {
535 int uid = valUid.getUid();
536 if (uid > 0) {
537 query.forUser(uid);
538 } else
539 throw new IllegalStateException(tr("Current value ''{0}'' for user ID is not valid", tfUid.getText()));
540 } else if (rbRestrictToUserName.isSelected()) {
541 if (!valUserName.isValid())
542 throw new IllegalStateException(
543 tr("Cannot restrict the changeset query to the user name ''{0}''", tfUserName.getText()));
544 query.forUser(tfUserName.getText());
545 }
546 }
547
548 public boolean isValidChangesetQuery() {
549 if (rbRestrictToUid.isSelected())
550 return valUid.isValid();
551 else if (rbRestrictToUserName.isSelected())
552 return valUserName.isValid();
553 return true;
554 }
555
556 protected void alertInvalidUid() {
557 HelpAwareOptionPane.showOptionDialog(
558 this,
559 tr("Please enter a valid user ID"),
560 tr("Invalid user ID"),
561 JOptionPane.ERROR_MESSAGE,
562 HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserId")
563 );
564 }
565
566 protected void alertInvalidUserName() {
567 HelpAwareOptionPane.showOptionDialog(
568 this,
569 tr("Please enter a non-empty user name"),
570 tr("Invalid user name"),
571 JOptionPane.ERROR_MESSAGE,
572 HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidUserName")
573 );
574 }
575
576 public void displayMessageIfInvalid() {
577 if (rbRestrictToUid.isSelected()) {
578 if (!valUid.isValid()) {
579 alertInvalidUid();
580 }
581 } else if (rbRestrictToUserName.isSelected()) {
582 if (!valUserName.isValid()) {
583 alertInvalidUserName();
584 }
585 }
586 }
587
588 public void rememberSettings() {
589 String prefRoot = "changeset-query.advanced.user-restrictions";
590 if (rbRestrictToMyself.isSelected()) {
591 Main.pref.put(prefRoot + ".query-type", "mine");
592 } else if (rbRestrictToUid.isSelected()) {
593 Main.pref.put(prefRoot + ".query-type", "uid");
594 } else if (rbRestrictToUserName.isSelected()) {
595 Main.pref.put(prefRoot + ".query-type", "username");
596 }
597 Main.pref.put(prefRoot + ".uid", tfUid.getText());
598 Main.pref.put(prefRoot + ".username", tfUserName.getText());
599 }
600
601 public void restoreFromSettings() {
602 String prefRoot = "changeset-query.advanced.user-restrictions";
603 String v = Main.pref.get(prefRoot + ".query-type", "mine");
604 if ("mine".equals(v)) {
605 JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
606 if (im.isAnonymous()) {
607 rbRestrictToUid.setSelected(true);
608 } else {
609 rbRestrictToMyself.setSelected(true);
610 }
611 } else if ("uid".equals(v)) {
612 rbRestrictToUid.setSelected(true);
613 } else if ("username".equals(v)) {
614 rbRestrictToUserName.setSelected(true);
615 }
616 tfUid.setText(Main.pref.get(prefRoot + ".uid", ""));
617 if (!valUid.isValid()) {
618 tfUid.setText("");
619 }
620 tfUserName.setText(Main.pref.get(prefRoot + ".username", ""));
621 }
622
623 class UserRestrictionChangedHandler implements ItemListener {
624 @Override
625 public void itemStateChanged(ItemEvent e) {
626 tfUid.setEnabled(rbRestrictToUid.isSelected());
627 tfUserName.setEnabled(rbRestrictToUserName.isSelected());
628 if (rbRestrictToUid.isSelected()) {
629 tfUid.requestFocusInWindow();
630 } else if (rbRestrictToUserName.isSelected()) {
631 tfUserName.requestFocusInWindow();
632 }
633 }
634 }
635 }
636
637 /**
638 * This is the panel to apply a time restriction to the changeset query
639 */
640 private static class TimeRestrictionPanel extends JPanel {
641
642 private final JRadioButton rbClosedAfter = new JRadioButton();
643 private final JRadioButton rbClosedAfterAndCreatedBefore = new JRadioButton();
644 private final JosmTextField tfClosedAfterDate1 = new JosmTextField();
645 private transient DateValidator valClosedAfterDate1;
646 private final JosmTextField tfClosedAfterTime1 = new JosmTextField();
647 private transient TimeValidator valClosedAfterTime1;
648 private final JosmTextField tfClosedAfterDate2 = new JosmTextField();
649 private transient DateValidator valClosedAfterDate2;
650 private final JosmTextField tfClosedAfterTime2 = new JosmTextField();
651 private transient TimeValidator valClosedAfterTime2;
652 private final JosmTextField tfCreatedBeforeDate = new JosmTextField();
653 private transient DateValidator valCreatedBeforeDate;
654 private final JosmTextField tfCreatedBeforeTime = new JosmTextField();
655 private transient TimeValidator valCreatedBeforeTime;
656
657 TimeRestrictionPanel() {
658 build();
659 }
660
661 protected JPanel buildClosedAfterInputPanel() {
662 JPanel pnl = new JPanel(new GridBagLayout());
663 GridBagConstraints gc = new GridBagConstraints();
664 gc.fill = GridBagConstraints.HORIZONTAL;
665 gc.weightx = 0.0;
666 gc.insets = new Insets(0, 0, 0, 3);
667 pnl.add(new JLabel(tr("Date: ")), gc);
668
669 gc.gridx = 1;
670 gc.weightx = 0.7;
671 pnl.add(tfClosedAfterDate1, gc);
672 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate1);
673 valClosedAfterDate1 = DateValidator.decorate(tfClosedAfterDate1);
674 tfClosedAfterDate1.setToolTipText(valClosedAfterDate1.getStandardTooltipTextAsHtml());
675
676 gc.gridx = 2;
677 gc.weightx = 0.0;
678 pnl.add(new JLabel(tr("Time:")), gc);
679
680 gc.gridx = 3;
681 gc.weightx = 0.3;
682 pnl.add(tfClosedAfterTime1, gc);
683 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime1);
684 valClosedAfterTime1 = TimeValidator.decorate(tfClosedAfterTime1);
685 tfClosedAfterTime1.setToolTipText(valClosedAfterTime1.getStandardTooltipTextAsHtml());
686 return pnl;
687 }
688
689 protected JPanel buildClosedAfterAndCreatedBeforeInputPanel() {
690 JPanel pnl = new JPanel(new GridBagLayout());
691 GridBagConstraints gc = new GridBagConstraints();
692 gc.fill = GridBagConstraints.HORIZONTAL;
693 gc.weightx = 0.0;
694 gc.insets = new Insets(0, 0, 0, 3);
695 pnl.add(new JLabel(tr("Closed after - ")), gc);
696
697 gc.gridx = 1;
698 gc.fill = GridBagConstraints.HORIZONTAL;
699 gc.weightx = 0.0;
700 gc.insets = new Insets(0, 0, 0, 3);
701 pnl.add(new JLabel(tr("Date:")), gc);
702
703 gc.gridx = 2;
704 gc.weightx = 0.7;
705 pnl.add(tfClosedAfterDate2, gc);
706 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterDate2);
707 valClosedAfterDate2 = DateValidator.decorate(tfClosedAfterDate2);
708 tfClosedAfterDate2.setToolTipText(valClosedAfterDate2.getStandardTooltipTextAsHtml());
709 gc.gridx = 3;
710 gc.weightx = 0.0;
711 pnl.add(new JLabel(tr("Time:")), gc);
712
713 gc.gridx = 4;
714 gc.weightx = 0.3;
715 pnl.add(tfClosedAfterTime2, gc);
716 SelectAllOnFocusGainedDecorator.decorate(tfClosedAfterTime2);
717 valClosedAfterTime2 = TimeValidator.decorate(tfClosedAfterTime2);
718 tfClosedAfterTime2.setToolTipText(valClosedAfterTime2.getStandardTooltipTextAsHtml());
719
720 gc.gridy = 1;
721 gc.gridx = 0;
722 gc.fill = GridBagConstraints.HORIZONTAL;
723 gc.weightx = 0.0;
724 gc.insets = new Insets(0, 0, 0, 3);
725 pnl.add(new JLabel(tr("Created before - ")), gc);
726
727 gc.gridx = 1;
728 gc.fill = GridBagConstraints.HORIZONTAL;
729 gc.weightx = 0.0;
730 gc.insets = new Insets(0, 0, 0, 3);
731 pnl.add(new JLabel(tr("Date:")), gc);
732
733 gc.gridx = 2;
734 gc.weightx = 0.7;
735 pnl.add(tfCreatedBeforeDate, gc);
736 SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeDate);
737 valCreatedBeforeDate = DateValidator.decorate(tfCreatedBeforeDate);
738 tfCreatedBeforeDate.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
739
740 gc.gridx = 3;
741 gc.weightx = 0.0;
742 pnl.add(new JLabel(tr("Time:")), gc);
743
744 gc.gridx = 4;
745 gc.weightx = 0.3;
746 pnl.add(tfCreatedBeforeTime, gc);
747 SelectAllOnFocusGainedDecorator.decorate(tfCreatedBeforeTime);
748 valCreatedBeforeTime = TimeValidator.decorate(tfCreatedBeforeTime);
749 tfCreatedBeforeTime.setToolTipText(valCreatedBeforeDate.getStandardTooltipTextAsHtml());
750
751 return pnl;
752 }
753
754 protected void build() {
755 setLayout(new GridBagLayout());
756 setBorder(BorderFactory.createCompoundBorder(
757 BorderFactory.createEmptyBorder(3, 3, 3, 3),
758 BorderFactory.createCompoundBorder(
759 BorderFactory.createLineBorder(Color.GRAY),
760 BorderFactory.createEmptyBorder(5, 5, 5, 5)
761 )
762 ));
763
764 // -- changesets closed after a specific date/time
765 //
766 GridBagConstraints gc = new GridBagConstraints();
767 gc.anchor = GridBagConstraints.NORTHWEST;
768 gc.gridx = 0;
769 gc.fill = GridBagConstraints.HORIZONTAL;
770 gc.weightx = 0.0;
771 add(rbClosedAfter, gc);
772
773 gc.gridx = 1;
774 gc.fill = GridBagConstraints.HORIZONTAL;
775 gc.weightx = 1.0;
776 add(new JMultilineLabel(tr("Only changesets closed after the following date/time")), gc);
777
778 gc.gridx = 1;
779 gc.gridy = 1;
780 gc.fill = GridBagConstraints.HORIZONTAL;
781 gc.weightx = 1.0;
782 add(buildClosedAfterInputPanel(), gc);
783
784 // -- changesets closed after a specific date/time and created before a specific date time
785 //
786 gc = new GridBagConstraints();
787 gc.anchor = GridBagConstraints.NORTHWEST;
788 gc.gridy = 2;
789 gc.gridx = 0;
790 gc.fill = GridBagConstraints.HORIZONTAL;
791 gc.weightx = 0.0;
792 add(rbClosedAfterAndCreatedBefore, gc);
793
794 gc.gridx = 1;
795 gc.fill = GridBagConstraints.HORIZONTAL;
796 gc.weightx = 1.0;
797 add(new JMultilineLabel(tr("Only changesets closed after and created before a specific date/time")), gc);
798
799 gc.gridx = 1;
800 gc.gridy = 3;
801 gc.fill = GridBagConstraints.HORIZONTAL;
802 gc.weightx = 1.0;
803 add(buildClosedAfterAndCreatedBeforeInputPanel(), gc);
804
805 ButtonGroup bg = new ButtonGroup();
806 bg.add(rbClosedAfter);
807 bg.add(rbClosedAfterAndCreatedBefore);
808
809 ItemListener restrictionChangeHandler = new TimeRestrictionChangedHandler();
810 rbClosedAfter.addItemListener(restrictionChangeHandler);
811 rbClosedAfterAndCreatedBefore.addItemListener(restrictionChangeHandler);
812
813 rbClosedAfter.setSelected(true);
814 }
815
816 public boolean isValidChangesetQuery() {
817 if (rbClosedAfter.isSelected())
818 return valClosedAfterDate1.isValid() && valClosedAfterTime1.isValid();
819 else if (rbClosedAfterAndCreatedBefore.isSelected())
820 return valClosedAfterDate2.isValid() && valClosedAfterTime2.isValid()
821 && valCreatedBeforeDate.isValid() && valCreatedBeforeTime.isValid();
822 // should not happen
823 return true;
824 }
825
826 class TimeRestrictionChangedHandler implements ItemListener {
827 @Override
828 public void itemStateChanged(ItemEvent e) {
829 tfClosedAfterDate1.setEnabled(rbClosedAfter.isSelected());
830 tfClosedAfterTime1.setEnabled(rbClosedAfter.isSelected());
831
832 tfClosedAfterDate2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
833 tfClosedAfterTime2.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
834 tfCreatedBeforeDate.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
835 tfCreatedBeforeTime.setEnabled(rbClosedAfterAndCreatedBefore.isSelected());
836 }
837 }
838
839 public void startUserInput() {
840 restoreFromSettings();
841 }
842
843 public void fillInQuery(ChangesetQuery query) {
844 if (!isValidChangesetQuery())
845 throw new IllegalStateException(tr("Cannot build changeset query with time based restrictions. Input is not valid."));
846 if (rbClosedAfter.isSelected()) {
847 LocalDate d1 = valClosedAfterDate1.getDate();
848 LocalTime d2 = valClosedAfterTime1.getDate();
849 final Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
850 query.closedAfter(d3);
851 } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
852 LocalDate d1 = valClosedAfterDate2.getDate();
853 LocalTime d2 = valClosedAfterTime2.getDate();
854 Date d3 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
855
856 d1 = valCreatedBeforeDate.getDate();
857 d2 = valCreatedBeforeTime.getDate();
858 Date d4 = new Date(d1.atTime(d2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
859
860 query.closedAfterAndCreatedBefore(d3, d4);
861 }
862 }
863
864 public void displayMessageIfInvalid() {
865 if (isValidChangesetQuery())
866 return;
867 HelpAwareOptionPane.showOptionDialog(
868 this,
869 tr(
870 "<html>Please enter valid date/time values to restrict<br>"
871 + "the query to a specific time range.</html>"
872 ),
873 tr("Invalid date/time values"),
874 JOptionPane.ERROR_MESSAGE,
875 HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidDateTimeValues")
876 );
877 }
878
879 public void rememberSettings() {
880 String prefRoot = "changeset-query.advanced.time-restrictions";
881 if (rbClosedAfter.isSelected()) {
882 Main.pref.put(prefRoot + ".query-type", "closed-after");
883 } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
884 Main.pref.put(prefRoot + ".query-type", "closed-after-created-before");
885 }
886 Main.pref.put(prefRoot + ".closed-after.date", tfClosedAfterDate1.getText());
887 Main.pref.put(prefRoot + ".closed-after.time", tfClosedAfterTime1.getText());
888 Main.pref.put(prefRoot + ".closed-created.closed.date", tfClosedAfterDate2.getText());
889 Main.pref.put(prefRoot + ".closed-created.closed.time", tfClosedAfterTime2.getText());
890 Main.pref.put(prefRoot + ".closed-created.created.date", tfCreatedBeforeDate.getText());
891 Main.pref.put(prefRoot + ".closed-created.created.time", tfCreatedBeforeTime.getText());
892 }
893
894 public void restoreFromSettings() {
895 String prefRoot = "changeset-query.advanced.open-restrictions";
896 String v = Main.pref.get(prefRoot + ".query-type", "closed-after");
897 rbClosedAfter.setSelected("closed-after".equals(v));
898 rbClosedAfterAndCreatedBefore.setSelected("closed-after-created-before".equals(v));
899 if (!rbClosedAfter.isSelected() && !rbClosedAfterAndCreatedBefore.isSelected()) {
900 rbClosedAfter.setSelected(true);
901 }
902 tfClosedAfterDate1.setText(Main.pref.get(prefRoot + ".closed-after.date", ""));
903 tfClosedAfterTime1.setText(Main.pref.get(prefRoot + ".closed-after.time", ""));
904 tfClosedAfterDate2.setText(Main.pref.get(prefRoot + ".closed-created.closed.date", ""));
905 tfClosedAfterTime2.setText(Main.pref.get(prefRoot + ".closed-created.closed.time", ""));
906 tfCreatedBeforeDate.setText(Main.pref.get(prefRoot + ".closed-created.created.date", ""));
907 tfCreatedBeforeTime.setText(Main.pref.get(prefRoot + ".closed-created.created.time", ""));
908 if (!valClosedAfterDate1.isValid()) {
909 tfClosedAfterDate1.setText("");
910 }
911 if (!valClosedAfterTime1.isValid()) {
912 tfClosedAfterTime1.setText("");
913 }
914 if (!valClosedAfterDate2.isValid()) {
915 tfClosedAfterDate2.setText("");
916 }
917 if (!valClosedAfterTime2.isValid()) {
918 tfClosedAfterTime2.setText("");
919 }
920 if (!valCreatedBeforeDate.isValid()) {
921 tfCreatedBeforeDate.setText("");
922 }
923 if (!valCreatedBeforeTime.isValid()) {
924 tfCreatedBeforeTime.setText("");
925 }
926 }
927 }
928
929 private static class BBoxRestrictionPanel extends BoundingBoxSelectionPanel {
930 BBoxRestrictionPanel() {
931 setBorder(BorderFactory.createCompoundBorder(
932 BorderFactory.createEmptyBorder(3, 3, 3, 3),
933 BorderFactory.createCompoundBorder(
934 BorderFactory.createLineBorder(Color.GRAY),
935 BorderFactory.createEmptyBorder(5, 5, 5, 5)
936 )
937 ));
938 }
939
940 public boolean isValidChangesetQuery() {
941 return getBoundingBox() != null;
942 }
943
944 public void fillInQuery(ChangesetQuery query) {
945 if (!isValidChangesetQuery())
946 throw new IllegalStateException(tr("Cannot restrict the changeset query to a specific bounding box. The input is invalid."));
947 query.inBbox(getBoundingBox());
948 }
949
950 public void displayMessageIfInvalid() {
951 if (isValidChangesetQuery())
952 return;
953 HelpAwareOptionPane.showOptionDialog(
954 this,
955 tr(
956 "<html>Please enter valid longitude/latitude values to restrict<br>" +
957 "the changeset query to a specific bounding box.</html>"
958 ),
959 tr("Invalid bounding box"),
960 JOptionPane.ERROR_MESSAGE,
961 HelpUtil.ht("/Dialog/ChangesetQueryDialog#InvalidBoundingBox")
962 );
963 }
964 }
965
966 /**
967 * Validator for user ids entered in a {@link JTextComponent}.
968 *
969 */
970 private static class UidInputFieldValidator extends AbstractTextComponentValidator {
971 UidInputFieldValidator(JTextComponent tc) {
972 super(tc);
973 }
974
975 public static UidInputFieldValidator decorate(JTextComponent tc) {
976 return new UidInputFieldValidator(tc);
977 }
978
979 @Override
980 public boolean isValid() {
981 return getUid() > 0;
982 }
983
984 @Override
985 public void validate() {
986 String value = getComponent().getText();
987 if (value == null || value.trim().isEmpty()) {
988 feedbackInvalid("");
989 return;
990 }
991 try {
992 int uid = Integer.parseInt(value);
993 if (uid <= 0) {
994 feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
995 return;
996 }
997 } catch (NumberFormatException e) {
998 feedbackInvalid(tr("The current value is not a valid user ID. Please enter an integer value > 0"));
999 return;
1000 }
1001 feedbackValid(tr("Please enter an integer value > 0"));
1002 }
1003
1004 public int getUid() {
1005 String value = getComponent().getText();
1006 if (value == null || value.trim().isEmpty()) return 0;
1007 try {
1008 int uid = Integer.parseInt(value.trim());
1009 if (uid > 0)
1010 return uid;
1011 return 0;
1012 } catch (NumberFormatException e) {
1013 return 0;
1014 }
1015 }
1016 }
1017
1018 /**
1019 * Validates dates entered as text in a {@link JTextComponent}. Validates the input
1020 * on the fly and gives feedback about whether the date is valid or not.
1021 *
1022 * Dates can be entered in one of four standard formats defined for the current locale.
1023 */
1024 private static class DateValidator extends AbstractTextComponentValidator {
1025 DateValidator(JTextComponent tc) {
1026 super(tc);
1027 }
1028
1029 public static DateValidator decorate(JTextComponent tc) {
1030 return new DateValidator(tc);
1031 }
1032
1033 @Override
1034 public boolean isValid() {
1035 return getDate() != null;
1036 }
1037
1038 public String getStandardTooltipTextAsHtml() {
1039 return "<html>" + getStandardTooltipText() + "</html>";
1040 }
1041
1042 public String getStandardTooltipText() {
1043 final ZonedDateTime now = ZonedDateTime.now();
1044 return tr(
1045 "Please enter a date in the usual format for your locale.<br>"
1046 + "Example: {0}<br>"
1047 + "Example: {1}<br>"
1048 + "Example: {2}<br>"
1049 + "Example: {3}<br>",
1050 DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(now),
1051 DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(now),
1052 DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(now),
1053 DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(now)
1054 );
1055 }
1056
1057 @Override
1058 public void validate() {
1059 if (!isValid()) {
1060 String msg = "<html>The current value isn't a valid date.<br>" + getStandardTooltipText()+ "</html>";
1061 feedbackInvalid(msg);
1062 return;
1063 } else {
1064 String msg = "<html>" + getStandardTooltipText() + "</html>";
1065 feedbackValid(msg);
1066 }
1067 }
1068
1069 public LocalDate getDate() {
1070 for (final FormatStyle format: FormatStyle.values()) {
1071 DateTimeFormatter df = DateTimeFormatter.ofLocalizedDate(format);
1072 try {
1073 return LocalDate.parse(getComponent().getText(), df);
1074 } catch (DateTimeParseException e) {
1075 // Try next format
1076 Main.trace(e);
1077 }
1078 }
1079 return null;
1080 }
1081 }
1082
1083 /**
1084 * Validates time values entered as text in a {@link JTextComponent}. Validates the input
1085 * on the fly and gives feedback about whether the time value is valid or not.
1086 *
1087 * Time values can be entered in one of four standard formats defined for the current locale.
1088 */
1089 private static class TimeValidator extends AbstractTextComponentValidator {
1090 TimeValidator(JTextComponent tc) {
1091 super(tc);
1092 }
1093
1094 public static TimeValidator decorate(JTextComponent tc) {
1095 return new TimeValidator(tc);
1096 }
1097
1098 @Override
1099 public boolean isValid() {
1100 if (getComponent().getText().trim().isEmpty())
1101 return true;
1102 return getDate() != null;
1103 }
1104
1105 public String getStandardTooltipTextAsHtml() {
1106 return "<html>" + getStandardTooltipText() + "</html>";
1107 }
1108
1109 public String getStandardTooltipText() {
1110 final ZonedDateTime now = ZonedDateTime.now();
1111 return tr(
1112 "Please enter a valid time in the usual format for your locale.<br>"
1113 + "Example: {0}<br>"
1114 + "Example: {1}<br>"
1115 + "Example: {2}<br>"
1116 + "Example: {3}<br>",
1117 DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(now),
1118 DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).format(now),
1119 DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG).format(now),
1120 DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).format(now)
1121 );
1122 }
1123
1124 @Override
1125 public void validate() {
1126 if (!isValid()) {
1127 String msg = "<html>The current value isn't a valid time.<br>" + getStandardTooltipText() + "</html>";
1128 feedbackInvalid(msg);
1129 return;
1130 } else {
1131 String msg = "<html>" + getStandardTooltipText() + "</html>";
1132 feedbackValid(msg);
1133 }
1134 }
1135
1136 public LocalTime getDate() {
1137 if (getComponent().getText().trim().isEmpty())
1138 return LocalTime.MIDNIGHT;
1139
1140 for (final FormatStyle format: FormatStyle.values()) {
1141 DateTimeFormatter df = DateTimeFormatter.ofLocalizedTime(format);
1142 try {
1143 return LocalTime.parse(getComponent().getText(), df);
1144 } catch (DateTimeParseException e) {
1145 // Try next format
1146 Main.trace(e);
1147 }
1148 }
1149 return LocalTime.MIDNIGHT;
1150 }
1151 }
1152}
Note: See TracBrowser for help on using the repository browser.