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