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'>&nbsp;</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'>&nbsp;</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}