001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.io; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.GridBagLayout; 007 import java.awt.event.ActionEvent; 008 import java.awt.event.ActionListener; 009 import java.awt.event.KeyAdapter; 010 import java.awt.event.KeyEvent; 011 import java.io.File; 012 import java.io.FileOutputStream; 013 import java.io.IOException; 014 import java.text.MessageFormat; 015 import java.util.Calendar; 016 017 import javax.swing.JButton; 018 import javax.swing.JCheckBox; 019 import javax.swing.JLabel; 020 import javax.swing.JList; 021 import javax.swing.JOptionPane; 022 import javax.swing.JPanel; 023 import javax.swing.JScrollPane; 024 import javax.swing.JTextArea; 025 import javax.swing.JTextField; 026 import javax.swing.ListSelectionModel; 027 028 import org.openstreetmap.josm.Main; 029 import org.openstreetmap.josm.data.gpx.GpxData; 030 import org.openstreetmap.josm.data.osm.DataSet; 031 import org.openstreetmap.josm.gui.ExtendedDialog; 032 import org.openstreetmap.josm.gui.layer.GpxLayer; 033 import org.openstreetmap.josm.gui.layer.Layer; 034 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 035 import org.openstreetmap.josm.tools.CheckParameterUtil; 036 import org.openstreetmap.josm.tools.GBC; 037 038 public class GpxExporter extends FileExporter { 039 private final static String warningGpl = "<html><font color='red' size='-2'>" 040 + tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.") + "</html>"; 041 042 public GpxExporter() { 043 super(GpxImporter.FILE_FILTER); 044 } 045 046 @Override 047 public boolean acceptFile(File pathname, Layer layer) { 048 if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer)) 049 return false; 050 return super.acceptFile(pathname, layer); 051 } 052 053 @Override 054 public void exportData(File file, Layer layer) throws IOException { 055 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 056 if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer)) 057 throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer 058 .getClass().getName())); 059 CheckParameterUtil.ensureParameterNotNull(file, "file"); 060 061 String fn = file.getPath(); 062 if (fn.indexOf('.') == -1) { 063 fn += ".gpx"; 064 file = new File(fn); 065 } 066 067 // open the dialog asking for options 068 JPanel p = new JPanel(new GridBagLayout()); 069 070 GpxData gpxData; 071 // At this moment, we only need to know the attributes of the GpxData, 072 // conversion of OsmDataLayer (if needed) will be done after the dialog 073 // is closed. 074 if (layer instanceof GpxLayer) { 075 gpxData = ((GpxLayer) layer).data; 076 } else { 077 gpxData = new GpxData(); 078 } 079 080 p.add(new JLabel(tr("GPS track description")), GBC.eol()); 081 JTextArea desc = new JTextArea(3, 40); 082 desc.setWrapStyleWord(true); 083 desc.setLineWrap(true); 084 desc.setText((String) gpxData.attr.get(GpxData.META_DESC)); 085 p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH)); 086 087 JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true)); 088 p.add(author, GBC.eol()); 089 JLabel nameLabel = new JLabel(tr("Real name")); 090 p.add(nameLabel, GBC.std().insets(10, 0, 5, 0)); 091 JTextField authorName = new JTextField(); 092 p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL)); 093 JLabel emailLabel = new JLabel(tr("E-Mail")); 094 p.add(emailLabel, GBC.std().insets(10, 0, 5, 0)); 095 JTextField email = new JTextField(); 096 p.add(email, GBC.eol().fill(GBC.HORIZONTAL)); 097 JLabel copyrightLabel = new JLabel(tr("Copyright (URL)")); 098 p.add(copyrightLabel, GBC.std().insets(10, 0, 5, 0)); 099 JTextField copyright = new JTextField(); 100 p.add(copyright, GBC.std().fill(GBC.HORIZONTAL)); 101 JButton predefined = new JButton(tr("Predefined")); 102 p.add(predefined, GBC.eol().insets(5, 0, 0, 0)); 103 JLabel copyrightYearLabel = new JLabel(tr("Copyright year")); 104 p.add(copyrightYearLabel, GBC.std().insets(10, 0, 5, 5)); 105 JTextField copyrightYear = new JTextField(""); 106 p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL)); 107 JLabel warning = new JLabel("<html><font size='-2'> </html"); 108 p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15, 0, 0, 0)); 109 addDependencies(gpxData, author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, 110 copyrightLabel, copyrightYearLabel, warning); 111 112 p.add(new JLabel(tr("Keywords")), GBC.eol()); 113 JTextField keywords = new JTextField(); 114 keywords.setText((String) gpxData.attr.get(GpxData.META_KEYWORDS)); 115 p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL)); 116 117 ExtendedDialog ed = new ExtendedDialog(Main.parent, 118 tr("Export options"), 119 new String[] { tr("Export and Save"), tr("Cancel") }); 120 ed.setButtonIcons(new String[] { "exportgpx.png", "cancel.png" }); 121 ed.setContent(p); 122 ed.showDialog(); 123 124 if (ed.getValue() != 1) 125 return; 126 127 Main.pref.put("lastAddAuthor", author.isSelected()); 128 if (authorName.getText().length() != 0) { 129 Main.pref.put("lastAuthorName", authorName.getText()); 130 } 131 if (copyright.getText().length() != 0) { 132 Main.pref.put("lastCopyright", copyright.getText()); 133 } 134 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 GpxData data, 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) { 194 if (copyrightYear.getText().length()==0) { 195 String sCopyrightYear = (String) data.attr.get(GpxData.META_COPYRIGHT_YEAR); 196 if (sCopyrightYear == null) { 197 sCopyrightYear = Integer.toString(Calendar.getInstance().get(Calendar.YEAR)); 198 } 199 copyrightYear.setText(sCopyrightYear); 200 } 201 if (copyright.getText().length()==0) { 202 String sCopyright = (String) data.attr.get(GpxData.META_COPYRIGHT_LICENSE); 203 if (sCopyright == null) { 204 sCopyright = Main.pref.get("lastCopyright", "http://creativecommons.org/licenses/by-sa/2.5"); 205 } 206 copyright.setText(sCopyright); 207 copyright.setCaretPosition(0); 208 } 209 } else { 210 copyrightYear.setText(""); 211 copyright.setText(""); 212 } 213 } 214 215 /** 216 * Add all those listeners to handle the enable state of the fields. 217 * @param copyrightYearLabel 218 * @param copyrightLabel 219 * @param emailLabel 220 * @param nameLabel 221 * @param warning 222 */ 223 private static void addDependencies( 224 final GpxData data, 225 final JCheckBox author, 226 final JTextField authorName, 227 final JTextField email, 228 final JTextField copyright, 229 final JButton predefined, 230 final JTextField copyrightYear, 231 final JLabel nameLabel, 232 final JLabel emailLabel, 233 final JLabel copyrightLabel, 234 final JLabel copyrightYearLabel, 235 final JLabel warning) { 236 237 ActionListener authorActionListener = new ActionListener(){ 238 public void actionPerformed(ActionEvent e) { 239 boolean b = author.isSelected(); 240 authorName.setEnabled(b); 241 email.setEnabled(b); 242 nameLabel.setEnabled(b); 243 emailLabel.setEnabled(b); 244 if (b) { 245 String sAuthorName = (String) data.attr.get(GpxData.META_AUTHOR_NAME); 246 if (sAuthorName == null) { 247 sAuthorName = Main.pref.get("lastAuthorName"); 248 } 249 authorName.setText(sAuthorName); 250 String sEmail = (String) data.attr.get(GpxData.META_AUTHOR_EMAIL); 251 if (sEmail == null) { 252 sEmail = Main.pref.get("lastAuthorEmail"); 253 } 254 email.setText(sEmail); 255 } else { 256 authorName.setText(""); 257 email.setText(""); 258 } 259 boolean isAuthorSet = authorName.getText().length() != 0; 260 GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b && isAuthorSet); 261 } 262 }; 263 author.addActionListener(authorActionListener); 264 265 KeyAdapter authorNameListener = new KeyAdapter(){ 266 @Override public void keyReleased(KeyEvent e) { 267 boolean b = authorName.getText().length()!=0 && author.isSelected(); 268 GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b); 269 } 270 }; 271 authorName.addKeyListener(authorNameListener); 272 273 predefined.addActionListener(new ActionListener(){ 274 public void actionPerformed(ActionEvent e) { 275 final String[] licenses = { 276 "Creative Commons By-SA", 277 "Open Database License (ODbL)", 278 "public domain", 279 "GNU Lesser Public License (LGPL)", 280 "BSD License (MIT/X11)"}; 281 JList l = new JList(licenses); 282 l.setVisibleRowCount(licenses.length); 283 l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 284 int answer = JOptionPane.showConfirmDialog( 285 Main.parent, 286 new JScrollPane(l), 287 tr("Choose a predefined license"), 288 JOptionPane.OK_CANCEL_OPTION, 289 JOptionPane.QUESTION_MESSAGE 290 ); 291 if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1) 292 return; 293 final String[] urls = { 294 "http://creativecommons.org/licenses/by-sa/2.5", 295 "http://opendatacommons.org/licenses/odbl/1.0", 296 "public domain", 297 "http://www.gnu.org/copyleft/lesser.html", 298 "http://www.opensource.org/licenses/bsd-license.php"}; 299 String license = ""; 300 for (int i : l.getSelectedIndices()) { 301 if (i == 2) { 302 license = "public domain"; 303 break; 304 } 305 license += license.length()==0 ? urls[i] : ", "+urls[i]; 306 } 307 copyright.setText(license); 308 copyright.setCaretPosition(0); 309 } 310 }); 311 312 authorActionListener.actionPerformed(null); 313 authorNameListener.keyReleased(null); 314 } 315 316 /** 317 * Replies the current dataset 318 * 319 * @return the current dataset. null, if no current dataset exists 320 */ 321 private DataSet getCurrentDataSet() { 322 return Main.main.getCurrentDataSet(); 323 } 324 325 }