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