Index: trunk/src/org/openstreetmap/josm/actions/JosmAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 15404)
@@ -219,6 +219,6 @@
         // make this action listen to layer change and selection change events
         if (listenToLayerChange()) {
-            layerChangeAdapter = new LayerChangeAdapter();
-            activeLayerChangeAdapter = new ActiveLayerChangeAdapter();
+            layerChangeAdapter = buildLayerChangeAdapter();
+            activeLayerChangeAdapter = buildActiveLayerChangeAdapter();
             getLayerManager().addLayerChangeListener(layerChangeAdapter);
             getLayerManager().addActiveLayerChangeListener(activeLayerChangeAdapter);
@@ -229,4 +229,22 @@
         }
         initEnabledState();
+    }
+
+    /**
+     * Override this if calling {@link #updateEnabledState()} on layer change events is not enough.
+     * @return the {@link LayerChangeAdapter} that will be called on layer change events
+     * @since 15404
+     */
+    protected LayerChangeAdapter buildLayerChangeAdapter() {
+        return new LayerChangeAdapter();
+    }
+
+    /**
+     * Override this if calling {@link #updateEnabledState()} on active layer change event is not enough.
+     * @return the {@link LayerChangeAdapter} that will be called on active layer change event
+     * @since 15404
+     */
+    protected ActiveLayerChangeAdapter buildActiveLayerChangeAdapter() {
+        return new ActiveLayerChangeAdapter();
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/SaveAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveAction.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/actions/SaveAction.java	(revision 15404)
@@ -6,4 +6,5 @@
 
 import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeListener;
 import java.io.File;
 
@@ -12,4 +13,8 @@
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.SaveToFile;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -21,4 +26,10 @@
 public final class SaveAction extends SaveActionBase {
     private static SaveAction instance = new SaveAction();
+
+    private final PropertyChangeListener updateOnRequireSaveChange = evt -> {
+        if (OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
+            updateEnabledState();
+        }
+    };
 
     /**
@@ -39,5 +50,35 @@
     }
 
-    @Override public File getFile(Layer layer) {
+    @Override
+    protected LayerChangeAdapter buildLayerChangeAdapter() {
+        return new LayerChangeAdapter() {
+            @Override
+            public void layerAdded(LayerAddEvent e) {
+                if (e.getAddedLayer() instanceof OsmDataLayer) {
+                    e.getAddedLayer().addPropertyChangeListener(updateOnRequireSaveChange);
+                }
+                super.layerAdded(e);
+            }
+
+            @Override
+            public void layerRemoving(LayerRemoveEvent e) {
+                if (e.getRemovedLayer() instanceof OsmDataLayer) {
+                    e.getRemovedLayer().removePropertyChangeListener(updateOnRequireSaveChange);
+                }
+                super.layerRemoving(e);
+            }
+        };
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        Layer activeLayer = getLayerManager().getActiveLayer();
+        setEnabled(activeLayer != null && activeLayer.isSavable()
+                && (!(activeLayer.getAssociatedFile() != null
+                    && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile())));
+    }
+
+    @Override
+    public File getFile(Layer layer) {
         File f = layer.getAssociatedFile();
         if (f != null && !f.exists()) {
Index: trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 15404)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.SaveToFile;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
@@ -73,5 +74,10 @@
         if (!layer.checkSaveConditions())
             return false;
-        return doInternalSave(layer, getFile(layer));
+        final boolean requiresSave = layer instanceof SaveToFile && ((SaveToFile) layer).requiresSaveToFile();
+        final boolean result = doInternalSave(layer, getFile(layer));
+        if (!requiresSave) {
+            updateEnabledState();
+        }
+        return result;
     }
 
@@ -122,5 +128,4 @@
                 ((OsmDataLayer) layer).onPostSaveToFile();
             }
-            MainApplication.getMainFrame().repaint();
         } catch (IOException | InvalidPathException e) {
             showAndLogException(e);
@@ -133,8 +138,4 @@
     protected abstract File getFile(Layer layer);
 
-    /**
-     * Refreshes the enabled state
-     *
-     */
     @Override
     protected void updateEnabledState() {
Index: trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 15404)
@@ -7,4 +7,5 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeListener;
 import java.util.LinkedList;
 import java.util.List;
@@ -25,8 +26,11 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.Notification;
 import org.openstreetmap.josm.gui.io.AsynchronousUploadPrimitivesTask;
 import org.openstreetmap.josm.gui.io.UploadDialog;
 import org.openstreetmap.josm.gui.io.UploadPrimitivesTask;
 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.util.GuiHelper;
@@ -41,5 +45,5 @@
  * Action that opens a connection to the osm server and uploads all changes.
  *
- * An dialog is displayed asking the user to specify a rectangle to grab.
+ * A dialog is displayed asking the user to specify a rectangle to grab.
  * The url and account settings from the preferences are used.
  *
@@ -91,4 +95,10 @@
     }
 
+    private final PropertyChangeListener updateOnRequireUploadChange = evt -> {
+        if (OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP.equals(evt.getPropertyName())) {
+            updateEnabledState();
+        }
+    };
+
     /**
      * Registers an upload hook. Adds the hook at the first position of the upload hooks.
@@ -146,7 +156,28 @@
 
     @Override
+    protected LayerChangeAdapter buildLayerChangeAdapter() {
+        return new LayerChangeAdapter() {
+            @Override
+            public void layerAdded(LayerAddEvent e) {
+                if (e.getAddedLayer() instanceof OsmDataLayer) {
+                    e.getAddedLayer().addPropertyChangeListener(updateOnRequireUploadChange);
+                }
+                super.layerAdded(e);
+            }
+
+            @Override
+            public void layerRemoving(LayerRemoveEvent e) {
+                if (e.getRemovedLayer() instanceof OsmDataLayer) {
+                    e.getRemovedLayer().removePropertyChangeListener(updateOnRequireUploadChange);
+                }
+                super.layerRemoving(e);
+            }
+        };
+    }
+
+    @Override
     protected void updateEnabledState() {
         OsmDataLayer editLayer = getLayerManager().getEditLayer();
-        setEnabled(editLayer != null && editLayer.isUploadable());
+        setEnabled(editLayer != null && editLayer.requiresUploadToServer());
     }
 
@@ -232,10 +263,5 @@
     public void uploadData(final OsmDataLayer layer, APIDataSet apiData) {
         if (apiData.isEmpty()) {
-            JOptionPane.showMessageDialog(
-                    MainApplication.getMainFrame(),
-                    tr("No changes to upload."),
-                    tr("Warning"),
-                    JOptionPane.INFORMATION_MESSAGE
-            );
+            new Notification(tr("No changes to upload.")).show();
             return;
         }
@@ -292,10 +318,5 @@
             return;
         if (MainApplication.getMap() == null) {
-            JOptionPane.showMessageDialog(
-                    MainApplication.getMainFrame(),
-                    tr("Nothing to upload. Get some data first."),
-                    tr("Warning"),
-                    JOptionPane.WARNING_MESSAGE
-            );
+            new Notification(tr("Nothing to upload. Get some data first.")).show();
             return;
         }
Index: trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 15404)
@@ -14,5 +14,4 @@
 import java.util.stream.Collectors;
 
-import javax.swing.JOptionPane;
 import javax.swing.SwingUtilities;
 
@@ -26,4 +25,5 @@
 import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.Notification;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.io.UploadSelectionDialog;
@@ -65,5 +65,8 @@
         updateEnabledStateOnModifiableSelection(selection);
         OsmDataLayer editLayer = getLayerManager().getEditLayer();
-        if (editLayer != null && !editLayer.isUploadable()) {
+        if (isEnabled() && editLayer != null && !editLayer.isUploadable()) {
+            setEnabled(false);
+        }
+        if (isEnabled() && selection.stream().noneMatch(OsmPrimitive::isModified)) {
             setEnabled(false);
         }
@@ -93,10 +96,5 @@
         Collection<OsmPrimitive> deletedCandidates = getDeletedPrimitives(editLayer.getDataSet());
         if (modifiedCandidates.isEmpty() && deletedCandidates.isEmpty()) {
-            JOptionPane.showMessageDialog(
-                    MainApplication.getMainFrame(),
-                    tr("No changes to upload."),
-                    tr("Warning"),
-                    JOptionPane.INFORMATION_MESSAGE
-            );
+            new Notification(tr("No changes to upload.")).show();
             return;
         }
@@ -111,10 +109,5 @@
         Collection<OsmPrimitive> toUpload = new UploadHullBuilder().build(dialog.getSelectedPrimitives());
         if (toUpload.isEmpty()) {
-            JOptionPane.showMessageDialog(
-                    MainApplication.getMainFrame(),
-                    tr("No changes to upload."),
-                    tr("Warning"),
-                    JOptionPane.INFORMATION_MESSAGE
-            );
+            new Notification(tr("No changes to upload.")).show();
             return;
         }
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 15404)
@@ -825,9 +825,5 @@
     @Override
     public boolean isModified() {
-        for (OsmPrimitive p : allPrimitives) {
-            if (p.isModified())
-                return true;
-        }
-        return false;
+        return allPrimitives.parallelStream().anyMatch(OsmPrimitive::isModified);
     }
 
@@ -838,9 +834,5 @@
      */
     public boolean requiresUploadToServer() {
-        for (OsmPrimitive p : allPrimitives) {
-            if (APIOperation.of(p) != null)
-                return true;
-        }
-        return false;
+        return allPrimitives.parallelStream().anyMatch(p -> APIOperation.of(p) != null);
     }
 
Index: trunk/src/org/openstreetmap/josm/io/session/GenericSessionExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/GenericSessionExporter.java	(revision 15403)
+++ trunk/src/org/openstreetmap/josm/io/session/GenericSessionExporter.java	(revision 15404)
@@ -28,4 +28,5 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.SaveToFile;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
@@ -73,5 +74,5 @@
         LayerSaveAction() {
             new ImageProvider("save").getResource().attachImageIcon(this);
-            putValue(SHORT_DESCRIPTION, ((AbstractModifiableLayer) layer).requiresSaveToFile() ?
+            putValue(SHORT_DESCRIPTION, ((SaveToFile) layer).requiresSaveToFile() ?
                     tr("Layer contains unsaved data - save to file.") :
                     tr("Layer does not contain unsaved data."));
@@ -86,5 +87,5 @@
 
         public final void updateEnabledState() {
-            setEnabled(((AbstractModifiableLayer) layer).requiresSaveToFile());
+            setEnabled(((SaveToFile) layer).requiresSaveToFile());
         }
     }
