-
diff --git a/trunk/src/org/openstreetmap/josm/actions/SaveAction.java b/trunk/src/org/openstreetmap/josm/actions/SaveAction.java
|
a
|
b
|
|
| 14 | 14 | import javax.swing.JPanel; |
| 15 | 15 | import javax.swing.SwingConstants; |
| 16 | 16 | |
| 17 | | import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener; |
| | 17 | import org.openstreetmap.josm.data.gpx.GpxConstants; |
| 18 | 18 | import org.openstreetmap.josm.gui.ExtendedDialog; |
| 19 | 19 | import org.openstreetmap.josm.gui.MainApplication; |
| | 20 | import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; |
| 20 | 21 | import org.openstreetmap.josm.gui.layer.GpxLayer; |
| 21 | 22 | import org.openstreetmap.josm.gui.layer.Layer; |
| 22 | 23 | import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent; |
| 23 | 24 | import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; |
| 24 | | import org.openstreetmap.josm.gui.layer.OsmDataLayer; |
| 25 | 25 | import org.openstreetmap.josm.gui.layer.SaveToFile; |
| 26 | 26 | import org.openstreetmap.josm.gui.util.GuiHelper; |
| 27 | 27 | import org.openstreetmap.josm.spi.preferences.Config; |
| … |
… |
|
| 37 | 37 | private static final SaveAction instance = new SaveAction(); |
| 38 | 38 | |
| 39 | 39 | 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())) { |
| 41 | 41 | updateEnabledState(); |
| 42 | 42 | } |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | | private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState(); |
| 46 | | |
| 47 | 45 | /** |
| 48 | 46 | * Construct the action with "Save" as label. |
| 49 | 47 | */ |
| … |
… |
|
| 68 | 66 | @Override |
| 69 | 67 | public void layerAdded(LayerAddEvent e) { |
| 70 | 68 | Layer l = e.getAddedLayer(); |
| 71 | | if (l instanceof OsmDataLayer) { |
| | 69 | if (l instanceof AbstractModifiableLayer) { |
| 72 | 70 | l.addPropertyChangeListener(updateOnRequireSaveChange); |
| 73 | 71 | } |
| 74 | | if (l instanceof GpxLayer) { |
| 75 | | ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx); |
| 76 | | } |
| 77 | 72 | super.layerAdded(e); |
| 78 | 73 | } |
| 79 | 74 | |
| … |
… |
|
| 80 | 75 | @Override |
| 81 | 76 | public void layerRemoving(LayerRemoveEvent e) { |
| 82 | 77 | Layer l = e.getRemovedLayer(); |
| 83 | | if (l instanceof OsmDataLayer) { |
| | 78 | if (l instanceof AbstractModifiableLayer) { |
| 84 | 79 | l.removePropertyChangeListener(updateOnRequireSaveChange); |
| 85 | 80 | } |
| 86 | | if (l instanceof GpxLayer) { |
| 87 | | ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx); |
| 88 | | } |
| 89 | 81 | super.layerRemoving(e); |
| 90 | 82 | } |
| 91 | 83 | }; |
| … |
… |
|
| 113 | 105 | } |
| 114 | 106 | |
| 115 | 107 | // 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 | |
| 117 | 114 | 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>"); |
| 119 | 118 | label.setHorizontalAlignment(SwingConstants.CENTER); |
| 120 | | JCheckBox remember = new JCheckBox(tr("Remember choice")); |
| | 119 | JCheckBox remember = new JCheckBox(tr("Always overwrite GPX files without asking")); |
| 121 | 120 | remember.setHorizontalAlignment(SwingConstants.CENTER); |
| 122 | 121 | p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10)); |
| 123 | 122 | p.add(remember, GBC.eop().fill(GBC.HORIZONTAL)); |
| … |
… |
|
| 124 | 123 | ExtendedDialog dialog = new ExtendedDialog( |
| 125 | 124 | MainApplication.getMainFrame(), |
| 126 | 125 | 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") |
| 129 | 128 | .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) { |
| 131 | 133 | f = null; |
| 132 | | } else if (remember.isSelected()) { |
| 133 | | Config.getPref().putBoolean("gpx.export.overwrite", true); |
| | 134 | } else { |
| | 135 | return null; |
| 134 | 136 | } |
| 135 | 137 | } |
| 136 | 138 | return f == null ? layer.createAndOpenSaveFileChooser() : f; |
-
diff --git a/trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java b/trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java
|
a
|
b
|
|
| 19 | 19 | import java.util.Map; |
| 20 | 20 | import java.util.Set; |
| 21 | 21 | import java.util.stream.Collectors; |
| | 22 | import java.util.stream.Stream; |
| 22 | 23 | |
| 23 | 24 | import javax.swing.BorderFactory; |
| 24 | 25 | import javax.swing.JCheckBox; |
| … |
… |
|
| 32 | 33 | import javax.swing.border.EtchedBorder; |
| 33 | 34 | import javax.swing.filechooser.FileFilter; |
| 34 | 35 | |
| | 36 | import org.openstreetmap.josm.data.preferences.BooleanProperty; |
| 35 | 37 | import org.openstreetmap.josm.gui.ExtendedDialog; |
| 36 | 38 | import org.openstreetmap.josm.gui.HelpAwareOptionPane; |
| 37 | 39 | import org.openstreetmap.josm.gui.MainApplication; |
| 38 | 40 | import org.openstreetmap.josm.gui.MapFrame; |
| 39 | 41 | import org.openstreetmap.josm.gui.MapFrameListener; |
| | 42 | import org.openstreetmap.josm.gui.Notification; |
| | 43 | import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; |
| 40 | 44 | import org.openstreetmap.josm.gui.layer.Layer; |
| 41 | 45 | import org.openstreetmap.josm.gui.util.WindowGeometry; |
| 42 | 46 | import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; |
| … |
… |
|
| 58 | 62 | private transient Map<Layer, SessionLayerExporter> exporters; |
| 59 | 63 | private transient MultiMap<Layer, Layer> dependencies; |
| 60 | 64 | |
| | 65 | private static final BooleanProperty SAVE_LOCAL_FILES_PROPERTY = new BooleanProperty("session.savelocal", true); |
| | 66 | |
| 61 | 67 | /** |
| 62 | 68 | * Constructs a new {@code SessionSaveAsAction}. |
| 63 | 69 | */ |
| … |
… |
|
| 156 | 162 | .filter(layer -> exporters.get(layer) != null && exporters.get(layer).shallExport()) |
| 157 | 163 | .collect(Collectors.toList()); |
| 158 | 164 | |
| | 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 | |
| 159 | 195 | int active = -1; |
| 160 | 196 | Layer activeLayer = getLayerManager().getActiveLayer(); |
| 161 | 197 | if (activeLayer != null) { |
| … |
… |
|
| 241 | 277 | } |
| 242 | 278 | |
| 243 | 279 | protected final Component build() { |
| | 280 | JPanel op = new JPanel(new GridBagLayout()); |
| 244 | 281 | JPanel ip = new JPanel(new GridBagLayout()); |
| 245 | 282 | for (Layer layer : layers) { |
| 246 | 283 | JPanel wrapper = new JPanel(new GridBagLayout()); |
| … |
… |
|
| 263 | 300 | p.add(sp, GBC.eol().fill()); |
| 264 | 301 | final JTabbedPane tabs = new JTabbedPane(); |
| 265 | 302 | 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; |
| 267 | 310 | } |
| 268 | 311 | |
| 269 | 312 | protected final Component getDisabledExportPanel(Layer layer) { |
-
diff --git a/trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java b/trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java
|
a
|
b
|
|
| 100 | 100 | String META_BOUNDS = META_PREFIX + "bounds"; |
| 101 | 101 | |
| 102 | 102 | /** |
| | 103 | * The creator element that will be written when exporting a GPX file |
| | 104 | */ |
| | 105 | String JOSM_CREATOR_NAME = "JOSM GPX export"; |
| | 106 | |
| | 107 | /** |
| 103 | 108 | * Namespace for the XSD |
| 104 | 109 | */ |
| 105 | 110 | String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance"; |
-
diff --git a/trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java b/trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
|
a
|
b
|
|
| 41 | 41 | * |
| 42 | 42 | * @author Raphael Mack <ramack@raphael-mack.de> |
| 43 | 43 | */ |
| 44 | | public class GpxData extends WithAttributes implements Data { |
| | 44 | public class GpxData extends WithAttributes implements Data, IGpxLayerPrefs { |
| 45 | 45 | |
| 46 | 46 | /** |
| 47 | 47 | * Constructs a new GpxData. |
| … |
… |
|
| 981 | 981 | * @return Modifiable map |
| 982 | 982 | * @since 15496 |
| 983 | 983 | */ |
| | 984 | @Override |
| 984 | 985 | public Map<String, String> getLayerPrefs() { |
| 985 | 986 | return layerPrefs; |
| 986 | 987 | } |
| … |
… |
|
| 1095 | 1096 | suppressedInvalidate = true; |
| 1096 | 1097 | } else { |
| 1097 | 1098 | if (setModified) { |
| 1098 | | modified = true; |
| | 1099 | setModified(true); |
| 1099 | 1100 | } |
| 1100 | 1101 | if (listeners.hasListeners()) { |
| 1101 | 1102 | GpxDataChangeEvent e = new GpxDataChangeEvent(this); |
| … |
… |
|
| 1137 | 1138 | * @param e The event |
| 1138 | 1139 | */ |
| 1139 | 1140 | 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 | } |
| 1140 | 1149 | } |
| 1141 | 1150 | |
| 1142 | 1151 | /** |
| … |
… |
|
| 1175 | 1184 | * @param value modified flag |
| 1176 | 1185 | * @since 15496 |
| 1177 | 1186 | */ |
| | 1187 | @Override |
| 1178 | 1188 | 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 | } |
| 1180 | 1195 | } |
| 1181 | 1196 | |
| 1182 | 1197 | /** |
-
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. |
| | 2 | package org.openstreetmap.josm.data.gpx; |
| | 3 | |
| | 4 | import java.util.Map; |
| | 5 | |
| | 6 | /** |
| | 7 | * Interface containing the layer preferences. |
| | 8 | * Implemented by GpxLayer and MarkerLayer |
| | 9 | * @since xxx |
| | 10 | */ |
| | 11 | public 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 | } |
-
diff --git a/trunk/src/org/openstreetmap/josm/gui/MainFrame.java b/trunk/src/org/openstreetmap/josm/gui/MainFrame.java
|
a
|
b
|
|
| 25 | 25 | import javax.swing.JPanel; |
| 26 | 26 | |
| 27 | 27 | import org.openstreetmap.josm.data.UserIdentityManager; |
| | 28 | import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; |
| 28 | 29 | import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent; |
| 29 | 30 | import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener; |
| 30 | 31 | import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent; |
| … |
… |
|
| 45 | 46 | private final transient LayerStateChangeListener updateTitleOnLayerStateChange = (layer, newValue) -> onLayerChange(layer); |
| 46 | 47 | |
| 47 | 48 | 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) |
| 49 | 50 | || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) { |
| 50 | | OsmDataLayer layer = (OsmDataLayer) evt.getSource(); |
| | 51 | AbstractModifiableLayer layer = (AbstractModifiableLayer) evt.getSource(); |
| 51 | 52 | onLayerChange(layer); |
| 52 | 53 | } |
| 53 | 54 | }; |
| … |
… |
|
| 186 | 187 | getRootPane().putClientProperty("Window.documentModified", dirty); |
| 187 | 188 | } |
| 188 | 189 | |
| 189 | | private void onLayerChange(OsmDataLayer layer) { |
| | 190 | private void onLayerChange(AbstractModifiableLayer layer) { |
| 190 | 191 | if (layer == MainApplication.getLayerManager().getEditLayer()) { |
| 191 | 192 | refreshTitle(); |
| 192 | 193 | } |
-
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
|
|
| 147 | 147 | } |
| 148 | 148 | if (data.gpxLayer != null) { |
| 149 | 149 | MainApplication.getLayerManager().addLayer(data.gpxLayer); |
| | 150 | MainApplication.getLayerManager().setActiveLayer(data.gpxLayer); |
| 150 | 151 | } |
| 151 | 152 | data.postLayerTask.run(); |
| 152 | 153 | }); |
-
diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java
|
a
|
b
|
|
| 16 | 16 | */ |
| 17 | 17 | public abstract class AbstractModifiableLayer extends Layer implements DownloadFromServer, UploadToServer, SaveToFile, Lockable { |
| 18 | 18 | |
| | 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 | |
| 19 | 23 | /** |
| 20 | 24 | * Constructs a new {@code ModifiableLayer}. |
| 21 | 25 | * @param name Layer name |
| … |
… |
|
| 55 | 59 | } |
| 56 | 60 | |
| 57 | 61 | /** |
| | 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 | /** |
| 58 | 73 | * Determines if data managed by this layer has been modified. |
| 59 | 74 | * @return true if data has been modified; false, otherwise |
| 60 | 75 | */ |
| … |
… |
|
| 130 | 145 | // Override if needed; |
| 131 | 146 | return null; |
| 132 | 147 | } |
| | 148 | |
| 133 | 149 | } |
-
diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
|
a
|
b
|
|
| 1 | 1 | // License: GPL. For details, see LICENSE file. |
| 2 | 2 | package org.openstreetmap.josm.gui.layer; |
| 3 | 3 | |
| | 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
| | 5 | import static org.openstreetmap.josm.tools.I18n.trn; |
| | 6 | |
| | 7 | import java.awt.Color; |
| | 8 | import java.awt.Dimension; |
| | 9 | import java.awt.Graphics2D; |
| | 10 | import java.awt.event.ActionEvent; |
| | 11 | import java.io.File; |
| | 12 | import java.time.Instant; |
| | 13 | import java.util.ArrayList; |
| | 14 | import java.util.Arrays; |
| | 15 | import java.util.Collections; |
| | 16 | import java.util.List; |
| | 17 | import java.util.NoSuchElementException; |
| | 18 | import java.util.stream.Collectors; |
| | 19 | |
| | 20 | import javax.swing.AbstractAction; |
| | 21 | import javax.swing.Action; |
| | 22 | import javax.swing.Icon; |
| | 23 | import javax.swing.JScrollPane; |
| | 24 | import javax.swing.SwingUtilities; |
| | 25 | |
| 4 | 26 | import org.openstreetmap.josm.actions.AutoScaleAction; |
| 5 | 27 | import org.openstreetmap.josm.actions.ExpertToggleAction; |
| 6 | 28 | import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener; |
| … |
… |
|
| 11 | 33 | import org.openstreetmap.josm.data.SystemOfMeasurement; |
| 12 | 34 | import org.openstreetmap.josm.data.gpx.GpxConstants; |
| 13 | 35 | import org.openstreetmap.josm.data.gpx.GpxData; |
| | 36 | import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeEvent; |
| 14 | 37 | import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener; |
| 15 | 38 | import org.openstreetmap.josm.data.gpx.IGpxTrack; |
| 16 | 39 | import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; |
| … |
… |
|
| 35 | 58 | import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction; |
| 36 | 59 | import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; |
| 37 | 60 | import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; |
| | 61 | import org.openstreetmap.josm.gui.util.GuiHelper; |
| 38 | 62 | import org.openstreetmap.josm.gui.widgets.HtmlPanel; |
| 39 | 63 | import org.openstreetmap.josm.tools.ImageProvider; |
| 40 | 64 | import org.openstreetmap.josm.tools.Logging; |
| … |
… |
|
| 41 | 65 | import org.openstreetmap.josm.tools.Utils; |
| 42 | 66 | import org.openstreetmap.josm.tools.date.Interval; |
| 43 | 67 | |
| 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 | | |
| 65 | 68 | /** |
| 66 | 69 | * A layer that displays data from a Gpx file / the OSM gpx downloads. |
| 67 | 70 | */ |
| … |
… |
|
| 83 | 86 | /** |
| 84 | 87 | * Added as field to be kept as reference. |
| 85 | 88 | */ |
| 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 | }; |
| 87 | 102 | /** |
| 88 | 103 | * The MarkerLayer imported from the same file. |
| 89 | 104 | */ |
| … |
… |
|
| 356 | 371 | } |
| 357 | 372 | |
| 358 | 373 | @Override |
| | 374 | public String getLabel() { |
| | 375 | return isDirty() ? super.getLabel() + ' ' + IS_DIRTY_SYMBOL : super.getLabel(); |
| | 376 | } |
| | 377 | |
| | 378 | @Override |
| 359 | 379 | public void visitBoundingBox(BoundingXYVisitor v) { |
| 360 | 380 | v.visit(data.recalculateBounds()); |
| 361 | 381 | } |
| … |
… |
|
| 592 | 612 | |
| 593 | 613 | @Override |
| 594 | 614 | public synchronized void destroy() { |
| | 615 | if (linkedMarkerLayer != null && MainApplication.getLayerManager().containsLayer(linkedMarkerLayer)) { |
| | 616 | linkedMarkerLayer.data.transferLayerPrefs(data.getLayerPrefs()); |
| | 617 | } |
| 595 | 618 | data.clear(); |
| 596 | 619 | data = null; |
| 597 | 620 | super.destroy(); |
-
diff --git a/trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java b/trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
|
a
|
b
|
|
| 147 | 147 | private static final int HATCHED_SIZE = 15; |
| 148 | 148 | // U+2205 EMPTY SET |
| 149 | 149 | 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"; |
| 153 | 150 | /** Property used to know if this layer has to be uploaded */ |
| 154 | 151 | public static final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer"; |
| 155 | 152 | |
| … |
… |
|
| 1065 | 1062 | return getAssociatedFile() != null && requiresSaveToFile; |
| 1066 | 1063 | } |
| 1067 | 1064 | |
| 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 | | |
| 1077 | 1065 | @Override |
| 1078 | 1066 | public String getLabel() { |
| 1079 | 1067 | String label = super.getLabel(); |
-
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
|
|
| 189 | 189 | */ |
| 190 | 190 | public WayPoint convertToWayPoint() { |
| 191 | 191 | WayPoint wpt = new WayPoint(getCoor()); |
| 192 | | wpt.setTimeInMillis((long) (time * 1000)); |
| | 192 | if (time != 0) { |
| | 193 | wpt.setTimeInMillis((long) (time * 1000)); |
| | 194 | } |
| 193 | 195 | if (text != null) { |
| 194 | 196 | wpt.getExtensions().add("josm", "text", text); |
| 195 | 197 | } else if (dataProvider != null) { |
-
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
|
|
| 20 | 20 | import java.util.ArrayList; |
| 21 | 21 | import java.util.Collection; |
| 22 | 22 | import java.util.Comparator; |
| | 23 | import java.util.HashMap; |
| 23 | 24 | import java.util.List; |
| | 25 | import java.util.Map; |
| 24 | 26 | import java.util.Optional; |
| 25 | 27 | |
| 26 | 28 | import javax.swing.AbstractAction; |
| … |
… |
|
| 37 | 39 | import org.openstreetmap.josm.data.gpx.GpxData; |
| 38 | 40 | import org.openstreetmap.josm.data.gpx.GpxExtension; |
| 39 | 41 | import org.openstreetmap.josm.data.gpx.GpxLink; |
| | 42 | import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs; |
| 40 | 43 | import org.openstreetmap.josm.data.gpx.WayPoint; |
| 41 | 44 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; |
| 42 | 45 | import org.openstreetmap.josm.data.preferences.IntegerProperty; |
| … |
… |
|
| 56 | 59 | import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; |
| 57 | 60 | import org.openstreetmap.josm.io.audio.AudioPlayer; |
| 58 | 61 | import org.openstreetmap.josm.spi.preferences.Config; |
| | 62 | import org.openstreetmap.josm.tools.ColorHelper; |
| 59 | 63 | import org.openstreetmap.josm.tools.ImageProvider; |
| 60 | 64 | import org.openstreetmap.josm.tools.Logging; |
| 61 | 65 | import org.openstreetmap.josm.tools.Utils; |
| … |
… |
|
| 76 | 80 | /** |
| 77 | 81 | * A list of markers. |
| 78 | 82 | */ |
| 79 | | public final List<Marker> data; |
| | 83 | public final MarkerData data; |
| 80 | 84 | private boolean mousePressed; |
| 81 | 85 | public GpxLayer fromLayer; |
| 82 | 86 | private Marker currentMarker; |
| … |
… |
|
| 100 | 104 | public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) { |
| 101 | 105 | super(name); |
| 102 | 106 | this.setAssociatedFile(associatedFile); |
| 103 | | this.data = new ArrayList<>(); |
| | 107 | this.data = new MarkerData(); |
| 104 | 108 | this.fromLayer = fromLayer; |
| 105 | 109 | double firstTime = -1.0; |
| 106 | 110 | String lastLinkedFile = ""; |
| 107 | 111 | |
| | 112 | if (fromLayer == null || fromLayer.data == null) { |
| | 113 | data.ownLayerPrefs = indata.getLayerPrefs(); |
| | 114 | } |
| | 115 | |
| | 116 | String cs = GPXSettingsPanel.tryGetDataPrefLocal(data, "markers.color"); |
| 108 | 117 | Color c = null; |
| 109 | | String cs = GPXSettingsPanel.tryGetLayerPrefLocal(indata, "markers.color"); |
| 110 | 118 | if (cs != null) { |
| 111 | | try { |
| 112 | | c = Color.decode(cs); |
| 113 | | } catch (NumberFormatException ex) { |
| | 119 | c = ColorHelper.html2color(cs); |
| | 120 | if (c == null) { |
| 114 | 121 | Logging.warn("Could not read marker color: " + cs); |
| 115 | 122 | } |
| 116 | 123 | } |
| … |
… |
|
| 459 | 466 | * @return <code>true</code> if text should be shown, <code>false</code> otherwise. |
| 460 | 467 | */ |
| 461 | 468 | private boolean isTextOrIconShown() { |
| 462 | | return Boolean.parseBoolean(GPXSettingsPanel.getLayerPref(fromLayer, "markers.show-text")); |
| | 469 | return Boolean.parseBoolean(GPXSettingsPanel.getDataPref(data, "markers.show-text")); |
| 463 | 470 | } |
| 464 | 471 | |
| 465 | 472 | @Override |
| … |
… |
|
| 475 | 482 | @Override |
| 476 | 483 | public void setColor(Color color) { |
| 477 | 484 | 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); |
| 484 | 488 | } |
| | 489 | GPXSettingsPanel.putDataPrefLocal(data, "markers.color", cs); |
| 485 | 490 | invalidate(); |
| 486 | 491 | } |
| 487 | 492 | |
| … |
… |
|
| 533 | 538 | |
| 534 | 539 | @Override |
| 535 | 540 | 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())); |
| 537 | 542 | layer.invalidate(); |
| 538 | 543 | } |
| 539 | 544 | |
| … |
… |
|
| 617 | 622 | invalidate(); |
| 618 | 623 | } |
| 619 | 624 | } |
| | 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 | } |
| 620 | 662 | } |
-
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
|
|
| 29 | 29 | import org.apache.commons.jcs3.access.exception.InvalidArgumentException; |
| 30 | 30 | import org.openstreetmap.josm.actions.ExpertToggleAction; |
| 31 | 31 | import org.openstreetmap.josm.data.gpx.GpxData; |
| | 32 | import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs; |
| 32 | 33 | import org.openstreetmap.josm.gui.MainApplication; |
| 33 | 34 | import org.openstreetmap.josm.gui.layer.GpxLayer; |
| 34 | 35 | import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; |
| … |
… |
|
| 172 | 173 | * @return the value |
| 173 | 174 | */ |
| 174 | 175 | 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) { |
| 175 | 187 | Object d = DEFAULT_PREFS.get(key); |
| 176 | 188 | String ds; |
| 177 | 189 | if (d != null) { |
| … |
… |
|
| 180 | 192 | Logging.warn("No default value found for layer preference \"" + key + "\"."); |
| 181 | 193 | ds = null; |
| 182 | 194 | } |
| 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)); |
| 184 | 196 | } |
| 185 | 197 | |
| 186 | 198 | /** |
| … |
… |
|
| 190 | 202 | * @return the integer value |
| 191 | 203 | */ |
| 192 | 204 | 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); |
| 194 | 217 | if (s != null) { |
| 195 | 218 | try { |
| 196 | 219 | return Integer.parseInt(s); |
| … |
… |
|
| 213 | 236 | * @return the value or <code>null</code> if not found |
| 214 | 237 | */ |
| 215 | 238 | 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; |
| 217 | 240 | } |
| 218 | 241 | |
| 219 | 242 | /** |
| … |
… |
|
| 222 | 245 | * @param key the drawing key to be read, without "draw.rawgps." |
| 223 | 246 | * @return the value or <code>null</code> if not found |
| 224 | 247 | */ |
| 225 | | public static String tryGetLayerPrefLocal(GpxData data, String key) { |
| | 248 | public static String tryGetDataPrefLocal(IGpxLayerPrefs data, String key) { |
| 226 | 249 | return data != null ? data.getLayerPrefs().get(key) : null; |
| 227 | 250 | } |
| 228 | 251 | |
| … |
… |
|
| 236 | 259 | String v = value == null ? null : value.toString(); |
| 237 | 260 | if (layers != null) { |
| 238 | 261 | for (GpxLayer l : layers) { |
| 239 | | putLayerPrefLocal(l.data, key, v); |
| | 262 | putDataPrefLocal(l.data, key, v); |
| 240 | 263 | } |
| 241 | 264 | } else { |
| 242 | 265 | Config.getPref().put("draw.rawgps." + key, v); |
| … |
… |
|
| 251 | 274 | */ |
| 252 | 275 | public static void putLayerPrefLocal(GpxLayer layer, String key, String value) { |
| 253 | 276 | if (layer == null) return; |
| 254 | | putLayerPrefLocal(layer.data, key, value); |
| | 277 | putDataPrefLocal(layer.data, key, value); |
| 255 | 278 | } |
| 256 | 279 | |
| 257 | 280 | /** |
| … |
… |
|
| 260 | 283 | * @param key the drawing key to be written, without "draw.rawgps." |
| 261 | 284 | * @param value the value or <code>null</code> to remove key |
| 262 | 285 | */ |
| 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); |
| 264 | 289 | if (value == null || value.trim().isEmpty() || |
| 265 | 290 | (getLayerPref(null, key).equals(value) && DEFAULT_PREFS.get(key) != null && DEFAULT_PREFS.get(key).toString().equals(value))) { |
| 266 | 291 | data.getLayerPrefs().remove(key); |
-
diff --git a/trunk/src/org/openstreetmap/josm/io/GpxWriter.java b/trunk/src/org/openstreetmap/josm/io/GpxWriter.java
|
a
|
b
|
|
| 127 | 127 | |
| 128 | 128 | validprefixes = namespaces.stream().map(n -> n.getPrefix()).collect(Collectors.toList()); |
| 129 | 129 | |
| | 130 | data.creator = JOSM_CREATOR_NAME; |
| 130 | 131 | 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\""); |
| 132 | 132 | |
| | 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 | |
| 133 | 137 | StringBuilder schemaLocations = new StringBuilder("http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); |
| 134 | 138 | |
| 135 | 139 | for (XMLNamespace n : namespaces) { |
-
diff --git a/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java b/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java
|
a
|
b
|
|
| 109 | 109 | */ |
| 110 | 110 | public void write(MarkerLayer layer) { |
| 111 | 111 | 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 | }); |
| 112 | 117 | data.put(GpxData.META_DESC, "exported JOSM marker layer"); |
| 113 | 118 | for (Marker m : layer.data) { |
| 114 | 119 | data.waypoints.add(m.convertToWayPoint()); |
-
diff --git a/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java b/trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java
|
a
|
b
|
|
| 5 | 5 | |
| 6 | 6 | import java.io.IOException; |
| 7 | 7 | import java.io.InputStream; |
| 8 | | import java.util.List; |
| 9 | 8 | |
| 10 | 9 | import javax.xml.xpath.XPath; |
| 11 | 10 | import javax.xml.xpath.XPathConstants; |
| … |
… |
|
| 14 | 13 | import javax.xml.xpath.XPathFactory; |
| 15 | 14 | |
| 16 | 15 | import org.openstreetmap.josm.gui.io.importexport.GpxImporter; |
| 17 | | import org.openstreetmap.josm.gui.layer.GpxLayer; |
| 18 | 16 | import org.openstreetmap.josm.gui.layer.Layer; |
| 19 | 17 | import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; |
| 20 | 18 | import org.openstreetmap.josm.gui.progress.ProgressMonitor; |
| … |
… |
|
| 49 | 47 | |
| 50 | 48 | support.addPostLayersTask(importData.getPostLayerTask()); |
| 51 | 49 | |
| 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(); |
| 67 | 51 | } |
| 68 | 52 | } catch (XPathExpressionException e) { |
| 69 | 53 | throw new IllegalDataException(e); |
-
diff --git a/trunk/src/org/openstreetmap/josm/io/session/SessionReader.java b/trunk/src/org/openstreetmap/josm/io/session/SessionReader.java
|
a
|
b
|
|
| 3 | 3 | |
| 4 | 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
| 5 | 5 | |
| 6 | | import java.awt.Color; |
| 7 | 6 | import java.awt.GraphicsEnvironment; |
| 8 | 7 | import java.io.BufferedInputStream; |
| 9 | 8 | import java.io.File; |
| … |
… |
|
| 46 | 45 | import org.openstreetmap.josm.io.Compression; |
| 47 | 46 | import org.openstreetmap.josm.io.IllegalDataException; |
| 48 | 47 | import org.openstreetmap.josm.tools.CheckParameterUtil; |
| 49 | | import org.openstreetmap.josm.tools.ColorHelper; |
| 50 | 48 | import org.openstreetmap.josm.tools.JosmRuntimeException; |
| 51 | 49 | import org.openstreetmap.josm.tools.Logging; |
| 52 | 50 | import org.openstreetmap.josm.tools.MultiMap; |
| … |
… |
|
| 619 | 617 | Logging.warn(ex); |
| 620 | 618 | } |
| 621 | 619 | } |
| 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 | | } |
| 631 | 620 | layer.setName(names.get(entry.getKey())); |
| 632 | 621 | layers.add(layer); |
| 633 | 622 | } |
-
diff --git a/trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java b/trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java
|
a
|
b
|
|
| 43 | 43 | import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer; |
| 44 | 44 | import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; |
| 45 | 45 | import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference; |
| 46 | | import org.openstreetmap.josm.tools.ColorHelper; |
| 47 | 46 | import org.openstreetmap.josm.tools.JosmRuntimeException; |
| 48 | 47 | import org.openstreetmap.josm.tools.Logging; |
| 49 | 48 | import org.openstreetmap.josm.tools.MultiMap; |
| … |
… |
|
| 240 | 239 | if (!Utils.equalsEpsilon(layer.getOpacity(), 1.0)) { |
| 241 | 240 | el.setAttribute("opacity", Double.toString(layer.getOpacity())); |
| 242 | 241 | } |
| 243 | | if (layer.getColor() != null) { |
| 244 | | el.setAttribute("color", ColorHelper.color2html(layer.getColor())); |
| 245 | | } |
| 246 | 242 | Set<Layer> deps = dependencies.get(layer); |
| 247 | 243 | final String depends = deps == null ? "" : deps.stream().map(depLayer -> { |
| 248 | 244 | int depIndex = layers.indexOf(depLayer); |
-
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 |
-
diff --git a/trunk/test/data/sessions/gpx_markers.jos b/trunk/test/data/sessions/gpx_markers.jos
|
a
|
b
|
|
| 15 | 15 | <layer index="1" name="GPX layer name" type="tracks" version="0.1" visible="true"> |
| 16 | 16 | <file>layers/01/data.gpx</file> |
| 17 | 17 | </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"> |
| 19 | 19 | <file>layers/02/data.gpx</file> |
| 20 | 20 | </layer> |
| 21 | 21 | </layers> |
-
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
-
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 |
-
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
|
|
| 8 | 8 | import java.util.ArrayList; |
| 9 | 9 | import java.util.HashMap; |
| 10 | 10 | |
| | 11 | import org.junit.jupiter.api.Test; |
| 11 | 12 | import org.junit.jupiter.api.extension.RegisterExtension; |
| 12 | | import org.junit.jupiter.api.Test; |
| 13 | 13 | import org.openstreetmap.josm.TestUtils; |
| 14 | 14 | import org.openstreetmap.josm.testutils.JOSMTestRules; |
| 15 | 15 | import org.openstreetmap.josm.tools.ListenerList; |
| … |
… |
|
| 31 | 31 | public JOSMTestRules test = new JOSMTestRules(); |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | | * Tests weather the track can read and write colors. |
| | 34 | * Tests whether the track can read and write colors. |
| 35 | 35 | */ |
| 36 | 36 | @Test |
| 37 | 37 | void testColors() { |
-
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
|
|
| 1 | 1 | // License: GPL. For details, see LICENSE file. |
| 2 | 2 | package org.openstreetmap.josm.io.session; |
| 3 | 3 | |
| | 4 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | 5 | import static org.junit.jupiter.api.Assertions.fail; |
| | 6 | |
| 4 | 7 | import java.awt.Color; |
| 5 | 8 | import java.io.File; |
| 6 | 9 | import java.io.IOException; |
| 7 | | import java.io.InputStream; |
| 8 | 10 | import java.nio.charset.StandardCharsets; |
| 9 | 11 | import java.nio.file.Files; |
| 10 | 12 | import java.nio.file.Path; |
| 11 | 13 | import java.nio.file.Paths; |
| | 14 | import java.time.Instant; |
| 12 | 15 | import java.util.Arrays; |
| 13 | 16 | import java.util.Collections; |
| 14 | 17 | import java.util.HashMap; |
| 15 | 18 | import java.util.List; |
| 16 | 19 | import java.util.Map; |
| | 20 | import java.util.stream.Collectors; |
| | 21 | import java.util.zip.ZipEntry; |
| 17 | 22 | import java.util.zip.ZipFile; |
| 18 | 23 | |
| 19 | 24 | import org.junit.jupiter.api.BeforeEach; |
| … |
… |
|
| 42 | 47 | |
| 43 | 48 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; |
| 44 | 49 | |
| 45 | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 46 | | |
| 47 | 50 | /** |
| 48 | 51 | * Unit tests for Session writing. |
| 49 | 52 | */ |
| … |
… |
|
| 108 | 111 | MainApplication.getLayerManager().addLayer(createOsmLayer()); |
| 109 | 112 | } |
| 110 | 113 | |
| 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 { |
| 112 | 115 | Map<Layer, SessionLayerExporter> exporters = new HashMap<>(); |
| 113 | 116 | if (zip) { |
| 114 | 117 | SessionWriter.registerSessionLayerExporter(OsmDataLayer.class, OsmHeadlessJozExporter.class); |
| … |
… |
|
| 127 | 130 | if (!zip) { |
| 128 | 131 | return null; |
| 129 | 132 | } |
| 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 | })); |
| 133 | 142 | } |
| 134 | 143 | } finally { |
| 135 | 144 | if (file.exists()) { |
| … |
… |
|
| 146 | 155 | |
| 147 | 156 | private GpxLayer createGpxLayer() { |
| 148 | 157 | 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); |
| 150 | 161 | data.waypoints.add(new WayPoint(new LatLon(42.72659, -0.00749))); |
| 151 | 162 | GpxLayer layer = new GpxLayer(data, "GPX layer name"); |
| 152 | 163 | layer.setAssociatedFile(new File("data.gpx")); |
| … |
… |
|
| 232 | 243 | @Test |
| 233 | 244 | void testWriteGpxAndMarkerJoz() throws IOException { |
| 234 | 245 | 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 | |
| 236 | 248 | Path path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/gpx_markers.jos"); |
| 237 | 249 | 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", ""); |
| 239 | 251 | 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); |
| 240 | 262 | } |
| 241 | 263 | |
| 242 | 264 | /** |