Ticket #20913: 20913-v2.6.patch

File 20913-v2.6.patch, 109.7 KB (added by Bjoeni, 5 years ago)
  • trunk/src/org/openstreetmap/josm/actions/SaveAction.java

    diff --git a/trunk/src/org/openstreetmap/josm/actions/SaveAction.java b/trunk/src/org/openstreetmap/josm/actions/SaveAction.java
    a b  
    1414import javax.swing.JPanel;
    1515import javax.swing.SwingConstants;
    1616
    17 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
     17import org.openstreetmap.josm.data.gpx.GpxConstants;
    1818import org.openstreetmap.josm.gui.ExtendedDialog;
    1919import org.openstreetmap.josm.gui.MainApplication;
     20import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    2021import org.openstreetmap.josm.gui.layer.GpxLayer;
    2122import org.openstreetmap.josm.gui.layer.Layer;
    2223import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    2324import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    24 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2525import org.openstreetmap.josm.gui.layer.SaveToFile;
    2626import org.openstreetmap.josm.gui.util.GuiHelper;
    2727import org.openstreetmap.josm.spi.preferences.Config;
     
    3737    private static final SaveAction instance = new SaveAction();
    3838
    3939    private final PropertyChangeListener updateOnRequireSaveChange = evt -> {
    40         if (OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
     40        if (AbstractModifiableLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
    4141            updateEnabledState();
    4242        }
    4343    };
    4444
    45     private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState();
    46 
    4745    /**
    4846     * Construct the action with "Save" as label.
    4947     */
     
    6866            @Override
    6967            public void layerAdded(LayerAddEvent e) {
    7068                Layer l = e.getAddedLayer();
    71                 if (l instanceof OsmDataLayer) {
     69                if (l instanceof AbstractModifiableLayer) {
    7270                    l.addPropertyChangeListener(updateOnRequireSaveChange);
    7371                }
    74                 if (l instanceof GpxLayer) {
    75                     ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx);
    76                 }
    7772                super.layerAdded(e);
    7873            }
    7974
     
    8075            @Override
    8176            public void layerRemoving(LayerRemoveEvent e) {
    8277                Layer l = e.getRemovedLayer();
    83                 if (l instanceof OsmDataLayer) {
     78                if (l instanceof AbstractModifiableLayer) {
    8479                    l.removePropertyChangeListener(updateOnRequireSaveChange);
    8580                }
    86                 if (l instanceof GpxLayer) {
    87                     ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx);
    88                 }
    8981                super.layerRemoving(e);
    9082            }
    9183        };
     
    113105        }
    114106
    115107        // Ask for overwrite in case of GpxLayer
    116         if (f != null && layer instanceof GpxLayer && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
     108        if (f != null
     109                && layer instanceof GpxLayer
     110                && (((GpxLayer) layer).data == null
     111                || !GpxConstants.JOSM_CREATOR_NAME.equals(((GpxLayer) layer).data.creator))
     112                && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
     113
    117114            JPanel p = new JPanel(new GridBagLayout());
    118             JLabel label = new JLabel(tr("File {0} exists. Overwrite?", f.getName()));
     115            JLabel label = new JLabel("<html>"
     116                    + tr("The file \"{0}\" will be modified.<br>Would you like to overwrite the existing file?", f.getName())
     117                    + "</html>");
    119118            label.setHorizontalAlignment(SwingConstants.CENTER);
    120             JCheckBox remember = new JCheckBox(tr("Remember choice"));
     119            JCheckBox remember = new JCheckBox(tr("Always overwrite GPX files without asking"));
    121120            remember.setHorizontalAlignment(SwingConstants.CENTER);
    122121            p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10));
    123122            p.add(remember, GBC.eop().fill(GBC.HORIZONTAL));
     
    124123            ExtendedDialog dialog = new ExtendedDialog(
    125124                    MainApplication.getMainFrame(),
    126125                    tr("Overwrite"),
    127                     tr("Overwrite"), tr("Cancel"))
    128                 .setButtonIcons("save_as", "cancel")
     126                    tr("Overwrite"), tr("New file"), tr("Cancel"))
     127                .setButtonIcons("save", "save_as", "cancel")
    129128                .setContent(p);
    130             if (dialog.showDialog().getValue() != 1) {
     129            int val = dialog.showDialog().getValue();
     130            if (val == 1) {
     131                Config.getPref().putBoolean("gpx.export.overwrite", remember.isSelected());
     132            } else if (val == 2) {
    131133                f = null;
    132             } else if (remember.isSelected()) {
    133                 Config.getPref().putBoolean("gpx.export.overwrite", true);
     134            } else {
     135                return null;
    134136            }
    135137        }
    136138        return f == null ? layer.createAndOpenSaveFileChooser() : f;
  • trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java

    diff --git a/trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java b/trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java
    a b  
    1919import java.util.Map;
    2020import java.util.Set;
    2121import java.util.stream.Collectors;
     22import java.util.stream.Stream;
    2223
    2324import javax.swing.BorderFactory;
    2425import javax.swing.JCheckBox;
     
    3233import javax.swing.border.EtchedBorder;
    3334import javax.swing.filechooser.FileFilter;
    3435
     36import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3537import org.openstreetmap.josm.gui.ExtendedDialog;
    3638import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    3739import org.openstreetmap.josm.gui.MainApplication;
    3840import org.openstreetmap.josm.gui.MapFrame;
    3941import org.openstreetmap.josm.gui.MapFrameListener;
     42import org.openstreetmap.josm.gui.Notification;
     43import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    4044import org.openstreetmap.josm.gui.layer.Layer;
    4145import org.openstreetmap.josm.gui.util.WindowGeometry;
    4246import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
     
    5862    private transient Map<Layer, SessionLayerExporter> exporters;
    5963    private transient MultiMap<Layer, Layer> dependencies;
    6064
     65    private static final BooleanProperty SAVE_LOCAL_FILES_PROPERTY = new BooleanProperty("session.savelocal", true);
     66
    6167    /**
    6268     * Constructs a new {@code SessionSaveAsAction}.
    6369     */
     
    156162                .filter(layer -> exporters.get(layer) != null && exporters.get(layer).shallExport())
    157163                .collect(Collectors.toList());
    158164
     165        Stream<Layer> layersToSaveStream = layersOut.stream()
     166                .filter(layer -> layer.isSavable()
     167                        && layer instanceof AbstractModifiableLayer
     168                        && ((AbstractModifiableLayer) layer).requiresSaveToFile()
     169                        && exporters.get(layer) != null
     170                        && !exporters.get(layer).requiresZip());
     171
     172        if (SAVE_LOCAL_FILES_PROPERTY.get()) {
     173            // individual files must be saved before the session file as the location may change
     174
     175            if (layersToSaveStream
     176                .map(layer -> SaveAction.getInstance().doSave(layer, true))
     177                .collect(Collectors.toList()) //force evaluation of all elements
     178                .contains(false)) {
     179
     180                new Notification(tr("Not all local files referenced by the session file could be saved."
     181                        + "<br>Make sure you save them before closing JOSM."))
     182                    .setIcon(JOptionPane.WARNING_MESSAGE)
     183                    .setDuration(Notification.TIME_LONG)
     184                    .show();
     185            }
     186
     187        } else if (layersToSaveStream.anyMatch(l -> true)) {
     188            new Notification(tr("Not all local files referenced by the session file are saved yet."
     189                    + "<br>Make sure you save them before closing JOSM."))
     190                .setIcon(JOptionPane.INFORMATION_MESSAGE)
     191                .setDuration(Notification.TIME_LONG)
     192                .show();
     193        }
     194
    159195        int active = -1;
    160196        Layer activeLayer = getLayerManager().getActiveLayer();
    161197        if (activeLayer != null) {
     
    241277        }
    242278
    243279        protected final Component build() {
     280            JPanel op = new JPanel(new GridBagLayout());
    244281            JPanel ip = new JPanel(new GridBagLayout());
    245282            for (Layer layer : layers) {
    246283                JPanel wrapper = new JPanel(new GridBagLayout());
     
    263300            p.add(sp, GBC.eol().fill());
    264301            final JTabbedPane tabs = new JTabbedPane();
    265302            tabs.addTab(tr("Layers"), p);
    266             return tabs;
     303            op.add(tabs, GBC.eol().fill());
     304            JCheckBox chkSaveLocal = new JCheckBox(tr("Save all local files to disk"), SAVE_LOCAL_FILES_PROPERTY.get());
     305            chkSaveLocal.addChangeListener(l -> {
     306                SAVE_LOCAL_FILES_PROPERTY.put(chkSaveLocal.isSelected());
     307            });
     308            op.add(chkSaveLocal);
     309            return op;
    267310        }
    268311
    269312        protected final Component getDisabledExportPanel(Layer layer) {
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java

    diff --git a/trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java b/trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java
    a b  
    100100    String META_BOUNDS = META_PREFIX + "bounds";
    101101
    102102    /**
     103     * The creator element that will be written when exporting a GPX file
     104     */
     105    String JOSM_CREATOR_NAME = "JOSM GPX export";
     106
     107    /**
    103108     * Namespace for the XSD
    104109     */
    105110    String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance";
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java

    diff --git a/trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java b/trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
    a b  
    4141 *
    4242 * @author Raphael Mack &lt;ramack@raphael-mack.de&gt;
    4343 */
    44 public class GpxData extends WithAttributes implements Data {
     44public class GpxData extends WithAttributes implements Data, IGpxLayerPrefs {
    4545
    4646    /**
    4747     * Constructs a new GpxData.
     
    981981     * @return Modifiable map
    982982     * @since 15496
    983983     */
     984    @Override
    984985    public Map<String, String> getLayerPrefs() {
    985986        return layerPrefs;
    986987    }
     
    10951096            suppressedInvalidate = true;
    10961097        } else {
    10971098            if (setModified) {
    1098                 modified = true;
     1099                setModified(true);
    10991100            }
    11001101            if (listeners.hasListeners()) {
    11011102                GpxDataChangeEvent e = new GpxDataChangeEvent(this);
     
    11371138         * @param e The event
    11381139         */
    11391140        void gpxDataChanged(GpxDataChangeEvent e);
     1141
     1142        /**
     1143         * Called when the modified state of the data changed
     1144         * @param modified the new modified state
     1145         */
     1146        default void modifiedStateChanged(boolean modified) {
     1147            // Override if needed
     1148        }
    11401149    }
    11411150
    11421151    /**
     
    11751184     * @param value modified flag
    11761185     * @since 15496
    11771186     */
     1187    @Override
    11781188    public void setModified(boolean value) {
    1179         modified = value;
     1189        if (!initializing && modified != value) {
     1190            modified = value;
     1191            if (listeners.hasListeners()) {
     1192                listeners.fireEvent(l -> l.modifiedStateChanged(modified));
     1193            }
     1194        }
    11801195    }
    11811196
    11821197    /**
  • trunk/src/org/openstreetmap/josm/data/gpx/IGpxLayerPrefs.java

    diff --git a/trunk/src/org/openstreetmap/josm/data/gpx/IGpxLayerPrefs.java b/trunk/src/org/openstreetmap/josm/data/gpx/IGpxLayerPrefs.java
    new file mode 100644
    a b  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.Map;
     5
     6/**
     7 * Interface containing the layer preferences.
     8 * Implemented by GpxLayer and MarkerLayer
     9 * @since xxx
     10 */
     11public interface IGpxLayerPrefs {
     12
     13    /**
     14     * The layer specific prefs formerly saved in the preferences, e.g. drawing options.
     15     * NOT the track specific settings (e.g. color, width)
     16     * @return Modifiable map
     17     */
     18    Map<String, String> getLayerPrefs();
     19
     20    /**
     21     * Sets the modified flag to the value.
     22     * @param value modified flag
     23     */
     24    void setModified(boolean value);
     25
     26}
  • trunk/src/org/openstreetmap/josm/gui/MainFrame.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/MainFrame.java b/trunk/src/org/openstreetmap/josm/gui/MainFrame.java
    a b  
    2525import javax.swing.JPanel;
    2626
    2727import org.openstreetmap.josm.data.UserIdentityManager;
     28import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    2829import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    2930import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
    3031import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     
    4546    private final transient LayerStateChangeListener updateTitleOnLayerStateChange = (layer, newValue) -> onLayerChange(layer);
    4647
    4748    private final transient PropertyChangeListener updateTitleOnSaveChange = evt -> {
    48         if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
     49        if (evt.getPropertyName().equals(AbstractModifiableLayer.REQUIRES_SAVE_TO_DISK_PROP)
    4950                || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) {
    50             OsmDataLayer layer = (OsmDataLayer) evt.getSource();
     51            AbstractModifiableLayer layer = (AbstractModifiableLayer) evt.getSource();
    5152            onLayerChange(layer);
    5253        }
    5354    };
     
    186187        getRootPane().putClientProperty("Window.documentModified", dirty);
    187188    }
    188189
    189     private void onLayerChange(OsmDataLayer layer) {
     190    private void onLayerChange(AbstractModifiableLayer layer) {
    190191        if (layer == MainApplication.getLayerManager().getEditLayer()) {
    191192            refreshTitle();
    192193        }
  • trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java b/trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java
    a b  
    147147            }
    148148            if (data.gpxLayer != null) {
    149149                MainApplication.getLayerManager().addLayer(data.gpxLayer);
     150                MainApplication.getLayerManager().setActiveLayer(data.gpxLayer);
    150151            }
    151152            data.postLayerTask.run();
    152153        });
  • trunk/src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java
    a b  
    1616 */
    1717public abstract class AbstractModifiableLayer extends Layer implements DownloadFromServer, UploadToServer, SaveToFile, Lockable {
    1818
     19    /** Property used to know if this layer has to be saved on disk */
     20    public static final String REQUIRES_SAVE_TO_DISK_PROP = AbstractModifiableLayer.class.getName() + ".requiresSaveToDisk";
     21    static final String IS_DIRTY_SYMBOL = "*";
     22
    1923    /**
    2024     * Constructs a new {@code ModifiableLayer}.
    2125     * @param name Layer name
     
    5559    }
    5660
    5761    /**
     62     * Determines if this layer is "dirty", i.e. requires save or upload
     63     * @return if this layer is "dirty"
     64     * @since 17626 in {@link OsmDataLayer}
     65     * @since xxx in {@link AbstractModifiableLayer}
     66     */
     67    public boolean isDirty() {
     68        // Override if needed
     69        return requiresSaveToFile() || (requiresUploadToServer() && !isUploadDiscouraged());
     70    }
     71
     72    /**
    5873     * Determines if data managed by this layer has been modified.
    5974     * @return true if data has been modified; false, otherwise
    6075     */
     
    130145        // Override if needed;
    131146        return null;
    132147    }
     148
    133149}
  • trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.layer;
    33
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5import static org.openstreetmap.josm.tools.I18n.trn;
     6
     7import java.awt.Color;
     8import java.awt.Dimension;
     9import java.awt.Graphics2D;
     10import java.awt.event.ActionEvent;
     11import java.io.File;
     12import java.time.Instant;
     13import java.util.ArrayList;
     14import java.util.Arrays;
     15import java.util.Collections;
     16import java.util.List;
     17import java.util.NoSuchElementException;
     18import java.util.stream.Collectors;
     19
     20import javax.swing.AbstractAction;
     21import javax.swing.Action;
     22import javax.swing.Icon;
     23import javax.swing.JScrollPane;
     24import javax.swing.SwingUtilities;
     25
    426import org.openstreetmap.josm.actions.AutoScaleAction;
    527import org.openstreetmap.josm.actions.ExpertToggleAction;
    628import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener;
     
    1133import org.openstreetmap.josm.data.SystemOfMeasurement;
    1234import org.openstreetmap.josm.data.gpx.GpxConstants;
    1335import org.openstreetmap.josm.data.gpx.GpxData;
     36import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeEvent;
    1437import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
    1538import org.openstreetmap.josm.data.gpx.IGpxTrack;
    1639import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
     
    3558import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
    3659import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    3760import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
     61import org.openstreetmap.josm.gui.util.GuiHelper;
    3862import org.openstreetmap.josm.gui.widgets.HtmlPanel;
    3963import org.openstreetmap.josm.tools.ImageProvider;
    4064import org.openstreetmap.josm.tools.Logging;
     
    4165import org.openstreetmap.josm.tools.Utils;
    4266import org.openstreetmap.josm.tools.date.Interval;
    4367
    44 import javax.swing.AbstractAction;
    45 import javax.swing.Action;
    46 import javax.swing.Icon;
    47 import javax.swing.JScrollPane;
    48 import javax.swing.SwingUtilities;
    49 import java.awt.Color;
    50 import java.awt.Dimension;
    51 import java.awt.Graphics2D;
    52 import java.awt.event.ActionEvent;
    53 import java.io.File;
    54 import java.time.Instant;
    55 import java.util.ArrayList;
    56 import java.util.Arrays;
    57 import java.util.Collections;
    58 import java.util.List;
    59 import java.util.NoSuchElementException;
    60 import java.util.stream.Collectors;
    61 
    62 import static org.openstreetmap.josm.tools.I18n.tr;
    63 import static org.openstreetmap.josm.tools.I18n.trn;
    64 
    6568/**
    6669 * A layer that displays data from a Gpx file / the OSM gpx downloads.
    6770 */
     
    8386    /**
    8487     * Added as field to be kept as reference.
    8588     */
    86     private final GpxDataChangeListener dataChangeListener = e -> this.invalidate();
     89    private final GpxDataChangeListener dataChangeListener = new GpxDataChangeListener() {
     90        @Override
     91        public void gpxDataChanged(GpxDataChangeEvent e) {
     92            invalidate();
     93        }
     94
     95        @Override
     96        public void modifiedStateChanged(boolean modified) {
     97            GuiHelper.runInEDT(() ->
     98                propertyChangeSupport.firePropertyChange(REQUIRES_SAVE_TO_DISK_PROP, !modified, modified)
     99            );
     100        }
     101    };
    87102    /**
    88103     * The MarkerLayer imported from the same file.
    89104     */
     
    356371    }
    357372
    358373    @Override
     374    public String getLabel() {
     375        return isDirty() ? super.getLabel() + ' ' + IS_DIRTY_SYMBOL : super.getLabel();
     376    }
     377
     378    @Override
    359379    public void visitBoundingBox(BoundingXYVisitor v) {
    360380        v.visit(data.recalculateBounds());
    361381    }
     
    592612
    593613    @Override
    594614    public synchronized void destroy() {
     615        if (linkedMarkerLayer != null && MainApplication.getLayerManager().containsLayer(linkedMarkerLayer)) {
     616            linkedMarkerLayer.data.transferLayerPrefs(data.getLayerPrefs());
     617        }
    595618        data.clear();
    596619        data = null;
    597620        super.destroy();
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
    a b  
    147147    private static final int HATCHED_SIZE = 15;
    148148    // U+2205 EMPTY SET
    149149    private static final String IS_EMPTY_SYMBOL = "\u2205";
    150     private static final String IS_DIRTY_SYMBOL = "*";
    151     /** Property used to know if this layer has to be saved on disk */
    152     public static final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
    153150    /** Property used to know if this layer has to be uploaded */
    154151    public static final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer";
    155152
     
    10651062        return getAssociatedFile() != null && requiresSaveToFile;
    10661063    }
    10671064
    1068     /**
    1069      * Determines if this layer is "dirty", i.e., requires save or upload
    1070      * @return if this layer is "dirty"
    1071      * @since 17626
    1072      */
    1073     public boolean isDirty() {
    1074         return requiresSaveToFile() || (requiresUploadToServer() && !isUploadDiscouraged());
    1075     }
    1076 
    10771065    @Override
    10781066    public String getLabel() {
    10791067        String label = super.getLabel();
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java b/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
    a b  
    189189     */
    190190    public WayPoint convertToWayPoint() {
    191191        WayPoint wpt = new WayPoint(getCoor());
    192         wpt.setTimeInMillis((long) (time * 1000));
     192        if (time != 0) {
     193            wpt.setTimeInMillis((long) (time * 1000));
     194        }
    193195        if (text != null) {
    194196            wpt.getExtensions().add("josm", "text", text);
    195197        } else if (dataProvider != null) {
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
    a b  
    2020import java.util.ArrayList;
    2121import java.util.Collection;
    2222import java.util.Comparator;
     23import java.util.HashMap;
    2324import java.util.List;
     25import java.util.Map;
    2426import java.util.Optional;
    2527
    2628import javax.swing.AbstractAction;
     
    3739import org.openstreetmap.josm.data.gpx.GpxData;
    3840import org.openstreetmap.josm.data.gpx.GpxExtension;
    3941import org.openstreetmap.josm.data.gpx.GpxLink;
     42import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs;
    4043import org.openstreetmap.josm.data.gpx.WayPoint;
    4144import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    4245import org.openstreetmap.josm.data.preferences.IntegerProperty;
     
    5659import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    5760import org.openstreetmap.josm.io.audio.AudioPlayer;
    5861import org.openstreetmap.josm.spi.preferences.Config;
     62import org.openstreetmap.josm.tools.ColorHelper;
    5963import org.openstreetmap.josm.tools.ImageProvider;
    6064import org.openstreetmap.josm.tools.Logging;
    6165import org.openstreetmap.josm.tools.Utils;
     
    7680    /**
    7781     * A list of markers.
    7882     */
    79     public final List<Marker> data;
     83    public final MarkerData data;
    8084    private boolean mousePressed;
    8185    public GpxLayer fromLayer;
    8286    private Marker currentMarker;
     
    100104    public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
    101105        super(name);
    102106        this.setAssociatedFile(associatedFile);
    103         this.data = new ArrayList<>();
     107        this.data = new MarkerData();
    104108        this.fromLayer = fromLayer;
    105109        double firstTime = -1.0;
    106110        String lastLinkedFile = "";
    107111
     112        if (fromLayer == null || fromLayer.data == null) {
     113            data.ownLayerPrefs = indata.getLayerPrefs();
     114        }
     115
     116        String cs = GPXSettingsPanel.tryGetDataPrefLocal(data, "markers.color");
    108117        Color c = null;
    109         String cs = GPXSettingsPanel.tryGetLayerPrefLocal(indata, "markers.color");
    110118        if (cs != null) {
    111             try {
    112                 c = Color.decode(cs);
    113             } catch (NumberFormatException ex) {
     119            c = ColorHelper.html2color(cs);
     120            if (c == null) {
    114121                Logging.warn("Could not read marker color: " + cs);
    115122            }
    116123        }
     
    459466     * @return <code>true</code> if text should be shown, <code>false</code> otherwise.
    460467     */
    461468    private boolean isTextOrIconShown() {
    462         return Boolean.parseBoolean(GPXSettingsPanel.getLayerPref(fromLayer, "markers.show-text"));
     469        return Boolean.parseBoolean(GPXSettingsPanel.getDataPref(data, "markers.show-text"));
    463470    }
    464471
    465472    @Override
     
    475482    @Override
    476483    public void setColor(Color color) {
    477484        setPrivateColors(color);
    478         if (fromLayer != null) {
    479             String cs = null;
    480             if (color != null) {
    481                 cs = String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue());
    482             }
    483             GPXSettingsPanel.putLayerPrefLocal(fromLayer, "markers.color", cs);
     485        String cs = null;
     486        if (color != null) {
     487            cs = ColorHelper.color2html(color);
    484488        }
     489        GPXSettingsPanel.putDataPrefLocal(data, "markers.color", cs);
    485490        invalidate();
    486491    }
    487492
     
    533538
    534539        @Override
    535540        public void actionPerformed(ActionEvent e) {
    536             GPXSettingsPanel.putLayerPrefLocal(layer.fromLayer, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown()));
     541            GPXSettingsPanel.putDataPrefLocal(layer.data, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown()));
    537542            layer.invalidate();
    538543        }
    539544
     
    617622            invalidate();
    618623        }
    619624    }
     625
     626    /**
     627     * the data of a MarkerLayer
     628     */
     629    public class MarkerData extends ArrayList<Marker> implements IGpxLayerPrefs {
     630
     631        private Map<String, String> ownLayerPrefs;
     632
     633        @Override
     634        public Map<String, String> getLayerPrefs() {
     635            if (ownLayerPrefs == null && fromLayer != null && fromLayer.data != null) {
     636                return fromLayer.data.getLayerPrefs();
     637            }
     638            // fallback to own layerPrefs if the corresponding gpxLayer has already been deleted
     639            // by the user or never existed when loaded from a session file
     640            if (ownLayerPrefs == null) {
     641                ownLayerPrefs = new HashMap<>();
     642            }
     643            return ownLayerPrefs;
     644        }
     645
     646        /**
     647         * Transfers the layerPrefs from the GpxData to MarkerData (when GpxData is deleted)
     648         * @param gpxLayerPrefs the layerPrefs from the GpxData object
     649         */
     650        public void transferLayerPrefs(Map<String, String> gpxLayerPrefs) {
     651            ownLayerPrefs = new HashMap<>(gpxLayerPrefs);
     652        }
     653
     654        @Override
     655        public void setModified(boolean value) {
     656            if (fromLayer != null && fromLayer.data != null) {
     657                fromLayer.data.setModified(value);
     658            }
     659        }
     660
     661    }
    620662}
  • trunk/src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java

    diff --git a/trunk/src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java b/trunk/src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java
    a b  
    2929import org.apache.commons.jcs3.access.exception.InvalidArgumentException;
    3030import org.openstreetmap.josm.actions.ExpertToggleAction;
    3131import org.openstreetmap.josm.data.gpx.GpxData;
     32import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs;
    3233import org.openstreetmap.josm.gui.MainApplication;
    3334import org.openstreetmap.josm.gui.layer.GpxLayer;
    3435import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
     
    172173     * @return the value
    173174     */
    174175    public static String getLayerPref(GpxLayer layer, String key) {
     176        GpxData data = layer != null ? layer.data : null;
     177        return getDataPref(data, key);
     178    }
     179
     180    /**
     181     * Reads the preference for the given layer or the default preference if not available
     182     * @param data the data. Can be <code>null</code>, default preference will be returned then
     183     * @param key the drawing key to be read, without "draw.rawgps."
     184     * @return the value
     185     */
     186    public static String getDataPref(IGpxLayerPrefs data, String key) {
    175187        Object d = DEFAULT_PREFS.get(key);
    176188        String ds;
    177189        if (d != null) {
     
    180192            Logging.warn("No default value found for layer preference \"" + key + "\".");
    181193            ds = null;
    182194        }
    183         return Optional.ofNullable(tryGetLayerPrefLocal(layer, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds));
     195        return Optional.ofNullable(tryGetDataPrefLocal(data, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds));
    184196    }
    185197
    186198    /**
     
    190202     * @return the integer value
    191203     */
    192204    public static int getLayerPrefInt(GpxLayer layer, String key) {
    193         String s = getLayerPref(layer, key);
     205        GpxData data = layer != null ? layer.data : null;
     206        return getDataPrefInt(data, key);
     207    }
     208
     209    /**
     210     * Reads the integer preference for the given data or the default preference if not available
     211     * @param data the data. Can be <code>null</code>, default preference will be returned then
     212     * @param key the drawing key to be read, without "draw.rawgps."
     213     * @return the integer value
     214     */
     215    public static int getDataPrefInt(IGpxLayerPrefs data, String key) {
     216        String s = getDataPref(data, key);
    194217        if (s != null) {
    195218            try {
    196219                return Integer.parseInt(s);
     
    213236     * @return the value or <code>null</code> if not found
    214237     */
    215238    public static String tryGetLayerPrefLocal(GpxLayer layer, String key) {
    216         return layer != null ? tryGetLayerPrefLocal(layer.data, key) : null;
     239        return layer != null ? tryGetDataPrefLocal(layer.data, key) : null;
    217240    }
    218241
    219242    /**
     
    222245     * @param key the drawing key to be read, without "draw.rawgps."
    223246     * @return the value or <code>null</code> if not found
    224247     */
    225     public static String tryGetLayerPrefLocal(GpxData data, String key) {
     248    public static String tryGetDataPrefLocal(IGpxLayerPrefs data, String key) {
    226249        return data != null ? data.getLayerPrefs().get(key) : null;
    227250    }
    228251
     
    236259        String v = value == null ? null : value.toString();
    237260        if (layers != null) {
    238261            for (GpxLayer l : layers) {
    239                 putLayerPrefLocal(l.data, key, v);
     262                putDataPrefLocal(l.data, key, v);
    240263            }
    241264        } else {
    242265            Config.getPref().put("draw.rawgps." + key, v);
     
    251274     */
    252275    public static void putLayerPrefLocal(GpxLayer layer, String key, String value) {
    253276        if (layer == null) return;
    254         putLayerPrefLocal(layer.data, key, value);
     277        putDataPrefLocal(layer.data, key, value);
    255278    }
    256279
    257280    /**
     
    260283     * @param key the drawing key to be written, without "draw.rawgps."
    261284     * @param value the value or <code>null</code> to remove key
    262285     */
    263     public static void putLayerPrefLocal(GpxData data, String key, String value) {
     286    public static void putDataPrefLocal(IGpxLayerPrefs data, String key, String value) {
     287        if (data == null) return;
     288        data.setModified(true);
    264289        if (value == null || value.trim().isEmpty() ||
    265290                (getLayerPref(null, key).equals(value) && DEFAULT_PREFS.get(key) != null && DEFAULT_PREFS.get(key).toString().equals(value))) {
    266291            data.getLayerPrefs().remove(key);
  • trunk/src/org/openstreetmap/josm/io/GpxWriter.java

    diff --git a/trunk/src/org/openstreetmap/josm/io/GpxWriter.java b/trunk/src/org/openstreetmap/josm/io/GpxWriter.java
    a b  
    127127
    128128        validprefixes = namespaces.stream().map(n -> n.getPrefix()).collect(Collectors.toList());
    129129
     130        data.creator = JOSM_CREATOR_NAME;
    130131        out.println("<?xml version='1.0' encoding='UTF-8'?>");
    131         out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
    132132
     133        out.print("<gpx version=\"1.1\" creator=\"");
     134        out.print(JOSM_CREATOR_NAME);
     135        out.println("\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
     136
    133137        StringBuilder schemaLocations = new StringBuilder("http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");
    134138
    135139        for (XMLNamespace n : namespaces) {
  • trunk/src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java

    diff --git a/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java b/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java
    a b  
    109109         */
    110110        public void write(MarkerLayer layer) {
    111111            GpxData data = new GpxData();
     112            layer.data.getLayerPrefs().forEach((k, v) -> {
     113                if (k != null && k.indexOf("markers.") == 0) {
     114                    data.getLayerPrefs().put(k, v);
     115                }
     116            });
    112117            data.put(GpxData.META_DESC, "exported JOSM marker layer");
    113118            for (Marker m : layer.data) {
    114119                data.waypoints.add(m.convertToWayPoint());
  • trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java

    diff --git a/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java b/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java
    a b  
    55
    66import java.io.IOException;
    77import java.io.InputStream;
    8 import java.util.List;
    98
    109import javax.xml.xpath.XPath;
    1110import javax.xml.xpath.XPathConstants;
     
    1413import javax.xml.xpath.XPathFactory;
    1514
    1615import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
    17 import org.openstreetmap.josm.gui.layer.GpxLayer;
    1816import org.openstreetmap.josm.gui.layer.Layer;
    1917import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    2018import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     
    4947
    5048                support.addPostLayersTask(importData.getPostLayerTask());
    5149
    52                 GpxLayer gpxLayer = null;
    53                 List<SessionReader.LayerDependency> deps = support.getLayerDependencies();
    54                 if (!deps.isEmpty()) {
    55                     Layer layer = deps.get(0).getLayer();
    56                     if (layer instanceof GpxLayer) {
    57                         gpxLayer = (GpxLayer) layer;
    58                     }
    59                 }
    60 
    61                 MarkerLayer markerLayer = importData.getMarkerLayer();
    62                 if (markerLayer != null) {
    63                     markerLayer.fromLayer = gpxLayer;
    64                 }
    65 
    66                 return markerLayer;
     50                return importData.getMarkerLayer();
    6751            }
    6852        } catch (XPathExpressionException e) {
    6953            throw new IllegalDataException(e);
  • trunk/src/org/openstreetmap/josm/io/session/SessionReader.java

    diff --git a/trunk/src/org/openstreetmap/josm/io/session/SessionReader.java b/trunk/src/org/openstreetmap/josm/io/session/SessionReader.java
    a b  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.Color;
    76import java.awt.GraphicsEnvironment;
    87import java.io.BufferedInputStream;
    98import java.io.File;
     
    4645import org.openstreetmap.josm.io.Compression;
    4746import org.openstreetmap.josm.io.IllegalDataException;
    4847import org.openstreetmap.josm.tools.CheckParameterUtil;
    49 import org.openstreetmap.josm.tools.ColorHelper;
    5048import org.openstreetmap.josm.tools.JosmRuntimeException;
    5149import org.openstreetmap.josm.tools.Logging;
    5250import org.openstreetmap.josm.tools.MultiMap;
     
    619617                    Logging.warn(ex);
    620618                }
    621619            }
    622             String colorString = el.getAttribute("color");
    623             if (colorString != null) {
    624                 try {
    625                     Color color = ColorHelper.html2color(colorString);
    626                     layer.setColor(color);
    627                 } catch (RuntimeException ex) {
    628                     Logging.warn("Cannot parse color " + colorString);
    629                 }
    630             }
    631620            layer.setName(names.get(entry.getKey()));
    632621            layers.add(layer);
    633622        }
  • trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java

    diff --git a/trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java b/trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java
    a b  
    4343import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
    4444import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    4545import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
    46 import org.openstreetmap.josm.tools.ColorHelper;
    4746import org.openstreetmap.josm.tools.JosmRuntimeException;
    4847import org.openstreetmap.josm.tools.Logging;
    4948import org.openstreetmap.josm.tools.MultiMap;
     
    240239            if (!Utils.equalsEpsilon(layer.getOpacity(), 1.0)) {
    241240                el.setAttribute("opacity", Double.toString(layer.getOpacity()));
    242241            }
    243             if (layer.getColor() != null) {
    244                 el.setAttribute("color", ColorHelper.color2html(layer.getColor()));
    245             }
    246242            Set<Layer> deps = dependencies.get(layer);
    247243            final String depends = deps == null ? "" : deps.stream().map(depLayer -> {
    248244                int depIndex = layers.indexOf(depLayer);
  • trunk/test/data/sessions/data_export.gpx

    diff --git a/trunk/test/data/sessions/data_export.gpx b/trunk/test/data/sessions/data_export.gpx
    new file mode 100644
    a b  
     1<?xml version='1.0' encoding='UTF-8'?>
     2<gpx version="1.1" creator="JOSM GPX export" xmlns="http://www.topografix.com/GPX/1/1"
     3    xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1"
     4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd">
     6  <metadata>
     7    <bounds minlat="42.72659" minlon="-0.00749" maxlat="42.72665" maxlon="-0.00747"/>
     8    <extensions>
     9      <josm:layerPreferences>
     10        <josm:entry key="markers.color" value="#34567812"/>
     11      </josm:layerPreferences>
     12    </extensions>
     13  </metadata>
     14  <wpt lat="42.72665" lon="-0.00747">
     15    <time>2021-01-01T10:15:30Z</time>
     16  </wpt>
     17  <wpt lat="42.72659" lon="-0.00749"/>
     18</gpx>
     19 No newline at end of file
  • trunk/test/data/sessions/gpx_markers.jos

    diff --git a/trunk/test/data/sessions/gpx_markers.jos b/trunk/test/data/sessions/gpx_markers.jos
    a b  
    1515        <layer index="1" name="GPX layer name" type="tracks" version="0.1" visible="true">
    1616            <file>layers/01/data.gpx</file>
    1717        </layer>
    18         <layer color="#34567812" index="2" name="Marker layer name" opacity="0.5" type="markers" version="0.1" visible="true">
     18        <layer index="2" name="Marker layer name" opacity="0.5" type="markers" version="0.1" visible="true">
    1919            <file>layers/02/data.gpx</file>
    2020        </layer>
    2121    </layers>
  • trunk/test/data/sessions/gpx_markers.joz

    diff --git a/trunk/test/data/sessions/gpx_markers.joz b/trunk/test/data/sessions/gpx_markers.joz
    GIT binary patch
    literal 1636
    zc$^FHW@Zs#;Nak3=q|eu#DD~ZfH)_yGPS5!-@s5mC9xz?FTJ2*Zji74V*`;r${*!*
    z-Y;DKWAPSV7rz~Ey|gbVF$&$9ZC*J=XQS-Rwk7}Ta@(hgt#uIiIjQpVv$HnQbNW{~
    zpHGO*F0p*va#rBQEt9oz(>LFq{r=|5H+!t?gkIO(xoDs+v-noimt)g@JYWCX`^)23
    zd<Snxzc%EWbZhF5gTMDJD3M@$<ePsj?A~_emA#7^eJr2;_`%Vtf3bW2v6Zp@e-xLo
    zzum+7!Ry-+Mk$*VgI^77;(FbB-$dQ6%kjwib!^PGO7FS6`*io~UyM`E+*+Vn%6mKP
    zx|m+5o{w&A*Y|~mUlf(AR2OGoP-m{4CZGO5%T17LPtKC*S4DhG9=yuhC6p1cOGWjq
    za>SLByb+GV4_p5nw|RL?@#-tTUqbrNROYXrmTsX~s4l78^R&C;L}CBtD8CbO>c>m8
    ze`kGT<&5YFQ{fI5dv~zyIotZDx{O=4OMl^*E#$v0HX=V=$L;*4>L#Ij>qwpOpPKXE
    zFKZ3^HE+j){5Kp`8;lLV$nEVcn~|;kB$`#!{&eKA3Jdcd(U*V3Os1-RRQMi$cgMBE
    zbL(8gk6aJ<%N*d%&awE}Bf0sE3=9XD85jbHie4i`^oIE6&jCiS`>Fa3cMi|naQ1ke
    zh-6XrQmsH`)v1f_#Z)MkFe=~Xe6xT3*=eyJx6dxSb8pYbJs)qh#eXerjdOSzWOnk9
    zdQRhllPPg#uA6?8{XcnIXWgaK+;Lm}Hn!D1N}i%A;I@jT{%2Krv)z3=g%{B)dECM_
    zUF57YTGp%ic2}Ro%~MXD$2Y0ZT(eR&c>b>+pO|juFF0<$bRB<ojAIISuNl{YD~nVu
    z1706&z87(2*M)h-hM^nY{^4nj={)x?Z{3kJuf2Nd-*!*%2))o?IN$D0%>7jV0Pgi$
    zZY?^Q5VPuZ1pnOZnViNfU(;A#__lrashYdE-0gGRze?kOa`*nPD>g2C_2{vw#6)i8
    zOSLopwAbVn^}gD)vc<DNWJ2kNsJbJ}j~0EENX(J7bonBDE^DW|+8onmi{BLf%RiEI
    zt~91oc11L6|AIGv+mowR9Fni_<t9#l;1d>f)rh?(&gCl)XIeYwG(UgNqF<+&mTz8L
    zym*`PRzcGx?-C_1ut}<J(p}D6k!1c(^hopJFU|*)wk6H++fcvb2LH7Hnbw@UQeU)g
    zR<5}`VL{WC{zJTxQ6IUM=>|ugX8c^+pS!2w?&eS#w!#zpimkTvo#Zw4k0=mNsQXnP
    z)iF)Q=DJhCtwRC53wc_9i|(?x91lt(SwEbnKLMr@OICbogd2#9Q;Ul;^Yiqw@{5;F
    zI+%UPK;r28L;u<*RL@@aYR#l;lPjb+cE6dmbiUp!Gu6)6zwf8J-CdQOyupG0es%wG
    z{rFSUVwVPRAM=WRKI0gtTL5?LsjBIZ7vFZ1j1yDdc70oy&JDY7qW(!;-R**(+P4bb
    zlg_=9$~UXwR-<FrZkhCtHqw_fXQi1e?Ju#5{F$}n*Q^(>QiZMNT`sYEZEKXXsY%;G
    z^vA9hFYRwOe%j+Xr{(j^v?qKmrf%;YE%<G8{sbw1T(-2KQIctsPK()w6$d!pZAm<$
    zT|To-T5iT0UY(z7=lp!?wDFeBdhy3OR>>-7!hhs%@cB6Tz5AVR-6s}D53fs5ikyDo
    zXKF%7^&A=d!eWl>_h;h{d6>s|l)svv(>2lB)Gn-b#?kHXJEJn6uKu*o*!{rw@OxgY
    zM`w1bOqp`$W5xB79+u=CEdsv;&GsHzXRdF)B;721hs7Uu-M`wmQa9D5r2Lp&{ScgM
    zJ0+!+BY`m@2hO#OOd<@3I6=<QLJa5tvyevDj~v^m`tLJhD+SQ?BfE_oML#nON(u?^
    RW@Q5l1K~~}J(U&20|2&tzI6Zq
  • trunk/test/data/sessions/markers.gpx

    literal 1371
    zc$^FHW@Zs#U|`^25Zv|ECGOaQYwnB;3_VN?3_?KBoW#o1qGEjmL;aM*l0?1qf{NZr
    zvDt?VMB3i7{&$`>JI+-8?}x=pL~q_m@G-m4=(C5zboEg)P0xtmpRb1rwl3U#`k{^b
    z`(mdXYc#n(C~rAAwMVwAgSo}$s^0X-H#O_#-R(5?ZeyMndRDSoB#<LWcSopI+xqvX
    zSu1`<FkcMuisrbby|nY;y<1v`cC5=z30-P*cK>I#rK^tU_Gm6EuwY=2`MARH=T(ol
    z%1e#k2s(YN`nb*K`=Td#dc9ALL<Q!g`O3}Q_jOrNUiI$XJ0IvZ-YyMHE;D$UG~;Uf
    zlNrZV^j#0hnf3dBzFKoRH@9`;fvkCQ+itf!l(RVac8=i9l~&iLva)VB<gqdN=C)X%
    zX;E<*r&5Yc&qK+)!oZFewtp<GJmt^3r=7hm(jV|@Qs?W5qLQxr#D6~Nez>o+??~-4
    z-qwPuwFZBk6*(sUk7s?qjp?v>|0VIe?_b@s{>d5u5B3+pVE;TP&cy;4>`iFFZUhhZ
    zxf7zj4;zRazt8@guYJ##l{?Smma4wJCz9fPkn`~N8^`o^sHt{ttE;Y_xv9%Q;N0bt
    z@9QlezFuZ?sC|o{<Xje>%M44-`K&9M$l||y_6Mto<(E12S3P5Q<}qfOIrWhA>;=1j
    zAJ@HO_r}p|>P9)o?3J-ieD}f)6-#52^R<E_KCY6QGjq}wgT$7FFIgDaZ1UzvdA`!l
    z$#G1ZVzlXgp4{eU9oH3GK0Bog9rS!1^5)m_-jglwZ^xc#?Pke5f3){>;H{${f~qER
    z>t9lw^88Zj%YwCM7Vh6K^36BOqRT(i@@1p&pQL-G?-qpo(x_>AW-N1dTDRyc^Vs_n
    zlDrmIZWlZ&xjBYswy~O1N`{Nu)QZ^3A4_FU?>k))dTPpAC8?lm=lU+~m@l^Y{G2c6
    zogK1&E-$}c6Z3ojy*<C5e&I%hGynFApxLK4UFHCWvkx$wxfx^_ic^b=GxPKGvhs^V
    zLpT|jd$&d>fN*IAHv=QfSD;F;=B1N1<{efLaQN==m%U`J?1tG(BsUnBt-7*N`{?uz
    zHfipT?Ekfl>RY2%1}#-GymR;QyT%9Y3okBle?2AB`^0q~j%}B&tg@Y2@bP#Ke|y%^
    z`@Pvl?RrVZ=dM|Xlxqu#y%()3Irb}iPLWV_|Fn#U%F<D<Uy3mAIx<H@%Coug?5-VG
    z8>~*8kyvhjt629~!p1j~j9jF0b2z`V-rf*9W5q40_6yt|+EO|XUp}4clhV`pV<UHD
    zeyff3k2>Q^+RN{iXvGVNZN9W;@udh~sVPslh{dh(zk484OXo;NQ(buNjhyoS-+O*c
    z(#_j+{?NS3zt8SlCLHwBP%1C~oA052i{t-<{+Bx1{=DR;rqd(g0B=Snc?Mjig$e@%
    zC_rh<vV#jM$H*YTu&{A4kLJ?zjHk^axsYjOJ20~bvK{4&*fI*T9in0P|A5T2@VWa7
    vnFiS*$BZjkNI>;7Ff3`j4-`R3B&?7`f|fu6yjj^mnwWu55-6y{0^$Jxc|SI;
    diff --git a/trunk/test/data/sessions/markers.gpx b/trunk/test/data/sessions/markers.gpx
    new file mode 100644
    a b  
     1<?xml version='1.0' encoding='UTF-8'?>
     2<gpx version="1.1" creator="JOSM GPX export" xmlns="http://www.topografix.com/GPX/1/1"
     3    xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1"
     4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd">
     6  <metadata>
     7    <desc>exported JOSM marker layer</desc>
     8    <bounds minlat="42.72659" minlon="-0.00749" maxlat="42.72665" maxlon="-0.00747"/>
     9    <extensions>
     10      <josm:layerPreferences>
     11        <josm:entry key="markers.color" value="#34567812"/>
     12      </josm:layerPreferences>
     13    </extensions>
     14  </metadata>
     15  <wpt lat="42.72665" lon="-0.00747">
     16    <time>2021-01-01T10:15:30Z</time>
     17  </wpt>
     18  <wpt lat="42.72659" lon="-0.00749"/>
     19</gpx>
     20 No newline at end of file
  • trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java

    diff --git a/trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java b/trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java
    a b  
    88import java.util.ArrayList;
    99import java.util.HashMap;
    1010
     11import org.junit.jupiter.api.Test;
    1112import org.junit.jupiter.api.extension.RegisterExtension;
    12 import org.junit.jupiter.api.Test;
    1313import org.openstreetmap.josm.TestUtils;
    1414import org.openstreetmap.josm.testutils.JOSMTestRules;
    1515import org.openstreetmap.josm.tools.ListenerList;
     
    3131    public JOSMTestRules test = new JOSMTestRules();
    3232
    3333    /**
    34      * Tests weather the track can read and write colors.
     34     * Tests whether the track can read and write colors.
    3535     */
    3636    @Test
    3737    void testColors() {
  • trunk/test/unit/org/openstreetmap/josm/io/session/SessionWriterTest.java

    diff --git a/trunk/test/unit/org/openstreetmap/josm/io/session/SessionWriterTest.java b/trunk/test/unit/org/openstreetmap/josm/io/session/SessionWriterTest.java
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.io.session;
    33
     4import static org.junit.jupiter.api.Assertions.assertEquals;
     5import static org.junit.jupiter.api.Assertions.fail;
     6
    47import java.awt.Color;
    58import java.io.File;
    69import java.io.IOException;
    7 import java.io.InputStream;
    810import java.nio.charset.StandardCharsets;
    911import java.nio.file.Files;
    1012import java.nio.file.Path;
    1113import java.nio.file.Paths;
     14import java.time.Instant;
    1215import java.util.Arrays;
    1316import java.util.Collections;
    1417import java.util.HashMap;
    1518import java.util.List;
    1619import java.util.Map;
     20import java.util.stream.Collectors;
     21import java.util.zip.ZipEntry;
    1722import java.util.zip.ZipFile;
    1823
    1924import org.junit.jupiter.api.BeforeEach;
     
    4247
    4348import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    4449
    45 import static org.junit.jupiter.api.Assertions.assertEquals;
    46 
    4750/**
    4851 * Unit tests for Session writing.
    4952 */
     
    108111        MainApplication.getLayerManager().addLayer(createOsmLayer());
    109112    }
    110113
    111     private byte[] testWrite(List<Layer> layers, final boolean zip) throws IOException {
     114    private Map<String, byte[]> testWrite(List<Layer> layers, final boolean zip) throws IOException {
    112115        Map<Layer, SessionLayerExporter> exporters = new HashMap<>();
    113116        if (zip) {
    114117            SessionWriter.registerSessionLayerExporter(OsmDataLayer.class, OsmHeadlessJozExporter.class);
     
    127130            if (!zip) {
    128131                return null;
    129132            }
    130             try (ZipFile zipFile = new ZipFile(file);
    131                  InputStream input = zipFile.getInputStream(zipFile.getEntry("session.jos"))) {
    132                 return Utils.readBytesFromStream(input);
     133            try (ZipFile zipFile = new ZipFile(file)) {
     134                return Collections.list(zipFile.entries()).stream().collect(Collectors.toMap(ZipEntry::getName, e -> {
     135                    try {
     136                        return Utils.readBytesFromStream(zipFile.getInputStream(e));
     137                    } catch (IOException ex) {
     138                        fail(ex);
     139                    }
     140                    return null;
     141                }));
    133142            }
    134143        } finally {
    135144            if (file.exists()) {
     
    146155
    147156    private GpxLayer createGpxLayer() {
    148157        GpxData data = new GpxData();
    149         data.waypoints.add(new WayPoint(new LatLon(42.72665, -0.00747)));
     158        WayPoint wp = new WayPoint(new LatLon(42.72665, -0.00747));
     159        wp.setInstant(Instant.parse("2021-01-01T10:15:30.00Z"));
     160        data.waypoints.add(wp);
    150161        data.waypoints.add(new WayPoint(new LatLon(42.72659, -0.00749)));
    151162        GpxLayer layer = new GpxLayer(data, "GPX layer name");
    152163        layer.setAssociatedFile(new File("data.gpx"));
     
    232243    @Test
    233244    void testWriteGpxAndMarkerJoz() throws IOException {
    234245        GpxLayer gpx = createGpxLayer();
    235         byte[] bytes = testWrite(Arrays.asList(gpx, createMarkerLayer(gpx)), true);
     246        Map<String, byte[]> bytes = testWrite(Arrays.asList(gpx, createMarkerLayer(gpx)), true);
     247
    236248        Path path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/gpx_markers.jos");
    237249        String expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
    238         String actual = new String(bytes, StandardCharsets.UTF_8).replace("\r", "");
     250        String actual = new String(bytes.get("session.jos"), StandardCharsets.UTF_8).replace("\r", "");
    239251        assertEquals(expected, actual);
     252
     253        path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/data_export.gpx");
     254        expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
     255        actual = new String(bytes.get("layers/01/data.gpx"), StandardCharsets.UTF_8).replace("\r", "");
     256        assertEquals(expected, actual);
     257
     258        path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/markers.gpx");
     259        expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
     260        actual = new String(bytes.get("layers/02/data.gpx"), StandardCharsets.UTF_8).replace("\r", "");
     261        assertEquals(expected, actual);
    240262    }
    241263
    242264    /**