source: josm/trunk/src/org/openstreetmap/josm/gui/io/BasicUploadSettingsPanel.java@ 14977

Last change on this file since 14977 was 14977, checked in by Don-vip, 5 years ago

ensures consistency of upload comment:

  • fix #11168 - ctrl-z/undo could reset unwanted old changeset comment
  • fix #13474 - selecting "new changeset" after having entered a changeset comment did reset it to the previous value
  • fix #17452 - ctrl-enter while typing a changeset comment did upload with the previous value
  • fix behaviour of upload.comment.max-age: values were reset after 5 months instead of intended 4 hours because seconds were compared to milliseconds
  • avoid creation of unneeded undo/redo internal classes for non-editable text fields
  • ensures consistency of upload dialog if upload.comment properties are modified manually from advanced preferences
  • add a source attribute to preference events to know which class modified the preference entry
  • refactor reflection utils
  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.GridBagLayout;
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
10import java.awt.event.FocusAdapter;
11import java.awt.event.FocusEvent;
12import java.awt.event.ItemEvent;
13import java.awt.event.KeyAdapter;
14import java.awt.event.KeyEvent;
15import java.util.Arrays;
16import java.util.Collections;
17import java.util.LinkedList;
18import java.util.List;
19import java.util.Objects;
20import java.util.concurrent.TimeUnit;
21
22import javax.swing.Action;
23import javax.swing.BorderFactory;
24import javax.swing.JCheckBox;
25import javax.swing.JEditorPane;
26import javax.swing.JPanel;
27import javax.swing.event.ChangeEvent;
28import javax.swing.event.ChangeListener;
29import javax.swing.event.HyperlinkEvent;
30
31import org.openstreetmap.josm.data.osm.Changeset;
32import org.openstreetmap.josm.gui.MainApplication;
33import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
34import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
35import org.openstreetmap.josm.spi.preferences.Config;
36import org.openstreetmap.josm.tools.GBC;
37import org.openstreetmap.josm.tools.Utils;
38
39/**
40 * BasicUploadSettingsPanel allows to enter the basic parameters required for uploading data.
41 * @since 2599
42 */
43public class BasicUploadSettingsPanel extends JPanel {
44 /**
45 * Preference name for history collection
46 */
47 public static final String HISTORY_KEY = "upload.comment.history";
48 /**
49 * Preference name for last used upload comment
50 */
51 public static final String HISTORY_LAST_USED_KEY = "upload.comment.last-used";
52 /**
53 * Preference name for the max age search comments may have
54 */
55 public static final String HISTORY_MAX_AGE_KEY = "upload.comment.max-age";
56 /**
57 * Preference name for the history of source values
58 */
59 public static final String SOURCE_HISTORY_KEY = "upload.source.history";
60
61 /** the history combo box for the upload comment */
62 private final HistoryComboBox hcbUploadComment = new HistoryComboBox();
63 private final HistoryComboBox hcbUploadSource = new HistoryComboBox();
64 /** the panel with a summary of the upload parameters */
65 private final UploadParameterSummaryPanel pnlUploadParameterSummary = new UploadParameterSummaryPanel();
66 /** the checkbox to request feedback from other users */
67 private final JCheckBox cbRequestReview = new JCheckBox(tr("I would like someone to review my edits."));
68 /** the changeset comment model */
69 private final transient ChangesetCommentModel changesetCommentModel;
70 private final transient ChangesetCommentModel changesetSourceModel;
71 private final transient ChangesetReviewModel changesetReviewModel;
72
73 protected JPanel buildUploadCommentPanel() {
74 JPanel pnl = new JPanel(new GridBagLayout());
75
76 JEditorPane commentLabel = new JMultilineLabel("<html><b>" + tr("Provide a brief comment for the changes you are uploading:"));
77 pnl.add(commentLabel, GBC.eol().insets(0, 5, 10, 3).fill(GBC.HORIZONTAL));
78 hcbUploadComment.setToolTipText(tr("Enter an upload comment"));
79 hcbUploadComment.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH);
80 populateHistoryComboBox(hcbUploadComment, HISTORY_KEY, new LinkedList<String>());
81 CommentModelListener commentModelListener = new CommentModelListener(hcbUploadComment, changesetCommentModel);
82 hcbUploadComment.getEditor().addActionListener(commentModelListener);
83 hcbUploadComment.getEditorComponent().addFocusListener(commentModelListener);
84 pnl.add(hcbUploadComment, GBC.eol().fill(GBC.HORIZONTAL));
85
86 JEditorPane sourceLabel = new JMultilineLabel("<html><b>" + tr("Specify the data source for the changes")
87 + "</b> (<a href=\"urn:changeset-source\">" + tr("obtain from current layers") + "</a>)<b>:</b>");
88 sourceLabel.addHyperlinkListener(e -> {
89 if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
90 final String source = MainApplication.getMap().mapView.getLayerInformationForSourceTag();
91 hcbUploadSource.setText(Utils.shortenString(source, Changeset.MAX_CHANGESET_TAG_LENGTH));
92 changesetSourceModel.setComment(hcbUploadSource.getText()); // Fix #9965
93 }
94 });
95 pnl.add(sourceLabel, GBC.eol().insets(0, 8, 10, 3).fill(GBC.HORIZONTAL));
96
97 hcbUploadSource.setToolTipText(tr("Enter a source"));
98 hcbUploadSource.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH);
99 populateHistoryComboBox(hcbUploadSource, SOURCE_HISTORY_KEY, getDefaultSources());
100 CommentModelListener sourceModelListener = new CommentModelListener(hcbUploadSource, changesetSourceModel);
101 hcbUploadSource.getEditor().addActionListener(sourceModelListener);
102 hcbUploadSource.getEditorComponent().addFocusListener(sourceModelListener);
103 pnl.add(hcbUploadSource, GBC.eol().fill(GBC.HORIZONTAL));
104 return pnl;
105 }
106
107 /**
108 * Refreshes contents of upload history combo boxes from preferences.
109 */
110 protected void refreshHistoryComboBoxes() {
111 populateHistoryComboBox(hcbUploadComment, HISTORY_KEY, new LinkedList<String>());
112 populateHistoryComboBox(hcbUploadSource, SOURCE_HISTORY_KEY, getDefaultSources());
113 }
114
115 private static void populateHistoryComboBox(HistoryComboBox hcb, String historyKey, List<String> defaultValues) {
116 List<String> cmtHistory = new LinkedList<>(Config.getPref().getList(historyKey, defaultValues));
117 Collections.reverse(cmtHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
118 hcb.setPossibleItems(cmtHistory);
119 hcb.discardAllUndoableEdits();
120 }
121
122 /**
123 * Discards undoable edits of upload history combo boxes.
124 */
125 protected void discardAllUndoableEdits() {
126 hcbUploadComment.discardAllUndoableEdits();
127 hcbUploadSource.discardAllUndoableEdits();
128 }
129
130 /**
131 * Returns the default list of sources.
132 * @return the default list of sources
133 */
134 public static List<String> getDefaultSources() {
135 return Arrays.asList("knowledge", "survey", "Bing");
136 }
137
138 protected void build() {
139 setLayout(new BorderLayout());
140 setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
141 add(buildUploadCommentPanel(), BorderLayout.NORTH);
142 add(pnlUploadParameterSummary, BorderLayout.CENTER);
143 add(cbRequestReview, BorderLayout.SOUTH);
144 cbRequestReview.addItemListener(e -> changesetReviewModel.setReviewRequested(e.getStateChange() == ItemEvent.SELECTED));
145 }
146
147 /**
148 * Creates the panel
149 *
150 * @param changesetCommentModel the model for the changeset comment. Must not be null
151 * @param changesetSourceModel the model for the changeset source. Must not be null.
152 * @param changesetReviewModel the model for the changeset review. Must not be null.
153 * @throws NullPointerException if a model is null
154 * @since 12719 (signature)
155 */
156 public BasicUploadSettingsPanel(ChangesetCommentModel changesetCommentModel, ChangesetCommentModel changesetSourceModel,
157 ChangesetReviewModel changesetReviewModel) {
158 this.changesetCommentModel = Objects.requireNonNull(changesetCommentModel, "changesetCommentModel");
159 this.changesetSourceModel = Objects.requireNonNull(changesetSourceModel, "changesetSourceModel");
160 this.changesetReviewModel = Objects.requireNonNull(changesetReviewModel, "changesetReviewModel");
161 changesetCommentModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadComment));
162 changesetSourceModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadSource));
163 changesetReviewModel.addChangeListener(new ChangesetReviewChangeListener());
164 build();
165 }
166
167 public void setUploadTagDownFocusTraversalHandlers(final Action handler) {
168 setHistoryComboBoxDownFocusTraversalHandler(handler, hcbUploadComment);
169 setHistoryComboBoxDownFocusTraversalHandler(handler, hcbUploadSource);
170 }
171
172 public void setHistoryComboBoxDownFocusTraversalHandler(final Action handler, final HistoryComboBox hcb) {
173 hcb.getEditor().addActionListener(handler);
174 hcb.getEditorComponent().addKeyListener(new HistoryComboBoxKeyAdapter(hcb, handler));
175 }
176
177 /**
178 * Remembers the user input in the preference settings
179 */
180 public void rememberUserInput() {
181 // store the history of comments
182 hcbUploadComment.addCurrentItemToHistory();
183 Config.getPref().putList(HISTORY_KEY, hcbUploadComment.getHistory());
184 Config.getPref().putLong(HISTORY_LAST_USED_KEY, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
185 // store the history of sources
186 hcbUploadSource.addCurrentItemToHistory();
187 Config.getPref().putList(SOURCE_HISTORY_KEY, hcbUploadSource.getHistory());
188 }
189
190 /**
191 * Initializes the panel for user input
192 */
193 public void startUserInput() {
194 hcbUploadComment.requestFocusInWindow();
195 hcbUploadComment.getEditorComponent().requestFocusInWindow();
196 }
197
198 /**
199 * Initializes editing of upload comment.
200 */
201 public void initEditingOfUploadComment() {
202 hcbUploadComment.getEditor().selectAll();
203 hcbUploadComment.requestFocusInWindow();
204 }
205
206 /**
207 * Initializes editing of upload source.
208 */
209 public void initEditingOfUploadSource() {
210 hcbUploadSource.getEditor().selectAll();
211 hcbUploadSource.requestFocusInWindow();
212 }
213
214 /**
215 * Returns the panel that displays a summary of data the user is about to upload.
216 * @return the upload parameter summary panel
217 */
218 public UploadParameterSummaryPanel getUploadParameterSummaryPanel() {
219 return pnlUploadParameterSummary;
220 }
221
222 /**
223 * Forces update of comment/source model if matching text field is focused.
224 * @since 14977
225 */
226 public void forceUpdateActiveField() {
227 updateModelIfFocused(hcbUploadComment, changesetCommentModel);
228 updateModelIfFocused(hcbUploadSource, changesetSourceModel);
229 }
230
231 private static void updateModelIfFocused(HistoryComboBox hcb, ChangesetCommentModel changesetModel) {
232 if (hcb.getEditorComponent().hasFocus()) {
233 changesetModel.setComment(hcb.getText());
234 }
235 }
236
237 static final class HistoryComboBoxKeyAdapter extends KeyAdapter {
238 private final HistoryComboBox hcb;
239 private final Action handler;
240
241 HistoryComboBoxKeyAdapter(HistoryComboBox hcb, Action handler) {
242 this.hcb = hcb;
243 this.handler = handler;
244 }
245
246 @Override
247 public void keyTyped(KeyEvent e) {
248 if (e.getKeyCode() == KeyEvent.VK_TAB) {
249 handler.actionPerformed(new ActionEvent(hcb, 0, "focusDown"));
250 }
251 }
252 }
253
254 /**
255 * Updates the changeset comment model upon changes in the input field.
256 */
257 static class CommentModelListener extends FocusAdapter implements ActionListener {
258
259 private final HistoryComboBox source;
260 private final ChangesetCommentModel destination;
261
262 CommentModelListener(HistoryComboBox source, ChangesetCommentModel destination) {
263 this.source = source;
264 this.destination = destination;
265 }
266
267 @Override
268 public void actionPerformed(ActionEvent e) {
269 destination.setComment(source.getText());
270 }
271
272 @Override
273 public void focusLost(FocusEvent e) {
274 destination.setComment(source.getText());
275 }
276 }
277
278 /**
279 * Observes the changeset comment model and keeps the comment input field
280 * in sync with the current changeset comment
281 */
282 static class ChangesetCommentChangeListener implements ChangeListener {
283
284 private final HistoryComboBox destination;
285
286 ChangesetCommentChangeListener(HistoryComboBox destination) {
287 this.destination = destination;
288 }
289
290 @Override
291 public void stateChanged(ChangeEvent e) {
292 if (!(e.getSource() instanceof ChangesetCommentModel)) return;
293 String newComment = ((ChangesetCommentModel) e.getSource()).getComment();
294 if (!destination.getText().equals(newComment)) {
295 destination.setText(newComment);
296 }
297 }
298 }
299
300 /**
301 * Observes the changeset review model and keeps the review checkbox
302 * in sync with the current changeset review request
303 */
304 class ChangesetReviewChangeListener implements ChangeListener {
305 @Override
306 public void stateChanged(ChangeEvent e) {
307 if (!(e.getSource() instanceof ChangesetReviewModel)) return;
308 boolean newState = ((ChangesetReviewModel) e.getSource()).isReviewRequested();
309 if (cbRequestReview.isSelected() != newState) {
310 cbRequestReview.setSelected(newState);
311 }
312 }
313 }
314}
Note: See TracBrowser for help on using the repository browser.