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

Last change on this file since 12452 was 12452, checked in by bastiK, 5 months ago

see #14794 - javadoc for the josm.gui.io package

  • Property svn:eol-style set to native
File size: 26.9 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.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Component;
9import java.awt.Dimension;
10import java.awt.FlowLayout;
11import java.awt.GraphicsEnvironment;
12import java.awt.GridBagLayout;
13import java.awt.event.ActionEvent;
14import java.awt.event.WindowAdapter;
15import java.awt.event.WindowEvent;
16import java.beans.PropertyChangeEvent;
17import java.beans.PropertyChangeListener;
18import java.lang.Character.UnicodeBlock;
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.Collections;
22import java.util.HashMap;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.Map.Entry;
27import java.util.Optional;
28import java.util.concurrent.TimeUnit;
29
30import javax.swing.AbstractAction;
31import javax.swing.BorderFactory;
32import javax.swing.Icon;
33import javax.swing.JButton;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JTabbedPane;
37
38import org.openstreetmap.josm.Main;
39import org.openstreetmap.josm.data.APIDataSet;
40import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
41import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
42import org.openstreetmap.josm.data.Version;
43import org.openstreetmap.josm.data.osm.Changeset;
44import org.openstreetmap.josm.data.osm.DataSet;
45import org.openstreetmap.josm.data.osm.OsmPrimitive;
46import org.openstreetmap.josm.data.preferences.Setting;
47import org.openstreetmap.josm.gui.ExtendedDialog;
48import org.openstreetmap.josm.gui.HelpAwareOptionPane;
49import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
50import org.openstreetmap.josm.gui.help.HelpUtil;
51import org.openstreetmap.josm.gui.util.GuiHelper;
52import org.openstreetmap.josm.io.OsmApi;
53import org.openstreetmap.josm.tools.GBC;
54import org.openstreetmap.josm.tools.ImageOverlay;
55import org.openstreetmap.josm.tools.ImageProvider;
56import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
57import org.openstreetmap.josm.tools.InputMapUtils;
58import org.openstreetmap.josm.tools.MultiLineFlowLayout;
59import org.openstreetmap.josm.tools.Utils;
60import org.openstreetmap.josm.tools.WindowGeometry;
61
62/**
63 * This is a dialog for entering upload options like the parameters for
64 * the upload changeset and the strategy for opening/closing a changeset.
65 * @since 2025
66 */
67public class UploadDialog extends AbstractUploadDialog implements PropertyChangeListener, PreferenceChangedListener {
68    /** the unique instance of the upload dialog */
69    private static UploadDialog uploadDialog;
70
71    /** list of custom components that can be added by plugins at JOSM startup */
72    private static final Collection<Component> customComponents = new ArrayList<>();
73
74    /** the "created_by" changeset OSM key */
75    private static final String CREATED_BY = "created_by";
76
77    /** the panel with the objects to upload */
78    private UploadedObjectsSummaryPanel pnlUploadedObjects;
79    /** the panel to select the changeset used */
80    private ChangesetManagementPanel pnlChangesetManagement;
81
82    private BasicUploadSettingsPanel pnlBasicUploadSettings;
83
84    private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel;
85
86    /** checkbox for selecting whether an atomic upload is to be used  */
87    private TagSettingsPanel pnlTagSettings;
88    /** the tabbed pane used below of the list of primitives  */
89    private JTabbedPane tpConfigPanels;
90    /** the upload button */
91    private JButton btnUpload;
92
93    /** the changeset comment model keeping the state of the changeset comment */
94    private final transient ChangesetCommentModel changesetCommentModel = new ChangesetCommentModel();
95    private final transient ChangesetCommentModel changesetSourceModel = new ChangesetCommentModel();
96
97    private transient DataSet dataSet;
98
99    /**
100     * Constructs a new {@code UploadDialog}.
101     */
102    public UploadDialog() {
103        super(GuiHelper.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 CompactTabbedPane();
139
140        pnlBasicUploadSettings = new BasicUploadSettingsPanel(changesetCommentModel, changesetSourceModel);
141        tpConfigPanels.add(pnlBasicUploadSettings);
142        tpConfigPanels.setTitleAt(0, tr("Settings"));
143        tpConfigPanels.setToolTipTextAt(0, tr("Decide how to upload the data and which changeset to use"));
144
145        pnlTagSettings = new TagSettingsPanel(changesetCommentModel, changesetSourceModel);
146        tpConfigPanels.add(pnlTagSettings);
147        tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
148        tpConfigPanels.setToolTipTextAt(1, tr("Apply tags to the changeset data is uploaded to"));
149
150        pnlChangesetManagement = new ChangesetManagementPanel(changesetCommentModel);
151        tpConfigPanels.add(pnlChangesetManagement);
152        tpConfigPanels.setTitleAt(2, tr("Changesets"));
153        tpConfigPanels.setToolTipTextAt(2, tr("Manage open changesets and select a changeset to upload to"));
154
155        pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel();
156        tpConfigPanels.add(pnlUploadStrategySelectionPanel);
157        tpConfigPanels.setTitleAt(3, tr("Advanced"));
158        tpConfigPanels.setToolTipTextAt(3, tr("Configure advanced settings"));
159
160        pnl.add(tpConfigPanels, GBC.eol().fill(GBC.HORIZONTAL));
161
162        pnl.add(buildActionPanel(), GBC.eol().fill(GBC.HORIZONTAL));
163        return pnl;
164    }
165
166    /**
167     * builds the panel with the OK and CANCEL buttons
168     *
169     * @return The panel with the OK and CANCEL buttons
170     */
171    protected JPanel buildActionPanel() {
172        JPanel pnl = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER));
173        pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
174
175        // -- upload button
176        btnUpload = new JButton(new UploadAction(this));
177        pnl.add(btnUpload);
178        btnUpload.setFocusable(true);
179        InputMapUtils.enableEnter(btnUpload);
180
181        // -- cancel button
182        CancelAction cancelAction = new CancelAction(this);
183        pnl.add(new JButton(cancelAction));
184        InputMapUtils.addEscapeAction(getRootPane(), cancelAction);
185        pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Dialog/Upload"))));
186        HelpUtil.setHelpContext(getRootPane(), ht("/Dialog/Upload"));
187        return pnl;
188    }
189
190    /**
191     * builds the gui
192     */
193    protected void build() {
194        setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl()));
195        setContentPane(buildContentPanel());
196
197        addWindowListener(new WindowEventHandler());
198
199
200        // make sure the configuration panels listen to each other
201        // changes
202        //
203        pnlChangesetManagement.addPropertyChangeListener(this);
204        pnlChangesetManagement.addPropertyChangeListener(
205                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
206        );
207        pnlChangesetManagement.addPropertyChangeListener(this);
208        pnlUploadedObjects.addPropertyChangeListener(
209                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
210        );
211        pnlUploadedObjects.addPropertyChangeListener(pnlUploadStrategySelectionPanel);
212        pnlUploadStrategySelectionPanel.addPropertyChangeListener(
213                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
214        );
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                new ConfigurationParameterRequestHandler() {
222                    @Override
223                    public void handleUploadStrategyConfigurationRequest() {
224                        tpConfigPanels.setSelectedIndex(3);
225                    }
226
227                    @Override
228                    public void handleChangesetConfigurationRequest() {
229                        tpConfigPanels.setSelectedIndex(2);
230                    }
231                }
232        );
233
234        pnlBasicUploadSettings.setUploadTagDownFocusTraversalHandlers(
235                new AbstractAction() {
236                    @Override
237                    public void actionPerformed(ActionEvent e) {
238                        btnUpload.requestFocusInWindow();
239                    }
240                }
241        );
242
243        setMinimumSize(new Dimension(600, 350));
244
245        Main.pref.addPreferenceChangeListener(this);
246    }
247
248    /**
249     * Sets the collection of primitives to upload
250     *
251     * @param toUpload the dataset with the objects to upload. If null, assumes the empty
252     * set of objects to upload
253     *
254     */
255    public void setUploadedPrimitives(APIDataSet toUpload) {
256        if (toUpload == null) {
257            List<OsmPrimitive> emptyList = Collections.emptyList();
258            pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);
259            return;
260        }
261        pnlUploadedObjects.setUploadedPrimitives(
262                toUpload.getPrimitivesToAdd(),
263                toUpload.getPrimitivesToUpdate(),
264                toUpload.getPrimitivesToDelete()
265        );
266    }
267
268    /**
269     * Sets the tags for this upload based on (later items overwrite earlier ones):
270     * <ul>
271     * <li>previous "source" and "comment" input</li>
272     * <li>the tags set in the dataset (see {@link DataSet#getChangeSetTags()})</li>
273     * <li>the tags from the selected open changeset</li>
274     * <li>the JOSM user agent (see {@link Version#getAgentString(boolean)})</li>
275     * </ul>
276     *
277     * @param dataSet to obtain the tags set in the dataset
278     */
279    public void setChangesetTags(DataSet dataSet) {
280        final Map<String, String> tags = new HashMap<>();
281
282        // obtain from previous input
283        tags.put("source", getLastChangesetSourceFromHistory());
284        tags.put("comment", getLastChangesetCommentFromHistory());
285
286        // obtain from dataset
287        if (dataSet != null) {
288            tags.putAll(dataSet.getChangeSetTags());
289        }
290        this.dataSet = dataSet;
291
292        // obtain from selected open changeset
293        if (pnlChangesetManagement.getSelectedChangeset() != null) {
294            tags.putAll(pnlChangesetManagement.getSelectedChangeset().getKeys());
295        }
296
297        // set/adapt created_by
298        final String agent = Version.getInstance().getAgentString(false);
299        final String createdBy = tags.get(CREATED_BY);
300        if (createdBy == null || createdBy.isEmpty()) {
301            tags.put(CREATED_BY, agent);
302        } else if (!createdBy.contains(agent)) {
303            tags.put(CREATED_BY, createdBy + ';' + agent);
304        }
305
306        // remove empty values
307        final Iterator<String> it = tags.keySet().iterator();
308        while (it.hasNext()) {
309            final String v = tags.get(it.next());
310            if (v == null || v.isEmpty()) {
311                it.remove();
312            }
313        }
314
315        pnlTagSettings.initFromTags(tags);
316        pnlTagSettings.tableChanged(null);
317    }
318
319    @Override
320    public void rememberUserInput() {
321        pnlBasicUploadSettings.rememberUserInput();
322        pnlUploadStrategySelectionPanel.rememberUserInput();
323    }
324
325    /**
326     * Initializes the panel for user input
327     */
328    public void startUserInput() {
329        tpConfigPanels.setSelectedIndex(0);
330        pnlBasicUploadSettings.startUserInput();
331        pnlTagSettings.startUserInput();
332        pnlUploadStrategySelectionPanel.initFromPreferences();
333        UploadParameterSummaryPanel pnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
334        pnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
335        pnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
336        pnl.setNumObjects(pnlUploadedObjects.getNumObjectsToUpload());
337    }
338
339    /**
340     * Replies the current changeset
341     *
342     * @return the current changeset
343     */
344    public Changeset getChangeset() {
345        Changeset cs = Optional.ofNullable(pnlChangesetManagement.getSelectedChangeset()).orElseGet(Changeset::new);
346        cs.setKeys(pnlTagSettings.getTags(false));
347        return cs;
348    }
349
350    /**
351     * Sets the changeset to be used in the next upload
352     *
353     * @param cs the changeset
354     */
355    public void setSelectedChangesetForNextUpload(Changeset cs) {
356        pnlChangesetManagement.setSelectedChangesetForNextUpload(cs);
357    }
358
359    @Override
360    public UploadStrategySpecification getUploadStrategySpecification() {
361        UploadStrategySpecification spec = pnlUploadStrategySelectionPanel.getUploadStrategySpecification();
362        spec.setCloseChangesetAfterUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
363        return spec;
364    }
365
366    @Override
367    public String getUploadComment() {
368        return changesetCommentModel.getComment();
369    }
370
371    @Override
372    public String getUploadSource() {
373        return changesetSourceModel.getComment();
374    }
375
376    @Override
377    public void setVisible(boolean visible) {
378        if (visible) {
379            new WindowGeometry(
380                    getClass().getName() + ".geometry",
381                    WindowGeometry.centerInWindow(
382                            Main.parent,
383                            new Dimension(400, 600)
384                    )
385            ).applySafe(this);
386            startUserInput();
387        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
388            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
389        }
390        super.setVisible(visible);
391    }
392
393    /**
394     * Adds a custom component to this dialog.
395     * Custom components added at JOSM startup are displayed between the objects list and the properties tab pane.
396     * @param c The custom component to add. If {@code null}, this method does nothing.
397     * @return {@code true} if the collection of custom components changed as a result of the call
398     * @since 5842
399     */
400    public static boolean addCustomComponent(Component c) {
401        if (c != null) {
402            return customComponents.add(c);
403        }
404        return false;
405    }
406
407    static final class CompactTabbedPane extends JTabbedPane {
408        @Override
409        public Dimension getPreferredSize() {
410            // make sure the tabbed pane never grabs more space than necessary
411            return super.getMinimumSize();
412        }
413    }
414
415    /**
416     * Handles an upload.
417     */
418    static class UploadAction extends AbstractAction {
419
420        private final transient IUploadDialog dialog;
421
422        UploadAction(IUploadDialog dialog) {
423            this.dialog = dialog;
424            putValue(NAME, tr("Upload Changes"));
425            putValue(SMALL_ICON, ImageProvider.get("upload"));
426            putValue(SHORT_DESCRIPTION, tr("Upload the changed primitives"));
427        }
428
429        /**
430         * Displays a warning message indicating that the upload comment is empty/short.
431         * @return true if the user wants to revisit, false if they want to continue
432         */
433        protected boolean warnUploadComment() {
434            return warnUploadTag(
435                    tr("Please revise upload comment"),
436                    tr("Your upload comment is <i>empty</i>, or <i>very short</i>.<br /><br />" +
437                            "This is technically allowed, but please consider that many users who are<br />" +
438                            "watching changes in their area depend on meaningful changeset comments<br />" +
439                            "to understand what is going on!<br /><br />" +
440                            "If you spend a minute now to explain your change, you will make life<br />" +
441                            "easier for many other mappers."),
442                    "upload_comment_is_empty_or_very_short"
443            );
444        }
445
446        /**
447         * Displays a warning message indicating that no changeset source is given.
448         * @return true if the user wants to revisit, false if they want to continue
449         */
450        protected boolean warnUploadSource() {
451            return warnUploadTag(
452                    tr("Please specify a changeset source"),
453                    tr("You did not specify a source for your changes.<br />" +
454                            "It is technically allowed, but this information helps<br />" +
455                            "other users to understand the origins of the data.<br /><br />" +
456                            "If you spend a minute now to explain your change, you will make life<br />" +
457                            "easier for many other mappers."),
458                    "upload_source_is_empty"
459            );
460        }
461
462        protected boolean warnUploadTag(final String title, final String message, final String togglePref) {
463            String[] buttonTexts = new String[] {tr("Revise"), tr("Cancel"), tr("Continue as is")};
464            Icon[] buttonIcons = new Icon[] {
465                    new ImageProvider("ok").setMaxSize(ImageSizes.LARGEICON).get(),
466                    new ImageProvider("cancel").setMaxSize(ImageSizes.LARGEICON).get(),
467                    new ImageProvider("upload").setMaxSize(ImageSizes.LARGEICON).addOverlay(
468                            new ImageOverlay(new ImageProvider("warning-small"), 0.5, 0.5, 1.0, 1.0)).get()};
469            String[] tooltips = new String[] {
470                    tr("Return to the previous dialog to enter a more descriptive comment"),
471                    tr("Cancel and return to the previous dialog"),
472                    tr("Ignore this hint and upload anyway")};
473
474            if (GraphicsEnvironment.isHeadless()) {
475                return false;
476            }
477
478            ExtendedDialog dlg = new ExtendedDialog((Component) dialog, title, buttonTexts);
479            dlg.setContent("<html>" + message + "</html>");
480            dlg.setButtonIcons(buttonIcons);
481            dlg.setToolTipTexts(tooltips);
482            dlg.setIcon(JOptionPane.WARNING_MESSAGE);
483            dlg.toggleEnable(togglePref);
484            dlg.setCancelButton(1, 2);
485            return dlg.showDialog().getValue() != 3;
486        }
487
488        protected void warnIllegalChunkSize() {
489            HelpAwareOptionPane.showOptionDialog(
490                    (Component) dialog,
491                    tr("Please enter a valid chunk size first"),
492                    tr("Illegal chunk size"),
493                    JOptionPane.ERROR_MESSAGE,
494                    ht("/Dialog/Upload#IllegalChunkSize")
495            );
496        }
497
498        static boolean isUploadCommentTooShort(String comment) {
499            String s = comment.trim();
500            boolean result = true;
501            if (!s.isEmpty()) {
502                UnicodeBlock block = Character.UnicodeBlock.of(s.charAt(0));
503                if (block != null && block.toString().contains("CJK")) {
504                    result = s.length() < 4;
505                } else {
506                    result = s.length() < 10;
507                }
508            }
509            return result;
510        }
511
512        @Override
513        public void actionPerformed(ActionEvent e) {
514            if (isUploadCommentTooShort(dialog.getUploadComment()) && warnUploadComment()) {
515                // abort for missing comment
516                dialog.handleMissingComment();
517                return;
518            }
519            if (dialog.getUploadSource().trim().isEmpty() && warnUploadSource()) {
520                // abort for missing changeset source
521                dialog.handleMissingSource();
522                return;
523            }
524
525            /* test for empty tags in the changeset metadata and proceed only after user's confirmation.
526             * though, accept if key and value are empty (cf. xor). */
527            List<String> emptyChangesetTags = new ArrayList<>();
528            for (final Entry<String, String> i : dialog.getTags(true).entrySet()) {
529                final boolean isKeyEmpty = i.getKey() == null || i.getKey().trim().isEmpty();
530                final boolean isValueEmpty = i.getValue() == null || i.getValue().trim().isEmpty();
531                final boolean ignoreKey = "comment".equals(i.getKey()) || "source".equals(i.getKey());
532                if ((isKeyEmpty ^ isValueEmpty) && !ignoreKey) {
533                    emptyChangesetTags.add(tr("{0}={1}", i.getKey(), i.getValue()));
534                }
535            }
536            if (!emptyChangesetTags.isEmpty() && JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(
537                    Main.parent,
538                    trn(
539                            "<html>The following changeset tag contains an empty key/value:<br>{0}<br>Continue?</html>",
540                            "<html>The following changeset tags contain an empty key/value:<br>{0}<br>Continue?</html>",
541                            emptyChangesetTags.size(), Utils.joinAsHtmlUnorderedList(emptyChangesetTags)),
542                    tr("Empty metadata"),
543                    JOptionPane.OK_CANCEL_OPTION,
544                    JOptionPane.WARNING_MESSAGE
545            )) {
546                dialog.handleMissingComment();
547                return;
548            }
549
550            UploadStrategySpecification strategy = dialog.getUploadStrategySpecification();
551            if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)
552                    && strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
553                warnIllegalChunkSize();
554                dialog.handleIllegalChunkSize();
555                return;
556            }
557            if (dialog instanceof AbstractUploadDialog) {
558                ((AbstractUploadDialog) dialog).setCanceled(false);
559                ((AbstractUploadDialog) dialog).setVisible(false);
560            }
561        }
562    }
563
564    /**
565     * Action for canceling the dialog.
566     */
567    static class CancelAction extends AbstractAction {
568
569        private final transient IUploadDialog dialog;
570
571        CancelAction(IUploadDialog dialog) {
572            this.dialog = dialog;
573            putValue(NAME, tr("Cancel"));
574            putValue(SMALL_ICON, ImageProvider.get("cancel"));
575            putValue(SHORT_DESCRIPTION, tr("Cancel the upload and resume editing"));
576        }
577
578        @Override
579        public void actionPerformed(ActionEvent e) {
580            if (dialog instanceof AbstractUploadDialog) {
581                ((AbstractUploadDialog) dialog).setCanceled(true);
582                ((AbstractUploadDialog) dialog).setVisible(false);
583            }
584        }
585    }
586
587    /**
588     * Listens to window closing events and processes them as cancel events.
589     * Listens to window open events and initializes user input
590     *
591     */
592    class WindowEventHandler extends WindowAdapter {
593        @Override
594        public void windowClosing(WindowEvent e) {
595            setCanceled(true);
596        }
597
598        @Override
599        public void windowActivated(WindowEvent arg0) {
600            if (tpConfigPanels.getSelectedIndex() == 0) {
601                pnlBasicUploadSettings.initEditingOfUploadComment();
602            }
603        }
604    }
605
606    /* -------------------------------------------------------------------------- */
607    /* Interface PropertyChangeListener                                           */
608    /* -------------------------------------------------------------------------- */
609    @Override
610    public void propertyChange(PropertyChangeEvent evt) {
611        if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
612            Changeset cs = (Changeset) evt.getNewValue();
613            setChangesetTags(dataSet);
614            if (cs == null) {
615                tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
616            } else {
617                tpConfigPanels.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
618            }
619        }
620    }
621
622    /* -------------------------------------------------------------------------- */
623    /* Interface PreferenceChangedListener                                        */
624    /* -------------------------------------------------------------------------- */
625    @Override
626    public void preferenceChanged(PreferenceChangeEvent e) {
627        if (e.getKey() == null || !"osm-server.url".equals(e.getKey()))
628            return;
629        final Setting<?> newValue = e.getNewValue();
630        final String url;
631        if (newValue == null || newValue.getValue() == null) {
632            url = OsmApi.getOsmApi().getBaseUrl();
633        } else {
634            url = newValue.getValue().toString();
635        }
636        setTitle(tr("Upload to ''{0}''", url));
637    }
638
639    private static String getLastChangesetTagFromHistory(String historyKey, List<String> def) {
640        Collection<String> history = Main.pref.getCollection(historyKey, def);
641        int age = (int) (System.currentTimeMillis() / 1000 - Main.pref.getInteger(BasicUploadSettingsPanel.HISTORY_LAST_USED_KEY, 0));
642        if (history != null && age < Main.pref.getLong(BasicUploadSettingsPanel.HISTORY_MAX_AGE_KEY, TimeUnit.HOURS.toMillis(4))
643                && !history.isEmpty()) {
644            return history.iterator().next();
645        } else {
646            return null;
647        }
648    }
649
650    /**
651     * Returns the last changeset comment from history.
652     * @return the last changeset comment from history
653     */
654    public String getLastChangesetCommentFromHistory() {
655        return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.HISTORY_KEY, new ArrayList<String>());
656    }
657
658    /**
659     * Returns the last changeset source from history.
660     * @return the last changeset source from history
661     */
662    public String getLastChangesetSourceFromHistory() {
663        return getLastChangesetTagFromHistory(BasicUploadSettingsPanel.SOURCE_HISTORY_KEY, BasicUploadSettingsPanel.getDefaultSources());
664    }
665
666    @Override
667    public Map<String, String> getTags(boolean keepEmpty) {
668        return pnlTagSettings.getTags(keepEmpty);
669    }
670
671    @Override
672    public void handleMissingComment() {
673        tpConfigPanels.setSelectedIndex(0);
674        pnlBasicUploadSettings.initEditingOfUploadComment();
675    }
676
677    @Override
678    public void handleMissingSource() {
679        tpConfigPanels.setSelectedIndex(0);
680        pnlBasicUploadSettings.initEditingOfUploadSource();
681    }
682
683    @Override
684    public void handleIllegalChunkSize() {
685        tpConfigPanels.setSelectedIndex(0);
686    }
687}
Note: See TracBrowser for help on using the repository browser.