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