001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.geoimage;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.FlowLayout;
008import java.awt.GridBagLayout;
009import java.awt.event.ActionEvent;
010import java.awt.event.ActionListener;
011import java.util.Objects;
012
013import javax.swing.BorderFactory;
014import javax.swing.JCheckBox;
015import javax.swing.JComponent;
016import javax.swing.JLabel;
017import javax.swing.JPanel;
018import javax.swing.JSpinner;
019import javax.swing.SpinnerNumberModel;
020import javax.swing.border.Border;
021
022import org.openstreetmap.josm.gui.ExtendedDialog;
023import org.openstreetmap.josm.spi.preferences.Config;
024import org.openstreetmap.josm.spi.preferences.IPreferences;
025import org.openstreetmap.josm.tools.GBC;
026
027/**
028 * Dialog for advanced GPX correlation settings.
029 * @since 18044 (extracted from {@link CorrelateGpxWithImages})
030 */
031public class AdvancedCorrelationSettingsDialog extends ExtendedDialog {
032
033    private JCheckBox cInterpolSeg;
034    private JCheckBox cInterpolSegTime;
035    private JSpinner sInterpolSegTime;
036    private JCheckBox cInterpolSegDist;
037    private JSpinner sInterpolSegDist;
038    private JCheckBox cTagSeg;
039    private JCheckBox cTagSegTime;
040    private JSpinner sTagSegTime;
041    private JCheckBox cInterpolTrack;
042    private JCheckBox cInterpolTrackTime;
043    private JSpinner sInterpolTrackTime;
044    private JCheckBox cInterpolTrackDist;
045    private JSpinner sInterpolTrackDist;
046    private JCheckBox cTagTrack;
047    private JCheckBox cTagTrackTime;
048    private JSpinner sTagTrackTime;
049    private JCheckBox cForce;
050
051    /**
052     * Constructs a new {@code AdvancedCorrelationSettingsDialog}.
053     * @param parent The parent element that will be used for position and maximum size
054     * @param forceTags Initial value of forced tagging of all pictures
055     */
056    public AdvancedCorrelationSettingsDialog(Component parent, boolean forceTags) {
057        super(parent, tr("Advanced settings"), tr("OK"), tr("Cancel"));
058        setButtonIcons("ok", "cancel");
059        setContent(buildContent(forceTags));
060    }
061
062    private Component buildContent(boolean forceTags) {
063        IPreferences s = Config.getPref();
064        JPanel p = new JPanel(new GridBagLayout());
065
066        Border border1 = BorderFactory.createEmptyBorder(0, 20, 0, 0);
067        Border border2 = BorderFactory.createEmptyBorder(10, 0, 5, 0);
068        Border border = BorderFactory.createEmptyBorder(0, 40, 0, 0);
069        FlowLayout layout = new FlowLayout();
070
071        JLabel l = new JLabel(tr("Segment settings"));
072        l.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
073        p.add(l, GBC.eol());
074        cInterpolSeg = new JCheckBox(tr("Interpolate between segments"), s.getBoolean("geoimage.seg.int", true));
075        cInterpolSeg.setBorder(border1);
076        p.add(cInterpolSeg, GBC.eol());
077
078        cInterpolSegTime = new JCheckBox(tr("only when the segments are less than # minutes apart:"),
079                s.getBoolean("geoimage.seg.int.time", true));
080        sInterpolSegTime = new JSpinner(
081                new SpinnerNumberModel(s.getInt("geoimage.seg.int.time.val", 60), 0, Integer.MAX_VALUE, 1));
082        ((JSpinner.DefaultEditor) sInterpolSegTime.getEditor()).getTextField().setColumns(3);
083        JPanel pInterpolSegTime = new JPanel(layout);
084        pInterpolSegTime.add(cInterpolSegTime);
085        pInterpolSegTime.add(sInterpolSegTime);
086        pInterpolSegTime.setBorder(border);
087        p.add(pInterpolSegTime, GBC.eol());
088
089        cInterpolSegDist = new JCheckBox(tr("only when the segments are less than # meters apart:"),
090                s.getBoolean("geoimage.seg.int.dist", true));
091        sInterpolSegDist = new JSpinner(
092                new SpinnerNumberModel(s.getInt("geoimage.seg.int.dist.val", 50), 0, Integer.MAX_VALUE, 1));
093        ((JSpinner.DefaultEditor) sInterpolSegDist.getEditor()).getTextField().setColumns(3);
094        JPanel pInterpolSegDist = new JPanel(layout);
095        pInterpolSegDist.add(cInterpolSegDist);
096        pInterpolSegDist.add(sInterpolSegDist);
097        pInterpolSegDist.setBorder(border);
098        p.add(pInterpolSegDist, GBC.eol());
099
100        cTagSeg = new JCheckBox(tr("Tag images at the closest end of a segment, when not interpolated"),
101                s.getBoolean("geoimage.seg.tag", true));
102        cTagSeg.setBorder(border1);
103        p.add(cTagSeg, GBC.eol());
104
105        cTagSegTime = new JCheckBox(tr("only within # minutes of the closest trackpoint:"),
106                s.getBoolean("geoimage.seg.tag.time", true));
107        sTagSegTime = new JSpinner(
108                new SpinnerNumberModel(s.getInt("geoimage.seg.tag.time.val", 2), 0, Integer.MAX_VALUE, 1));
109        ((JSpinner.DefaultEditor) sTagSegTime.getEditor()).getTextField().setColumns(3);
110        JPanel pTagSegTime = new JPanel(layout);
111        pTagSegTime.add(cTagSegTime);
112        pTagSegTime.add(sTagSegTime);
113        pTagSegTime.setBorder(border);
114        p.add(pTagSegTime, GBC.eol());
115
116        l = new JLabel(tr("Track settings (note that multiple tracks can be in one GPX file)"));
117        l.setBorder(border2);
118        p.add(l, GBC.eol());
119        cInterpolTrack = new JCheckBox(tr("Interpolate between tracks"), s.getBoolean("geoimage.trk.int", false));
120        cInterpolTrack.setBorder(border1);
121        p.add(cInterpolTrack, GBC.eol());
122
123        cInterpolTrackTime = new JCheckBox(tr("only when the tracks are less than # minutes apart:"),
124                s.getBoolean("geoimage.trk.int.time", false));
125        sInterpolTrackTime = new JSpinner(
126                new SpinnerNumberModel(s.getInt("geoimage.trk.int.time.val", 60), 0, Integer.MAX_VALUE, 1));
127        ((JSpinner.DefaultEditor) sInterpolTrackTime.getEditor()).getTextField().setColumns(3);
128        JPanel pInterpolTrackTime = new JPanel(layout);
129        pInterpolTrackTime.add(cInterpolTrackTime);
130        pInterpolTrackTime.add(sInterpolTrackTime);
131        pInterpolTrackTime.setBorder(border);
132        p.add(pInterpolTrackTime, GBC.eol());
133
134        cInterpolTrackDist = new JCheckBox(tr("only when the tracks are less than # meters apart:"),
135                s.getBoolean("geoimage.trk.int.dist", false));
136        sInterpolTrackDist = new JSpinner(
137                new SpinnerNumberModel(s.getInt("geoimage.trk.int.dist.val", 50), 0, Integer.MAX_VALUE, 1));
138        ((JSpinner.DefaultEditor) sInterpolTrackDist.getEditor()).getTextField().setColumns(3);
139        JPanel pInterpolTrackDist = new JPanel(layout);
140        pInterpolTrackDist.add(cInterpolTrackDist);
141        pInterpolTrackDist.add(sInterpolTrackDist);
142        pInterpolTrackDist.setBorder(border);
143        p.add(pInterpolTrackDist, GBC.eol());
144
145        cTagTrack = new JCheckBox("<html>" +
146                tr("Tag images at the closest end of a track, when not interpolated<br>" +
147                "(also applies before the first and after the last track)") + "</html>",
148                s.getBoolean("geoimage.trk.tag", true));
149        cTagTrack.setBorder(border1);
150        p.add(cTagTrack, GBC.eol());
151
152        cTagTrackTime = new JCheckBox(tr("only within # minutes of the closest trackpoint:"),
153                s.getBoolean("geoimage.trk.tag.time", true));
154        sTagTrackTime = new JSpinner(
155                new SpinnerNumberModel(s.getInt("geoimage.trk.tag.time.val", 2), 0, Integer.MAX_VALUE, 1));
156        ((JSpinner.DefaultEditor) sTagTrackTime.getEditor()).getTextField().setColumns(3);
157        JPanel pTagTrackTime = new JPanel(layout);
158        pTagTrackTime.add(cTagTrackTime);
159        pTagTrackTime.add(sTagTrackTime);
160        pTagTrackTime.setBorder(border);
161        p.add(pTagTrackTime, GBC.eol());
162
163        l = new JLabel(tr("Advanced"));
164        l.setBorder(border2);
165        p.add(l, GBC.eol());
166        cForce = new JCheckBox("<html>" +
167                tr("Force tagging of all pictures (temporarily overrides the settings above).") + "<br>" +
168                tr("This option will not be saved permanently.") + "</html>", forceTags);
169        cForce.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 0));
170        p.add(cForce, GBC.eol());
171
172        addCheckBoxActionListener(cInterpolSegTime, sInterpolSegTime);
173        addCheckBoxActionListener(cInterpolSegDist, sInterpolSegDist);
174        addCheckBoxActionListener(cInterpolSeg, pInterpolSegTime, pInterpolSegDist);
175
176        addCheckBoxActionListener(cTagSegTime, sTagSegTime);
177        addCheckBoxActionListener(cTagSeg, pTagSegTime);
178
179        addCheckBoxActionListener(cInterpolTrackTime, sInterpolTrackTime);
180        addCheckBoxActionListener(cInterpolTrackDist, sInterpolTrackDist);
181        addCheckBoxActionListener(cInterpolTrack, pInterpolTrackTime, pInterpolTrackDist);
182
183        addCheckBoxActionListener(cTagTrackTime, sTagTrackTime);
184        addCheckBoxActionListener(cTagTrack, pTagTrackTime);
185
186        return p;
187    }
188
189    @Override
190    protected void buttonAction(int buttonIndex, ActionEvent evt) {
191        super.buttonAction(buttonIndex, evt);
192        if (buttonIndex == 0) {
193            IPreferences s = Config.getPref();
194
195            s.putBoolean("geoimage.seg.int", cInterpolSeg.isSelected());
196            s.putBoolean("geoimage.seg.int.dist", cInterpolSegDist.isSelected());
197            s.putInt("geoimage.seg.int.dist.val", (int) sInterpolSegDist.getValue());
198            s.putBoolean("geoimage.seg.int.time", cInterpolSegTime.isSelected());
199            s.putInt("geoimage.seg.int.time.val", (int) sInterpolSegTime.getValue());
200            s.putBoolean("geoimage.seg.tag", cTagSeg.isSelected());
201            s.putBoolean("geoimage.seg.tag.time", cTagSegTime.isSelected());
202            s.putInt("geoimage.seg.tag.time.val", (int) sTagSegTime.getValue());
203
204            s.putBoolean("geoimage.trk.int", cInterpolTrack.isSelected());
205            s.putBoolean("geoimage.trk.int.dist", cInterpolTrackDist.isSelected());
206            s.putInt("geoimage.trk.int.dist.val", (int) sInterpolTrackDist.getValue());
207            s.putBoolean("geoimage.trk.int.time", cInterpolTrackTime.isSelected());
208            s.putInt("geoimage.trk.int.time.val", (int) sInterpolTrackTime.getValue());
209            s.putBoolean("geoimage.trk.tag", cTagTrack.isSelected());
210            s.putBoolean("geoimage.trk.tag.time", cTagTrackTime.isSelected());
211            s.putInt("geoimage.trk.tag.time.val", (int) sTagTrackTime.getValue());
212        }
213    }
214
215    /**
216     * Determines if the forced tagging of all pictures is selected.
217     * @return {@code true} if the forced tagging of all pictures is selected
218     */
219    public boolean isForceTaggingSelected() {
220        return cForce.isSelected();
221    }
222
223    protected static class CheckBoxActionListener implements ActionListener {
224        private final JComponent[] comps;
225
226        CheckBoxActionListener(JComponent... c) {
227            comps = Objects.requireNonNull(c);
228        }
229
230        @Override
231        public void actionPerformed(ActionEvent e) {
232            setEnabled((JCheckBox) e.getSource());
233        }
234
235        public void setEnabled(JCheckBox cb) {
236            for (JComponent comp : comps) {
237                if (comp instanceof JSpinner) {
238                    comp.setEnabled(cb.isSelected());
239                } else if (comp instanceof JPanel) {
240                    boolean en = cb.isSelected();
241                    for (Component c : comp.getComponents()) {
242                        if (c instanceof JSpinner) {
243                            c.setEnabled(en);
244                        } else {
245                            c.setEnabled(cb.isSelected());
246                            if (en && c instanceof JCheckBox) {
247                                en = ((JCheckBox) c).isSelected();
248                            }
249                        }
250                    }
251                }
252            }
253        }
254    }
255
256    protected static void addCheckBoxActionListener(JCheckBox cb, JComponent... c) {
257        CheckBoxActionListener listener = new CheckBoxActionListener(c);
258        cb.addActionListener(listener);
259        listener.setEnabled(cb);
260    }
261}