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}