001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.actions;
003    
004    import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005    import static org.openstreetmap.josm.tools.I18n.tr;
006    
007    import java.awt.Component;
008    import java.awt.Dimension;
009    import java.awt.GridBagLayout;
010    import java.awt.event.ActionEvent;
011    import java.io.File;
012    import java.io.IOException;
013    import java.util.ArrayList;
014    import java.util.Arrays;
015    import java.util.Collection;
016    import java.util.Collections;
017    import java.util.HashMap;
018    import java.util.HashSet;
019    import java.util.List;
020    import java.util.Map;
021    import java.util.Set;
022    
023    import javax.swing.BorderFactory;
024    import javax.swing.JCheckBox;
025    import javax.swing.JFileChooser;
026    import javax.swing.JLabel;
027    import javax.swing.JOptionPane;
028    import javax.swing.JPanel;
029    import javax.swing.JScrollPane;
030    import javax.swing.JTabbedPane;
031    import javax.swing.SwingConstants;
032    import javax.swing.border.EtchedBorder;
033    import javax.swing.filechooser.FileFilter;
034    
035    import org.openstreetmap.josm.Main;
036    import org.openstreetmap.josm.gui.ExtendedDialog;
037    import org.openstreetmap.josm.gui.HelpAwareOptionPane;
038    import org.openstreetmap.josm.gui.layer.Layer;
039    import org.openstreetmap.josm.io.session.SessionLayerExporter;
040    import org.openstreetmap.josm.io.session.SessionWriter;
041    import org.openstreetmap.josm.tools.GBC;
042    import org.openstreetmap.josm.tools.MultiMap;
043    import org.openstreetmap.josm.tools.WindowGeometry;
044    
045    public class SessionSaveAsAction extends DiskAccessAction {
046    
047        private List<Layer> layers;
048        private Map<Layer, SessionLayerExporter> exporters;
049        private MultiMap<Layer, Layer> dependencies;
050    
051        private boolean zipRequired;
052    
053        /**
054         * Construct the action with "Save" as label.
055         */
056        public SessionSaveAsAction() {
057            super(tr("Save Session As..."), "save_as", tr("Save the current session to a new file."), null, true, "save_as-session", true);
058            putValue("help", ht("/Action/SessionSaveAs"));
059        }
060    
061        @Override
062        public void actionPerformed(ActionEvent e) {
063            if (!isEnabled()) {
064                return;
065            }
066    
067            SessionSaveAsDialog dlg = new SessionSaveAsDialog();
068            dlg.showDialog();
069            if (dlg.getValue() != 2) return;
070    
071            zipRequired = false;
072            for (Layer l : layers) {
073                SessionLayerExporter ex = exporters.get(l);
074                if (ex.requiresZip()) {
075                    zipRequired = true;
076                    break;
077                }
078            }
079            
080            FileFilter joz = new ExtensionFileFilter("joz", "joz", tr("Session file (archive) (*.joz)"));
081            FileFilter jos = new ExtensionFileFilter("jos", "jos", tr("Session file (*.jos)"));
082    
083            JFileChooser fc;
084            
085            if (zipRequired) {
086                fc = createAndOpenFileChooser(false, false, tr("Save session"), joz, JFileChooser.FILES_ONLY, "lastDirectory");
087            } else {
088                fc = createAndOpenFileChooser(false, false, tr("Save session"), Arrays.asList(new FileFilter[]{jos, joz}), jos, JFileChooser.FILES_ONLY, "lastDirectory");
089            }
090    
091            if (fc == null)
092                return;
093    
094            File file = fc.getSelectedFile();
095            String fn = file.getName();
096    
097            boolean zip;
098            FileFilter ff = fc.getFileFilter();
099            if (zipRequired) {
100                zip = true;
101            } else if (ff == joz) {
102                zip = true;
103            } else if (ff == jos) {
104                zip = false;
105            } else {
106                if (fn.toLowerCase().endsWith(".joz")) {
107                    zip = true;
108                } else {
109                    zip = false;
110                }
111            }
112            if (fn.indexOf('.') == -1) {
113                file = new File(file.getPath() + (zip ? ".joz" : ".jos"));
114            }
115            if (!SaveActionBase.confirmOverwrite(file))
116                return;
117    
118            List<Layer> layersOut = new ArrayList<Layer>();
119            for (Layer layer : layers) {
120                if (exporters.get(layer) == null || !exporters.get(layer).shallExport()) continue;
121                // TODO: resolve dependencies for layers excluded by the user
122                layersOut.add(layer);
123            }
124    
125            SessionWriter sw = new SessionWriter(layersOut, exporters, dependencies, zip);
126            try {
127                sw.write(file);
128            } catch (IOException ex) {
129                ex.printStackTrace();
130                HelpAwareOptionPane.showMessageDialogInEDT(
131                        Main.parent,
132                        tr("<html>Could not save session file ''{0}''.<br>Error is:<br>{1}</html>", file.getName(), ex.getMessage()),
133                        tr("IO Error"),
134                        JOptionPane.ERROR_MESSAGE,
135                        null
136                );
137            }
138        }
139    
140        public class SessionSaveAsDialog extends ExtendedDialog {
141    
142            public SessionSaveAsDialog() {
143                super(Main.parent, tr("Save Session"), new String[] {tr("Cancel"), tr("Save As")});
144                initialize();
145                setButtonIcons(new String[] {"cancel", "save_as"});
146                setDefaultButton(2);
147                setRememberWindowGeometry(getClass().getName() + ".geometry",
148                        WindowGeometry.centerInWindow(Main.parent, new Dimension(350, 450)));
149                setContent(build(), false);
150            }
151    
152            public void initialize() {
153                layers = new ArrayList<Layer>(Main.map.mapView.getAllLayersAsList());
154                exporters = new HashMap<Layer, SessionLayerExporter>();
155                dependencies = new MultiMap<Layer, Layer>();
156    
157                Set<Layer> noExporter = new HashSet<Layer>();
158    
159                for (Layer layer : layers) {
160                    SessionLayerExporter exporter = SessionWriter.getSessionLayerExporter(layer);
161                    if (exporter != null) {
162                        exporters.put(layer, exporter);
163                        Collection<Layer> deps = exporter.getDependencies();
164                        if (deps != null) {
165                            dependencies.putAll(layer, deps);
166                        } else {
167                            dependencies.putVoid(layer);
168                        }
169                    } else {
170                        noExporter.add(layer);
171                        exporters.put(layer, null);
172                    }
173                }
174    
175                int numNoExporter = 0;
176                WHILE:while (numNoExporter != noExporter.size()) {
177                    numNoExporter = noExporter.size();
178                    for (Layer layer : layers) {
179                        if (noExporter.contains(layer)) continue;
180                        for (Layer depLayer : dependencies.get(layer)) {
181                            if (noExporter.contains(depLayer)) {
182                                noExporter.add(layer);
183                                exporters.put(layer, null);
184                                break WHILE;
185                            }
186                        }
187                    }
188                }
189            }
190    
191            public Component build() {
192                JPanel p = new JPanel(new GridBagLayout());
193                JPanel ip = new JPanel(new GridBagLayout());
194                for (Layer layer : layers) {
195                    JPanel wrapper = new JPanel(new GridBagLayout());
196                    wrapper.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
197                    Component exportPanel;
198                    SessionLayerExporter exporter = exporters.get(layer);
199                    if (exporter == null) {
200                        if (!exporters.containsKey(layer)) throw new AssertionError();
201                        exportPanel = getDisabledExportPanel(layer);
202                    } else {
203                        exportPanel = exporter.getExportPanel();
204                    }
205                    wrapper.add(exportPanel, GBC.std().fill(GBC.HORIZONTAL));
206                    ip.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(2,2,4,2));
207                }
208                ip.add(GBC.glue(0,1), GBC.eol().fill(GBC.VERTICAL));
209                JScrollPane sp = new JScrollPane(ip);
210                sp.setBorder(BorderFactory.createEmptyBorder());
211                p.add(sp, GBC.eol().fill());
212                final JTabbedPane tabs = new JTabbedPane();
213                tabs.addTab(tr("Layers"), p);
214                return tabs;
215            }
216    
217            protected Component getDisabledExportPanel(Layer layer) {
218                JPanel p = new JPanel(new GridBagLayout());
219                JCheckBox include = new JCheckBox();
220                include.setEnabled(false);
221                JLabel lbl = new JLabel(layer.getName(), layer.getIcon(), SwingConstants.LEFT);
222                lbl.setToolTipText(tr("No exporter for this layer"));
223                lbl.setEnabled(false);
224                p.add(include, GBC.std());
225                p.add(lbl, GBC.std());
226                p.add(GBC.glue(1,0), GBC.std().fill(GBC.HORIZONTAL));
227                return p;
228            }
229        }
230    
231    }