Changeset 18283 in josm for trunk/src/org


Ignore:
Timestamp:
2021-10-17T15:17:42+02:00 (3 years ago)
Author:
Don-vip
Message:

fix #21427 - further simplify UploadDialog (patch by marcello, modified)

  • The dialog was simplified by combining the function of two radiobuttons and one combobox into one combobox.
  • When an open changeset was selected on tab 2, existing tags on the open changeset could overwrite the data the user entered on tab 1. The user might spot this by looking closely at the tag table on tab 2, but then he may not. This non-obvious behaviour was removed.
  • The exception thrown when closing an already closed changeset was fixed.
  • More cosmetic changes to the dialog.
  • Maybe also a solution to #19319, #21387 (added revalidate()).
Location:
trunk/src/org/openstreetmap/josm
Files:
1 deleted
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/UploadAction.java

    r18173 r18283  
    3636import org.openstreetmap.josm.spi.preferences.Config;
    3737import org.openstreetmap.josm.tools.ImageProvider;
     38import org.openstreetmap.josm.tools.Logging;
    3839import org.openstreetmap.josm.tools.Shortcut;
    3940import org.openstreetmap.josm.tools.Utils;
     
    240241
    241242        final UploadDialog dialog = UploadDialog.getUploadDialog();
     243        dialog.setUploadedPrimitives(apiData);
    242244        dialog.initLifeCycle(layer.getDataSet());
    243         dialog.setUploadedPrimitives(apiData);
    244245        dialog.setVisible(true);
    245246        dialog.rememberUserInput();
     
    267268
    268269        UploadStrategySpecification uploadStrategySpecification = dialog.getUploadStrategySpecification();
     270        Logging.info("Starting upload with tags {0}", changesetTags);
     271        Logging.info(uploadStrategySpecification.toString());
     272        Logging.info(cs.toString());
    269273        dialog.clean();
    270274
  • trunk/src/org/openstreetmap/josm/data/osm/Changeset.java

    r17749 r18283  
    318318                throw new IllegalArgumentException("Changeset tag value is too long: "+value);
    319319        });
    320         this.tags = keys;
     320        this.tags = new HashMap<>(keys);
    321321    }
    322322
  • trunk/src/org/openstreetmap/josm/data/osm/ChangesetCache.java

    r18208 r18283  
    1313
    1414import org.openstreetmap.josm.data.UserIdentityManager;
     15import org.openstreetmap.josm.io.ChangesetQuery;
     16import org.openstreetmap.josm.io.OsmServerChangesetReader;
     17import org.openstreetmap.josm.io.OsmTransferException;
    1518import org.openstreetmap.josm.spi.preferences.Config;
    1619import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
    1720import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener;
     21import org.openstreetmap.josm.tools.Logging;
    1822import org.openstreetmap.josm.tools.SubclassFilteredCollection;
    1923import org.openstreetmap.josm.tools.Utils;
     
    255259    }
    256260
     261    /**
     262     * Refreshes the changesets from the server.
     263     * <p>
     264     * The server automatically closes changesets after a timeout.  We don't get notified of this
     265     * fact when it happens.  This method requests a fresh list from the server and updates the
     266     * local list.  Calling this method reduces (but does not eliminate) the probability of
     267     * attempting an upload to an already closed changeset.
     268     *
     269     * @throws OsmTransferException on server error
     270     */
     271    public void refreshChangesetsFromServer() throws OsmTransferException {
     272        OsmServerChangesetReader reader;
     273        synchronized (this) {
     274            reader = new OsmServerChangesetReader();
     275        }
     276        List<Changeset> server = reader.queryChangesets(ChangesetQuery.forCurrentUser().beingOpen(true), null);
     277        Logging.info("{0} open changesets on server", server.size());
     278
     279        DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
     280        // flag timed out changesets
     281        for (Changeset cs : getOpenChangesetsForCurrentUser()) {
     282            if (!server.contains(cs))
     283                remove(cs.getId(), e);
     284        }
     285        for (Changeset cs: server) {
     286            update(cs, e);
     287        }
     288        fireChangesetCacheEvent(e);
     289    }
     290
    257291    /* ------------------------------------------------------------------------- */
    258292    /* interface PreferenceChangedListener                                       */
  • trunk/src/org/openstreetmap/josm/gui/io/BasicUploadSettingsPanel.java

    r18221 r18283  
    103103    }
    104104
     105    protected void build() {
     106        setLayout(new GridBagLayout());
     107        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
     108        GBC gbc = GBC.eop().fill(GBC.HORIZONTAL);
     109        add(buildUploadCommentPanel(), gbc);
     110        add(buildUploadSourcePanel(), gbc);
     111        add(pnlUploadParameterSummary, gbc);
     112        if (Config.getPref().getBoolean("upload.show.review.request", true)) {
     113            add(cbRequestReview, gbc);
     114            cbRequestReview.addItemListener(this);
     115        }
     116        add(areaValidatorFeedback, gbc);
     117        add(new JPanel(), GBC.std().fill(GBC.BOTH));
     118    }
     119
    105120    protected JPanel buildUploadCommentPanel() {
    106121        JPanel pnl = new JPanel(new GridBagLayout());
     
    114129        editor.addFocusListener(this);
    115130        editor.addActionListener(this);
    116         pnl.add(hcbUploadComment, GBC.eol().fill(GBC.HORIZONTAL));
    117         pnl.add(uploadCommentFeedback, GBC.eol().insets(0, 3, 0, 0).fill(GBC.HORIZONTAL));
     131        GBC gbc = GBC.eol().insets(3).fill(GBC.HORIZONTAL);
     132        pnl.add(hcbUploadComment, gbc);
     133        pnl.add(uploadCommentFeedback, gbc);
    118134        return pnl;
    119135    }
     
    142158        obtainSource.add(obtainSourceOnce, GBC.std().anchor(GBC.WEST));
    143159        obtainSource.add(new JLabel(), GBC.eol().fill(GBC.HORIZONTAL));
    144         if (Config.getPref().getBoolean("upload.show.automatic.source", true)) {
    145             pnl.add(obtainSource, GBC.eol().insets(0, 0, 10, 3).fill(GBC.HORIZONTAL));
    146         }
    147160
    148161        hcbUploadSource.setToolTipText(tr("Enter a source"));
     
    153166        editor.addFocusListener(this);
    154167        editor.addActionListener(this);
    155         pnl.add(hcbUploadSource, GBC.eol().fill(GBC.HORIZONTAL));
    156         pnl.add(hcbUploadSourceFeedback, GBC.eol().insets(0, 3, 0, 0).fill(GBC.HORIZONTAL));
    157 
     168        GBC gbc = GBC.eol().insets(3).fill(GBC.HORIZONTAL);
     169        if (Config.getPref().getBoolean("upload.show.automatic.source", true)) {
     170            pnl.add(obtainSource, gbc);
     171        }
     172        pnl.add(hcbUploadSource, gbc);
     173        pnl.add(hcbUploadSourceFeedback, gbc);
    158174        return pnl;
    159175    }
     
    162178     * Initializes this life cycle of the panel.
    163179     *
    164      * Adds any changeset tags to the map.
     180     * Adds the comment and source tags from history, and/or obtains the source from the layer if
     181     * the user said so.
    165182     *
    166183     * @param map Map where tags are added to.
     
    179196        hcbUploadSource.getModel().prefs().load(SOURCE_HISTORY_KEY, getDefaultSources());
    180197        hcbUploadSource.discardAllUndoableEdits();
     198        hcbUploadComment.getEditorComponent().requestFocusInWindow();
     199        uploadCommentValidator.validate();
     200        uploadSourceValidator.validate();
    181201    }
    182202
     
    233253    protected List<UploadTextComponentValidator> getUploadTextValidators() {
    234254        return Arrays.asList(areaValidator, uploadCommentValidator, uploadSourceValidator);
    235     }
    236 
    237     protected void build() {
    238         setLayout(new GridBagLayout());
    239         setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
    240         GBC gbc = GBC.eol().insets(0, 0, 0, 20).fill(GBC.HORIZONTAL);
    241         add(buildUploadCommentPanel(), gbc);
    242         add(buildUploadSourcePanel(), gbc);
    243         add(pnlUploadParameterSummary, gbc);
    244         if (Config.getPref().getBoolean("upload.show.review.request", true)) {
    245             add(cbRequestReview, gbc);
    246             cbRequestReview.addItemListener(this);
    247         }
    248         add(areaValidatorFeedback, gbc);
    249         add(new JPanel(), GBC.std().fill(GBC.BOTH));
    250255    }
    251256
     
    266271        // store current value of obtaining source automatically
    267272        Config.getPref().putBoolean("upload.source.obtainautomatically", obtainSourceAutomatically.isSelected());
    268     }
    269 
    270     /**
    271      * Initializes the panel for user input
    272      */
    273     public void startUserInput() {
    274         hcbUploadComment.getEditorComponent().requestFocusInWindow();
    275         uploadCommentValidator.validate();
    276         uploadSourceValidator.validate();
    277273    }
    278274
  • trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java

    r17717 r18283  
    6363        if (cs != null) {
    6464            setIcon(icon);
    65             StringBuilder sb = new StringBuilder();
    66             String comment = cs.getComment();
    67             if (!comment.isEmpty()) {
    68                 sb.append(cs.getId()).append(" - ").append(comment);
    69             } else if (cs.get("name") != null) {
    70                 sb.append(cs.getId()).append(" - ").append(cs.get("name"));
     65            if (cs.getId() == 0) {
     66                setText("New changeset");
    7167            } else {
    72                 sb.append(tr("Changeset {0}", cs.getId()));
     68                StringBuilder sb = new StringBuilder();
     69                String comment = cs.getComment();
     70                if (!comment.isEmpty()) {
     71                    sb.append(cs.getId()).append(" - ").append(comment);
     72                } else if (cs.get("name") != null) {
     73                    sb.append(cs.getId()).append(" - ").append(cs.get("name"));
     74                } else {
     75                    sb.append(tr("Changeset {0}", cs.getId()));
     76                }
     77                setText(sb.toString());
    7378            }
    74             setText(sb.toString());
    7579            setToolTipText(buildToolTipText(cs));
    7680        } else {
    77             setText(tr("No open changesets"));
     81            setIcon(null);
     82            setText("");
    7883        }
    7984        return this;
  • trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java

    r18174 r18283  
    1212import java.awt.event.ItemListener;
    1313import java.util.Collections;
     14import java.util.Optional;
    1415
    1516import javax.swing.AbstractAction;
    1617import javax.swing.BorderFactory;
    17 import javax.swing.ButtonGroup;
    1818import javax.swing.JButton;
    1919import javax.swing.JCheckBox;
    2020import javax.swing.JPanel;
    21 import javax.swing.JRadioButton;
    22 import javax.swing.event.ListDataEvent;
    23 import javax.swing.event.ListDataListener;
     21import javax.swing.SwingUtilities;
    2422
    2523import org.openstreetmap.josm.data.osm.Changeset;
    2624import org.openstreetmap.josm.data.osm.ChangesetCache;
     25import org.openstreetmap.josm.data.osm.ChangesetCacheEvent;
     26import org.openstreetmap.josm.data.osm.ChangesetCacheListener;
    2727import org.openstreetmap.josm.gui.MainApplication;
    2828import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
    2929import org.openstreetmap.josm.gui.widgets.JosmComboBox;
     30import org.openstreetmap.josm.gui.widgets.JosmComboBoxModel;
     31import org.openstreetmap.josm.io.OsmTransferException;
    3032import org.openstreetmap.josm.spi.preferences.Config;
    3133import org.openstreetmap.josm.tools.ImageProvider;
     
    4547 * </ul>
    4648 */
    47 public class ChangesetManagementPanel extends JPanel implements ListDataListener {
     49public class ChangesetManagementPanel extends JPanel implements ItemListener, ChangesetCacheListener {
    4850    static final String SELECTED_CHANGESET_PROP = ChangesetManagementPanel.class.getName() + ".selectedChangeset";
    4951    static final String CLOSE_CHANGESET_AFTER_UPLOAD = ChangesetManagementPanel.class.getName() + ".closeChangesetAfterUpload";
    5052
    51     private JRadioButton rbUseNew;
    52     private JRadioButton rbExisting;
    5353    private JosmComboBox<Changeset> cbOpenChangesets;
     54    private JosmComboBoxModel<Changeset> model;
    5455    private JCheckBox cbCloseAfterUpload;
    55     private OpenChangesetComboBoxModel model;
    56 
    57     /** the changeset comment model */
    58     private final transient UploadDialogModel uploadDialogModel;
     56    private JButton btnClose;
    5957
    6058    /**
    6159     * Constructs a new {@code ChangesetManagementPanel}.
    6260     *
    63      * @param uploadDialogModel The tag editor model.
    64      *
    65      * @since 18173 (signature)
    66      */
    67     public ChangesetManagementPanel(UploadDialogModel uploadDialogModel) {
    68         this.uploadDialogModel = uploadDialogModel;
     61     * @since 18283 (signature)
     62     */
     63    public ChangesetManagementPanel() {
    6964        build();
    70         refreshGUI();
     65    }
     66
     67    /**
     68     * Initializes this life cycle of the panel.
     69     *
     70     * @since 18283
     71     */
     72    public void initLifeCycle() {
     73        refreshChangesets();
     74    }
     75
     76    /**
     77     * Returns the model in use.
     78     * @return the model
     79     */
     80    public JosmComboBoxModel<Changeset> getModel() {
     81        return model;
    7182    }
    7283
     
    7687    protected void build() {
    7788        setLayout(new GridBagLayout());
     89        setBorder(BorderFactory.createTitledBorder(tr("Please select a changeset:")));
     90
    7891        GridBagConstraints gc = new GridBagConstraints();
    79         setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
    80 
    81         ButtonGroup bgUseNewOrExisting = new ButtonGroup();
    82 
    83         gc.gridwidth = 4;
    8492        gc.gridx = 0;
    8593        gc.gridy = 0;
    86         gc.fill = GridBagConstraints.HORIZONTAL;
    8794        gc.weightx = 1.0;
    8895        gc.weighty = 0.0;
    89         gc.insets = new Insets(0, 0, 5, 0);
    90         add(new JMultilineLabel(
    91                 tr("Please decide what changeset the data is uploaded to and whether to close the changeset after the next upload.")), gc);
    92 
    93         gc.gridwidth = 4;
    94         gc.gridy = 1;
    9596        gc.fill = GridBagConstraints.HORIZONTAL;
    96         gc.weightx = 1.0;
    97         gc.weighty = 0.0;
    98         gc.insets = new Insets(0, 0, 0, 0);
    99         gc.anchor = GridBagConstraints.FIRST_LINE_START;
    100         rbUseNew = new JRadioButton(tr("Upload to a new changeset"));
    101         rbUseNew.setToolTipText(tr("Open a new changeset and use it in the next upload"));
    102         bgUseNewOrExisting.add(rbUseNew);
    103         add(rbUseNew, gc);
    104 
    105         gc.gridx = 0;
    106         gc.gridy = 2;
     97        gc.anchor = GridBagConstraints.NORTHWEST;
     98        gc.insets = new Insets(3, 3, 3, 3);
     99
     100        gc.gridwidth = 3;
     101        add(new JMultilineLabel(tr(
     102            "Please select which changeset the data shall be uploaded to and whether to close that changeset after the next upload."
     103            )), gc);
     104
    107105        gc.gridwidth = 1;
    108         gc.weightx = 0.0;
    109         gc.fill = GridBagConstraints.HORIZONTAL;
    110         rbExisting = new JRadioButton(tr("Upload to an existing changeset"));
    111         rbExisting.setToolTipText(tr("Upload data to an already existing and open changeset"));
    112         bgUseNewOrExisting.add(rbExisting);
    113         add(rbExisting, gc);
    114 
    115         gc.gridx = 1;
    116         gc.gridy = 2;
    117         gc.gridwidth = 1;
    118         gc.weightx = 1.0;
    119         model = new OpenChangesetComboBoxModel();
    120         ChangesetCache.getInstance().addChangesetCacheListener(model);
     106        gc.gridy++;
     107        model = new JosmComboBoxModel<>();
    121108        cbOpenChangesets = new JosmComboBox<>(model);
    122         cbOpenChangesets.setToolTipText(tr("Select an open changeset"));
     109        cbOpenChangesets.setToolTipText(tr("Select a changeset"));
    123110        cbOpenChangesets.setRenderer(new ChangesetCellRenderer());
    124         cbOpenChangesets.addItemListener(new ChangesetListItemStateListener());
    125111        Dimension d = cbOpenChangesets.getPreferredSize();
    126112        d.width = 200;
     
    128114        d.width = 100;
    129115        cbOpenChangesets.setMinimumSize(d);
    130         model.addListDataListener(this);
    131116        add(cbOpenChangesets, gc);
    132 
    133         gc.gridx = 2;
    134         gc.gridy = 2;
    135         gc.weightx = 0.0;
    136         gc.gridwidth = 1;
     117        int h = cbOpenChangesets.getPreferredSize().height;
     118        Dimension prefSize = new Dimension(h, h);
     119
     120        gc.gridx++;
    137121        gc.weightx = 0.0;
    138122        JButton btnRefresh = new JButton(new RefreshAction());
    139         btnRefresh.setMargin(new Insets(0, 0, 0, 0));
     123        btnRefresh.setPreferredSize(prefSize);
     124        btnRefresh.setMinimumSize(prefSize);
    140125        add(btnRefresh, gc);
    141126
    142         gc.gridx = 3;
    143         gc.gridy = 2;
    144         gc.gridwidth = 1;
     127        gc.gridx++;
    145128        CloseChangesetAction closeChangesetAction = new CloseChangesetAction();
    146         JButton btnClose = new JButton(closeChangesetAction);
    147         btnClose.setMargin(new Insets(0, 0, 0, 0));
    148         cbOpenChangesets.addItemListener(closeChangesetAction);
    149         rbExisting.addItemListener(closeChangesetAction);
     129        btnClose = new JButton(closeChangesetAction);
     130        btnClose.setPreferredSize(prefSize);
     131        btnClose.setMinimumSize(prefSize);
    150132        add(btnClose, gc);
    151133
     134        gc.gridy++;
    152135        gc.gridx = 0;
    153         gc.gridy = 3;
    154         gc.gridwidth = 4;
     136        gc.gridwidth = 3;
    155137        gc.weightx = 1.0;
    156138        cbCloseAfterUpload = new JCheckBox(tr("Close changeset after upload"));
    157139        cbCloseAfterUpload.setToolTipText(tr("Select to close the changeset after the next upload"));
    158140        add(cbCloseAfterUpload, gc);
     141
     142        cbOpenChangesets.addItemListener(this);
     143        cbOpenChangesets.addItemListener(closeChangesetAction);
     144
    159145        cbCloseAfterUpload.setSelected(Config.getPref().getBoolean("upload.changeset.close", true));
    160146        cbCloseAfterUpload.addItemListener(new CloseAfterUploadItemStateListener());
    161147
    162         rbUseNew.getModel().addItemListener(new RadioButtonHandler());
    163         rbExisting.getModel().addItemListener(new RadioButtonHandler());
    164     }
    165 
    166     protected void refreshGUI() {
    167         rbExisting.setEnabled(model.getSize() > 0);
    168         if (model.getSize() == 0 && !rbUseNew.isSelected()) {
    169             rbUseNew.setSelected(true);
    170         }
    171         cbOpenChangesets.setEnabled(model.getSize() > 0 && rbExisting.isSelected());
     148        ChangesetCache.getInstance().addChangesetCacheListener(this);
    172149    }
    173150
    174151    /**
    175152     * Sets the changeset to be used in the next upload
     153     * <p>
     154     * Note: The changeset may be a new changeset that was automatically opened because the old
     155     * changeset overflowed.  In that case it was already added to the changeset cache and the
     156     * combobox.
    176157     *
    177158     * @param cs the changeset
     159     * @see UploadPrimitivesTask#handleChangesetFullResponse
    178160     */
    179161    public void setSelectedChangesetForNextUpload(Changeset cs) {
    180         int idx = model.getIndexOf(cs);
    181         if (idx >= 0) {
    182             rbExisting.setSelected(true);
    183             model.setSelectedItem(cs);
    184         }
    185     }
    186 
    187     /**
    188      * Replies the currently selected changeset. null, if no changeset is
    189      * selected or if the user has chosen to use a new changeset.
    190      *
    191      * @return the currently selected changeset. null, if no changeset is
    192      * selected.
     162        model.setSelectedItem(cs);
     163    }
     164
     165    /**
     166     * Returns the currently selected changeset or an empty new one.
     167     *
     168     * @return the currently selected changeset
    193169     */
    194170    public Changeset getSelectedChangeset() {
    195         if (rbUseNew.isSelected())
    196             return null;
    197         return (Changeset) cbOpenChangesets.getSelectedItem();
     171        return Optional.ofNullable((Changeset) model.getSelectedItem()).orElse(new Changeset());
    198172    }
    199173
     
    206180    }
    207181
    208     /* ---------------------------------------------------------------------------- */
    209     /* Interface ListDataListener                                                   */
    210     /* ---------------------------------------------------------------------------- */
     182    /**
     183     * Listens to changes in the selected changeset and fires property change events.
     184    */
    211185    @Override
    212     public void contentsChanged(ListDataEvent e) {
    213         refreshGUI();
    214     }
    215 
    216     @Override
    217     public void intervalAdded(ListDataEvent e) {
    218         refreshGUI();
    219     }
    220 
    221     @Override
    222     public void intervalRemoved(ListDataEvent e) {
    223         refreshGUI();
    224     }
    225 
    226     /**
    227      * Listens to changes in the selected changeset and fires property change events.
    228      */
    229     class ChangesetListItemStateListener implements ItemListener {
    230         @Override
    231         public void itemStateChanged(ItemEvent e) {
    232             Changeset cs = (Changeset) cbOpenChangesets.getSelectedItem();
    233             if (cs == null) return;
    234             if (rbExisting.isSelected()) {
    235                 firePropertyChange(SELECTED_CHANGESET_PROP, null, cs);
    236             }
    237         }
     186    public void itemStateChanged(ItemEvent e) {
     187        firePropertyChange(SELECTED_CHANGESET_PROP, null, model.getSelectedItem());
    238188    }
    239189
     
    261211
    262212    /**
    263      * Listens to changes in the two radio buttons rbUseNew and rbUseExisting.
    264      */
    265     class RadioButtonHandler implements ItemListener {
    266         @Override
    267         public void itemStateChanged(ItemEvent e) {
    268             if (rbUseNew.isSelected()) {
    269                 cbOpenChangesets.setEnabled(false);
    270                 firePropertyChange(SELECTED_CHANGESET_PROP, null, null);
    271             } else if (rbExisting.isSelected()) {
    272                 cbOpenChangesets.setEnabled(true);
    273                 if (cbOpenChangesets.getSelectedItem() == null) {
    274                     model.selectFirstChangeset();
    275                 }
    276                 Changeset cs = (Changeset) cbOpenChangesets.getSelectedItem();
    277                 if (cs == null) return;
    278                 uploadDialogModel.putAll(cs.getKeys());
    279                 firePropertyChange(SELECTED_CHANGESET_PROP, null, cs);
    280             }
    281         }
    282     }
    283 
    284     /**
    285213     * Refreshes the list of open changesets
    286214     */
     
    315243
    316244        protected void refreshEnabledState() {
    317             setEnabled(
    318                     cbOpenChangesets.getModel().getSize() > 0
    319                     && cbOpenChangesets.getSelectedItem() != null
    320                     && rbExisting.isSelected()
    321             );
     245            setEnabled(!getSelectedChangeset().isNew());
    322246        }
    323247
     
    327251        }
    328252    }
     253
     254    /**
     255     * Refreshes the changesets combobox form the server.
     256     * <p>
     257     * Note: This calls into {@link #refreshCombo} through {@link #changesetCacheUpdated}
     258     *
     259     * @see ChangesetCache#refreshChangesetsFromServer
     260     */
     261    protected void refreshChangesets() {
     262        try {
     263            ChangesetCache.getInstance().refreshChangesetsFromServer();
     264        } catch (OsmTransferException e) {
     265            return;
     266        }
     267    }
     268
     269    private void refreshCombo() {
     270        Changeset selected = (Changeset) cbOpenChangesets.getSelectedItem();
     271        model.removeAllElements();
     272        model.addElement(new Changeset());
     273        model.addAllElements(ChangesetCache.getInstance().getOpenChangesetsForCurrentUser());
     274        cbOpenChangesets.setSelectedItem(selected != null && model.getIndexOf(selected) != -1 ? selected : model.getElementAt(0));
     275    }
     276
     277    @Override
     278    public void changesetCacheUpdated(ChangesetCacheEvent event) {
     279        // This listener might have been called by a background task.
     280        SwingUtilities.invokeLater(() -> refreshCombo());
     281    }
    329282}
  • trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java

    r18173 r18283  
    1010import java.awt.Dimension;
    1111import java.awt.FlowLayout;
     12import java.awt.GridBagConstraints;
    1213import java.awt.GridBagLayout;
    1314import java.awt.event.ActionEvent;
     
    2425import java.util.Map;
    2526import java.util.Map.Entry;
    26 import java.util.Optional;
    2727import java.util.stream.Collectors;
    2828
     
    3434import javax.swing.JSplitPane;
    3535import javax.swing.JTabbedPane;
    36 import javax.swing.border.TitledBorder;
    3736
    3837import org.openstreetmap.josm.data.APIDataSet;
     
    5857import org.openstreetmap.josm.tools.ImageProvider;
    5958import org.openstreetmap.josm.tools.InputMapUtils;
    60 import org.openstreetmap.josm.tools.Logging;
    6159import org.openstreetmap.josm.tools.Utils;
    6260
     
    6664 * @since 2025
    6765 */
    68 public class UploadDialog extends AbstractUploadDialog implements PropertyChangeListener, PreferenceChangedListener {
     66public class UploadDialog extends AbstractUploadDialog implements PreferenceChangedListener, PropertyChangeListener {
    6967    /** the unique instance of the upload dialog */
    7068    private static UploadDialog uploadDialog;
     
    7270    /** the panel with the objects to upload */
    7371    private UploadedObjectsSummaryPanel pnlUploadedObjects;
     72
     73    /** the "description" tab */
     74    private BasicUploadSettingsPanel pnlBasicUploadSettings;
     75
    7476    /** the panel to select the changeset used */
    7577    private ChangesetManagementPanel pnlChangesetManagement;
    76 
    77     private BasicUploadSettingsPanel pnlBasicUploadSettings;
    78 
     78    /** the panel to select the upload strategy */
    7979    private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel;
    8080
    81     private TitledBorder tagSettingsBorder;
    82     /** a border around the tag editor panel */
    83     private JPanel pnlTagEditorBorder;
    8481    /** the tag editor panel */
    8582    private TagEditorPanel pnlTagEditor;
     
    9794     * Constructs a new {@code UploadDialog}.
    9895     */
    99     public UploadDialog() {
     96    protected UploadDialog() {
    10097        super(GuiHelper.getFrameForComponent(MainApplication.getMainFrame()), ModalityType.DOCUMENT_MODAL);
    10198        build();
     
    139136
    140137        JPanel pnlSettings = new JPanel(new GridBagLayout());
    141         pnlTagEditorBorder = new JPanel(new BorderLayout());
    142         tagSettingsBorder = BorderFactory.createTitledBorder(tr("Tags of new changeset"));
    143         pnlTagEditorBorder.setBorder(tagSettingsBorder);
     138        pnlSettings.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
     139        JPanel pnlTagEditorBorder = new JPanel(new BorderLayout());
     140        pnlTagEditorBorder.setBorder(BorderFactory.createTitledBorder(tr("Changeset tags:")));
    144141        pnlTagEditor = new TagEditorPanel(model, null, Changeset.MAX_CHANGESET_TAG_LENGTH);
    145142        pnlTagEditorBorder.add(pnlTagEditor, BorderLayout.CENTER);
    146143
    147         pnlChangesetManagement = new ChangesetManagementPanel(model);
     144        pnlChangesetManagement = new ChangesetManagementPanel();
    148145        pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel();
    149         pnlSettings.add(pnlChangesetManagement, GBC.eop().fill(GBC.HORIZONTAL));
    150         pnlSettings.add(pnlUploadStrategySelectionPanel, GBC.eop().fill(GBC.HORIZONTAL));
    151         pnlSettings.add(pnlTagEditorBorder, GBC.eol().fill(GBC.BOTH));
     146        pnlSettings.add(pnlChangesetManagement, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
     147        pnlSettings.add(pnlUploadStrategySelectionPanel, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
     148        pnlSettings.add(pnlTagEditorBorder, GBC.eol().fill(GridBagConstraints.BOTH));
    152149
    153150        tpConfigPanels.add(pnlSettings);
     
    197194        addWindowListener(new WindowEventHandler());
    198195
    199         // make sure the configuration panels listen to each other changes
     196        // make sure the configuration panels listen to each others changes
    200197        //
     198        UploadParameterSummaryPanel sp = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
     199        // the summary panel must know everything
     200        pnlChangesetManagement.addPropertyChangeListener(sp);
     201        pnlUploadedObjects.addPropertyChangeListener(sp);
     202        pnlUploadStrategySelectionPanel.addPropertyChangeListener(sp);
     203
     204        // update tags from selected changeset
    201205        pnlChangesetManagement.addPropertyChangeListener(this);
    202         pnlChangesetManagement.addPropertyChangeListener(
    203                 pnlBasicUploadSettings.getUploadParameterSummaryPanel()
    204         );
    205         pnlUploadedObjects.addPropertyChangeListener(pnlUploadStrategySelectionPanel);
    206         pnlUploadedObjects.addPropertyChangeListener(
    207                 pnlBasicUploadSettings.getUploadParameterSummaryPanel()
    208         );
    209         pnlUploadStrategySelectionPanel.addPropertyChangeListener(this);
    210         pnlUploadStrategySelectionPanel.addPropertyChangeListener(
    211                 pnlBasicUploadSettings.getUploadParameterSummaryPanel()
    212         );
    213206
    214207        // users can click on either of two links in the upload parameter
     
    242235        this.dataSet = dataSet;
    243236        pnlBasicUploadSettings.initLifeCycle(map);
     237        pnlChangesetManagement.initLifeCycle();
    244238        model.clear();
    245         model.putAll(map);
    246         model.putAll(this.dataSet);
     239        model.putAll(map);          // init with tags from history
     240        model.putAll(this.dataSet); // overwrite with tags from the dataset
     241
     242        tpConfigPanels.setSelectedIndex(0);
     243        pnlTagEditor.initAutoCompletion(MainApplication.getLayerManager().getEditLayer());
     244        pnlUploadStrategySelectionPanel.initFromPreferences();
     245
     246        // update the summary
     247        UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
     248        sumPnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
     249        sumPnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
    247250    }
    248251
     
    255258     */
    256259    public void setUploadedPrimitives(APIDataSet toUpload) {
     260        UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
    257261        if (toUpload == null) {
    258262            if (pnlUploadedObjects != null) {
    259263                List<OsmPrimitive> emptyList = Collections.emptyList();
    260264                pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);
     265                sumPnl.setNumObjects(0);
    261266            }
    262267            return;
    263268        }
    264         pnlBasicUploadSettings.setUploadedPrimitives(toUpload.getPrimitives());
     269        List<OsmPrimitive> l = toUpload.getPrimitives();
     270        pnlBasicUploadSettings.setUploadedPrimitives(l);
    265271        pnlUploadedObjects.setUploadedPrimitives(
    266272                toUpload.getPrimitivesToAdd(),
     
    268274                toUpload.getPrimitivesToDelete()
    269275        );
     276        sumPnl.setNumObjects(l.size());
     277        pnlUploadStrategySelectionPanel.setNumUploadedObjects(l.size());
    270278    }
    271279
     
    285293
    286294    /**
    287      * Initializes the panel for user input
    288      */
    289     public void startUserInput() {
    290         tpConfigPanels.setSelectedIndex(0);
    291         pnlBasicUploadSettings.startUserInput();
    292         pnlTagEditor.initAutoCompletion(MainApplication.getLayerManager().getEditLayer());
    293         pnlUploadStrategySelectionPanel.initFromPreferences();
    294         UploadParameterSummaryPanel pnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
    295         pnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
    296         pnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
    297         pnl.setNumObjects(pnlUploadedObjects.getNumObjectsToUpload());
    298     }
    299 
    300     /**
    301      * Replies the current changeset
    302      *
    303      * @return the current changeset
     295     * Returns the changeset to use complete with tags
     296     *
     297     * @return the changeset to use
    304298     */
    305299    public Changeset getChangeset() {
    306         Changeset cs = Optional.ofNullable(pnlChangesetManagement.getSelectedChangeset()).orElseGet(Changeset::new);
    307         cs.setKeys(model.getTags(false));
     300        Changeset cs = pnlChangesetManagement.getSelectedChangeset();
     301        cs.setKeys(getTags(true));
    308302        return cs;
    309303    }
     
    337331    @Override
    338332    public String getUploadComment() {
    339         return model.getValue("comment");
     333        return model.getValue(UploadDialogModel.COMMENT);
    340334    }
    341335
    342336    @Override
    343337    public String getUploadSource() {
    344         return model.getValue("source");
     338        return model.getValue(UploadDialogModel.SOURCE);
    345339    }
    346340
     
    355349                    )
    356350            ).applySafe(this);
    357             startUserInput();
    358351        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
    359352            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
     
    437430        public void actionPerformed(ActionEvent e) {
    438431            Map<String, String> tags = dialog.getTags(true);
    439             Logging.info("Starting upload with tags {0}", tags);
    440 
    441             /* test for empty tags in the changeset metadata and proceed only after user's confirmation.
    442              * though, accept if key and value are empty (cf. xor). */
     432
     433            // If there are empty tags in the changeset proceed only after user's confirmation.
    443434            List<String> emptyChangesetTags = new ArrayList<>();
    444435            for (final Entry<String, String> i : tags.entrySet()) {
    445436                final boolean isKeyEmpty = Utils.isStripEmpty(i.getKey());
    446437                final boolean isValueEmpty = Utils.isStripEmpty(i.getValue());
    447                 final boolean ignoreKey = "comment".equals(i.getKey()) || "source".equals(i.getKey());
    448                 if ((isKeyEmpty ^ isValueEmpty) && !ignoreKey) {
     438                final boolean ignoreKey = UploadDialogModel.isCommentOrSource(i.getKey());
     439                if ((isKeyEmpty || isValueEmpty) && !ignoreKey) {
    449440                    emptyChangesetTags.add(tr("{0}={1}", i.getKey(), i.getValue()));
    450441                }
     
    528519    public void propertyChange(PropertyChangeEvent evt) {
    529520        if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
     521            // put the tags from the newly selected changeset into the model
    530522            Changeset cs = (Changeset) evt.getNewValue();
    531             if (cs == null) {
    532                 tagSettingsBorder.setTitle(tr("Tags of new changeset"));
    533             } else {
    534                 tagSettingsBorder.setTitle(tr("Tags of changeset {0}", cs.getId()));
     523            if (cs != null) {
     524                for (Map.Entry<String, String> entry : cs.getKeys().entrySet()) {
     525                    String key = entry.getKey();
     526                    // do NOT overwrite comment and source when selecting a changeset, it is confusing
     527                    if (!UploadDialogModel.isCommentOrSource(key))
     528                        model.put(key, entry.getValue());
     529                }
    535530            }
    536531        }
  • trunk/src/org/openstreetmap/josm/gui/io/UploadDialogModel.java

    r18205 r18283  
    2424    /** the "created_by" changeset OSM key */
    2525    private static final String CREATED_BY = "created_by";
     26    /** the "comment" changeset OSM key */
     27    public static final String COMMENT = "comment";
     28    /** the "source" changeset OSM key */
     29    public static final String SOURCE = "source";
    2630    /** the user-agent */
    2731    private final String agent = Version.getInstance().getAgentString(false);
     
    3943                // add "hashtags" if any
    4044                if (hashtags) {
    41                     put("hashtags", findHashTags(getValue("comment")));
     45                    put("hashtags", findHashTags(getValue(COMMENT)));
    4246                }
    4347                // add/update "created_by"
     
    116120        if (!l.isEmpty()) {
    117121            if (value != null)
    118                 l.get(0).setValue(value);
     122                for (TagModel tm : l) {
     123                    tm.setValue(value);
     124                }
    119125            else
    120                 tags.remove(l.get(0));
     126                tags.removeIf(tm -> tm.getName().equals(key));
    121127        } else if (value != null) {
    122128            tags.add(new TagModel(key, value));
     
    163169        if (dataSet != null) {
    164170            putAll(dataSet.getChangeSetTags());
    165             put("comment", addHashTagsFromDataSet(getValue("comment"), dataSet));
     171            put(COMMENT, addHashTagsFromDataSet(getValue(COMMENT), dataSet));
    166172        }
    167173    }
     174
     175    /**
     176     * Determines if the key is "comment" or "source".
     177     * @param key changeset key
     178     * @return {@code true} if the key is "comment" or "source"
     179     * @since 18283
     180     */
     181    public static boolean isCommentOrSource(String key) {
     182        return COMMENT.equals(key) || SOURCE.equals(key);
     183    }
    168184}
  • trunk/src/org/openstreetmap/josm/gui/io/UploadParameterSummaryPanel.java

    r18211 r18283  
    5454            return tr("Objects are uploaded to a <strong>new changeset</strong>.");
    5555        } else {
    56             return tr("Objects are uploaded to the <strong>open changeset</strong> {0} with upload comment ''{1}''.",
     56            return tr("Objects are uploaded to the <strong>open changeset</strong> {0} ''{1}''.",
    5757                    selectedChangeset.getId(),
    5858                    selectedChangeset.getComment()
     
    200200            closeChangesetAfterNextUpload = (Boolean) evt.getNewValue();
    201201            updateSummary();
    202         } else if (evt.getPropertyName().equals(UploadedObjectsSummaryPanel.NUM_OBJECTS_TO_UPLOAD_PROP)) {
    203             numObjects = (Integer) evt.getNewValue();
    204             updateSummary();
    205202        } else if (evt.getPropertyName().equals(UploadStrategySelectionPanel.UPLOAD_STRATEGY_SPECIFICATION_PROP)) {
    206203            this.spec = (UploadStrategySpecification) evt.getNewValue();
  • trunk/src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java

    r16807 r18283  
    8383    }
    8484
    85     protected MaxChangesetSizeExceededPolicy askMaxChangesetSizeExceedsPolicy() {
     85    /**
     86     * Prompt the user about how to proceed.
     87     *
     88     * @return the policy selected by the user
     89     */
     90    protected MaxChangesetSizeExceededPolicy promptUserForPolicy() {
    8691        ButtonSpec[] specs = {
    8792                new ButtonSpec(
     
    146151
    147152    /**
    148      * Opens a new changeset.
    149      */
    150     protected void openNewChangeset() {
    151         // make sure the current changeset is removed from the upload dialog.
    152         ChangesetCache.getInstance().update(changeset);
    153         Changeset newChangeSet = new Changeset();
    154         newChangeSet.setKeys(this.changeset.getKeys());
    155         this.changeset = newChangeSet;
    156     }
    157 
    158     protected boolean recoverFromChangesetFullException() throws OsmTransferException {
    159         if (toUpload.getSize() - processedPrimitives.size() == 0) {
     153     * Handles a server changeset full response.
     154     * <p>
     155     * Handles a server changeset full response by either aborting or opening a new changeset, if the
     156     * user requested it so.
     157     *
     158     * @return true if the upload process should continue with the new changeset, false if the
     159     *         upload should be interrupted
     160     * @throws OsmTransferException "if something goes wrong."
     161     */
     162    protected boolean handleChangesetFullResponse() throws OsmTransferException {
     163        if (processedPrimitives.size() == toUpload.getSize()) {
    160164            strategy.setPolicy(MaxChangesetSizeExceededPolicy.ABORT);
    161165            return false;
    162166        }
    163167        if (strategy.getPolicy() == null || strategy.getPolicy() == MaxChangesetSizeExceededPolicy.ABORT) {
    164             strategy.setPolicy(askMaxChangesetSizeExceedsPolicy());
     168            strategy.setPolicy(promptUserForPolicy());
    165169        }
    166170        switch(strategy.getPolicy()) {
    167171        case AUTOMATICALLY_OPEN_NEW_CHANGESETS:
    168             // prepare the state of the task for a next iteration in uploading.
    169             closeChangesetIfRequired();
    170             openNewChangeset();
     172            Changeset newChangeSet = new Changeset();
     173            newChangeSet.setKeys(changeset.getKeys());
     174            closeChangeset();
     175            this.changeset = newChangeSet;
    171176            toUpload.removeProcessed(processedPrimitives);
    172177            return true;
     
    260265                    }
    261266                    writer.uploadOsm(strategy, toUpload.getPrimitives(), changeset, getProgressMonitor().createSubTaskMonitor(1, false));
    262 
     267                    // If the changeset was new, now it is open.
     268                    ChangesetCache.getInstance().update(changeset);
    263269                    // if we get here we've successfully uploaded the data. Exit the loop.
    264270                    break;
     
    277283                    case UPLOAD_DATA:
    278284                        // Most likely the changeset is full. Try to recover and continue
    279                         // with a new changeset, but let the user decide first (see
    280                         // recoverFromChangesetFullException)
    281                         if (recoverFromChangesetFullException()) {
     285                        // with a new changeset, but let the user decide first.
     286                        if (handleChangesetFullResponse()) {
    282287                            continue;
    283288                        }
    284289                        lastException = e;
    285290                        break uploadloop;
     291                    case UPDATE_CHANGESET:
     292                    case CLOSE_CHANGESET:
    286293                    case UNSPECIFIED:
    287                     case UPDATE_CHANGESET:
    288294                    default:
    289295                        // The changeset was closed when we tried to update it. Probably, our
     
    292298                        // Rethrow exception - this will be handled later.
    293299                        changeset.setOpen(false);
     300                        ChangesetCache.getInstance().update(changeset);
    294301                        throw e;
    295302                    }
     
    320327    }
    321328
     329    /**
     330     * Closes the changeset on the server and locally.
     331     *
     332     * @throws OsmTransferException "if something goes wrong."
     333     */
     334    private void closeChangeset() throws OsmTransferException {
     335        if (changeset != null && !changeset.isNew() && changeset.isOpen()) {
     336            try {
     337                OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0, false));
     338            } catch (ChangesetClosedException e) {
     339                // Do not raise a stink, probably the changeset timed out.
     340                Logging.trace(e);
     341            } finally {
     342                changeset.setOpen(false);
     343                ChangesetCache.getInstance().update(changeset);
     344            }
     345        }
     346    }
     347
    322348    private void closeChangesetIfRequired() throws OsmTransferException {
    323         if (strategy.isCloseChangesetAfterUpload() && changeset != null && !changeset.isNew() && changeset.isOpen()) {
    324             OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0, false));
    325         }
    326     }
    327 
    328     @Override protected void finish() {
    329 
    330         // depending on the success of the upload operation and on the policy for
    331         // multi changeset uploads this will sent the user back to the appropriate
    332         // place in JOSM, either
    333         // - to an error dialog
    334         // - to the Upload Dialog
    335         // - to map editing
     349        if (strategy.isCloseChangesetAfterUpload()) {
     350            closeChangeset();
     351        }
     352    }
     353
     354    /**
     355     * Depending on the success of the upload operation and on the policy for
     356     * multi changeset uploads this will send the user back to the appropriate
     357     * place in JOSM, either:
     358     * <ul>
     359     * <li>to an error dialog,
     360     * <li>to the Upload Dialog, or
     361     * <li>to map editing.
     362     * </ul>
     363     */
     364    @Override
     365    protected void finish() {
    336366        GuiHelper.runInEDT(() -> {
    337367            // if the changeset is still open after this upload we want it to be selected on the next upload
  • trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java

    r17709 r18283  
    1111import java.awt.event.ActionEvent;
    1212import java.awt.event.ActionListener;
    13 import java.awt.event.FocusAdapter;
    1413import java.awt.event.FocusEvent;
     14import java.awt.event.FocusListener;
    1515import java.awt.event.ItemEvent;
    1616import java.awt.event.ItemListener;
    17 import java.beans.PropertyChangeEvent;
    18 import java.beans.PropertyChangeListener;
    1917import java.util.EnumMap;
    2018import java.util.Map;
     
    4543 * {@link #UPLOAD_STRATEGY_SPECIFICATION_PROP}.
    4644 */
    47 public class UploadStrategySelectionPanel extends JPanel implements PropertyChangeListener {
     45public class UploadStrategySelectionPanel extends JPanel {
    4846
    4947    /**
     
    5755    private final JosmTextField tfChunkSize = new JosmTextField(4);
    5856    private final JPanel pnlMultiChangesetPolicyPanel = new JPanel(new GridBagLayout());
    59     private final JRadioButton rbFillOneChangeset = new JRadioButton(
    60             tr("Fill up one changeset and return to the Upload Dialog"));
    61     private final JRadioButton rbUseMultipleChangesets = new JRadioButton(
    62             tr("Open and use as many new changesets as necessary"));
     57    private final JRadioButton rbFillOneChangeset = new JRadioButton();
     58    private final JRadioButton rbUseMultipleChangesets = new JRadioButton();
    6359    private JMultilineLabel lblMultiChangesetPoliciesHeader;
    6460
     
    9288        gc.gridwidth = 1;
    9389        gc.fill = GridBagConstraints.HORIZONTAL;
    94         gc.insets = new Insets(0, 0, 3, 0);
     90        gc.insets = new Insets(3, 3, 3, 3);
    9591        gc.anchor = GridBagConstraints.FIRST_LINE_START;
    9692        JRadioButton radioButton = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
    97         radioButton.setText(tr("Upload data in one request"));
    98         pnl.add(radioButton, gc);
    99         gc.gridx = 3;
    100         gc.gridy = 1;
    101         gc.weightx = 1.0;
    102         gc.weighty = 0.0;
    103         gc.gridwidth = 1;
    104         pnl.add(lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
    105 
    106         // -- chunked dataset strategy
    107         gc.gridx = 0;
    108         gc.gridy = 2;
    109         gc.weightx = 0.0;
    110         gc.weighty = 0.0;
    111         radioButton = rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY);
    112         radioButton.setText(tr("Upload data in chunks of objects. Chunk size: "));
     93        radioButton.setText(tr("Upload all objects in one request"));
    11394        pnl.add(radioButton, gc);
    11495        gc.gridx = 2;
    115         gc.gridy = 2;
     96        gc.weightx = 1.0;
     97        pnl.add(lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
     98
     99        // -- chunked dataset strategy
     100        gc.gridy++;
     101        gc.gridx = 0;
    116102        gc.weightx = 0.0;
    117         gc.weighty = 0.0;
    118         gc.gridwidth = 1;
     103        radioButton = rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY);
     104        radioButton.setText(tr("Upload objects in chunks of size: "));
     105        pnl.add(radioButton, gc);
     106        gc.gridx = 1;
    119107        pnl.add(tfChunkSize, gc);
    120         gc.gridx = 3;
    121         gc.gridy = 2;
    122         gc.weightx = 0.0;
    123         gc.weighty = 0.0;
    124         gc.gridwidth = 1;
     108        gc.gridx = 2;
    125109        pnl.add(lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
    126110
    127111        // -- single request strategy
     112        gc.gridy++;
    128113        gc.gridx = 0;
    129         gc.gridy = 3;
    130         gc.weightx = 0.0;
    131         gc.weighty = 0.0;
    132114        radioButton = rbStrategy.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY);
    133115        radioButton.setText(tr("Upload each object individually"));
    134116        pnl.add(radioButton, gc);
    135         gc.gridx = 3;
    136         gc.gridy = 3;
    137         gc.weightx = 0.0;
    138         gc.weighty = 0.0;
    139         gc.gridwidth = 1;
     117        gc.gridx = 2;
    140118        pnl.add(lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
    141119
    142         tfChunkSize.addFocusListener(new TextFieldFocusHandler());
    143120        new ChunkSizeValidator(tfChunkSize);
    144121
     
    159136        gc.fill = GridBagConstraints.HORIZONTAL;
    160137        gc.anchor = GridBagConstraints.FIRST_LINE_START;
     138        gc.insets = new Insets(3, 3, 3, 3);
    161139        gc.weightx = 1.0;
    162140        lblMultiChangesetPoliciesHeader = new JMultilineLabel(
    163                 tr("<html>There are <strong>multiple changesets</strong> necessary in order to upload {0} objects. " +
    164                    "Which strategy do you want to use?</html>",
     141                tr("<html><strong>Multiple changesets</strong> are necessary to upload {0} objects. " +
     142                   "Please select a strategy:</html>",
    165143                        numUploadedObjects));
    166144        pnlMultiChangesetPolicyPanel.add(lblMultiChangesetPoliciesHeader, gc);
    167         gc.gridy = 1;
     145        gc.gridy++;
     146        rbFillOneChangeset.setText(tr("Fill up one changeset and return to the Upload Dialog"));
    168147        pnlMultiChangesetPolicyPanel.add(rbFillOneChangeset, gc);
    169         gc.gridy = 2;
     148        gc.gridy++;
     149        rbUseMultipleChangesets.setText(tr("Open and use as many new changesets as necessary"));
    170150        pnlMultiChangesetPolicyPanel.add(rbUseMultipleChangesets, gc);
    171151
     
    185165        gc.weighty = 0.0;
    186166        gc.anchor = GridBagConstraints.NORTHWEST;
    187         gc.insets = new Insets(3, 3, 3, 3);
    188167
    189168        add(buildUploadStrategyPanel(), gc);
    190169        gc.gridy = 1;
    191170        add(buildMultiChangesetPolicyPanel(), gc);
    192 
    193         // consume remaining space
    194         gc.gridy = 2;
    195         gc.fill = GridBagConstraints.BOTH;
    196         gc.weightx = 1.0;
    197         gc.weighty = 1.0;
    198         add(new JPanel(), gc);
    199171
    200172        Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
     
    313285            rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(false);
    314286            JRadioButton lbl = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
    315             lbl.setText(tr("Upload in one request not possible (too many objects to upload)"));
     287            lbl.setEnabled(false);
    316288            lbl.setToolTipText(tr("<html>Cannot upload {0} objects in one request because the<br>"
    317289                    + "max. changeset size {1} on server ''{2}'' is exceeded.</html>",
     
    334306            rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(true);
    335307            JRadioButton lbl = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
    336             lbl.setText(tr("Upload data in one request"));
     308            lbl.setEnabled(true);
    337309            lbl.setToolTipText(null);
    338310            lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setVisible(true);
     
    369341    }
    370342
    371     @Override
    372     public void propertyChange(PropertyChangeEvent evt) {
    373         if (evt.getPropertyName().equals(UploadedObjectsSummaryPanel.NUM_OBJECTS_TO_UPLOAD_PROP)) {
    374             setNumUploadedObjects((Integer) evt.getNewValue());
    375         }
    376     }
    377 
    378     static class TextFieldFocusHandler extends FocusAdapter {
    379         @Override
    380         public void focusGained(FocusEvent e) {
    381             Component c = e.getComponent();
    382             if (c instanceof JosmTextField) {
    383                 JosmTextField tf = (JosmTextField) c;
    384                 tf.selectAll();
    385             }
    386         }
    387     }
    388 
    389343    class ChunkSizeValidator extends AbstractTextComponentValidator {
    390344        ChunkSizeValidator(JTextComponent tc) {
     
    424378    }
    425379
    426     class StrategyChangeListener extends FocusAdapter implements ItemListener, ActionListener {
     380    class StrategyChangeListener implements FocusListener, ItemListener, ActionListener {
    427381
    428382        protected void notifyStrategy() {
     
    447401
    448402        @Override
     403        public void focusGained(FocusEvent e) {
     404            Component c = e.getComponent();
     405            if (c instanceof JosmTextField) {
     406                JosmTextField tf = (JosmTextField) c;
     407                tf.selectAll();
     408            }
     409        }
     410
     411        @Override
    449412        public void focusLost(FocusEvent e) {
    450413            notifyStrategy();
  • trunk/src/org/openstreetmap/josm/gui/io/UploadedObjectsSummaryPanel.java

    r13660 r18283  
    2929 */
    3030public class UploadedObjectsSummaryPanel extends JPanel {
    31     /**
    32      * The swing property name for the number of objects to upload
    33      */
    34     public static final String NUM_OBJECTS_TO_UPLOAD_PROP = UploadedObjectsSummaryPanel.class.getName() + ".numObjectsToUpload";
    35 
    3631    /** the list with the added primitives */
    3732    private PrimitiveList lstAdd;
     
    146141            add(spDelete, gcList);
    147142        }
    148 
    149         firePropertyChange(NUM_OBJECTS_TO_UPLOAD_PROP, 0, getNumObjectsToUpload());
     143        revalidate();
    150144    }
    151145
  • trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java

    r18173 r18283  
    3131import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
    3232import org.openstreetmap.josm.tools.CheckParameterUtil;
     33import org.openstreetmap.josm.tools.Utils;
    3334
    3435/**
     
    5455    private transient OsmPrimitive primitive;
    5556
    56     private EndEditListener endEditListener;
     57    private transient EndEditListener endEditListener;
    5758
    5859    /**
     
    279280     */
    280281    public void deleteTagNames(int... tagIndices) {
    281         if (tags == null)
    282             return;
    283282        commitPendingEdit();
    284283        for (int tagIdx : tagIndices) {
     
    298297     */
    299298    public void deleteTagValues(int... tagIndices) {
    300         if (tags == null)
    301             return;
    302299        commitPendingEdit();
    303300        for (int tagIdx : tagIndices) {
     
    333330     */
    334331    public void deleteTags(int... tagIndices) {
    335         if (tags == null)
    336             return;
    337332        commitPendingEdit();
    338333        List<TagModel> toDelete = Arrays.stream(tagIndices).mapToObj(tags::get).filter(Objects::nonNull).collect(Collectors.toList());
     
    442437                continue;
    443438            }
     439            boolean isKeyEmpty = Utils.isStripEmpty(tag.getName());
     440            boolean isValueEmpty = Utils.isStripEmpty(tag.getValue());
     441
     442            // just the empty line at the bottom of the JTable
     443            if (isKeyEmpty && isValueEmpty) {
     444                continue;
     445            }
    444446
    445447            // tag name holds an empty key. Don't apply it to the selection.
    446             if (!keepEmpty && (tag.getName().trim().isEmpty() || tag.getValue().trim().isEmpty())) {
     448            if (!keepEmpty && (isKeyEmpty || isValueEmpty)) {
    447449                continue;
    448450            }
    449             result.put(tag.getName().trim(), tag.getValue().trim());
     451            result.put(Utils.strip(tag.getName()), Utils.strip(tag.getValue()));
    450452        }
    451453        return result;
     
    497499        // tag name holds an empty key. Don't apply it to the selection.
    498500        //
    499         if (tag.getName().trim().isEmpty())
     501        if (Utils.isStripEmpty(tag.getName()))
    500502            return null;
    501503
     
    529531    public List<String> getKeys() {
    530532        return tags.stream()
    531                 .filter(tag -> !tag.getName().trim().isEmpty())
     533                .filter(tag -> !Utils.isStripEmpty(tag.getName()))
    532534                .map(TagModel::getName)
    533535                .collect(Collectors.toList());
  • trunk/src/org/openstreetmap/josm/gui/tagging/TagTable.java

    r17709 r18283  
    3636import org.openstreetmap.josm.tools.ImageProvider;
    3737import org.openstreetmap.josm.tools.Logging;
     38import org.openstreetmap.josm.tools.Utils;
    3839
    3940/**
     
    8990                // we are at the end. Append an empty row and move the focus to its second column
    9091                String key = ((TagModel) model.getValueAt(row, 0)).getName();
    91                 if (!key.trim().isEmpty()) {
     92                if (!Utils.isStripEmpty(key)) {
    9293                    model.appendNewTag();
    9394                    col = 0;
     
    243244            }
    244245            final int rowIdx = model.getRowCount()-1;
    245             if (rowIdx < 0 || !((TagModel) model.getValueAt(rowIdx, 0)).getName().trim().isEmpty()) {
     246            if (rowIdx < 0 || !Utils.isStripEmpty(((TagModel) model.getValueAt(rowIdx, 0)).getName())) {
    246247                model.appendNewTag();
    247248            }
  • trunk/src/org/openstreetmap/josm/gui/widgets/JosmComboBoxModel.java

    r18221 r18283  
    5555     *
    5656     * @param element the element to get the index of
    57      * @return an int representing the index position, where 0 is the first position
     57     * @return the index of the first occurrence of the specified element in this model,
     58     *         or -1 if this model does not contain the element
    5859     */
    5960    public int getIndexOf(E element) {
     
    6162    }
    6263
     64    protected void doAddElement(E element) {
     65        if (element != null && (maxSize == -1 || getSize() < maxSize)) {
     66            elements.add(element);
     67        }
     68    }
     69
    6370    //
    6471    // interface java.lang.Iterable
     
    7986    @Override
    8087    public void addElement(E element) {
    81         if (element != null && (maxSize == -1 || getSize() < maxSize)) {
    82             elements.add(element);
    83         }
     88        doAddElement(element);
     89        fireIntervalAdded(this, elements.size() - 1, elements.size() - 1);
    8490    }
    8591
    8692    @Override
    8793    public void removeElement(Object elem) {
    88         elements.remove(elem);
     94        int index = elements.indexOf(elem);
     95        if (elem == selected) {
     96            if (index == 0) {
     97                setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
     98            } else {
     99                setSelectedItem(getElementAt(index - 1));
     100            }
     101        }
     102        if (elements.remove(elem))
     103            fireIntervalRemoved(this, index, index);
    89104    }
    90105
     
    115130        }
    116131        elements.add(index, element);
     132        fireIntervalAdded(this, index, index);
    117133    }
    118134
     
    167183     */
    168184    public void addAllElements(Collection<E> elems) {
    169         elems.forEach(e -> addElement(e));
     185        int index0 = elements.size();
     186        elems.forEach(e -> doAddElement(e));
     187        int index1 = elements.size() - 1;
     188        if (index0 <= index1)
     189            fireIntervalAdded(this, index0, index1);
    170190    }
    171191
     
    178198     */
    179199    public void addAllElements(Collection<String> strings, Function<String, E> buildE) {
    180         strings.forEach(s -> addElement(buildE.apply(s)));
     200        int index0 = elements.size();
     201        strings.forEach(s -> doAddElement(buildE.apply(s)));
     202        int index1 = elements.size() - 1;
     203        if (index0 <= index1)
     204            fireIntervalAdded(this, index0, index1);
    181205    }
    182206
     
    205229    public void removeAllElements() {
    206230        if (!elements.isEmpty()) {
    207             int firstIndex = 0;
    208231            int lastIndex = elements.size() - 1;
    209232            elements.clear();
    210233            selected = null;
    211             fireIntervalRemoved(this, firstIndex, lastIndex);
     234            fireIntervalRemoved(this, 0, lastIndex);
    212235        } else {
    213236            selected = null;
  • trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java

    r17840 r18283  
    3131    public static final String ERROR_HEADER_PATTERN = "The changeset (\\d+) was closed at (.*)";
    3232
     33    /**
     34     * Identifies when the changeset exception occurred.
     35     */
    3336    public enum Source {
    3437        /**
     
    4346         */
    4447        UPLOAD_DATA,
     48        /**
     49         * The exception was thrown when we tried to close a changeset.  Probably the changeset
     50         * already timed out on the server.
     51         * @since 18283
     52         */
     53        CLOSE_CHANGESET,
    4554        /**
    4655         * Unspecified source
     
    163172    }
    164173
     174    /**
     175     * Sets the source where the exception was thrown
     176     *
     177     * @param source the source where the exception was thrown
     178     */
    165179    public void setSource(Source source) {
    166180        this.source = source == null ? Source.UNSPECIFIED : source;
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r17506 r18283  
    388388                }
    389389            }
     390        } catch (ChangesetClosedException e) {
     391            e.setSource(ChangesetClosedException.Source.UPDATE_CHANGESET);
     392            throw e;
    390393        } catch (NumberFormatException e) {
    391394            throw new OsmTransferException(errHandler.apply(ret), e);
     
    529532            // send "\r\n" instead of empty string, so we don't send zero payload - workaround bugs in proxy software
    530533            sendPutRequest("changeset/" + changeset.getId() + "/close", "\r\n", monitor);
     534        } catch (ChangesetClosedException e) {
     535            e.setSource(ChangesetClosedException.Source.CLOSE_CHANGESET);
     536            throw e;
     537        } finally {
    531538            changeset.setOpen(false);
    532         } finally {
    533539            monitor.finishTask();
    534540        }
     
    565571            throws OsmTransferException {
    566572        try {
     573            ensureValidChangeset();
    567574            monitor.beginTask("", list.size() * 2);
    568             if (changeset == null)
    569                 throw new OsmTransferException(tr("No changeset present for diff upload."));
    570575
    571576            initialize(monitor);
     
    594599                    monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)
    595600            );
    596         } catch (OsmTransferException e) {
     601        } catch (ChangesetClosedException e) {
     602            e.setSource(ChangesetClosedException.Source.UPLOAD_DATA);
    597603            throw e;
    598604        } catch (XmlParsingException e) {
     
    752758                case HttpURLConnection.HTTP_CONFLICT:
    753759                    if (ChangesetClosedException.errorHeaderMatchesPattern(errorHeader))
    754                         throw new ChangesetClosedException(errorBody, ChangesetClosedException.Source.UPLOAD_DATA);
     760                        throw new ChangesetClosedException(errorBody, ChangesetClosedException.Source.UNSPECIFIED);
    755761                    else
    756762                        throw new OsmApiException(retCode, errorHeader, errorBody);
  • trunk/src/org/openstreetmap/josm/io/UploadStrategySpecification.java

    r12687 r18283  
    151151
    152152    @Override
     153    public String toString() {
     154        return String.format("Strategy: %s, ChunkSize: %d, Policy: %s, Close after: %b",
     155            strategy.toString(), chunkSize, policy == null ? "none" : policy.toString(), closeChangesetAfterUpload);
     156    }
     157
     158    @Override
    153159    public int hashCode() {
    154160        return Objects.hash(strategy, chunkSize, policy, closeChangesetAfterUpload);
Note: See TracChangeset for help on using the changeset viewer.