001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.display; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trc; 006 007import java.awt.Color; 008import java.awt.Component; 009import java.awt.Dimension; 010import java.awt.GridBagLayout; 011import java.awt.event.ActionListener; 012import java.util.Enumeration; 013 014import javax.swing.AbstractButton; 015import javax.swing.BorderFactory; 016import javax.swing.Box; 017import javax.swing.ButtonGroup; 018import javax.swing.JCheckBox; 019import javax.swing.JLabel; 020import javax.swing.JOptionPane; 021import javax.swing.JPanel; 022import javax.swing.JRadioButton; 023import javax.swing.JSlider; 024 025import org.openstreetmap.josm.actions.ExpertToggleAction; 026import org.openstreetmap.josm.data.PreferencesUtils; 027import org.openstreetmap.josm.data.preferences.NamedColorProperty; 028import org.openstreetmap.josm.gui.MainApplication; 029import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; 030import org.openstreetmap.josm.gui.layer.markerlayer.Marker; 031import org.openstreetmap.josm.gui.layer.markerlayer.Marker.TemplateEntryProperty; 032import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener; 033import org.openstreetmap.josm.gui.widgets.JosmComboBox; 034import org.openstreetmap.josm.gui.widgets.JosmTextField; 035import org.openstreetmap.josm.spi.preferences.Config; 036import org.openstreetmap.josm.tools.GBC; 037import org.openstreetmap.josm.tools.Logging; 038import org.openstreetmap.josm.tools.template_engine.ParseError; 039import org.openstreetmap.josm.tools.template_engine.TemplateParser; 040 041/** 042 * Panel for GPX settings. 043 */ 044public class GPXSettingsPanel extends JPanel implements ValidationListener { 045 046 private static final int WAYPOINT_LABEL_CUSTOM = 6; 047 private static final String[] LABEL_PATTERN_TEMPLATE = new String[] {Marker.LABEL_PATTERN_AUTO, Marker.LABEL_PATTERN_NAME, 048 Marker.LABEL_PATTERN_DESC, "{special:everything}", "?{ '{name}' | '{desc}' | '{formattedWaypointOffset}' }", " "}; 049 private static final String[] LABEL_PATTERN_DESC = new String[] {tr("Auto"), /* gpx data field name */ trc("gpx_field", "Name"), 050 /* gpx data field name */ trc("gpx_field", "Desc(ription)"), tr("Everything"), tr("Name or offset"), tr("None"), tr("Custom")}; 051 052 053 private final JRadioButton drawRawGpsLinesGlobal = new JRadioButton(tr("Use global settings")); 054 private final JRadioButton drawRawGpsLinesAll = new JRadioButton(tr("All")); 055 private final JRadioButton drawRawGpsLinesLocal = new JRadioButton(tr("Local files")); 056 private final JRadioButton drawRawGpsLinesNone = new JRadioButton(tr("None")); 057 private transient ActionListener drawRawGpsLinesActionListener; 058 private final JosmTextField drawRawGpsMaxLineLength = new JosmTextField(8); 059 private final JosmTextField drawRawGpsMaxLineLengthLocal = new JosmTextField(8); 060 private final JosmTextField drawLineWidth = new JosmTextField(2); 061 private final JCheckBox forceRawGpsLines = new JCheckBox(tr("Force lines if no segments imported")); 062 private final JCheckBox largeGpsPoints = new JCheckBox(tr("Draw large GPS points")); 063 private final JCheckBox hdopCircleGpsPoints = new JCheckBox(tr("Draw a circle from HDOP value")); 064 private final JRadioButton colorTypeVelocity = new JRadioButton(tr("Velocity (red = slow, green = fast)")); 065 private final JRadioButton colorTypeDirection = new JRadioButton(tr("Direction (red = west, yellow = north, green = east, blue = south)")); 066 private final JRadioButton colorTypeDilution = new JRadioButton(tr("Dilution of Position (red = high, green = low, if available)")); 067 private final JRadioButton colorTypeQuality = new JRadioButton(tr("Quality (RTKLib only, if available)")); 068 private final JRadioButton colorTypeTime = new JRadioButton(tr("Track date")); 069 private final JRadioButton colorTypeHeatMap = new JRadioButton(tr("Heat Map (dark = few, bright = many)")); 070 private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized for named layers)")); 071 private final JRadioButton colorTypeGlobal = new JRadioButton(tr("Use global settings")); 072 private final JosmComboBox<String> colorTypeVelocityTune = new JosmComboBox<>(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")}); 073 private final JosmComboBox<String> colorTypeHeatMapTune = new JosmComboBox<>(new String[] { 074 trc("Heat map", "User Normal"), 075 trc("Heat map", "User Light"), 076 trc("Heat map", "Traffic Lights"), 077 trc("Heat map", "Inferno"), 078 trc("Heat map", "Viridis"), 079 trc("Heat map", "Wood"), 080 trc("Heat map", "Heat")}); 081 private final JCheckBox colorTypeHeatMapPoints = new JCheckBox(tr("Use points instead of lines for heat map")); 082 private final JSlider colorTypeHeatMapGain = new JSlider(); 083 private final JSlider colorTypeHeatMapLowerLimit = new JSlider(); 084 private final JCheckBox makeAutoMarkers = new JCheckBox(tr("Create markers when reading GPX")); 085 private final JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows")); 086 private final JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)")); 087 private final JosmTextField drawGpsArrowsMinDist = new JosmTextField(8); 088 private final JCheckBox colorDynamic = new JCheckBox(tr("Dynamic color range based on data limits")); 089 private final JosmComboBox<String> waypointLabel = new JosmComboBox<>(LABEL_PATTERN_DESC); 090 private final JosmTextField waypointLabelPattern = new JosmTextField(); 091 private final JosmComboBox<String> audioWaypointLabel = new JosmComboBox<>(LABEL_PATTERN_DESC); 092 private final JosmTextField audioWaypointLabelPattern = new JosmTextField(); 093 private final JCheckBox useGpsAntialiasing = new JCheckBox(tr("Smooth GPX graphics (antialiasing)")); 094 private final JCheckBox drawLineWithAlpha = new JCheckBox(tr("Draw with Opacity (alpha blending) ")); 095 096 private String layerName; 097 private final boolean local; // flag to display LocalOnly checkbox 098 private final boolean nonlocal; // flag to display AllLines checkbox 099 100 /** 101 * Constructs a new {@code GPXSettingsPanel} for a given layer name. 102 * @param layerName The GPX layer name 103 * @param local flag to display LocalOnly checkbox 104 * @param nonlocal flag to display AllLines checkbox 105 */ 106 public GPXSettingsPanel(String layerName, boolean local, boolean nonlocal) { 107 super(new GridBagLayout()); 108 this.local = local; 109 this.nonlocal = nonlocal; 110 this.layerName = "layer "+layerName; 111 initComponents(); 112 loadPreferences(); 113 } 114 115 /** 116 * Constructs a new {@code GPXSettingsPanel}. 117 */ 118 public GPXSettingsPanel() { 119 super(new GridBagLayout()); 120 initComponents(); 121 local = false; 122 nonlocal = false; 123 loadPreferences(); // preferences -> controls 124 } 125 126 // CHECKSTYLE.OFF: ExecutableStatementCountCheck 127 private void initComponents() { 128 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 129 130 // makeAutoMarkers 131 makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer.")); 132 ExpertToggleAction.addVisibilitySwitcher(makeAutoMarkers); 133 add(makeAutoMarkers, GBC.eol().insets(20, 0, 0, 5)); 134 135 // drawRawGpsLines 136 ButtonGroup gpsLinesGroup = new ButtonGroup(); 137 if (layerName != null) { 138 gpsLinesGroup.add(drawRawGpsLinesGlobal); 139 } 140 gpsLinesGroup.add(drawRawGpsLinesNone); 141 gpsLinesGroup.add(drawRawGpsLinesLocal); 142 gpsLinesGroup.add(drawRawGpsLinesAll); 143 144 /* ensure that default is in data base */ 145 146 JLabel label = new JLabel(tr("Draw lines between raw GPS points")); 147 add(label, GBC.eol().insets(20, 0, 0, 0)); 148 if (layerName != null) { 149 add(drawRawGpsLinesGlobal, GBC.eol().insets(40, 0, 0, 0)); 150 } 151 add(drawRawGpsLinesNone, GBC.eol().insets(40, 0, 0, 0)); 152 if (layerName == null || local) { 153 add(drawRawGpsLinesLocal, GBC.eol().insets(40, 0, 0, 0)); 154 } 155 if (layerName == null || nonlocal) { 156 add(drawRawGpsLinesAll, GBC.eol().insets(40, 0, 0, 0)); 157 } 158 ExpertToggleAction.addVisibilitySwitcher(label); 159 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesGlobal); 160 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesNone); 161 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesLocal); 162 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesAll); 163 164 drawRawGpsLinesActionListener = e -> { 165 boolean f = drawRawGpsLinesNone.isSelected() || drawRawGpsLinesGlobal.isSelected(); 166 forceRawGpsLines.setEnabled(!f); 167 drawRawGpsMaxLineLength.setEnabled(!(f || drawRawGpsLinesLocal.isSelected())); 168 drawRawGpsMaxLineLengthLocal.setEnabled(!f); 169 drawGpsArrows.setEnabled(!f); 170 drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 171 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 172 }; 173 174 drawRawGpsLinesGlobal.addActionListener(drawRawGpsLinesActionListener); 175 drawRawGpsLinesNone.addActionListener(drawRawGpsLinesActionListener); 176 drawRawGpsLinesLocal.addActionListener(drawRawGpsLinesActionListener); 177 drawRawGpsLinesAll.addActionListener(drawRawGpsLinesActionListener); 178 179 // drawRawGpsMaxLineLengthLocal 180 drawRawGpsMaxLineLengthLocal.setToolTipText( 181 tr("Maximum length (in meters) to draw lines for local files. Set to ''-1'' to draw all lines.")); 182 label = new JLabel(tr("Maximum length for local files (meters)")); 183 add(label, GBC.std().insets(40, 0, 0, 0)); 184 add(drawRawGpsMaxLineLengthLocal, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 185 ExpertToggleAction.addVisibilitySwitcher(label); 186 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsMaxLineLengthLocal); 187 188 // drawRawGpsMaxLineLength 189 drawRawGpsMaxLineLength.setToolTipText(tr("Maximum length (in meters) to draw lines. Set to ''-1'' to draw all lines.")); 190 label = new JLabel(tr("Maximum length (meters)")); 191 add(label, GBC.std().insets(40, 0, 0, 0)); 192 add(drawRawGpsMaxLineLength, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 193 ExpertToggleAction.addVisibilitySwitcher(label); 194 ExpertToggleAction.addVisibilitySwitcher(drawRawGpsMaxLineLength); 195 196 // forceRawGpsLines 197 forceRawGpsLines.setToolTipText(tr("Force drawing of lines if the imported data contain no line information.")); 198 add(forceRawGpsLines, GBC.eop().insets(40, 0, 0, 0)); 199 ExpertToggleAction.addVisibilitySwitcher(forceRawGpsLines); 200 201 // drawGpsArrows 202 drawGpsArrows.addActionListener(e -> { 203 drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 204 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 205 }); 206 drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points.")); 207 add(drawGpsArrows, GBC.eop().insets(20, 0, 0, 0)); 208 209 // drawGpsArrowsFast 210 drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math.")); 211 add(drawGpsArrowsFast, GBC.eop().insets(40, 0, 0, 0)); 212 ExpertToggleAction.addVisibilitySwitcher(drawGpsArrowsFast); 213 214 // drawGpsArrowsMinDist 215 drawGpsArrowsMinDist.setToolTipText(tr("Do not draw arrows if they are not at least this distance away from the last one.")); 216 add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(40, 0, 0, 0)); 217 add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 218 219 // hdopCircleGpsPoints 220 hdopCircleGpsPoints.setToolTipText(tr("Draw a circle from HDOP value")); 221 add(hdopCircleGpsPoints, GBC.eop().insets(20, 0, 0, 0)); 222 ExpertToggleAction.addVisibilitySwitcher(hdopCircleGpsPoints); 223 224 // largeGpsPoints 225 largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points.")); 226 add(largeGpsPoints, GBC.eop().insets(20, 0, 0, 0)); 227 228 // drawLineWidth 229 drawLineWidth.setToolTipText(tr("Width of drawn GPX line (0 for default)")); 230 add(new JLabel(tr("Drawing width of GPX lines")), GBC.std().insets(20, 0, 0, 0)); 231 add(drawLineWidth, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 232 233 // antialiasing 234 useGpsAntialiasing.setToolTipText(tr("Apply antialiasing to the GPX lines resulting in a smoother appearance.")); 235 add(useGpsAntialiasing, GBC.eop().insets(20, 0, 0, 0)); 236 ExpertToggleAction.addVisibilitySwitcher(useGpsAntialiasing); 237 238 // alpha blending 239 drawLineWithAlpha.setToolTipText(tr("Apply dynamic alpha-blending and adjust width based on zoom level for all GPX lines.")); 240 add(drawLineWithAlpha, GBC.eop().insets(20, 0, 0, 0)); 241 ExpertToggleAction.addVisibilitySwitcher(drawLineWithAlpha); 242 243 // colorTracks 244 ButtonGroup colorGroup = new ButtonGroup(); 245 if (layerName != null) { 246 colorGroup.add(colorTypeGlobal); 247 } 248 colorGroup.add(colorTypeNone); 249 colorGroup.add(colorTypeVelocity); 250 colorGroup.add(colorTypeDirection); 251 colorGroup.add(colorTypeDilution); 252 colorGroup.add(colorTypeQuality); 253 colorGroup.add(colorTypeTime); 254 colorGroup.add(colorTypeHeatMap); 255 256 colorTypeNone.setToolTipText(tr("All points and track segments will have the same color. Can be customized in Layer Manager.")); 257 colorTypeVelocity.setToolTipText(tr("Colors points and track segments by velocity.")); 258 colorTypeDirection.setToolTipText(tr("Colors points and track segments by direction.")); 259 colorTypeDilution.setToolTipText( 260 tr("Colors points and track segments by dilution of position (HDOP). Your capture device needs to log that information.")); 261 colorTypeQuality.setToolTipText( 262 tr("Colors points and track segments by RTKLib quality flag (Q). Your capture device needs to log that information.")); 263 colorTypeTime.setToolTipText(tr("Colors points and track segments by its timestamp.")); 264 colorTypeHeatMap.setToolTipText(tr("Collected points and track segments for a position and displayed as heat map.")); 265 266 // color Tracks by Velocity Tune 267 colorTypeVelocityTune.setToolTipText(tr("Allows to tune the track coloring for different average speeds.")); 268 269 colorTypeHeatMapTune.setToolTipText(tr("Selects the color schema for heat map.")); 270 JLabel colorTypeHeatIconLabel = new JLabel(); 271 272 add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0)); 273 274 add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20, 0, 0, 0)); 275 if (layerName != null) { 276 add(colorTypeGlobal, GBC.eol().insets(40, 0, 0, 0)); 277 } 278 add(colorTypeNone, GBC.eol().insets(40, 0, 0, 0)); 279 add(colorTypeVelocity, GBC.std().insets(40, 0, 0, 0)); 280 add(colorTypeVelocityTune, GBC.eop().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 281 add(colorTypeDirection, GBC.eol().insets(40, 0, 0, 0)); 282 add(colorTypeDilution, GBC.eol().insets(40, 0, 0, 0)); 283 add(colorTypeQuality, GBC.eol().insets(40, 0, 0, 0)); 284 add(colorTypeTime, GBC.eol().insets(40, 0, 0, 0)); 285 add(colorTypeHeatMap, GBC.std().insets(40, 0, 0, 0)); 286 add(colorTypeHeatIconLabel, GBC.std().insets(5, 0, 0, 5)); 287 add(colorTypeHeatMapTune, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 288 289 JLabel colorTypeHeatMapGainLabel = new JLabel(tr("Overlay gain adjustment")); 290 JLabel colorTypeHeatMapLowerLimitLabel = new JLabel(tr("Lower limit of visibility")); 291 add(colorTypeHeatMapGainLabel, GBC.std().insets(80, 0, 0, 0)); 292 add(colorTypeHeatMapGain, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 293 add(colorTypeHeatMapLowerLimitLabel, GBC.std().insets(80, 0, 0, 0)); 294 add(colorTypeHeatMapLowerLimit, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 295 add(colorTypeHeatMapPoints, GBC.eol().insets(60, 0, 0, 0)); 296 297 colorTypeHeatMapGain.setToolTipText(tr("Adjust the gain of overlay blending.")); 298 colorTypeHeatMapGain.setOrientation(JSlider.HORIZONTAL); 299 colorTypeHeatMapGain.setPaintLabels(true); 300 colorTypeHeatMapGain.setMinimum(-10); 301 colorTypeHeatMapGain.setMaximum(+10); 302 colorTypeHeatMapGain.setMinorTickSpacing(1); 303 colorTypeHeatMapGain.setMajorTickSpacing(5); 304 305 colorTypeHeatMapLowerLimit.setToolTipText(tr("Draw all GPX traces that exceed this threshold.")); 306 colorTypeHeatMapLowerLimit.setOrientation(JSlider.HORIZONTAL); 307 colorTypeHeatMapLowerLimit.setMinimum(0); 308 colorTypeHeatMapLowerLimit.setMaximum(254); 309 colorTypeHeatMapLowerLimit.setPaintLabels(true); 310 colorTypeHeatMapLowerLimit.setMinorTickSpacing(10); 311 colorTypeHeatMapLowerLimit.setMajorTickSpacing(100); 312 313 colorTypeHeatMapPoints.setToolTipText(tr("Render engine uses points with simulated position error instead of lines. ")); 314 315 // iterate over the buttons, add change listener to any change event 316 for (Enumeration<AbstractButton> button = colorGroup.getElements(); button.hasMoreElements();) { 317 (button.nextElement()).addChangeListener(e -> { 318 colorTypeVelocityTune.setEnabled(colorTypeVelocity.isSelected()); 319 colorTypeHeatMapTune.setEnabled(colorTypeHeatMap.isSelected()); 320 colorTypeHeatMapPoints.setEnabled(colorTypeHeatMap.isSelected()); 321 colorTypeHeatMapGain.setEnabled(colorTypeHeatMap.isSelected()); 322 colorTypeHeatMapLowerLimit.setEnabled(colorTypeHeatMap.isSelected()); 323 colorTypeHeatMapGainLabel.setEnabled(colorTypeHeatMap.isSelected()); 324 colorTypeHeatMapLowerLimitLabel.setEnabled(colorTypeHeatMap.isSelected()); 325 colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected()); 326 }); 327 } 328 329 colorTypeHeatMapTune.addActionListener(e -> { 330 final Dimension dim = colorTypeHeatMapTune.getPreferredSize(); 331 if (null != dim) { 332 // get image size of environment 333 final int iconSize = (int) dim.getHeight(); 334 final Color color; 335 // ask the GPX draw for the correct color of that layer ( if there is one ) 336 if (null != layerName) { 337 color = GpxDrawHelper.DEFAULT_COLOR.getChildColor( 338 NamedColorProperty.COLOR_CATEGORY_LAYER, layerName, GpxDrawHelper.DEFAULT_COLOR.getName()).get(); 339 } else { 340 color = GpxDrawHelper.DEFAULT_COLOR.getDefaultValue(); 341 } 342 colorTypeHeatIconLabel.setIcon(GpxDrawHelper.getColorMapImageIcon(color, colorTypeHeatMapTune.getSelectedIndex(), iconSize)); 343 } 344 }); 345 346 ExpertToggleAction.addVisibilitySwitcher(colorTypeDirection); 347 ExpertToggleAction.addVisibilitySwitcher(colorTypeDilution); 348 ExpertToggleAction.addVisibilitySwitcher(colorTypeQuality); 349 ExpertToggleAction.addVisibilitySwitcher(colorTypeHeatMapLowerLimit); 350 ExpertToggleAction.addVisibilitySwitcher(colorTypeHeatMapLowerLimitLabel); 351 352 colorDynamic.setToolTipText(tr("Colors points and track segments by data limits.")); 353 add(colorDynamic, GBC.eop().insets(40, 0, 0, 0)); 354 ExpertToggleAction.addVisibilitySwitcher(colorDynamic); 355 356 if (layerName == null) { 357 // Setting waypoints for gpx layer doesn't make sense - waypoints are shown in marker layer that has different name - so show 358 // this only for global config 359 360 // waypointLabel 361 label = new JLabel(tr("Waypoint labelling")); 362 add(label, GBC.std().insets(20, 0, 0, 0)); 363 label.setLabelFor(waypointLabel); 364 add(waypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 365 waypointLabel.addActionListener(e -> updateWaypointPattern(waypointLabel, waypointLabelPattern)); 366 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker(layerName)); 367 add(waypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 368 ExpertToggleAction.addVisibilitySwitcher(label); 369 ExpertToggleAction.addVisibilitySwitcher(waypointLabel); 370 ExpertToggleAction.addVisibilitySwitcher(waypointLabelPattern); 371 372 // audioWaypointLabel 373 Component glue = Box.createVerticalGlue(); 374 add(glue, GBC.eol().insets(0, 20, 0, 0)); 375 ExpertToggleAction.addVisibilitySwitcher(glue); 376 377 label = new JLabel(tr("Audio waypoint labelling")); 378 add(label, GBC.std().insets(20, 0, 0, 0)); 379 label.setLabelFor(audioWaypointLabel); 380 add(audioWaypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 381 audioWaypointLabel.addActionListener(e -> updateWaypointPattern(audioWaypointLabel, audioWaypointLabelPattern)); 382 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker(layerName)); 383 add(audioWaypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 384 ExpertToggleAction.addVisibilitySwitcher(label); 385 ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabel); 386 ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabelPattern); 387 } 388 389 add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 390 } 391 // CHECKSTYLE.ON: ExecutableStatementCountCheck 392 393 /** 394 * Loads preferences to UI controls 395 */ 396 public final void loadPreferences() { 397 makeAutoMarkers.setSelected(Config.getPref().getBoolean("marker.makeautomarkers", true)); 398 if (layerName != null && Config.getPref().get("draw.rawgps.lines."+layerName).isEmpty() 399 && Config.getPref().get("draw.rawgps.lines.local."+layerName).isEmpty()) { 400 // no line preferences for layer is found 401 drawRawGpsLinesGlobal.setSelected(true); 402 } else { 403 Boolean lf = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines.local", layerName, true); 404 if (PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines", layerName, true)) { 405 drawRawGpsLinesAll.setSelected(true); 406 } else if (lf) { 407 drawRawGpsLinesLocal.setSelected(true); 408 } else { 409 drawRawGpsLinesNone.setSelected(true); 410 } 411 } 412 413 drawRawGpsMaxLineLengthLocal.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(), 414 "draw.rawgps.max-line-length.local", layerName, -1))); 415 drawRawGpsMaxLineLength.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(), 416 "draw.rawgps.max-line-length", layerName, 200))); 417 drawLineWidth.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(), 418 "draw.rawgps.linewidth", layerName, 0))); 419 drawLineWithAlpha.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 420 "draw.rawgps.lines.alpha-blend", layerName, false)); 421 forceRawGpsLines.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 422 "draw.rawgps.lines.force", layerName, false)); 423 drawGpsArrows.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 424 "draw.rawgps.direction", layerName, false)); 425 drawGpsArrowsFast.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 426 "draw.rawgps.alternatedirection", layerName, false)); 427 drawGpsArrowsMinDist.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(), 428 "draw.rawgps.min-arrow-distance", layerName, 40))); 429 hdopCircleGpsPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 430 "draw.rawgps.hdopcircle", layerName, false)); 431 largeGpsPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 432 "draw.rawgps.large", layerName, false)); 433 useGpsAntialiasing.setSelected(Config.getPref().getBoolean("mappaint.gpx.use-antialiasing", false)); 434 435 drawRawGpsLinesActionListener.actionPerformed(null); 436 437 if (layerName != null && Config.getPref().get("draw.rawgps.colors."+layerName).isEmpty()) { 438 colorTypeGlobal.setSelected(true); 439 colorDynamic.setSelected(false); 440 colorDynamic.setEnabled(false); 441 colorTypeHeatMapPoints.setSelected(false); 442 colorTypeHeatMapGain.setValue(0); 443 colorTypeHeatMapLowerLimit.setValue(0); 444 } else { 445 int colorType = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", layerName, 0); 446 switch (colorType) { 447 case 0: colorTypeNone.setSelected(true); break; 448 case 1: colorTypeVelocity.setSelected(true); break; 449 case 2: colorTypeDilution.setSelected(true); break; 450 case 3: colorTypeDirection.setSelected(true); break; 451 case 4: colorTypeTime.setSelected(true); break; 452 case 5: colorTypeHeatMap.setSelected(true); break; 453 case 6: colorTypeQuality.setSelected(true); break; 454 default: Logging.warn("Unknown color type: " + colorType); 455 } 456 int ccts = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colorTracksTune", layerName, 45); 457 colorTypeVelocityTune.setSelectedIndex(ccts == 10 ? 2 : (ccts == 20 ? 1 : 0)); 458 colorTypeHeatMapTune.setSelectedIndex(PreferencesUtils.getInteger(Config.getPref(), 459 "draw.rawgps.heatmap.colormap", layerName, 0)); 460 colorDynamic.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 461 "draw.rawgps.colors.dynamic", layerName, false)); 462 colorTypeHeatMapPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(), 463 "draw.rawgps.heatmap.use-points", layerName, false)); 464 colorTypeHeatMapGain.setValue(PreferencesUtils.getInteger(Config.getPref(), 465 "draw.rawgps.heatmap.gain", layerName, 0)); 466 colorTypeHeatMapLowerLimit.setValue(PreferencesUtils.getInteger(Config.getPref(), 467 "draw.rawgps.heatmap.lower-limit", layerName, 0)); 468 } 469 } 470 471 /** 472 * Save preferences from UI controls, globally or for a specified layer. 473 * @param layerName The GPX layer name. Can be {@code null}, in that case, global preferences are written 474 * @param locLayer {@code true} if the GPX layer is a local one. Ignored if {@code layerName} is null 475 * @return {@code true} when restart is required, {@code false} otherwise 476 */ 477 public boolean savePreferences(String layerName, boolean locLayer) { 478 String layerNameDot = ".layer "+layerName; 479 if (layerName == null) { 480 layerNameDot = ""; 481 } 482 Config.getPref().putBoolean("marker.makeautomarkers"+layerNameDot, makeAutoMarkers.isSelected()); 483 if (drawRawGpsLinesGlobal.isSelected()) { 484 Config.getPref().put("draw.rawgps.lines" + layerNameDot, null); 485 Config.getPref().put("draw.rawgps.max-line-length" + layerNameDot, null); 486 Config.getPref().put("draw.rawgps.lines.local" + layerNameDot, null); 487 Config.getPref().put("draw.rawgps.max-line-length.local" + layerNameDot, null); 488 Config.getPref().put("draw.rawgps.lines.force"+layerNameDot, null); 489 Config.getPref().put("draw.rawgps.direction"+layerNameDot, null); 490 Config.getPref().put("draw.rawgps.alternatedirection"+layerNameDot, null); 491 Config.getPref().put("draw.rawgps.min-arrow-distance"+layerNameDot, null); 492 } else { 493 if (layerName == null || !locLayer) { 494 Config.getPref().putBoolean("draw.rawgps.lines" + layerNameDot, drawRawGpsLinesAll.isSelected()); 495 Config.getPref().put("draw.rawgps.max-line-length" + layerNameDot, drawRawGpsMaxLineLength.getText()); 496 } 497 if (layerName == null || locLayer) { 498 Config.getPref().putBoolean("draw.rawgps.lines.local" + layerNameDot, 499 drawRawGpsLinesAll.isSelected() || drawRawGpsLinesLocal.isSelected()); 500 Config.getPref().put("draw.rawgps.max-line-length.local" + layerNameDot, 501 drawRawGpsMaxLineLengthLocal.getText()); 502 } 503 Config.getPref().putBoolean("draw.rawgps.lines.force"+layerNameDot, forceRawGpsLines.isSelected()); 504 Config.getPref().putBoolean("draw.rawgps.direction"+layerNameDot, drawGpsArrows.isSelected()); 505 Config.getPref().putBoolean("draw.rawgps.alternatedirection"+layerNameDot, drawGpsArrowsFast.isSelected()); 506 Config.getPref().put("draw.rawgps.min-arrow-distance"+layerNameDot, drawGpsArrowsMinDist.getText()); 507 } 508 509 Config.getPref().putBoolean("draw.rawgps.hdopcircle"+layerNameDot, hdopCircleGpsPoints.isSelected()); 510 Config.getPref().putBoolean("draw.rawgps.large"+layerNameDot, largeGpsPoints.isSelected()); 511 Config.getPref().put("draw.rawgps.linewidth"+layerNameDot, drawLineWidth.getText()); 512 Config.getPref().putBoolean("draw.rawgps.lines.alpha-blend"+layerNameDot, drawLineWithAlpha.isSelected()); 513 514 Config.getPref().putBoolean("mappaint.gpx.use-antialiasing", useGpsAntialiasing.isSelected()); 515 516 TemplateEntryProperty.forMarker(layerName).put(waypointLabelPattern.getText()); 517 TemplateEntryProperty.forAudioMarker(layerName).put(audioWaypointLabelPattern.getText()); 518 519 if (colorTypeGlobal.isSelected()) { 520 Config.getPref().put("draw.rawgps.colors"+layerNameDot, null); 521 Config.getPref().put("draw.rawgps.colors.dynamic"+layerNameDot, null); 522 Config.getPref().put("draw.rawgps.colorTracksTunec"+layerNameDot, null); 523 return false; 524 } else if (colorTypeVelocity.isSelected()) { 525 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 1); 526 } else if (colorTypeDilution.isSelected()) { 527 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 2); 528 } else if (colorTypeDirection.isSelected()) { 529 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 3); 530 } else if (colorTypeTime.isSelected()) { 531 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 4); 532 } else if (colorTypeHeatMap.isSelected()) { 533 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 5); 534 } else if (colorTypeQuality.isSelected()) { 535 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 6); 536 } else { 537 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 0); 538 } 539 Config.getPref().putBoolean("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected()); 540 int ccti = colorTypeVelocityTune.getSelectedIndex(); 541 Config.getPref().putInt("draw.rawgps.colorTracksTune"+layerNameDot, ccti == 2 ? 10 : (ccti == 1 ? 20 : 45)); 542 Config.getPref().putInt("draw.rawgps.heatmap.colormap"+layerNameDot, colorTypeHeatMapTune.getSelectedIndex()); 543 Config.getPref().putBoolean("draw.rawgps.heatmap.use-points"+layerNameDot, colorTypeHeatMapPoints.isSelected()); 544 Config.getPref().putInt("draw.rawgps.heatmap.gain"+layerNameDot, colorTypeHeatMapGain.getValue()); 545 Config.getPref().putInt("draw.rawgps.heatmap.lower-limit"+layerNameDot, colorTypeHeatMapLowerLimit.getValue()); 546 547 return false; 548 } 549 550 /** 551 * Save preferences from UI controls for initial layer or globally 552 * @return {@code true} when restart is required, {@code false} otherwise 553 */ 554 public boolean savePreferences() { 555 return savePreferences(null, false); 556 } 557 558 private static void updateWaypointLabelCombobox(JosmComboBox<String> cb, JosmTextField tf, TemplateEntryProperty property) { 559 String labelPattern = property.getAsString(); 560 boolean found = false; 561 for (int i = 0; i < LABEL_PATTERN_TEMPLATE.length; i++) { 562 if (LABEL_PATTERN_TEMPLATE[i].equals(labelPattern)) { 563 cb.setSelectedIndex(i); 564 found = true; 565 break; 566 } 567 } 568 if (!found) { 569 cb.setSelectedIndex(WAYPOINT_LABEL_CUSTOM); 570 tf.setEnabled(true); 571 tf.setText(labelPattern); 572 } 573 } 574 575 private static void updateWaypointPattern(JosmComboBox<String> cb, JosmTextField tf) { 576 if (cb.getSelectedIndex() == WAYPOINT_LABEL_CUSTOM) { 577 tf.setEnabled(true); 578 } else { 579 tf.setEnabled(false); 580 tf.setText(LABEL_PATTERN_TEMPLATE[cb.getSelectedIndex()]); 581 } 582 } 583 584 @Override 585 public boolean validatePreferences() { 586 TemplateParser parser = new TemplateParser(waypointLabelPattern.getText()); 587 try { 588 parser.parse(); 589 } catch (ParseError e) { 590 Logging.warn(e); 591 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 592 tr("Incorrect waypoint label pattern: {0}", e.getMessage()), tr("Incorrect pattern"), JOptionPane.ERROR_MESSAGE); 593 waypointLabelPattern.requestFocus(); 594 return false; 595 } 596 parser = new TemplateParser(audioWaypointLabelPattern.getText()); 597 try { 598 parser.parse(); 599 } catch (ParseError e) { 600 Logging.warn(e); 601 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 602 tr("Incorrect audio waypoint label pattern: {0}", e.getMessage()), tr("Incorrect pattern"), JOptionPane.ERROR_MESSAGE); 603 audioWaypointLabelPattern.requestFocus(); 604 return false; 605 } 606 return true; 607 } 608}