[4685] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.actions;
|
---|
| 3 |
|
---|
[5438] | 4 | import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
|
---|
[4685] | 5 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 6 |
|
---|
| 7 | import java.awt.Component;
|
---|
| 8 | import java.awt.Dimension;
|
---|
| 9 | import java.awt.GridBagLayout;
|
---|
| 10 | import java.awt.event.ActionEvent;
|
---|
| 11 | import java.io.File;
|
---|
| 12 | import java.io.IOException;
|
---|
| 13 | import java.util.ArrayList;
|
---|
[5438] | 14 | import java.util.Arrays;
|
---|
[4685] | 15 | import java.util.Collection;
|
---|
| 16 | import java.util.HashMap;
|
---|
| 17 | import java.util.HashSet;
|
---|
| 18 | import java.util.List;
|
---|
| 19 | import java.util.Map;
|
---|
| 20 | import java.util.Set;
|
---|
| 21 |
|
---|
| 22 | import javax.swing.BorderFactory;
|
---|
| 23 | import javax.swing.JCheckBox;
|
---|
| 24 | import javax.swing.JFileChooser;
|
---|
| 25 | import javax.swing.JLabel;
|
---|
| 26 | import javax.swing.JOptionPane;
|
---|
| 27 | import javax.swing.JPanel;
|
---|
| 28 | import javax.swing.JScrollPane;
|
---|
| 29 | import javax.swing.JTabbedPane;
|
---|
| 30 | import javax.swing.SwingConstants;
|
---|
| 31 | import javax.swing.border.EtchedBorder;
|
---|
| 32 | import javax.swing.filechooser.FileFilter;
|
---|
| 33 |
|
---|
| 34 | import org.openstreetmap.josm.Main;
|
---|
| 35 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
---|
| 36 | import org.openstreetmap.josm.gui.HelpAwareOptionPane;
|
---|
[9629] | 37 | import org.openstreetmap.josm.gui.MapFrame;
|
---|
| 38 | import org.openstreetmap.josm.gui.MapFrameListener;
|
---|
[4685] | 39 | import org.openstreetmap.josm.gui.layer.Layer;
|
---|
[7578] | 40 | import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
|
---|
[4685] | 41 | import org.openstreetmap.josm.io.session.SessionLayerExporter;
|
---|
| 42 | import org.openstreetmap.josm.io.session.SessionWriter;
|
---|
| 43 | import org.openstreetmap.josm.tools.GBC;
|
---|
| 44 | import org.openstreetmap.josm.tools.MultiMap;
|
---|
[8919] | 45 | import org.openstreetmap.josm.tools.UserCancelException;
|
---|
[8404] | 46 | import org.openstreetmap.josm.tools.Utils;
|
---|
[4685] | 47 | import org.openstreetmap.josm.tools.WindowGeometry;
|
---|
| 48 |
|
---|
[6245] | 49 | /**
|
---|
| 50 | * Saves a JOSM session
|
---|
| 51 | * @since 4685
|
---|
| 52 | */
|
---|
[9629] | 53 | public class SessionSaveAsAction extends DiskAccessAction implements MapFrameListener {
|
---|
[4685] | 54 |
|
---|
[8308] | 55 | private transient List<Layer> layers;
|
---|
| 56 | private transient Map<Layer, SessionLayerExporter> exporters;
|
---|
| 57 | private transient MultiMap<Layer, Layer> dependencies;
|
---|
[4685] | 58 |
|
---|
[6245] | 59 | /**
|
---|
| 60 | * Constructs a new {@code SessionSaveAsAction}.
|
---|
| 61 | */
|
---|
[4685] | 62 | public SessionSaveAsAction() {
|
---|
[9152] | 63 | this(true, true);
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | /**
|
---|
| 67 | * Constructs a new {@code SessionSaveAsAction}.
|
---|
| 68 | * @param toolbar Register this action for the toolbar preferences?
|
---|
| 69 | * @param installAdapters False, if you don't want to install layer changed and selection changed adapters
|
---|
| 70 | */
|
---|
| 71 | protected SessionSaveAsAction(boolean toolbar, boolean installAdapters) {
|
---|
[9173] | 72 | super(tr("Save Session As..."), "session", tr("Save the current session to a new file."),
|
---|
| 73 | null, toolbar, "save_as-session", installAdapters);
|
---|
[4685] | 74 | putValue("help", ht("/Action/SessionSaveAs"));
|
---|
[9629] | 75 | Main.addMapFrameListener(this);
|
---|
[4685] | 76 | }
|
---|
| 77 |
|
---|
| 78 | @Override
|
---|
| 79 | public void actionPerformed(ActionEvent e) {
|
---|
[8913] | 80 | try {
|
---|
| 81 | saveSession();
|
---|
[8919] | 82 | } catch (UserCancelException ignore) {
|
---|
[10420] | 83 | Main.trace(ignore);
|
---|
[8913] | 84 | }
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | /**
|
---|
| 88 | * Attempts to save the session.
|
---|
[8919] | 89 | * @throws UserCancelException when the user has cancelled the save process.
|
---|
[8913] | 90 | * @since 8913
|
---|
| 91 | */
|
---|
[8919] | 92 | public void saveSession() throws UserCancelException {
|
---|
[4685] | 93 | if (!isEnabled()) {
|
---|
| 94 | return;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | SessionSaveAsDialog dlg = new SessionSaveAsDialog();
|
---|
| 98 | dlg.showDialog();
|
---|
[8913] | 99 | if (dlg.getValue() != 1) {
|
---|
[8919] | 100 | throw new UserCancelException();
|
---|
[8913] | 101 | }
|
---|
[4685] | 102 |
|
---|
[6267] | 103 | boolean zipRequired = false;
|
---|
[4685] | 104 | for (Layer l : layers) {
|
---|
| 105 | SessionLayerExporter ex = exporters.get(l);
|
---|
[5501] | 106 | if (ex != null && ex.requiresZip()) {
|
---|
[4685] | 107 | zipRequired = true;
|
---|
| 108 | break;
|
---|
| 109 | }
|
---|
| 110 | }
|
---|
[6069] | 111 |
|
---|
[4685] | 112 | FileFilter joz = new ExtensionFileFilter("joz", "joz", tr("Session file (archive) (*.joz)"));
|
---|
| 113 | FileFilter jos = new ExtensionFileFilter("jos", "jos", tr("Session file (*.jos)"));
|
---|
[5438] | 114 |
|
---|
[7578] | 115 | AbstractFileChooser fc;
|
---|
[5505] | 116 |
|
---|
[4685] | 117 | if (zipRequired) {
|
---|
[5438] | 118 | fc = createAndOpenFileChooser(false, false, tr("Save session"), joz, JFileChooser.FILES_ONLY, "lastDirectory");
|
---|
[4685] | 119 | } else {
|
---|
[8509] | 120 | fc = createAndOpenFileChooser(false, false, tr("Save session"), Arrays.asList(new FileFilter[]{jos, joz}), jos,
|
---|
| 121 | JFileChooser.FILES_ONLY, "lastDirectory");
|
---|
[4685] | 122 | }
|
---|
[5438] | 123 |
|
---|
[8913] | 124 | if (fc == null) {
|
---|
[8919] | 125 | throw new UserCancelException();
|
---|
[8913] | 126 | }
|
---|
[4685] | 127 |
|
---|
| 128 | File file = fc.getSelectedFile();
|
---|
| 129 | String fn = file.getName();
|
---|
| 130 |
|
---|
| 131 | boolean zip;
|
---|
| 132 | FileFilter ff = fc.getFileFilter();
|
---|
[11109] | 133 | if (zipRequired || joz.equals(ff)) {
|
---|
[4685] | 134 | zip = true;
|
---|
[6981] | 135 | } else if (jos.equals(ff)) {
|
---|
[4685] | 136 | zip = false;
|
---|
| 137 | } else {
|
---|
[8404] | 138 | if (Utils.hasExtension(fn, "joz")) {
|
---|
[4685] | 139 | zip = true;
|
---|
| 140 | } else {
|
---|
| 141 | zip = false;
|
---|
| 142 | }
|
---|
| 143 | }
|
---|
| 144 | if (fn.indexOf('.') == -1) {
|
---|
| 145 | file = new File(file.getPath() + (zip ? ".joz" : ".jos"));
|
---|
[8913] | 146 | if (!SaveActionBase.confirmOverwrite(file)) {
|
---|
[8919] | 147 | throw new UserCancelException();
|
---|
[8913] | 148 | }
|
---|
[4685] | 149 | }
|
---|
| 150 |
|
---|
[7005] | 151 | List<Layer> layersOut = new ArrayList<>();
|
---|
[4685] | 152 | for (Layer layer : layers) {
|
---|
| 153 | if (exporters.get(layer) == null || !exporters.get(layer).shallExport()) continue;
|
---|
| 154 | // TODO: resolve dependencies for layers excluded by the user
|
---|
| 155 | layersOut.add(layer);
|
---|
| 156 | }
|
---|
[7509] | 157 |
|
---|
[6271] | 158 | int active = -1;
|
---|
[10318] | 159 | Layer activeLayer = Main.getLayerManager().getActiveLayer();
|
---|
[6271] | 160 | if (activeLayer != null) {
|
---|
| 161 | active = layersOut.indexOf(activeLayer);
|
---|
| 162 | }
|
---|
[4685] | 163 |
|
---|
[6271] | 164 | SessionWriter sw = new SessionWriter(layersOut, active, exporters, dependencies, zip);
|
---|
[4685] | 165 | try {
|
---|
| 166 | sw.write(file);
|
---|
[8802] | 167 | SaveActionBase.addToFileOpenHistory(file);
|
---|
[4685] | 168 | } catch (IOException ex) {
|
---|
[6643] | 169 | Main.error(ex);
|
---|
[4685] | 170 | HelpAwareOptionPane.showMessageDialogInEDT(
|
---|
| 171 | Main.parent,
|
---|
[11848] | 172 | tr("<html>Could not save session file ''{0}''.<br>Error is:<br>{1}</html>",
|
---|
| 173 | file.getName(), Utils.escapeReservedCharactersHTML(ex.getMessage())),
|
---|
[4685] | 174 | tr("IO Error"),
|
---|
| 175 | JOptionPane.ERROR_MESSAGE,
|
---|
| 176 | null
|
---|
| 177 | );
|
---|
| 178 | }
|
---|
| 179 | }
|
---|
| 180 |
|
---|
[6245] | 181 | /**
|
---|
| 182 | * The "Save Session" dialog
|
---|
| 183 | */
|
---|
[4685] | 184 | public class SessionSaveAsDialog extends ExtendedDialog {
|
---|
| 185 |
|
---|
[6245] | 186 | /**
|
---|
| 187 | * Constructs a new {@code SessionSaveAsDialog}.
|
---|
| 188 | */
|
---|
[4685] | 189 | public SessionSaveAsDialog() {
|
---|
[5684] | 190 | super(Main.parent, tr("Save Session"), new String[] {tr("Save As"), tr("Cancel")});
|
---|
[4685] | 191 | initialize();
|
---|
[5684] | 192 | setButtonIcons(new String[] {"save_as", "cancel"});
|
---|
| 193 | setDefaultButton(1);
|
---|
[4685] | 194 | setRememberWindowGeometry(getClass().getName() + ".geometry",
|
---|
| 195 | WindowGeometry.centerInWindow(Main.parent, new Dimension(350, 450)));
|
---|
| 196 | setContent(build(), false);
|
---|
| 197 | }
|
---|
| 198 |
|
---|
[6271] | 199 | /**
|
---|
| 200 | * Initializes action.
|
---|
| 201 | */
|
---|
[6890] | 202 | public final void initialize() {
|
---|
[10318] | 203 | layers = new ArrayList<>(Main.getLayerManager().getLayers());
|
---|
[7005] | 204 | exporters = new HashMap<>();
|
---|
| 205 | dependencies = new MultiMap<>();
|
---|
[4685] | 206 |
|
---|
[7005] | 207 | Set<Layer> noExporter = new HashSet<>();
|
---|
[4685] | 208 |
|
---|
| 209 | for (Layer layer : layers) {
|
---|
| 210 | SessionLayerExporter exporter = SessionWriter.getSessionLayerExporter(layer);
|
---|
| 211 | if (exporter != null) {
|
---|
| 212 | exporters.put(layer, exporter);
|
---|
| 213 | Collection<Layer> deps = exporter.getDependencies();
|
---|
| 214 | if (deps != null) {
|
---|
| 215 | dependencies.putAll(layer, deps);
|
---|
| 216 | } else {
|
---|
| 217 | dependencies.putVoid(layer);
|
---|
| 218 | }
|
---|
| 219 | } else {
|
---|
| 220 | noExporter.add(layer);
|
---|
| 221 | exporters.put(layer, null);
|
---|
| 222 | }
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | int numNoExporter = 0;
|
---|
[8510] | 226 | WHILE: while (numNoExporter != noExporter.size()) {
|
---|
[4685] | 227 | numNoExporter = noExporter.size();
|
---|
| 228 | for (Layer layer : layers) {
|
---|
| 229 | if (noExporter.contains(layer)) continue;
|
---|
| 230 | for (Layer depLayer : dependencies.get(layer)) {
|
---|
| 231 | if (noExporter.contains(depLayer)) {
|
---|
| 232 | noExporter.add(layer);
|
---|
| 233 | exporters.put(layer, null);
|
---|
| 234 | break WHILE;
|
---|
| 235 | }
|
---|
| 236 | }
|
---|
| 237 | }
|
---|
| 238 | }
|
---|
| 239 | }
|
---|
| 240 |
|
---|
[6890] | 241 | protected final Component build() {
|
---|
[4685] | 242 | JPanel ip = new JPanel(new GridBagLayout());
|
---|
| 243 | for (Layer layer : layers) {
|
---|
| 244 | JPanel wrapper = new JPanel(new GridBagLayout());
|
---|
| 245 | wrapper.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
|
---|
| 246 | Component exportPanel;
|
---|
| 247 | SessionLayerExporter exporter = exporters.get(layer);
|
---|
| 248 | if (exporter == null) {
|
---|
| 249 | if (!exporters.containsKey(layer)) throw new AssertionError();
|
---|
| 250 | exportPanel = getDisabledExportPanel(layer);
|
---|
| 251 | } else {
|
---|
| 252 | exportPanel = exporter.getExportPanel();
|
---|
| 253 | }
|
---|
| 254 | wrapper.add(exportPanel, GBC.std().fill(GBC.HORIZONTAL));
|
---|
[8510] | 255 | ip.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(2, 2, 4, 2));
|
---|
[4685] | 256 | }
|
---|
[8510] | 257 | ip.add(GBC.glue(0, 1), GBC.eol().fill(GBC.VERTICAL));
|
---|
[4685] | 258 | JScrollPane sp = new JScrollPane(ip);
|
---|
| 259 | sp.setBorder(BorderFactory.createEmptyBorder());
|
---|
[9062] | 260 | JPanel p = new JPanel(new GridBagLayout());
|
---|
[4685] | 261 | p.add(sp, GBC.eol().fill());
|
---|
| 262 | final JTabbedPane tabs = new JTabbedPane();
|
---|
| 263 | tabs.addTab(tr("Layers"), p);
|
---|
| 264 | return tabs;
|
---|
| 265 | }
|
---|
| 266 |
|
---|
[6981] | 267 | protected final Component getDisabledExportPanel(Layer layer) {
|
---|
[4685] | 268 | JPanel p = new JPanel(new GridBagLayout());
|
---|
| 269 | JCheckBox include = new JCheckBox();
|
---|
| 270 | include.setEnabled(false);
|
---|
| 271 | JLabel lbl = new JLabel(layer.getName(), layer.getIcon(), SwingConstants.LEFT);
|
---|
| 272 | lbl.setToolTipText(tr("No exporter for this layer"));
|
---|
[8426] | 273 | lbl.setLabelFor(include);
|
---|
[4685] | 274 | lbl.setEnabled(false);
|
---|
| 275 | p.add(include, GBC.std());
|
---|
| 276 | p.add(lbl, GBC.std());
|
---|
[8510] | 277 | p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
|
---|
[4685] | 278 | return p;
|
---|
| 279 | }
|
---|
| 280 | }
|
---|
| 281 |
|
---|
[6149] | 282 | @Override
|
---|
| 283 | protected void updateEnabledState() {
|
---|
| 284 | setEnabled(Main.isDisplayingMapView());
|
---|
| 285 | }
|
---|
[9629] | 286 |
|
---|
| 287 | @Override
|
---|
| 288 | public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
|
---|
| 289 | updateEnabledState();
|
---|
[9631] | 290 | }
|
---|
[4685] | 291 | }
|
---|