001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trn; 007 008import java.awt.BorderLayout; 009import java.awt.Component; 010import java.awt.Dimension; 011import java.awt.FlowLayout; 012import java.awt.GraphicsEnvironment; 013import java.awt.GridBagLayout; 014import java.awt.event.ActionEvent; 015import java.awt.event.KeyEvent; 016import java.awt.event.WindowAdapter; 017import java.awt.event.WindowEvent; 018import java.beans.PropertyChangeEvent; 019import java.beans.PropertyChangeListener; 020import java.lang.Character.UnicodeBlock; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029 030import javax.swing.AbstractAction; 031import javax.swing.BorderFactory; 032import javax.swing.Icon; 033import javax.swing.JButton; 034import javax.swing.JComponent; 035import javax.swing.JOptionPane; 036import javax.swing.JPanel; 037import javax.swing.JTabbedPane; 038import javax.swing.KeyStroke; 039 040import org.openstreetmap.josm.Main; 041import org.openstreetmap.josm.data.APIDataSet; 042import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; 043import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 044import org.openstreetmap.josm.data.Version; 045import org.openstreetmap.josm.data.osm.Changeset; 046import org.openstreetmap.josm.data.osm.DataSet; 047import org.openstreetmap.josm.data.osm.OsmPrimitive; 048import org.openstreetmap.josm.data.preferences.Setting; 049import org.openstreetmap.josm.gui.ExtendedDialog; 050import org.openstreetmap.josm.gui.HelpAwareOptionPane; 051import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; 052import org.openstreetmap.josm.gui.help.HelpUtil; 053import org.openstreetmap.josm.gui.util.GuiHelper; 054import org.openstreetmap.josm.io.OsmApi; 055import org.openstreetmap.josm.tools.GBC; 056import org.openstreetmap.josm.tools.ImageOverlay; 057import org.openstreetmap.josm.tools.ImageProvider; 058import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 059import org.openstreetmap.josm.tools.InputMapUtils; 060import org.openstreetmap.josm.tools.Utils; 061import org.openstreetmap.josm.tools.WindowGeometry; 062 063/** 064 * This is a dialog for entering upload options like the parameters for 065 * the upload changeset and the strategy for opening/closing a changeset. 066 * @since 2025 067 */ 068public class UploadDialog extends AbstractUploadDialog implements PropertyChangeListener, PreferenceChangedListener { 069 /** the unique instance of the upload dialog */ 070 private static UploadDialog uploadDialog; 071 072 /** list of custom components that can be added by plugins at JOSM startup */ 073 private static final Collection<Component> customComponents = new ArrayList<>(); 074 075 /** the "created_by" changeset OSM key */ 076 private static final String CREATED_BY = "created_by"; 077 078 /** the panel with the objects to upload */ 079 private UploadedObjectsSummaryPanel pnlUploadedObjects; 080 /** the panel to select the changeset used */ 081 private ChangesetManagementPanel pnlChangesetManagement; 082 083 private BasicUploadSettingsPanel pnlBasicUploadSettings; 084 085 private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel; 086 087 /** checkbox for selecting whether an atomic upload is to be used */ 088 private TagSettingsPanel pnlTagSettings; 089 /** the tabbed pane used below of the list of primitives */ 090 private JTabbedPane tpConfigPanels; 091 /** the upload button */ 092 private JButton btnUpload; 093 094 /** the changeset comment model keeping the state of the changeset comment */ 095 private final transient ChangesetCommentModel changesetCommentModel = new ChangesetCommentModel(); 096 private final transient ChangesetCommentModel changesetSourceModel = new ChangesetCommentModel(); 097 098 private transient DataSet dataSet; 099 100 /** 101 * Constructs a new {@code UploadDialog}. 102 */ 103 public UploadDialog() { 104 super(GuiHelper.getFrameForComponent(Main.parent), ModalityType.DOCUMENT_MODAL); 105 build(); 106 } 107 108 /** 109 * Replies the unique instance of the upload dialog 110 * 111 * @return the unique instance of the upload dialog 112 */ 113 public static synchronized UploadDialog getUploadDialog() { 114 if (uploadDialog == null) { 115 uploadDialog = new UploadDialog(); 116 } 117 return uploadDialog; 118 } 119 120 /** 121 * builds the content panel for the upload dialog 122 * 123 * @return the content panel 124 */ 125 protected JPanel buildContentPanel() { 126 JPanel pnl = new JPanel(new GridBagLayout()); 127 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 128 129 // the panel with the list of uploaded objects 130 pnlUploadedObjects = new UploadedObjectsSummaryPanel(); 131 pnl.add(pnlUploadedObjects, GBC.eol().fill(GBC.BOTH)); 132 133 // Custom components 134 for (Component c : customComponents) { 135 pnl.add(c, GBC.eol().fill(GBC.HORIZONTAL)); 136 } 137 138 // a tabbed pane with configuration panels in the lower half 139 tpConfigPanels = new JTabbedPane() { 140 @Override 141 public Dimension getPreferredSize() { 142 // make sure the tabbed pane never grabs more space than necessary 143 return super.getMinimumSize(); 144 } 145 }; 146 147 pnlBasicUploadSettings = new BasicUploadSettingsPanel(changesetCommentModel, changesetSourceModel); 148 tpConfigPanels.add(pnlBasicUploadSettings); 149 tpConfigPanels.setTitleAt(0, tr("Settings")); 150 tpConfigPanels.setToolTipTextAt(0, tr("Decide how to upload the data and which changeset to use")); 151 152 pnlTagSettings = new TagSettingsPanel(changesetCommentModel, changesetSourceModel); 153 tpConfigPanels.add(pnlTagSettings); 154 tpConfigPanels.setTitleAt(1, tr("Tags of new changeset")); 155 tpConfigPanels.setToolTipTextAt(1, tr("Apply tags to the changeset data is uploaded to")); 156 157 pnlChangesetManagement = new ChangesetManagementPanel(changesetCommentModel); 158 tpConfigPanels.add(pnlChangesetManagement); 159 tpConfigPanels.setTitleAt(2, tr("Changesets")); 160 tpConfigPanels.setToolTipTextAt(2, tr("Manage open changesets and select a changeset to upload to")); 161 162 pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel(); 163 tpConfigPanels.add(pnlUploadStrategySelectionPanel); 164 tpConfigPanels.setTitleAt(3, tr("Advanced")); 165 tpConfigPanels.setToolTipTextAt(3, tr("Configure advanced settings")); 166 167 pnl.add(tpConfigPanels, GBC.eol().fill(GBC.HORIZONTAL)); 168 return pnl; 169 } 170 171 /** 172 * builds the panel with the OK and CANCEL buttons 173 * 174 * @return The panel with the OK and CANCEL buttons 175 */ 176 protected JPanel buildActionPanel() { 177 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); 178 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 179 180 // -- upload button 181 btnUpload = new JButton(new UploadAction(this)); 182 pnl.add(btnUpload); 183 btnUpload.setFocusable(true); 184 InputMapUtils.enableEnter(btnUpload); 185 186 // -- cancel button 187 CancelAction cancelAction = new CancelAction(this); 188 pnl.add(new JButton(cancelAction)); 189 getRootPane().registerKeyboardAction( 190 cancelAction, 191 KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 192 JComponent.WHEN_IN_FOCUSED_WINDOW 193 ); 194 pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Dialog/Upload")))); 195 HelpUtil.setHelpContext(getRootPane(), ht("/Dialog/Upload")); 196 return pnl; 197 } 198 199 /** 200 * builds the gui 201 */ 202 protected void build() { 203 setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl())); 204 getContentPane().setLayout(new BorderLayout()); 205 getContentPane().add(buildContentPanel(), BorderLayout.CENTER); 206 getContentPane().add(buildActionPanel(), BorderLayout.SOUTH); 207 208 addWindowListener(new WindowEventHandler()); 209 210 211 // make sure the configuration panels listen to each other 212 // changes 213 // 214 pnlChangesetManagement.addPropertyChangeListener(this); 215 pnlChangesetManagement.addPropertyChangeListener( 216 pnlBasicUploadSettings.getUploadParameterSummaryPanel() 217 ); 218 pnlChangesetManagement.addPropertyChangeListener(this); 219 pnlUploadedObjects.addPropertyChangeListener( 220 pnlBasicUploadSettings.getUploadParameterSummaryPanel() 221 ); 222 pnlUploadedObjects.addPropertyChangeListener(pnlUploadStrategySelectionPanel); 223 pnlUploadStrategySelectionPanel.addPropertyChangeListener( 224 pnlBasicUploadSettings.getUploadParameterSummaryPanel() 225 ); 226 227 228 // users can click on either of two links in the upload parameter 229 // summary handler. This installs the handler for these two events. 230 // We simply select the appropriate tab in the tabbed pane with the configuration dialogs. 231 // 232 pnlBasicUploadSettings.getUploadParameterSummaryPanel().setConfigurationParameterRequestListener( 233 new ConfigurationParameterRequestHandler() { 234 @Override 235 public void handleUploadStrategyConfigurationRequest() { 236 tpConfigPanels.setSelectedIndex(3); 237 } 238 239 @Override 240 public void handleChangesetConfigurationRequest() { 241 tpConfigPanels.setSelectedIndex(2); 242 } 243 } 244 ); 245 246 pnlBasicUploadSettings.setUploadTagDownFocusTraversalHandlers( 247 new AbstractAction() { 248 @Override 249 public void actionPerformed(ActionEvent e) { 250 btnUpload.requestFocusInWindow(); 251 } 252 } 253 ); 254 255 setMinimumSize(new Dimension(300, 350)); 256 257 Main.pref.addPreferenceChangeListener(this); 258 } 259 260 /** 261 * Sets the collection of primitives to upload 262 * 263 * @param toUpload the dataset with the objects to upload. If null, assumes the empty 264 * set of objects to upload 265 * 266 */ 267 public void setUploadedPrimitives(APIDataSet toUpload) { 268 if (toUpload == null) { 269 List<OsmPrimitive> emptyList = Collections.emptyList(); 270 pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList); 271 return; 272 } 273 pnlUploadedObjects.setUploadedPrimitives( 274 toUpload.getPrimitivesToAdd(), 275 toUpload.getPrimitivesToUpdate(), 276 toUpload.getPrimitivesToDelete() 277 ); 278 } 279 280 /** 281 * Sets the tags for this upload based on (later items overwrite earlier ones): 282 * <ul> 283 * <li>previous "source" and "comment" input</li> 284 * <li>the tags set in the dataset (see {@link DataSet#getChangeSetTags()})</li> 285 * <li>the tags from the selected open changeset</li> 286 * <li>the JOSM user agent (see {@link Version#getAgentString(boolean)})</li> 287 * </ul> 288 * 289 * @param dataSet to obtain the tags set in the dataset 290 */ 291 public void setChangesetTags(DataSet dataSet) { 292 final Map<String, String> tags = new HashMap<>(); 293 294 // obtain from previous input 295 tags.put("source", getLastChangesetSourceFromHistory()); 296 tags.put("comment", getLastChangesetCommentFromHistory()); 297 298 // obtain from dataset 299 if (dataSet != null) { 300 tags.putAll(dataSet.getChangeSetTags()); 301 } 302 this.dataSet = dataSet; 303 304 // obtain from selected open changeset 305 if (pnlChangesetManagement.getSelectedChangeset() != null) { 306 tags.putAll(pnlChangesetManagement.getSelectedChangeset().getKeys()); 307 } 308 309 // set/adapt created_by 310 final String agent = Version.getInstance().getAgentString(false); 311 final String createdBy = tags.get(CREATED_BY); 312 if (createdBy == null || createdBy.isEmpty()) { 313 tags.put(CREATED_BY, agent); 314 } else if (!createdBy.contains(agent)) { 315 tags.put(CREATED_BY, createdBy + ';' + agent); 316 } 317 318 // remove empty values 319 final Iterator<String> it = tags.keySet().iterator(); 320 while (it.hasNext()) { 321 final String v = tags.get(it.next()); 322 if (v == null || v.isEmpty()) { 323 it.remove(); 324 } 325 } 326 327 pnlTagSettings.initFromTags(tags); 328 pnlTagSettings.tableChanged(null); 329 } 330 331 @Override 332 public void rememberUserInput() { 333 pnlBasicUploadSettings.rememberUserInput(); 334 pnlUploadStrategySelectionPanel.rememberUserInput(); 335 } 336 337 /** 338 * Initializes the panel for user input 339 */ 340 public void startUserInput() { 341 tpConfigPanels.setSelectedIndex(0); 342 pnlBasicUploadSettings.startUserInput(); 343 pnlTagSettings.startUserInput(); 344 pnlUploadStrategySelectionPanel.initFromPreferences(); 345 UploadParameterSummaryPanel pnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel(); 346 pnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification()); 347 pnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload()); 348 pnl.setNumObjects(pnlUploadedObjects.getNumObjectsToUpload()); 349 } 350 351 /** 352 * Replies the current changeset 353 * 354 * @return the current changeset 355 */ 356 public Changeset getChangeset() { 357 Changeset cs = pnlChangesetManagement.getSelectedChangeset(); 358 if (cs == null) { 359 cs = new Changeset(); 360 } 361 cs.setKeys(pnlTagSettings.getTags(false)); 362 return cs; 363 } 364 365 /** 366 * Sets the changeset to be used in the next upload 367 * 368 * @param cs the changeset 369 */ 370 public void setSelectedChangesetForNextUpload(Changeset cs) { 371 pnlChangesetManagement.setSelectedChangesetForNextUpload(cs); 372 } 373 374 @Override 375 public UploadStrategySpecification getUploadStrategySpecification() { 376 UploadStrategySpecification spec = pnlUploadStrategySelectionPanel.getUploadStrategySpecification(); 377 spec.setCloseChangesetAfterUpload(pnlChangesetManagement.isCloseChangesetAfterUpload()); 378 return spec; 379 } 380 381 @Override 382 public String getUploadComment() { 383 return changesetCommentModel.getComment(); 384 } 385 386 @Override 387 public String getUploadSource() { 388 return changesetSourceModel.getComment(); 389 } 390 391 @Override 392 public void setVisible(boolean visible) { 393 if (visible) { 394 new WindowGeometry( 395 getClass().getName() + ".geometry", 396 WindowGeometry.centerInWindow( 397 Main.parent, 398 new Dimension(400, 600) 399 ) 400 ).applySafe(this); 401 startUserInput(); 402 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775 403 new WindowGeometry(this).remember(getClass().getName() + ".geometry"); 404 } 405 super.setVisible(visible); 406 } 407 408 /** 409 * Adds a custom component to this dialog. 410 * Custom components added at JOSM startup are displayed between the objects list and the properties tab pane. 411 * @param c The custom component to add. If {@code null}, this method does nothing. 412 * @return {@code true} if the collection of custom components changed as a result of the call 413 * @since 5842 414 */ 415 public static boolean addCustomComponent(Component c) { 416 if (c != null) { 417 return customComponents.add(c); 418 } 419 return false; 420 } 421 422 /** 423 * Handles an upload. 424 */ 425 static class UploadAction extends AbstractAction { 426 427 private final transient IUploadDialog dialog; 428 429 UploadAction(IUploadDialog dialog) { 430 this.dialog = dialog; 431 putValue(NAME, tr("Upload Changes")); 432 putValue(SMALL_ICON, ImageProvider.get("upload")); 433 putValue(SHORT_DESCRIPTION, tr("Upload the changed primitives")); 434 } 435 436 /** 437 * Displays a warning message indicating that the upload comment is empty/short. 438 * @return true if the user wants to revisit, false if they want to continue 439 */ 440 protected boolean warnUploadComment() { 441 return warnUploadTag( 442 tr("Please revise upload comment"), 443 tr("Your upload comment is <i>empty</i>, or <i>very short</i>.<br /><br />" + 444 "This is technically allowed, but please consider that many users who are<br />" + 445 "watching changes in their area depend on meaningful changeset comments<br />" + 446 "to understand what is going on!<br /><br />" + 447 "If you spend a minute now to explain your change, you will make life<br />" + 448 "easier for many other mappers."), 449 "upload_comment_is_empty_or_very_short" 450 ); 451 } 452 453 /** 454 * Displays a warning message indicating that no changeset source is given. 455 * @return true if the user wants to revisit, false if they want to continue 456 */ 457 protected boolean warnUploadSource() { 458 return warnUploadTag( 459 tr("Please specify a changeset source"), 460 tr("You did not specify a source for your changes.<br />" + 461 "It is technically allowed, but this information helps<br />" + 462 "other users to understand the origins of the data.<br /><br />" + 463 "If you spend a minute now to explain your change, you will make life<br />" + 464 "easier for many other mappers."), 465 "upload_source_is_empty" 466 ); 467 } 468 469 protected boolean warnUploadTag(final String title, final String message, final String togglePref) { 470 String[] buttonTexts = new String[] {tr("Revise"), tr("Cancel"), tr("Continue as is")}; 471 Icon[] buttonIcons = new Icon[] { 472 new ImageProvider("ok").setMaxSize(ImageSizes.LARGEICON).get(), 473 new ImageProvider("cancel").setMaxSize(ImageSizes.LARGEICON).get(), 474 new ImageProvider("upload").setMaxSize(ImageSizes.LARGEICON).addOverlay( 475 new ImageOverlay(new ImageProvider("warning-small"), 0.5, 0.5, 1.0, 1.0)).get()}; 476 String[] tooltips = new String[] { 477 tr("Return to the previous dialog to enter a more descriptive comment"), 478 tr("Cancel and return to the previous dialog"), 479 tr("Ignore this hint and upload anyway")}; 480 481 if (GraphicsEnvironment.isHeadless()) { 482 return false; 483 } 484 485 ExtendedDialog dlg = new ExtendedDialog((Component) dialog, title, buttonTexts); 486 dlg.setContent("<html>" + message + "</html>"); 487 dlg.setButtonIcons(buttonIcons); 488 dlg.setToolTipTexts(tooltips); 489 dlg.setIcon(JOptionPane.WARNING_MESSAGE); 490 dlg.toggleEnable(togglePref); 491 dlg.setCancelButton(1, 2); 492 return dlg.showDialog().getValue() != 3; 493 } 494 495 protected void warnIllegalChunkSize() { 496 HelpAwareOptionPane.showOptionDialog( 497 (Component) dialog, 498 tr("Please enter a valid chunk size first"), 499 tr("Illegal chunk size"), 500 JOptionPane.ERROR_MESSAGE, 501 ht("/Dialog/Upload#IllegalChunkSize") 502 ); 503 } 504 505 static boolean isUploadCommentTooShort(String comment) { 506 String s = comment.trim(); 507 boolean result = true; 508 if (!s.isEmpty()) { 509 UnicodeBlock block = Character.UnicodeBlock.of(s.charAt(0)); 510 if (block != null && block.toString().contains("CJK")) { 511 result = s.length() < 4; 512 } else { 513 result = s.length() < 10; 514 } 515 } 516 return result; 517 } 518 519 @Override 520 public void actionPerformed(ActionEvent e) { 521 if (isUploadCommentTooShort(dialog.getUploadComment()) && warnUploadComment()) { 522 // abort for missing comment 523 dialog.handleMissingComment(); 524 return; 525 } 526 if (dialog.getUploadSource().trim().isEmpty() && warnUploadSource()) { 527 // abort for missing changeset source 528 dialog.handleMissingSource(); 529 return; 530 } 531 532 /* test for empty tags in the changeset metadata and proceed only after user's confirmation. 533 * though, accept if key and value are empty (cf. xor). */ 534 List<String> emptyChangesetTags = new ArrayList<>(); 535 for (final Entry<String, String> i : dialog.getTags(true).entrySet()) { 536 final boolean isKeyEmpty = i.getKey() == null || i.getKey().trim().isEmpty(); 537 final boolean isValueEmpty = i.getValue() == null || i.getValue().trim().isEmpty(); 538 final boolean ignoreKey = "comment".equals(i.getKey()) || "source".equals(i.getKey()); 539 if ((isKeyEmpty ^ isValueEmpty) && !ignoreKey) { 540 emptyChangesetTags.add(tr("{0}={1}", i.getKey(), i.getValue())); 541 } 542 } 543 if (!emptyChangesetTags.isEmpty() && JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog( 544 Main.parent, 545 trn( 546 "<html>The following changeset tag contains an empty key/value:<br>{0}<br>Continue?</html>", 547 "<html>The following changeset tags contain an empty key/value:<br>{0}<br>Continue?</html>", 548 emptyChangesetTags.size(), Utils.joinAsHtmlUnorderedList(emptyChangesetTags)), 549 tr("Empty metadata"), 550 JOptionPane.OK_CANCEL_OPTION, 551 JOptionPane.WARNING_MESSAGE 552 )) { 553 dialog.handleMissingComment(); 554 return; 555 } 556 557 UploadStrategySpecification strategy = dialog.getUploadStrategySpecification(); 558 if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY) 559 && strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) { 560 warnIllegalChunkSize(); 561 dialog.handleIllegalChunkSize(); 562 return; 563 } 564 if (dialog instanceof AbstractUploadDialog) { 565 ((AbstractUploadDialog) dialog).setCanceled(false); 566 ((AbstractUploadDialog) dialog).setVisible(false); 567 } 568 } 569 } 570 571 /** 572 * Action for canceling the dialog. 573 */ 574 static class CancelAction extends AbstractAction { 575 576 private final transient IUploadDialog dialog; 577 578 CancelAction(IUploadDialog dialog) { 579 this.dialog = dialog; 580 putValue(NAME, tr("Cancel")); 581 putValue(SMALL_ICON, ImageProvider.get("cancel")); 582 putValue(SHORT_DESCRIPTION, tr("Cancel the upload and resume editing")); 583 } 584 585 @Override 586 public void actionPerformed(ActionEvent e) { 587 if (dialog instanceof AbstractUploadDialog) { 588 ((AbstractUploadDialog) dialog).setCanceled(true); 589 ((AbstractUploadDialog) dialog).setVisible(false); 590 } 591 } 592 } 593 594 /** 595 * Listens to window closing events and processes them as cancel events. 596 * Listens to window open events and initializes user input 597 * 598 */ 599 class WindowEventHandler extends WindowAdapter { 600 @Override 601 public void windowClosing(WindowEvent e) { 602 setCanceled(true); 603 } 604 605 @Override 606 public void windowActivated(WindowEvent arg0) { 607 if (tpConfigPanels.getSelectedIndex() == 0) { 608 pnlBasicUploadSettings.initEditingOfUploadComment(); 609 } 610 } 611 } 612 613 /* -------------------------------------------------------------------------- */ 614 /* Interface PropertyChangeListener */ 615 /* -------------------------------------------------------------------------- */ 616 @Override 617 public void propertyChange(PropertyChangeEvent evt) { 618 if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) { 619 Changeset cs = (Changeset) evt.getNewValue(); 620 setChangesetTags(dataSet); 621 if (cs == null) { 622 tpConfigPanels.setTitleAt(1, tr("Tags of new changeset")); 623 } else { 624 tpConfigPanels.setTitleAt(1, tr("Tags of changeset {0}", cs.getId())); 625 } 626 } 627 } 628 629 /* -------------------------------------------------------------------------- */ 630 /* Interface PreferenceChangedListener */ 631 /* -------------------------------------------------------------------------- */ 632 @Override 633 public void preferenceChanged(PreferenceChangeEvent e) { 634 if (e.getKey() == null || !"osm-server.url".equals(e.getKey())) 635 return; 636 final Setting<?> newValue = e.getNewValue(); 637 final String url; 638 if (newValue == null || newValue.getValue() == null) { 639 url = OsmApi.getOsmApi().getBaseUrl(); 640 } else { 641 url = newValue.getValue().toString(); 642 } 643 setTitle(tr("Upload to ''{0}''", url)); 644 } 645 646 private static String getLastChangesetTagFromHistory(String historyKey, List<String> def) { 647 Collection<String> history = Main.pref.getCollection(historyKey, def); 648 int age = (int) (System.currentTimeMillis() / 1000 - Main.pref.getInteger(BasicUploadSettingsPanel.HISTORY_LAST_USED_KEY, 0)); 649 if (age < Main.pref.getInteger(BasicUploadSettingsPanel.HISTORY_MAX_AGE_KEY, 4 * 3600 * 1000) && history != null && !history.isEmpty()) { 650 return history.iterator().next(); 651 } else { 652 return null; 653 } 654 } 655 656 /** 657 * Returns the last changeset comment from history. 658 * @return the last changeset comment from history 659 */ 660 public String getLastChangesetCommentFromHistory() { 661 return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.HISTORY_KEY, new ArrayList<String>()); 662 } 663 664 /** 665 * Returns the last changeset source from history. 666 * @return the last changeset source from history 667 */ 668 public String getLastChangesetSourceFromHistory() { 669 return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.SOURCE_HISTORY_KEY, BasicUploadSettingsPanel.getDefaultSources()); 670 } 671 672 @Override 673 public Map<String, String> getTags(boolean keepEmpty) { 674 return pnlTagSettings.getTags(keepEmpty); 675 } 676 677 @Override 678 public void handleMissingComment() { 679 tpConfigPanels.setSelectedIndex(0); 680 pnlBasicUploadSettings.initEditingOfUploadComment(); 681 } 682 683 @Override 684 public void handleMissingSource() { 685 tpConfigPanels.setSelectedIndex(0); 686 pnlBasicUploadSettings.initEditingOfUploadSource(); 687 } 688 689 @Override 690 public void handleIllegalChunkSize() { 691 tpConfigPanels.setSelectedIndex(0); 692 } 693}