001    package org.openstreetmap.josm.gui.preferences.display;
002    
003    import static org.openstreetmap.josm.tools.I18n.tr;
004    import static org.openstreetmap.josm.tools.I18n.trc;
005    
006    import java.awt.Component;
007    import java.awt.GridBagLayout;
008    import java.awt.event.ActionEvent;
009    import java.awt.event.ActionListener;
010    
011    import javax.swing.BorderFactory;
012    import javax.swing.Box;
013    import javax.swing.ButtonGroup;
014    import javax.swing.JCheckBox;
015    import javax.swing.JLabel;
016    import javax.swing.JOptionPane;
017    import javax.swing.JPanel;
018    import javax.swing.JRadioButton;
019    import javax.swing.JTextField;
020    import javax.swing.event.ChangeEvent;
021    import javax.swing.event.ChangeListener;
022    
023    import org.openstreetmap.josm.Main;
024    import org.openstreetmap.josm.actions.ExpertToggleAction;
025    import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
026    import org.openstreetmap.josm.gui.layer.markerlayer.Marker.TemplateEntryProperty;
027    import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
028    import org.openstreetmap.josm.gui.widgets.JosmComboBox;
029    import org.openstreetmap.josm.tools.GBC;
030    import org.openstreetmap.josm.tools.template_engine.ParseError;
031    import org.openstreetmap.josm.tools.template_engine.TemplateParser;
032    
033    public class GPXSettingsPanel extends JPanel implements ValidationListener {
034    
035        private static final int WAYPOINT_LABEL_CUSTOM = 6;
036        private static final String[] LABEL_PATTERN_TEMPLATE = new String[] {Marker.LABEL_PATTERN_AUTO, Marker.LABEL_PATTERN_NAME,
037            Marker.LABEL_PATTERN_DESC, "{special:everything}", "?{ '{name}' | '{desc}' | '{formattedWaypointOffset}' }", ""};
038        private static final String[] LABEL_PATTERN_DESC = new String[] {tr("Auto"), /* gpx data field name */ trc("gpx_field", "Name"),
039            /* gpx data field name */ trc("gpx_field", "Desc(ription)"), tr("Everything"), tr("Name or offset"), tr("None"), tr("Custom")};
040    
041    
042        private JRadioButton drawRawGpsLinesGlobal = new JRadioButton(tr("Use global settings"));
043        private JRadioButton drawRawGpsLinesAll = new JRadioButton(tr("All"));
044        private JRadioButton drawRawGpsLinesLocal = new JRadioButton(tr("Local files"));
045        private JRadioButton drawRawGpsLinesNone = new JRadioButton(tr("None"));
046        private ActionListener drawRawGpsLinesActionListener;
047        private JTextField drawRawGpsMaxLineLength = new JTextField(8);
048        private JTextField drawRawGpsMaxLineLengthLocal = new JTextField(8);
049        private JTextField drawLineWidth = new JTextField(2);
050        private JCheckBox forceRawGpsLines = new JCheckBox(tr("Force lines if no segments imported"));
051        private JCheckBox largeGpsPoints = new JCheckBox(tr("Draw large GPS points"));
052        private JCheckBox hdopCircleGpsPoints = new JCheckBox(tr("Draw a circle from HDOP value"));
053        private ButtonGroup colorGroup;
054        private JRadioButton colorTypeVelocity = new JRadioButton(tr("Velocity (red = slow, green = fast)"));
055        private JRadioButton colorTypeDirection = new JRadioButton(tr("Direction (red = west, yellow = north, green = east, blue = south)"));
056        private JRadioButton colorTypeDilution = new JRadioButton(tr("Dilution of Position (red = high, green = low, if available)"));
057        private JRadioButton colorTypeTime = new JRadioButton(tr("Track date"));
058        private JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized for named layers)"));
059        private JRadioButton colorTypeGlobal  = new JRadioButton(tr("Use global settings"));
060        private JosmComboBox colorTypeVelocityTune = new JosmComboBox(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")});
061        private JCheckBox makeAutoMarkers = new JCheckBox(tr("Create markers when reading GPX"));
062        private JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows"));
063        private JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)"));
064        private JTextField drawGpsArrowsMinDist = new JTextField(8);
065        private JCheckBox colorDynamic = new JCheckBox(tr("Dynamic color range based on data limits"));
066        private JosmComboBox waypointLabel = new JosmComboBox(LABEL_PATTERN_DESC);
067        private JTextField waypointLabelPattern = new JTextField();
068        private JosmComboBox audioWaypointLabel = new JosmComboBox(LABEL_PATTERN_DESC);
069        private JTextField audioWaypointLabelPattern = new JTextField();
070    
071        private String layerName;
072        private boolean local; // flag to display LocalOnly checkbox
073        private boolean nonlocal; // flag to display AllLines checkbox
074    
075        public GPXSettingsPanel(String layerName, boolean local, boolean nonlocal) {
076            super(new GridBagLayout());
077            this.local=local; this.nonlocal=nonlocal;
078            this.layerName = "layer "+layerName;
079            initComponents();
080            loadPreferences();
081        }
082    
083        public GPXSettingsPanel() {
084            super(new GridBagLayout());
085            initComponents();
086            local=false; nonlocal=false;
087            loadPreferences(); // preferences -> controls
088        }
089    
090        private void initComponents() {
091            setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
092    
093            // makeAutoMarkers
094            makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer."));
095            ExpertToggleAction.addVisibilitySwitcher(makeAutoMarkers);
096            add(makeAutoMarkers, GBC.eol().insets(20,0,0,5));
097    
098            // drawRawGpsLines
099            ButtonGroup gpsLinesGroup = new ButtonGroup();
100            if (layerName!=null) {
101                gpsLinesGroup.add(drawRawGpsLinesGlobal);
102            }
103            gpsLinesGroup.add(drawRawGpsLinesNone);
104            gpsLinesGroup.add(drawRawGpsLinesLocal);
105            gpsLinesGroup.add(drawRawGpsLinesAll);
106    
107            /* ensure that default is in data base */
108    
109            JLabel label = new JLabel(tr("Draw lines between raw GPS points"));
110            add(label, GBC.eol().insets(20,0,0,0));
111            if (layerName!=null) {
112                add(drawRawGpsLinesGlobal, GBC.eol().insets(40,0,0,0));
113            }
114            add(drawRawGpsLinesNone, GBC.eol().insets(40,0,0,0));
115            if (layerName==null || local) {
116                add(drawRawGpsLinesLocal, GBC.eol().insets(40,0,0,0));
117            }
118            if (layerName==null || nonlocal) {
119                add(drawRawGpsLinesAll, GBC.eol().insets(40,0,0,0));
120            }
121            ExpertToggleAction.addVisibilitySwitcher(label);
122            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesGlobal);
123            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesNone);
124            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesLocal);
125            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsLinesAll);
126    
127            drawRawGpsLinesActionListener = new ActionListener(){
128                public void actionPerformed(ActionEvent e) {
129                    boolean f=drawRawGpsLinesNone.isSelected()||drawRawGpsLinesGlobal.isSelected();
130                    forceRawGpsLines.setEnabled(!f);
131                    drawRawGpsMaxLineLength.setEnabled(!(f || drawRawGpsLinesLocal.isSelected()));
132                    drawRawGpsMaxLineLengthLocal.setEnabled(!f);
133                    drawGpsArrows.setEnabled(!f);
134                    drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
135                    drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
136                }
137            };
138    
139            drawRawGpsLinesGlobal.addActionListener(drawRawGpsLinesActionListener);
140            drawRawGpsLinesNone.addActionListener(drawRawGpsLinesActionListener);
141            drawRawGpsLinesLocal.addActionListener(drawRawGpsLinesActionListener);
142            drawRawGpsLinesAll.addActionListener(drawRawGpsLinesActionListener);
143    
144            // drawRawGpsMaxLineLengthLocal
145            drawRawGpsMaxLineLengthLocal.setToolTipText(tr("Maximum length (in meters) to draw lines for local files. Set to ''-1'' to draw all lines."));
146            label = new JLabel(tr("Maximum length for local files (meters)"));
147            add(label, GBC.std().insets(40,0,0,0));
148            add(drawRawGpsMaxLineLengthLocal, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
149            ExpertToggleAction.addVisibilitySwitcher(label);
150            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsMaxLineLengthLocal);
151    
152            // drawRawGpsMaxLineLength
153            drawRawGpsMaxLineLength.setToolTipText(tr("Maximum length (in meters) to draw lines. Set to ''-1'' to draw all lines."));
154            label = new JLabel(tr("Maximum length (meters)"));
155            add(label, GBC.std().insets(40,0,0,0));
156            add(drawRawGpsMaxLineLength, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
157            ExpertToggleAction.addVisibilitySwitcher(label);
158            ExpertToggleAction.addVisibilitySwitcher(drawRawGpsMaxLineLength);
159    
160            // forceRawGpsLines
161            forceRawGpsLines.setToolTipText(tr("Force drawing of lines if the imported data contain no line information."));
162            add(forceRawGpsLines, GBC.eop().insets(40,0,0,0));
163            ExpertToggleAction.addVisibilitySwitcher(forceRawGpsLines);
164    
165            // drawGpsArrows
166            drawGpsArrows.addActionListener(new ActionListener(){
167                public void actionPerformed(ActionEvent e) {
168                    drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
169                    drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
170                }
171            });
172            drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points."));
173            add(drawGpsArrows, GBC.eop().insets(40,0,0,0));
174    
175            // drawGpsArrowsFast
176            drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math."));
177            add(drawGpsArrowsFast, GBC.eop().insets(60,0,0,0));
178            ExpertToggleAction.addVisibilitySwitcher(drawGpsArrowsFast);
179    
180            // drawGpsArrowsMinDist
181            drawGpsArrowsMinDist.setToolTipText(tr("Do not draw arrows if they are not at least this distance away from the last one."));
182            add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(60,0,0,0));
183            add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
184    
185            // hdopCircleGpsPoints
186            hdopCircleGpsPoints.setToolTipText(tr("Draw a circle from HDOP value."));
187            add(hdopCircleGpsPoints, GBC.eop().insets(20,0,0,0));
188            ExpertToggleAction.addVisibilitySwitcher(hdopCircleGpsPoints);
189    
190            // largeGpsPoints
191            largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points."));
192            add(largeGpsPoints, GBC.eop().insets(20,0,0,0));
193    
194            // drawLineWidth
195            drawLineWidth.setToolTipText(tr("Width of drawn GPX line (0 for default)"));
196            add(new JLabel(tr("Drawing width of GPX lines")), GBC.std().insets(20,0,0,0));
197            add(drawLineWidth, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
198    
199            // colorTracks
200            colorGroup = new ButtonGroup();
201            if (layerName!=null) {
202                colorGroup.add(colorTypeGlobal);
203            }
204            colorGroup.add(colorTypeNone);
205            colorGroup.add(colorTypeVelocity);
206            colorGroup.add(colorTypeDirection);
207            colorGroup.add(colorTypeDilution);
208            colorGroup.add(colorTypeTime);
209    
210            colorTypeVelocity.addChangeListener(new ChangeListener(){
211                public void stateChanged(ChangeEvent e) {
212                    colorTypeVelocityTune.setEnabled(colorTypeVelocity.isSelected());
213                    colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected());
214                }
215            });
216            colorTypeDilution.addChangeListener(new ChangeListener(){
217                public void stateChanged(ChangeEvent e) {
218                    colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected());
219                }
220            });
221    
222            colorTypeNone.setToolTipText(tr("All points and track segments will have the same color. Can be customized in Layer Manager."));
223            colorTypeVelocity.setToolTipText(tr("Colors points and track segments by velocity."));
224            colorTypeDirection.setToolTipText(tr("Colors points and track segments by direction."));
225            colorTypeDilution.setToolTipText(tr("Colors points and track segments by dilution of position (HDOP). Your capture device needs to log that information."));
226            colorTypeTime.setToolTipText(tr("Colors points and track segments by its timestamp."));
227    
228            // color Tracks by Velocity Tune
229            colorTypeVelocityTune.setToolTipText(tr("Allows to tune the track coloring for different average speeds."));
230    
231            add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
232    
233            add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20,0,0,0));
234            if (layerName!=null) {
235                add(colorTypeGlobal, GBC.eol().insets(40,0,0,0));
236            }
237            add(colorTypeNone, GBC.eol().insets(40,0,0,0));
238            add(colorTypeVelocity, GBC.std().insets(40,0,0,0));
239            add(colorTypeVelocityTune, GBC.eop().insets(5,0,0,5));
240            add(colorTypeDirection, GBC.eol().insets(40,0,0,0));
241            add(colorTypeDilution, GBC.eol().insets(40,0,0,0));
242            add(colorTypeTime, GBC.eol().insets(40,0,0,0));
243            ExpertToggleAction.addVisibilitySwitcher(colorTypeDirection);
244            ExpertToggleAction.addVisibilitySwitcher(colorTypeDilution);
245    
246            colorDynamic.setToolTipText(tr("Colors points and track segments by data limits."));
247            add(colorDynamic, GBC.eop().insets(40,0,0,0));
248            ExpertToggleAction.addVisibilitySwitcher(colorDynamic);
249    
250            if (layerName == null) {
251                // Setting waypoints for gpx layer doesn't make sense - waypoints are shown in marker layer that has different name - so show
252                // this only for global config
253    
254                // waypointLabel
255                label = new JLabel(tr("Waypoint labelling"));
256                add(label, GBC.std().insets(20,0,0,0));
257                add(waypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
258                waypointLabel.addActionListener(new ActionListener() {
259                    @Override
260                    public void actionPerformed(ActionEvent e) {
261                        updateWaypointPattern(waypointLabel, waypointLabelPattern);
262                    }
263                });
264                updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker(layerName));
265                add(waypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20,0,0,5));
266                ExpertToggleAction.addVisibilitySwitcher(label);
267                ExpertToggleAction.addVisibilitySwitcher(waypointLabel);
268                ExpertToggleAction.addVisibilitySwitcher(waypointLabelPattern);
269    
270                // audioWaypointLabel
271                Component glue = Box.createVerticalGlue();
272                add(glue, GBC.eol().insets(0, 20, 0, 0));
273                ExpertToggleAction.addVisibilitySwitcher(glue);
274    
275                label = new JLabel(tr("Audio waypoint labelling"));
276                add(label, GBC.std().insets(20,0,0,0));
277                add(audioWaypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
278                audioWaypointLabel.addActionListener(new ActionListener() {
279                    @Override
280                    public void actionPerformed(ActionEvent e) {
281                        updateWaypointPattern(audioWaypointLabel, audioWaypointLabelPattern);
282                    }
283                });
284                updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker(layerName));
285                add(audioWaypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20,0,0,5));
286                ExpertToggleAction.addVisibilitySwitcher(label);
287                ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabel);
288                ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabelPattern);
289            }
290    
291            add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
292        }
293    
294        /**
295         * Loads preferences to UI controls
296         */
297        public void loadPreferences () {
298            makeAutoMarkers.setSelected(Main.pref.getBoolean("marker.makeautomarkers", true));
299            if(layerName!=null && Main.pref.get("draw.rawgps.lines."+layerName).isEmpty()
300                    && Main.pref.get("draw.rawgps.lines.local."+layerName).isEmpty()){
301                // no line preferences for layer is found
302                drawRawGpsLinesGlobal.setSelected(true);
303            } else {
304                Boolean lf = Main.pref.getBoolean("draw.rawgps.lines.local",layerName, true);
305                if(Main.pref.getBoolean("draw.rawgps.lines",layerName, true)) {
306                    drawRawGpsLinesAll.setSelected(true);
307                } else if (lf) {
308                    drawRawGpsLinesLocal.setSelected(true);
309                } else {
310                    drawRawGpsLinesNone.setSelected(true);
311                }
312            }
313    
314            drawRawGpsMaxLineLengthLocal.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length.local",layerName, -1)));
315            drawRawGpsMaxLineLength.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length",layerName, 200)));
316            drawLineWidth.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.linewidth",layerName, 0)));
317            forceRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines.force",layerName, false));
318            drawGpsArrows.setSelected(Main.pref.getBoolean("draw.rawgps.direction",layerName, false));
319            drawGpsArrowsFast.setSelected(Main.pref.getBoolean("draw.rawgps.alternatedirection",layerName, false));
320            drawGpsArrowsMinDist.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.min-arrow-distance",layerName, 40)));
321            hdopCircleGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.hdopcircle",layerName, false));
322            largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large",layerName, false));
323            drawRawGpsLinesActionListener.actionPerformed(null);
324    
325            if(layerName!=null && Main.pref.get("draw.rawgps.colors."+layerName).isEmpty()) {
326                colorTypeGlobal.setSelected(true);
327                colorDynamic.setSelected(false);
328                colorDynamic.setEnabled(false);
329            } else {
330                switch(Main.pref.getInteger("draw.rawgps.colors",layerName, 0)) {
331                case 0: colorTypeNone.setSelected(true);   break;
332                case 1: colorTypeVelocity.setSelected(true);  break;
333                case 2: colorTypeDilution.setSelected(true);  break;
334                case 3: colorTypeDirection.setSelected(true); break;
335                case 4: colorTypeTime.setSelected(true);  break;
336                }
337                int ccts = Main.pref.getInteger("draw.rawgps.colorTracksTune",layerName, 45);
338                colorTypeVelocityTune.setSelectedIndex(ccts==10 ? 2 : (ccts==20 ? 1 : 0));
339                colorTypeVelocityTune.setEnabled(colorTypeVelocity.isSelected() && colorTypeVelocity.isEnabled());
340                colorDynamic.setSelected(Main.pref.getBoolean("draw.rawgps.colors.dynamic",layerName, false));
341                colorDynamic.setEnabled(colorTypeVelocity.isSelected() || colorTypeDilution.isSelected());
342            }
343        }
344    
345    
346        /**
347         * Save preferences from UI controls for specified layer
348         * if layerName==null, global preferences are written
349         */
350        public boolean savePreferences (String layerName, boolean locLayer) {
351            String layerNameDot = ".layer "+layerName;
352            if (layerName==null) {
353                layerNameDot="";
354            }
355            Main.pref.put("marker.makeautomarkers"+layerNameDot, makeAutoMarkers.isSelected());
356            if (drawRawGpsLinesGlobal.isSelected()) {
357                Main.pref.put("draw.rawgps.lines" + layerNameDot, null);
358                Main.pref.put("draw.rawgps.max-line-length" + layerNameDot, null);
359                Main.pref.put("draw.rawgps.lines.local" + layerNameDot, null);
360                Main.pref.put("draw.rawgps.max-line-length.local" + layerNameDot, null);
361                Main.pref.put("draw.rawgps.lines.force"+layerNameDot, null);
362                Main.pref.put("draw.rawgps.direction"+layerNameDot, null);
363                Main.pref.put("draw.rawgps.alternatedirection"+layerNameDot, null);
364                Main.pref.put("draw.rawgps.min-arrow-distance"+layerNameDot, null);
365            } else {
366                if (layerName==null || !locLayer) {
367                    Main.pref.put("draw.rawgps.lines" +  layerNameDot, drawRawGpsLinesAll.isSelected());
368                    Main.pref.put("draw.rawgps.max-line-length" + layerNameDot, drawRawGpsMaxLineLength.getText());
369                }
370                if (layerName==null || locLayer) {
371                    Main.pref.put("draw.rawgps.lines.local" + layerNameDot, drawRawGpsLinesAll.isSelected() || drawRawGpsLinesLocal.isSelected());
372                    Main.pref.put("draw.rawgps.max-line-length.local" + layerNameDot, drawRawGpsMaxLineLengthLocal.getText());
373                }
374                Main.pref.put("draw.rawgps.lines.force"+layerNameDot, forceRawGpsLines.isSelected());
375                Main.pref.put("draw.rawgps.direction"+layerNameDot, drawGpsArrows.isSelected());
376                Main.pref.put("draw.rawgps.alternatedirection"+layerNameDot, drawGpsArrowsFast.isSelected());
377                Main.pref.put("draw.rawgps.min-arrow-distance"+layerNameDot, drawGpsArrowsMinDist.getText());
378            }
379    
380            Main.pref.put("draw.rawgps.hdopcircle"+layerNameDot, hdopCircleGpsPoints.isSelected());
381            Main.pref.put("draw.rawgps.large"+layerNameDot, largeGpsPoints.isSelected());
382            Main.pref.put("draw.rawgps.linewidth"+layerNameDot, drawLineWidth.getText());
383    
384            TemplateEntryProperty.forMarker(layerName).put(waypointLabelPattern.getText());
385            TemplateEntryProperty.forAudioMarker(layerName).put(audioWaypointLabelPattern.getText());
386    
387            if(colorTypeGlobal.isSelected()) {
388                Main.pref.put("draw.rawgps.colors"+layerNameDot, null);
389                Main.pref.put("draw.rawgps.colors.dynamic"+layerNameDot, null);
390                Main.pref.put("draw.rawgps.colorTracksTunec"+layerNameDot, null);
391                return false;
392            } else if(colorTypeVelocity.isSelected()) {
393                Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 1);
394            } else if(colorTypeDilution.isSelected()) {
395                Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 2);
396            } else if(colorTypeDirection.isSelected()) {
397                Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 3);
398            } else if(colorTypeTime.isSelected()) {
399                Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 4);
400            } else {
401                Main.pref.putInteger("draw.rawgps.colors"+layerNameDot, 0);
402            }
403            Main.pref.put("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());
404            int ccti=colorTypeVelocityTune.getSelectedIndex();
405            Main.pref.putInteger("draw.rawgps.colorTracksTune"+layerNameDot, ccti==2 ? 10 : (ccti==1 ? 20 : 45));
406            return false;
407        }
408    
409        /**
410         * Save preferences from UI controls for initial layer or globally
411         */
412        public void savePreferences() {
413            savePreferences(null, false);
414        }
415    
416        private void updateWaypointLabelCombobox(JosmComboBox cb, JTextField tf, TemplateEntryProperty property) {
417            String labelPattern = property.getAsString();
418            boolean found = false;
419            for (int i=0; i<LABEL_PATTERN_TEMPLATE.length; i++) {
420                if (LABEL_PATTERN_TEMPLATE[i].equals(labelPattern)) {
421                    cb.setSelectedIndex(i);
422                    found = true;
423                    break;
424                }
425            }
426            if (!found) {
427                cb.setSelectedIndex(WAYPOINT_LABEL_CUSTOM);
428                tf.setEnabled(true);
429                tf.setText(labelPattern);
430            }
431        }
432    
433        private void updateWaypointPattern(JosmComboBox cb, JTextField tf) {
434            if (cb.getSelectedIndex() == WAYPOINT_LABEL_CUSTOM) {
435                tf.setEnabled(true);
436            } else {
437                tf.setEnabled(false);
438                tf.setText(LABEL_PATTERN_TEMPLATE[cb.getSelectedIndex()]);
439            }
440        }
441    
442        @Override
443        public boolean validatePreferences() {
444            TemplateParser parser = new TemplateParser(waypointLabelPattern.getText());
445            try {
446                parser.parse();
447            } catch (ParseError e) {
448                JOptionPane.showMessageDialog(Main.parent, tr("Incorrect waypoint label pattern: {0}", e.getMessage()), tr("Incorrect pattern"), JOptionPane.ERROR_MESSAGE);
449                waypointLabelPattern.requestFocus();
450                return false;
451            }
452            parser = new TemplateParser(audioWaypointLabelPattern.getText());
453            try {
454                parser.parse();
455            } catch (ParseError e) {
456                JOptionPane.showMessageDialog(Main.parent, tr("Incorrect audio waypoint label pattern: {0}", e.getMessage()), tr("Incorrect pattern"), JOptionPane.ERROR_MESSAGE);
457                audioWaypointLabelPattern.requestFocus();
458                return false;
459            }
460            return true;
461        }
462    
463    }