source: josm/trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java

Last change on this file was 18491, checked in by taylor.smock, 23 months ago

Fix #20823: Reject uploads that do not follow either comment policy or source policy (patch by ljdelight)

This fixes an issue where a the length of the comment or source was checked first,
and a warning instead of a rejection was generated when the upload policy would
have otherwise rejected the comment or source.

This also performs the validation check first, prior to the user being able to
make changes in the upload dialog UI, so that the initial dialog state matches
the upload policy.

  • Property svn:eol-style set to native
File size: 25.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.marktr;
6import static org.openstreetmap.josm.tools.I18n.tr;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.awt.BorderLayout;
10import java.awt.Color;
11import java.awt.Component;
12import java.awt.Dimension;
13import java.awt.FlowLayout;
14import java.awt.GridBagConstraints;
15import java.awt.GridBagLayout;
16import java.awt.event.ActionEvent;
17import java.awt.event.WindowAdapter;
18import java.awt.event.WindowEvent;
19import java.beans.PropertyChangeEvent;
20import java.beans.PropertyChangeListener;
21import java.lang.Character.UnicodeBlock;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Locale;
27import java.util.Map;
28import java.util.Map.Entry;
29import java.util.stream.Collectors;
30
31import javax.swing.AbstractAction;
32import javax.swing.BorderFactory;
33import javax.swing.JButton;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JSplitPane;
37import javax.swing.JTabbedPane;
38import javax.swing.event.ChangeListener;
39
40import org.openstreetmap.josm.data.APIDataSet;
41import org.openstreetmap.josm.data.osm.Changeset;
42import org.openstreetmap.josm.data.osm.DataSet;
43import org.openstreetmap.josm.data.osm.OsmPrimitive;
44import org.openstreetmap.josm.data.preferences.NamedColorProperty;
45import org.openstreetmap.josm.gui.HelpAwareOptionPane;
46import org.openstreetmap.josm.gui.MainApplication;
47import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
48import org.openstreetmap.josm.gui.help.HelpUtil;
49import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
50import org.openstreetmap.josm.gui.util.GuiHelper;
51import org.openstreetmap.josm.gui.util.MultiLineFlowLayout;
52import org.openstreetmap.josm.gui.util.WindowGeometry;
53import org.openstreetmap.josm.io.OsmApi;
54import org.openstreetmap.josm.io.UploadStrategy;
55import org.openstreetmap.josm.io.UploadStrategySpecification;
56import org.openstreetmap.josm.spi.preferences.Config;
57import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
58import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener;
59import org.openstreetmap.josm.spi.preferences.Setting;
60import org.openstreetmap.josm.tools.GBC;
61import org.openstreetmap.josm.tools.ImageProvider;
62import org.openstreetmap.josm.tools.InputMapUtils;
63import org.openstreetmap.josm.tools.Utils;
64
65/**
66 * This is a dialog for entering upload options like the parameters for
67 * the upload changeset and the strategy for opening/closing a changeset.
68 * @since 2025
69 */
70public class UploadDialog extends AbstractUploadDialog implements PreferenceChangedListener, PropertyChangeListener {
71 /** A warning color to indicate something is non-default in the changeset tags */
72 private static final Color WARNING_BACKGROUND = new NamedColorProperty(
73 marktr("Changesets: Non-default advanced settings"), new Color(0xF89042)).get();
74 /** the unique instance of the upload dialog */
75 private static UploadDialog uploadDialog;
76
77 /** the panel with the objects to upload */
78 private UploadedObjectsSummaryPanel pnlUploadedObjects;
79
80 /** the "description" tab */
81 private BasicUploadSettingsPanel pnlBasicUploadSettings;
82
83 /** the panel to select the changeset used */
84 private ChangesetManagementPanel pnlChangesetManagement;
85 /** the panel to select the upload strategy */
86 private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel;
87
88 /** the tag editor panel */
89 private TagEditorPanel pnlTagEditor;
90 /** the tabbed pane used below of the list of primitives */
91 private JTabbedPane tpConfigPanels;
92 /** the upload button */
93 private JButton btnUpload;
94
95 /** the model keeping the state of the changeset tags */
96 private final transient UploadDialogModel model = new UploadDialogModel();
97
98 private transient DataSet dataSet;
99 private transient ChangeListener changesetTagListener;
100
101 /**
102 * Constructs a new {@code UploadDialog}.
103 */
104 protected UploadDialog() {
105 super(GuiHelper.getFrameForComponent(MainApplication.getMainFrame()), ModalityType.DOCUMENT_MODAL);
106 build();
107 pack();
108 }
109
110 /**
111 * Replies the unique instance of the upload dialog
112 *
113 * @return the unique instance of the upload dialog
114 */
115 public static synchronized UploadDialog getUploadDialog() {
116 if (uploadDialog == null) {
117 uploadDialog = new UploadDialog();
118 }
119 return uploadDialog;
120 }
121
122 /**
123 * builds the content panel for the upload dialog
124 *
125 * @return the content panel
126 */
127 protected JPanel buildContentPanel() {
128 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
129 splitPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
130
131 // the panel with the list of uploaded objects
132 pnlUploadedObjects = new UploadedObjectsSummaryPanel();
133 pnlUploadedObjects.setMinimumSize(new Dimension(200, 50));
134 splitPane.setLeftComponent(pnlUploadedObjects);
135
136 // a tabbed pane with configuration panels in the lower half
137 tpConfigPanels = new CompactTabbedPane();
138 splitPane.setRightComponent(tpConfigPanels);
139
140 pnlBasicUploadSettings = new BasicUploadSettingsPanel(model);
141 tpConfigPanels.add(pnlBasicUploadSettings);
142 tpConfigPanels.setTitleAt(0, tr("Description"));
143 tpConfigPanels.setToolTipTextAt(0, tr("Describe the changes you made"));
144
145 JPanel pnlSettings = new JPanel(new GridBagLayout());
146 pnlSettings.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
147 JPanel pnlTagEditorBorder = new JPanel(new BorderLayout());
148 pnlTagEditorBorder.setBorder(BorderFactory.createTitledBorder(tr("Changeset tags:")));
149 pnlTagEditor = new TagEditorPanel(model, null, Changeset.MAX_CHANGESET_TAG_LENGTH);
150 pnlTagEditorBorder.add(pnlTagEditor, BorderLayout.CENTER);
151
152 pnlChangesetManagement = new ChangesetManagementPanel();
153 pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel();
154 pnlSettings.add(pnlChangesetManagement, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
155 pnlSettings.add(pnlUploadStrategySelectionPanel, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
156 pnlSettings.add(pnlTagEditorBorder, GBC.eol().fill(GridBagConstraints.BOTH));
157
158 // if another tab is added, please don't forget to update setChangesetTagsModifiedProgramatically
159 tpConfigPanels.add(pnlSettings);
160 tpConfigPanels.setTitleAt(1, tr("Settings"));
161 tpConfigPanels.setToolTipTextAt(1, tr("Decide how to upload the data and which changeset to use"));
162
163 JPanel pnl = new JPanel(new BorderLayout());
164 pnl.add(splitPane, BorderLayout.CENTER);
165 pnl.add(buildActionPanel(), BorderLayout.SOUTH);
166 return pnl;
167 }
168
169 /**
170 * builds the panel with the OK and CANCEL buttons
171 *
172 * @return The panel with the OK and CANCEL buttons
173 */
174 protected JPanel buildActionPanel() {
175 JPanel pnl = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER));
176 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
177
178 // -- upload button
179 btnUpload = new JButton(new UploadAction(this));
180 pnl.add(btnUpload);
181 btnUpload.setFocusable(true);
182 InputMapUtils.enableEnter(btnUpload);
183 InputMapUtils.addCtrlEnterAction(getRootPane(), btnUpload.getAction());
184
185 // -- cancel button
186 CancelAction cancelAction = new CancelAction(this);
187 pnl.add(new JButton(cancelAction));
188 InputMapUtils.addEscapeAction(getRootPane(), cancelAction);
189
190 // -- help button
191 pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Dialog/Upload"))));
192 HelpUtil.setHelpContext(getRootPane(), ht("/Dialog/Upload"));
193 return pnl;
194 }
195
196 /**
197 * builds the gui
198 */
199 protected void build() {
200 setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl()));
201 setContentPane(buildContentPanel());
202
203 addWindowListener(new WindowEventHandler());
204
205 // make sure the configuration panels listen to each others changes
206 //
207 UploadParameterSummaryPanel sp = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
208 // the summary panel must know everything
209 pnlChangesetManagement.addPropertyChangeListener(sp);
210 pnlUploadedObjects.addPropertyChangeListener(sp);
211 pnlUploadStrategySelectionPanel.addPropertyChangeListener(sp);
212
213 // update tags from selected changeset
214 pnlChangesetManagement.addPropertyChangeListener(this);
215
216 // users can click on either of two links in the upload parameter
217 // summary handler. This installs the handler for these two events.
218 // We simply select the appropriate tab in the tabbed pane with the configuration dialogs.
219 //
220 pnlBasicUploadSettings.getUploadParameterSummaryPanel().setConfigurationParameterRequestListener(
221 () -> tpConfigPanels.setSelectedIndex(2)
222 );
223
224 // Set the initial state of the upload button
225 btnUpload.setEnabled(pnlBasicUploadSettings.getUploadTextValidators()
226 .stream().noneMatch(UploadTextComponentValidator::isUploadRejected));
227
228 // Enable/disable the upload button if at least an upload validator rejects upload
229 pnlBasicUploadSettings.getUploadTextValidators().forEach(v -> v.addChangeListener(e -> btnUpload.setEnabled(
230 pnlBasicUploadSettings.getUploadTextValidators().stream().noneMatch(UploadTextComponentValidator::isUploadRejected))));
231
232 setMinimumSize(new Dimension(600, 350));
233
234 Config.getPref().addPreferenceChangeListener(this);
235 }
236
237 /**
238 * Initializes this life cycle of the dialog.
239 *
240 * Initializes the dialog each time before it is made visible. We cannot do
241 * this in the constructor because the dialog is a singleton.
242 *
243 * @param dataSet The Dataset we want to upload
244 * @since 18173
245 */
246 public void initLifeCycle(DataSet dataSet) {
247 Map<String, String> map = new HashMap<>();
248 this.dataSet = dataSet;
249 pnlBasicUploadSettings.initLifeCycle(map);
250 pnlChangesetManagement.initLifeCycle();
251 model.clear();
252 model.putAll(map); // init with tags from history
253 model.putAll(this.dataSet); // overwrite with tags from the dataset
254 if (Config.getPref().getBoolean("upload.source.obtainautomatically", false)
255 && this.dataSet.getChangeSetTags().containsKey(UploadDialogModel.SOURCE)) {
256 model.put(UploadDialogModel.SOURCE, pnlBasicUploadSettings.getSourceFromLayer());
257 }
258
259 tpConfigPanels.setSelectedIndex(0);
260 pnlTagEditor.initAutoCompletion(MainApplication.getLayerManager().getEditLayer());
261 pnlUploadStrategySelectionPanel.initFromPreferences();
262
263 // update the summary
264 UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
265 sumPnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
266 sumPnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
267 }
268
269 /**
270 * Sets the collection of primitives to upload
271 *
272 * @param toUpload the dataset with the objects to upload. If null, assumes the empty
273 * set of objects to upload
274 *
275 */
276 public void setUploadedPrimitives(APIDataSet toUpload) {
277 UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
278 if (toUpload == null) {
279 if (pnlUploadedObjects != null) {
280 List<OsmPrimitive> emptyList = Collections.emptyList();
281 pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);
282 sumPnl.setNumObjects(0);
283 }
284 return;
285 }
286 List<OsmPrimitive> l = toUpload.getPrimitives();
287 pnlBasicUploadSettings.setUploadedPrimitives(l);
288 pnlUploadedObjects.setUploadedPrimitives(
289 toUpload.getPrimitivesToAdd(),
290 toUpload.getPrimitivesToUpdate(),
291 toUpload.getPrimitivesToDelete()
292 );
293 sumPnl.setNumObjects(l.size());
294 pnlUploadStrategySelectionPanel.setNumUploadedObjects(l.size());
295 }
296
297 /**
298 * Sets the input focus to upload button.
299 * @since 18173
300 */
301 public void setFocusToUploadButton() {
302 btnUpload.requestFocus();
303 }
304
305 @Override
306 public void rememberUserInput() {
307 pnlBasicUploadSettings.rememberUserInput();
308 pnlUploadStrategySelectionPanel.rememberUserInput();
309 }
310
311 /**
312 * Returns the changeset to use complete with tags
313 *
314 * @return the changeset to use
315 */
316 public Changeset getChangeset() {
317 Changeset cs = pnlChangesetManagement.getSelectedChangeset();
318 cs.setKeys(getTags(true));
319 return cs;
320 }
321
322 /**
323 * Sets the changeset to be used in the next upload
324 *
325 * @param cs the changeset
326 */
327 public void setSelectedChangesetForNextUpload(Changeset cs) {
328 pnlChangesetManagement.setSelectedChangesetForNextUpload(cs);
329 }
330
331 @Override
332 public UploadStrategySpecification getUploadStrategySpecification() {
333 UploadStrategySpecification spec = pnlUploadStrategySelectionPanel.getUploadStrategySpecification();
334 spec.setCloseChangesetAfterUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
335 return spec;
336 }
337
338 /**
339 * Get the upload dialog model.
340 *
341 * @return The model.
342 * @since 18173
343 */
344 public UploadDialogModel getModel() {
345 return model;
346 }
347
348 @Override
349 public String getUploadComment() {
350 return model.getValue(UploadDialogModel.COMMENT);
351 }
352
353 @Override
354 public String getUploadSource() {
355 return model.getValue(UploadDialogModel.SOURCE);
356 }
357
358 @Override
359 public void setVisible(boolean visible) {
360 if (visible) {
361 new WindowGeometry(
362 getClass().getName() + ".geometry",
363 WindowGeometry.centerInWindow(
364 MainApplication.getMainFrame(),
365 new Dimension(800, 600)
366 )
367 ).applySafe(this);
368 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
369 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
370 }
371 super.setVisible(visible);
372 }
373
374 /**
375 * This is called by {@link UploadAction} if {@link org.openstreetmap.josm.actions.upload.UploadHook}s change
376 * the changeset tags.
377 */
378 public synchronized void setChangesetTagsModifiedProgramatically() {
379 final Color originalColor = this.tpConfigPanels.getBackgroundAt(1);
380 this.tpConfigPanels.setBackgroundAt(1, WARNING_BACKGROUND);
381 this.tpConfigPanels.setIconAt(1, ImageProvider.get("warning-small"));
382 if (this.changesetTagListener != null) {
383 this.tpConfigPanels.removeChangeListener(this.changesetTagListener);
384 }
385 this.changesetTagListener = event -> {
386 if (this.tpConfigPanels.getSelectedIndex() == 1) {
387 tpConfigPanels.setBackgroundAt(1, originalColor);
388 tpConfigPanels.setIconAt(1, ImageProvider.get("apply"));
389 synchronized (this) {
390 this.tpConfigPanels.removeChangeListener(this.changesetTagListener);
391 changesetTagListener = null;
392 }
393 }
394 };
395
396 this.tpConfigPanels.addChangeListener(this.changesetTagListener);
397 }
398
399 static final class CompactTabbedPane extends JTabbedPane {
400 @Override
401 public Dimension getPreferredSize() {
402 // This probably fixes #18523. Don't know why. Don't know how. It just does.
403 super.getPreferredSize();
404 // make sure the tabbed pane never grabs more space than necessary
405 return super.getMinimumSize();
406 }
407 }
408
409 /**
410 * Handles an upload.
411 */
412 static class UploadAction extends AbstractAction {
413
414 private final transient IUploadDialog dialog;
415
416 UploadAction(IUploadDialog dialog) {
417 this.dialog = dialog;
418 putValue(NAME, tr("Upload Changes"));
419 new ImageProvider("upload").getResource().attachImageIcon(this, true);
420 putValue(SHORT_DESCRIPTION, tr("Upload the changed primitives"));
421 }
422
423 protected void warnIllegalChunkSize() {
424 HelpAwareOptionPane.showOptionDialog(
425 (Component) dialog,
426 tr("Please enter a valid chunk size first"),
427 tr("Illegal chunk size"),
428 JOptionPane.ERROR_MESSAGE,
429 ht("/Dialog/Upload#IllegalChunkSize")
430 );
431 }
432
433 static boolean isUploadCommentTooShort(String comment) {
434 String s = Utils.strip(comment);
435 if (s.isEmpty()) {
436 return true;
437 }
438 UnicodeBlock block = Character.UnicodeBlock.of(s.charAt(0));
439 if (block != null && block.toString().contains("CJK")) {
440 return s.length() < 4;
441 } else {
442 return s.length() < 10;
443 }
444 }
445
446 private static String lower(String s) {
447 return s.toLowerCase(Locale.ENGLISH);
448 }
449
450 static String validateUploadTag(String uploadValue, String preferencePrefix,
451 List<String> defMandatory, List<String> defForbidden, List<String> defException) {
452 String uploadValueLc = lower(uploadValue);
453 // Check mandatory terms
454 List<String> missingTerms = Config.getPref().getList(preferencePrefix+".mandatory-terms", defMandatory)
455 .stream().map(UploadAction::lower).filter(x -> !uploadValueLc.contains(x)).collect(Collectors.toList());
456 if (!missingTerms.isEmpty()) {
457 return tr("The following required terms are missing: {0}", missingTerms);
458 }
459 // Check forbidden terms
460 List<String> exceptions = Config.getPref().getList(preferencePrefix+".exception-terms", defException);
461 List<String> forbiddenTerms = Config.getPref().getList(preferencePrefix+".forbidden-terms", defForbidden)
462 .stream().map(UploadAction::lower)
463 .filter(x -> uploadValueLc.contains(x) && exceptions.stream().noneMatch(uploadValueLc::contains))
464 .collect(Collectors.toList());
465 if (!forbiddenTerms.isEmpty()) {
466 return tr("The following forbidden terms have been found: {0}", forbiddenTerms);
467 }
468 return null;
469 }
470
471 @Override
472 public void actionPerformed(ActionEvent e) {
473 Map<String, String> tags = dialog.getTags(true);
474
475 // If there are empty tags in the changeset proceed only after user's confirmation.
476 List<String> emptyChangesetTags = new ArrayList<>();
477 for (final Entry<String, String> i : tags.entrySet()) {
478 final boolean isKeyEmpty = Utils.isStripEmpty(i.getKey());
479 final boolean isValueEmpty = Utils.isStripEmpty(i.getValue());
480 final boolean ignoreKey = UploadDialogModel.isCommentOrSource(i.getKey());
481 if ((isKeyEmpty || isValueEmpty) && !ignoreKey) {
482 emptyChangesetTags.add(tr("{0}={1}", i.getKey(), i.getValue()));
483 }
484 }
485 if (!emptyChangesetTags.isEmpty() && JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(
486 MainApplication.getMainFrame(),
487 trn(
488 "<html>The following changeset tag contains an empty key/value:<br>{0}<br>Continue?</html>",
489 "<html>The following changeset tags contain an empty key/value:<br>{0}<br>Continue?</html>",
490 emptyChangesetTags.size(), Utils.joinAsHtmlUnorderedList(emptyChangesetTags)),
491 tr("Empty metadata"),
492 JOptionPane.OK_CANCEL_OPTION,
493 JOptionPane.WARNING_MESSAGE
494 )) {
495 dialog.handleMissingComment();
496 return;
497 }
498
499 UploadStrategySpecification strategy = dialog.getUploadStrategySpecification();
500 if (strategy.getStrategy() == UploadStrategy.CHUNKED_DATASET_STRATEGY
501 && strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
502 warnIllegalChunkSize();
503 dialog.handleIllegalChunkSize();
504 return;
505 }
506 if (dialog instanceof AbstractUploadDialog) {
507 ((AbstractUploadDialog) dialog).setCanceled(false);
508 ((AbstractUploadDialog) dialog).setVisible(false);
509 }
510 }
511 }
512
513 /**
514 * Action for canceling the dialog.
515 */
516 static class CancelAction extends AbstractAction {
517
518 private final transient IUploadDialog dialog;
519
520 CancelAction(IUploadDialog dialog) {
521 this.dialog = dialog;
522 putValue(NAME, tr("Cancel"));
523 new ImageProvider("cancel").getResource().attachImageIcon(this, true);
524 putValue(SHORT_DESCRIPTION, tr("Cancel the upload and resume editing"));
525 }
526
527 @Override
528 public void actionPerformed(ActionEvent e) {
529 if (dialog instanceof AbstractUploadDialog) {
530 ((AbstractUploadDialog) dialog).setCanceled(true);
531 ((AbstractUploadDialog) dialog).setVisible(false);
532 }
533 }
534 }
535
536 /**
537 * Listens to window closing events and processes them as cancel events.
538 * Listens to window open events and initializes user input
539 */
540 class WindowEventHandler extends WindowAdapter {
541 private boolean activatedOnce;
542
543 @Override
544 public void windowClosing(WindowEvent e) {
545 setCanceled(true);
546 }
547
548 @Override
549 public void windowActivated(WindowEvent e) {
550 if (!activatedOnce && tpConfigPanels.getSelectedIndex() == 0) {
551 pnlBasicUploadSettings.initEditingOfUploadComment();
552 activatedOnce = true;
553 }
554 }
555 }
556
557 /* -------------------------------------------------------------------------- */
558 /* Interface PropertyChangeListener */
559 /* -------------------------------------------------------------------------- */
560 @Override
561 public void propertyChange(PropertyChangeEvent evt) {
562 if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
563 // put the tags from the newly selected changeset into the model
564 Changeset cs = (Changeset) evt.getNewValue();
565 if (cs != null) {
566 for (Map.Entry<String, String> entry : cs.getKeys().entrySet()) {
567 String key = entry.getKey();
568 // do NOT overwrite comment and source when selecting a changeset, it is confusing
569 if (!UploadDialogModel.isCommentOrSource(key))
570 model.put(key, entry.getValue());
571 }
572 }
573 }
574 }
575
576 /* -------------------------------------------------------------------------- */
577 /* Interface PreferenceChangedListener */
578 /* -------------------------------------------------------------------------- */
579 @Override
580 public void preferenceChanged(PreferenceChangeEvent e) {
581 if (e.getKey() != null
582 && e.getSource() != getClass()
583 && e.getSource() != BasicUploadSettingsPanel.class
584 && "osm-server.url".equals(e.getKey())) {
585 osmServerUrlChanged(e.getNewValue());
586 }
587 }
588
589 private void osmServerUrlChanged(Setting<?> newValue) {
590 final String url;
591 if (newValue == null || newValue.getValue() == null) {
592 url = OsmApi.getOsmApi().getBaseUrl();
593 } else {
594 url = newValue.getValue().toString();
595 }
596 setTitle(tr("Upload to ''{0}''", url));
597 }
598
599 /* -------------------------------------------------------------------------- */
600 /* Interface IUploadDialog */
601 /* -------------------------------------------------------------------------- */
602 @Override
603 public Map<String, String> getTags(boolean keepEmpty) {
604 saveEdits();
605 return model.getTags(keepEmpty);
606 }
607
608 @Override
609 public void handleMissingComment() {
610 tpConfigPanels.setSelectedIndex(0);
611 pnlBasicUploadSettings.initEditingOfUploadComment();
612 }
613
614 @Override
615 public void handleMissingSource() {
616 tpConfigPanels.setSelectedIndex(0);
617 pnlBasicUploadSettings.initEditingOfUploadSource();
618 }
619
620 @Override
621 public void handleIllegalChunkSize() {
622 tpConfigPanels.setSelectedIndex(0);
623 }
624
625 /**
626 * Save all outstanding edits to the model.
627 * <p>
628 * The combobox editors and the tag cell editor need to be manually saved
629 * because they normally save on focus loss, eg. when the "Upload" button is
630 * pressed, but there's no focus change when Ctrl+Enter is pressed.
631 *
632 * @since 18173
633 */
634 public void saveEdits() {
635 pnlBasicUploadSettings.saveEdits();
636 pnlTagEditor.saveEdits();
637 }
638
639 /**
640 * Clean dialog state and release resources.
641 * @since 14251
642 */
643 public void clean() {
644 setUploadedPrimitives(null);
645 dataSet = null;
646 synchronized (this) {
647 if (this.changesetTagListener != null) {
648 this.changesetTagListener.stateChanged(null);
649 this.tpConfigPanels.removeChangeListener(this.changesetTagListener);
650 this.changesetTagListener = null;
651 }
652 }
653 }
654}
Note: See TracBrowser for help on using the repository browser.