Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java	(revision 17499)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java	(revision 17500)
@@ -15,8 +15,10 @@
 import javax.swing.AbstractAction;
 import javax.swing.BorderFactory;
+import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
 
 import org.openstreetmap.josm.actions.downloadtasks.ChangesetHeaderDownloadTask;
@@ -24,7 +26,12 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.NoteInputDialog;
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ExceptionUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
 
 /**
@@ -39,4 +46,5 @@
 
     private final UpdateChangesetDiscussionAction actUpdateChangesets = new UpdateChangesetDiscussionAction();
+    private final AddChangesetCommentAction actAddChangesetComment = new AddChangesetCommentAction();
 
     private final ChangesetDiscussionTableModel model = new ChangesetDiscussionTableModel();
@@ -49,13 +57,21 @@
         JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
 
-        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
+        JToolBar tb = new JToolBar(SwingConstants.VERTICAL);
         tb.setFloatable(false);
 
         // -- changeset discussion update
         tb.add(actUpdateChangesets);
-        actUpdateChangesets.initProperties(current);
+        // -- add a comment to changeset discussion
+        tb.add(actAddChangesetComment);
+
+        initProperties();
 
         pnl.add(tb);
         return pnl;
+    }
+
+    void initProperties() {
+        actUpdateChangesets.initProperties(current);
+        actAddChangesetComment.initProperties(current);
     }
 
@@ -82,6 +98,42 @@
         }
 
-        public void initProperties(Changeset cs) {
+        void initProperties(Changeset cs) {
             setEnabled(cs != null && !NetworkManager.isOffline(OnlineResource.OSM_API));
+        }
+    }
+
+    /**
+     * Adds a discussion comment to the current changeset
+     */
+    class AddChangesetCommentAction extends AbstractAction {
+        AddChangesetCommentAction() {
+            putValue(NAME, tr("Comment"));
+            new ImageProvider("dialogs/notes", "note_comment").getResource().attachImageIcon(this);
+            putValue(SHORT_DESCRIPTION, tr("Add comment"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent evt) {
+            if (current == null)
+                return;
+            NoteInputDialog dialog = new NoteInputDialog(MainApplication.getMainFrame(), tr("Comment on changeset"), tr("Add comment"));
+            dialog.showNoteDialog(tr("Add comment to changeset:"), ImageProvider.get("dialogs/notes", "note_comment"));
+            if (dialog.getValue() != 1) {
+                return;
+            }
+            try {
+                OsmApi.getOsmApi().addCommentToChangeset(current, dialog.getInputText(), null);
+            } catch (OsmTransferException e) {
+                Logging.error(e);
+                JOptionPane.showMessageDialog(
+                        MainApplication.getMainFrame(),
+                        ExceptionUtil.explainOsmTransferException(e),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE);
+            }
+        }
+
+        void initProperties(Changeset cs) {
+            setEnabled(cs != null && !cs.isOpen() && !NetworkManager.isOffline(OnlineResource.OSM_API));
         }
     }
@@ -101,5 +153,5 @@
             updateView(cs);
         }
-        actUpdateChangesets.initProperties(current);
+        initProperties();
         if (cs != null && cs.getDiscussion().size() < cs.getCommentsCount()) {
             actUpdateChangesets.actionPerformed(null);
Index: trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 17499)
+++ trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 17500)
@@ -536,4 +536,23 @@
 
     /**
+     * Adds a comment to the discussion of a closed changeset.
+     *
+     * @param changeset the changeset where to add a comment. Must be closed. changeset.getId() &gt; 0 required.
+     * @param comment Text of the comment
+     * @param monitor the progress monitor. If null, uses {@link NullProgressMonitor#INSTANCE}
+     *
+     * @throws OsmTransferException if something goes wrong.
+     * @since 17500
+     */
+    public void addCommentToChangeset(Changeset changeset, String comment, ProgressMonitor monitor) throws OsmTransferException {
+        if (changeset.isOpen())
+            throw new IllegalArgumentException(tr("Changeset must be closed in order to add a comment"));
+        else if (changeset.getId() <= 0)
+            throw new IllegalArgumentException(tr("Changeset ID > 0 expected. Got {0}.", changeset.getId()));
+        sendRequest("POST", "changeset/" + changeset.getId() + "/comment",
+                "text=" + Utils.encodeUrl(comment), monitor, "application/x-www-form-urlencoded", true, false);
+    }
+
+    /**
      * Uploads a list of changes in "diff" form to the server.
      *
@@ -644,4 +663,9 @@
     }
 
+    protected final String sendRequest(String requestMethod, String urlSuffix, String requestBody, ProgressMonitor monitor,
+            boolean doAuthenticate, boolean fastFail) throws OsmTransferException {
+        return sendRequest(requestMethod, urlSuffix, requestBody, monitor, null, doAuthenticate, fastFail);
+    }
+
     /**
      * Generic method for sending requests to the OSM API.
@@ -655,6 +679,7 @@
      * @param requestBody the body of the HTTP request, if any.
      * @param monitor the progress monitor
-     * @param doAuthenticate  set to true, if the request sent to the server shall include authentication
-     * credentials;
+     * @param contentType Content-Type to set for PUT/POST/DELETE requests.
+     *    Can be set to {@code null}, in that case it means {@code text/xml}
+     * @param doAuthenticate  set to true, if the request sent to the server shall include authentication credentials;
      * @param fastFail true to request a short timeout
      *
@@ -664,5 +689,5 @@
      */
     protected final String sendRequest(String requestMethod, String urlSuffix, String requestBody, ProgressMonitor monitor,
-            boolean doAuthenticate, boolean fastFail) throws OsmTransferException {
+            String contentType, boolean doAuthenticate, boolean fastFail) throws OsmTransferException {
         int retries = fastFail ? 0 : getMaxRetries();
 
@@ -686,5 +711,5 @@
 
                 if ("PUT".equals(requestMethod) || "POST".equals(requestMethod) || "DELETE".equals(requestMethod)) {
-                    client.setHeader("Content-Type", "text/xml");
+                    client.setHeader("Content-Type", contentType == null ? "text/xml" : contentType);
                     // It seems that certain bits of the Ruby API are very unhappy upon
                     // receipt of a PUT/POST message without a Content-length header,
