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