| 1 | // License: GPL. For details, see LICENSE file. |
|---|
| 2 | package org.openstreetmap.josm.io; |
|---|
| 3 | |
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
|---|
| 5 | |
|---|
| 6 | import java.awt.GridBagLayout; |
|---|
| 7 | import java.awt.event.ActionEvent; |
|---|
| 8 | import java.awt.event.ActionListener; |
|---|
| 9 | import java.awt.event.KeyAdapter; |
|---|
| 10 | import java.awt.event.KeyEvent; |
|---|
| 11 | import java.io.File; |
|---|
| 12 | import java.io.FileOutputStream; |
|---|
| 13 | import java.io.IOException; |
|---|
| 14 | import java.text.MessageFormat; |
|---|
| 15 | import java.util.Calendar; |
|---|
| 16 | |
|---|
| 17 | import javax.swing.JButton; |
|---|
| 18 | import javax.swing.JCheckBox; |
|---|
| 19 | import javax.swing.JLabel; |
|---|
| 20 | import javax.swing.JList; |
|---|
| 21 | import javax.swing.JOptionPane; |
|---|
| 22 | import javax.swing.JPanel; |
|---|
| 23 | import javax.swing.JScrollPane; |
|---|
| 24 | import javax.swing.JTextArea; |
|---|
| 25 | import javax.swing.JTextField; |
|---|
| 26 | import javax.swing.ListSelectionModel; |
|---|
| 27 | |
|---|
| 28 | import org.openstreetmap.josm.Main; |
|---|
| 29 | import org.openstreetmap.josm.actions.ExtensionFileFilter; |
|---|
| 30 | import org.openstreetmap.josm.data.gpx.GpxData; |
|---|
| 31 | import org.openstreetmap.josm.data.osm.DataSet; |
|---|
| 32 | import org.openstreetmap.josm.gui.ExtendedDialog; |
|---|
| 33 | import org.openstreetmap.josm.gui.layer.GpxLayer; |
|---|
| 34 | import org.openstreetmap.josm.gui.layer.Layer; |
|---|
| 35 | import org.openstreetmap.josm.gui.layer.OsmDataLayer; |
|---|
| 36 | import org.openstreetmap.josm.io.auth.CredentialsManager; |
|---|
| 37 | import org.openstreetmap.josm.tools.CheckParameterUtil; |
|---|
| 38 | import org.openstreetmap.josm.tools.GBC; |
|---|
| 39 | |
|---|
| 40 | public class GpxExporter extends FileExporter { |
|---|
| 41 | private final static String warningGpl = "<html><font color='red' size='-2'>" |
|---|
| 42 | + tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.") + "</html>"; |
|---|
| 43 | |
|---|
| 44 | public GpxExporter() { |
|---|
| 45 | super(new ExtensionFileFilter("gpx,gpx.gz", "gpx", tr("GPX Files") + " (*.gpx *.gpx.gz)")); |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | @Override |
|---|
| 49 | public boolean acceptFile(File pathname, Layer layer) { |
|---|
| 50 | if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer)) |
|---|
| 51 | return false; |
|---|
| 52 | return super.acceptFile(pathname, layer); |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | @Override |
|---|
| 56 | public void exportData(File file, Layer layer) throws IOException { |
|---|
| 57 | CheckParameterUtil.ensureParameterNotNull(layer, "layer"); |
|---|
| 58 | if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer)) |
|---|
| 59 | throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer |
|---|
| 60 | .getClass().getName())); |
|---|
| 61 | CheckParameterUtil.ensureParameterNotNull(file, "file"); |
|---|
| 62 | |
|---|
| 63 | String fn = file.getPath(); |
|---|
| 64 | if (fn.indexOf('.') == -1) { |
|---|
| 65 | fn += ".gpx"; |
|---|
| 66 | file = new File(fn); |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | // open the dialog asking for options |
|---|
| 70 | JPanel p = new JPanel(new GridBagLayout()); |
|---|
| 71 | |
|---|
| 72 | p.add(new JLabel(tr("GPS track description")), GBC.eol()); |
|---|
| 73 | JTextArea desc = new JTextArea(3, 40); |
|---|
| 74 | desc.setWrapStyleWord(true); |
|---|
| 75 | desc.setLineWrap(true); |
|---|
| 76 | p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH)); |
|---|
| 77 | |
|---|
| 78 | JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true)); |
|---|
| 79 | author.setSelected(true); |
|---|
| 80 | p.add(author, GBC.eol()); |
|---|
| 81 | JLabel nameLabel = new JLabel(tr("Real name")); |
|---|
| 82 | p.add(nameLabel, GBC.std().insets(10, 0, 5, 0)); |
|---|
| 83 | JTextField authorName = new JTextField(Main.pref.get("lastAuthorName")); |
|---|
| 84 | p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL)); |
|---|
| 85 | JLabel emailLabel = new JLabel(tr("E-Mail")); |
|---|
| 86 | p.add(emailLabel, GBC.std().insets(10, 0, 5, 0)); |
|---|
| 87 | String user = CredentialsManager.getInstance().getUsername(); |
|---|
| 88 | JTextField email = new JTextField(user == null ? "" : user); |
|---|
| 89 | p.add(email, GBC.eol().fill(GBC.HORIZONTAL)); |
|---|
| 90 | JLabel copyrightLabel = new JLabel(tr("Copyright (URL)")); |
|---|
| 91 | p.add(copyrightLabel, GBC.std().insets(10, 0, 5, 0)); |
|---|
| 92 | JTextField copyright = new JTextField(); |
|---|
| 93 | p.add(copyright, GBC.std().fill(GBC.HORIZONTAL)); |
|---|
| 94 | JButton predefined = new JButton(tr("Predefined")); |
|---|
| 95 | p.add(predefined, GBC.eol().insets(5, 0, 0, 0)); |
|---|
| 96 | JLabel copyrightYearLabel = new JLabel(tr("Copyright year")); |
|---|
| 97 | p.add(copyrightYearLabel, GBC.std().insets(10, 0, 5, 5)); |
|---|
| 98 | JTextField copyrightYear = new JTextField(""); |
|---|
| 99 | p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL)); |
|---|
| 100 | JLabel warning = new JLabel("<html><font size='-2'> </html"); |
|---|
| 101 | p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15, 0, 0, 0)); |
|---|
| 102 | addDependencies(author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, |
|---|
| 103 | copyrightLabel, copyrightYearLabel, warning); |
|---|
| 104 | |
|---|
| 105 | // if the user name is not the email address, but the osm user name |
|---|
| 106 | // move it from the email textfield to the author textfield |
|---|
| 107 | if (!email.getText().contains("@")) { |
|---|
| 108 | authorName.setText(email.getText()); |
|---|
| 109 | email.setText(""); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | p.add(new JLabel(tr("Keywords")), GBC.eol()); |
|---|
| 113 | JTextField keywords = new JTextField(); |
|---|
| 114 | p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL)); |
|---|
| 115 | |
|---|
| 116 | ExtendedDialog ed = new ExtendedDialog(Main.parent, |
|---|
| 117 | tr("Export options"), |
|---|
| 118 | new String[] { tr("Export and Save"), tr("Cancel") }); |
|---|
| 119 | ed.setButtonIcons(new String[] { "exportgpx.png", "cancel.png" }); |
|---|
| 120 | ed.setContent(p); |
|---|
| 121 | ed.showDialog(); |
|---|
| 122 | |
|---|
| 123 | if (ed.getValue() != 1) |
|---|
| 124 | return; |
|---|
| 125 | |
|---|
| 126 | Main.pref.put("lastAddAuthor", author.isSelected()); |
|---|
| 127 | if (authorName.getText().length() != 0) { |
|---|
| 128 | Main.pref.put("lastAuthorName", authorName.getText()); |
|---|
| 129 | } |
|---|
| 130 | if (copyright.getText().length() != 0) { |
|---|
| 131 | Main.pref.put("lastCopyright", copyright.getText()); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | GpxData gpxData; |
|---|
| 135 | if (layer instanceof OsmDataLayer) { |
|---|
| 136 | gpxData = ((OsmDataLayer) layer).toGpxData(); |
|---|
| 137 | } else if (layer instanceof GpxLayer) { |
|---|
| 138 | gpxData = ((GpxLayer) layer).data; |
|---|
| 139 | } else { |
|---|
| 140 | gpxData = OsmDataLayer.toGpxData(getCurrentDataSet(), file); |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | // add author and copyright details to the gpx data |
|---|
| 144 | if (author.isSelected()) { |
|---|
| 145 | if (authorName.getText().length() > 0) { |
|---|
| 146 | gpxData.attr.put(GpxData.META_AUTHOR_NAME, authorName.getText()); |
|---|
| 147 | gpxData.attr.put(GpxData.META_COPYRIGHT_AUTHOR, authorName.getText()); |
|---|
| 148 | } |
|---|
| 149 | if (email.getText().length() > 0) { |
|---|
| 150 | gpxData.attr.put(GpxData.META_AUTHOR_EMAIL, email.getText()); |
|---|
| 151 | } |
|---|
| 152 | if (copyright.getText().length() > 0) { |
|---|
| 153 | gpxData.attr.put(GpxData.META_COPYRIGHT_LICENSE, copyright.getText()); |
|---|
| 154 | } |
|---|
| 155 | if (copyrightYear.getText().length() > 0) { |
|---|
| 156 | gpxData.attr.put(GpxData.META_COPYRIGHT_YEAR, copyrightYear.getText()); |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | // add the description to the gpx data |
|---|
| 161 | if (desc.getText().length() > 0) { |
|---|
| 162 | gpxData.attr.put(GpxData.META_DESC, desc.getText()); |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | // add keywords to the gpx data |
|---|
| 166 | if (keywords.getText().length() > 0) { |
|---|
| 167 | gpxData.attr.put(GpxData.META_KEYWORDS, keywords.getText()); |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | try { |
|---|
| 171 | FileOutputStream fo = new FileOutputStream(file); |
|---|
| 172 | new GpxWriter(fo).write(gpxData); |
|---|
| 173 | fo.flush(); |
|---|
| 174 | fo.close(); |
|---|
| 175 | } catch (IOException x) { |
|---|
| 176 | x.printStackTrace(); |
|---|
| 177 | JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}:\n{1}", fn, x.getMessage()), |
|---|
| 178 | tr("Error"), JOptionPane.ERROR_MESSAGE); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | private static void enableCopyright(final JTextField copyright, final JButton predefined, |
|---|
| 184 | final JTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel, |
|---|
| 185 | final JLabel warning, boolean enable) { |
|---|
| 186 | copyright.setEnabled(enable); |
|---|
| 187 | predefined.setEnabled(enable); |
|---|
| 188 | copyrightYear.setEnabled(enable); |
|---|
| 189 | copyrightLabel.setEnabled(enable); |
|---|
| 190 | copyrightYearLabel.setEnabled(enable); |
|---|
| 191 | warning.setText(enable ? warningGpl : "<html><font size='-2'> </html"); |
|---|
| 192 | |
|---|
| 193 | if (enable && copyrightYear.getText().length()==0) { |
|---|
| 194 | copyrightYear.setText(enable ? Integer.toString(Calendar.getInstance().get(Calendar.YEAR)) : ""); |
|---|
| 195 | } else if (!enable) { |
|---|
| 196 | copyrightYear.setText(""); |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | if (enable && copyright.getText().length()==0) { |
|---|
| 200 | copyright.setText(enable ? Main.pref.get("lastCopyright", "http://creativecommons.org/licenses/by-sa/2.5") : ""); |
|---|
| 201 | copyright.setCaretPosition(0); |
|---|
| 202 | } else if (!enable) { |
|---|
| 203 | copyright.setText(""); |
|---|
| 204 | } |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | /** |
|---|
| 208 | * Add all those listeners to handle the enable state of the fields. |
|---|
| 209 | * @param copyrightYearLabel |
|---|
| 210 | * @param copyrightLabel |
|---|
| 211 | * @param emailLabel |
|---|
| 212 | * @param nameLabel |
|---|
| 213 | * @param warning |
|---|
| 214 | */ |
|---|
| 215 | private static void addDependencies( |
|---|
| 216 | final JCheckBox author, |
|---|
| 217 | final JTextField authorName, |
|---|
| 218 | final JTextField email, |
|---|
| 219 | final JTextField copyright, |
|---|
| 220 | final JButton predefined, |
|---|
| 221 | final JTextField copyrightYear, |
|---|
| 222 | final JLabel nameLabel, |
|---|
| 223 | final JLabel emailLabel, |
|---|
| 224 | final JLabel copyrightLabel, |
|---|
| 225 | final JLabel copyrightYearLabel, |
|---|
| 226 | final JLabel warning) { |
|---|
| 227 | |
|---|
| 228 | ActionListener authorActionListener = new ActionListener(){ |
|---|
| 229 | public void actionPerformed(ActionEvent e) { |
|---|
| 230 | boolean b = author.isSelected(); |
|---|
| 231 | authorName.setEnabled(b); |
|---|
| 232 | email.setEnabled(b); |
|---|
| 233 | nameLabel.setEnabled(b); |
|---|
| 234 | emailLabel.setEnabled(b); |
|---|
| 235 | authorName.setText(b ? Main.pref.get("lastAuthorName") : ""); |
|---|
| 236 | String user = CredentialsManager.getInstance().getUsername(); |
|---|
| 237 | email.setText(b ? (user == null ? "" : user) : ""); |
|---|
| 238 | |
|---|
| 239 | boolean authorSet = authorName.getText().length() != 0; |
|---|
| 240 | GpxExporter.enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b && authorSet); |
|---|
| 241 | } |
|---|
| 242 | }; |
|---|
| 243 | author.addActionListener(authorActionListener); |
|---|
| 244 | |
|---|
| 245 | KeyAdapter authorNameListener = new KeyAdapter(){ |
|---|
| 246 | @Override public void keyReleased(KeyEvent e) { |
|---|
| 247 | boolean b = authorName.getText().length()!=0 && author.isSelected(); |
|---|
| 248 | GpxExporter.enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b); |
|---|
| 249 | } |
|---|
| 250 | }; |
|---|
| 251 | authorName.addKeyListener(authorNameListener); |
|---|
| 252 | |
|---|
| 253 | predefined.addActionListener(new ActionListener(){ |
|---|
| 254 | public void actionPerformed(ActionEvent e) { |
|---|
| 255 | final String[] licenses = { |
|---|
| 256 | "Creative Commons By-SA", |
|---|
| 257 | "Open Database License (ODbL)", |
|---|
| 258 | "public domain", |
|---|
| 259 | "GNU Lesser Public License (LGPL)", |
|---|
| 260 | "BSD License (MIT/X11)"}; |
|---|
| 261 | JList l = new JList(licenses); |
|---|
| 262 | l.setVisibleRowCount(licenses.length); |
|---|
| 263 | l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
|---|
| 264 | int answer = JOptionPane.showConfirmDialog( |
|---|
| 265 | Main.parent, |
|---|
| 266 | new JScrollPane(l), |
|---|
| 267 | tr("Choose a predefined license"), |
|---|
| 268 | JOptionPane.OK_CANCEL_OPTION, |
|---|
| 269 | JOptionPane.QUESTION_MESSAGE |
|---|
| 270 | ); |
|---|
| 271 | if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1) |
|---|
| 272 | return; |
|---|
| 273 | final String[] urls = { |
|---|
| 274 | "http://creativecommons.org/licenses/by-sa/2.5", |
|---|
| 275 | "http://opendatacommons.org/licenses/odbl/1.0", |
|---|
| 276 | "public domain", |
|---|
| 277 | "http://www.gnu.org/copyleft/lesser.html", |
|---|
| 278 | "http://www.opensource.org/licenses/bsd-license.php"}; |
|---|
| 279 | String license = ""; |
|---|
| 280 | for (int i : l.getSelectedIndices()) { |
|---|
| 281 | if (i == 2) { |
|---|
| 282 | license = "public domain"; |
|---|
| 283 | break; |
|---|
| 284 | } |
|---|
| 285 | license += license.length()==0 ? urls[i] : ", "+urls[i]; |
|---|
| 286 | } |
|---|
| 287 | copyright.setText(license); |
|---|
| 288 | copyright.setCaretPosition(0); |
|---|
| 289 | } |
|---|
| 290 | }); |
|---|
| 291 | |
|---|
| 292 | authorActionListener.actionPerformed(null); |
|---|
| 293 | authorNameListener.keyReleased(null); |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | /** |
|---|
| 297 | * Replies the current dataset |
|---|
| 298 | * |
|---|
| 299 | * @return the current dataset. null, if no current dataset exists |
|---|
| 300 | */ |
|---|
| 301 | private DataSet getCurrentDataSet() { |
|---|
| 302 | return Main.main.getCurrentDataSet(); |
|---|
| 303 | } |
|---|
| 304 | |
|---|
| 305 | } |
|---|