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

Last change on this file since 12687 was 12687, checked in by Don-vip, 3 months ago

see #15182 - move UploadStrategySpecification and required enums from gui.io to io

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