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