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}