Ignore:
Timestamp:
2022-06-06T19:07:05+02:00 (4 years ago)
Author:
taylor.smock
Message:

Fix #21813: Improve marker handling in sessions and #21923: Improve session workflow/Add "save session" (patch by Bjoeni)

  • Allow saving a previously saved session
  • Add "File" -> "Save Session"
  • Add shortcuts for saving sessions
  • Add warning if a layer in a session is being removed when saving over the session
  • Improve GPX marker handling
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/SessionSaveAsAction.java

    r18406 r18466  
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
    7 import java.awt.Component;
    8 import java.awt.Dimension;
    9 import java.awt.GridBagLayout;
    107import java.awt.event.ActionEvent;
    11 import java.io.File;
    12 import java.io.IOException;
    13 import java.util.ArrayList;
    14 import java.util.Arrays;
    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 import java.util.stream.Collectors;
    22 import java.util.stream.Stream;
     8import java.awt.event.KeyEvent;
    239
    24 import javax.swing.BorderFactory;
    25 import javax.swing.JCheckBox;
    26 import javax.swing.JFileChooser;
    27 import javax.swing.JLabel;
    28 import javax.swing.JOptionPane;
    29 import javax.swing.JPanel;
    30 import javax.swing.JScrollPane;
    31 import javax.swing.JTabbedPane;
    32 import javax.swing.SwingConstants;
    33 import javax.swing.border.EtchedBorder;
    34 import javax.swing.filechooser.FileFilter;
    35 
    36 import org.openstreetmap.josm.data.preferences.BooleanProperty;
    37 import org.openstreetmap.josm.gui.ExtendedDialog;
    38 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    3910import org.openstreetmap.josm.gui.MainApplication;
    40 import org.openstreetmap.josm.gui.MapFrame;
    41 import org.openstreetmap.josm.gui.MapFrameListener;
    42 import org.openstreetmap.josm.gui.Notification;
    43 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    44 import org.openstreetmap.josm.gui.layer.Layer;
    45 import org.openstreetmap.josm.gui.util.WindowGeometry;
    46 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
    47 import org.openstreetmap.josm.io.session.SessionLayerExporter;
    48 import org.openstreetmap.josm.io.session.SessionWriter;
    49 import org.openstreetmap.josm.tools.GBC;
    50 import org.openstreetmap.josm.tools.JosmRuntimeException;
    5111import org.openstreetmap.josm.tools.Logging;
    52 import org.openstreetmap.josm.tools.MultiMap;
     12import org.openstreetmap.josm.tools.Shortcut;
    5313import org.openstreetmap.josm.tools.UserCancelException;
    54 import org.openstreetmap.josm.tools.Utils;
    5514
    5615/**
    57  * Saves a JOSM session
     16 * Saves a JOSM session to a new file
    5817 * @since 4685
    5918 */
    60 public class SessionSaveAsAction extends DiskAccessAction implements MapFrameListener {
    61 
    62     private transient List<Layer> layers;
    63     private transient Map<Layer, SessionLayerExporter> exporters;
    64     private transient MultiMap<Layer, Layer> dependencies;
    65 
    66     private static final BooleanProperty SAVE_LOCAL_FILES_PROPERTY = new BooleanProperty("session.savelocal", true);
     19public class SessionSaveAsAction extends SessionSaveAction {
    6720
    6821    /**
     
    8033     */
    8134    protected SessionSaveAsAction(boolean toolbar, boolean installAdapters) {
     35
    8236        super(tr("Save Session As..."), "session", tr("Save the current session to a new file."),
    83                 null, toolbar, "save_as-session", installAdapters);
     37                Shortcut.registerShortcut("system:savesessionas", tr("File: {0}", tr("Save Session As...")),
     38                        KeyEvent.VK_S, Shortcut.ALT_CTRL_SHIFT),
     39                toolbar, "save_as-session", installAdapters);
     40
    8441        setHelpId(ht("/Action/SessionSaveAs"));
    85         MainApplication.addMapFrameListener(this);
    8642    }
    8743
     
    8945    public void actionPerformed(ActionEvent e) {
    9046        try {
    91             saveSession();
     47            saveSession(true, false);
    9248        } catch (UserCancelException ignore) {
    9349            Logging.trace(ignore);
     
    9652
    9753    @Override
    98     public void destroy() {
    99         MainApplication.removeMapFrameListener(this);
    100         super.destroy();
    101     }
    102 
    103     /**
    104      * Attempts to save the session.
    105      * @throws UserCancelException when the user has cancelled the save process.
    106      * @since 8913
    107      */
    108     public void saveSession() throws UserCancelException {
    109         if (!isEnabled()) {
    110             return;
    111         }
    112 
    113         SessionSaveAsDialog dlg = new SessionSaveAsDialog();
    114         dlg.showDialog();
    115         if (dlg.getValue() != 1) {
    116             throw new UserCancelException();
    117         }
    118 
    119         // TODO: resolve dependencies for layers excluded by the user
    120         List<Layer> layersOut = layers.stream()
    121                 .filter(layer -> exporters.get(layer) != null && exporters.get(layer).shallExport())
    122                 .collect(Collectors.toList());
    123 
    124         boolean zipRequired = layersOut.stream().map(exporters::get)
    125                 .anyMatch(ex -> ex != null && ex.requiresZip());
    126 
    127         FileFilter joz = new ExtensionFileFilter("joz", "joz", tr("Session file (archive) (*.joz)"));
    128         FileFilter jos = new ExtensionFileFilter("jos", "jos", tr("Session file (*.jos)"));
    129 
    130         AbstractFileChooser fc;
    131 
    132         if (zipRequired) {
    133             fc = createAndOpenFileChooser(false, false, tr("Save Session"), joz, JFileChooser.FILES_ONLY, "lastDirectory");
    134         } else {
    135             fc = createAndOpenFileChooser(false, false, tr("Save Session"), Arrays.asList(jos, joz), jos,
    136                     JFileChooser.FILES_ONLY, "lastDirectory");
    137         }
    138 
    139         if (fc == null) {
    140             throw new UserCancelException();
    141         }
    142 
    143         File file = fc.getSelectedFile();
    144         String fn = file.getName();
    145 
    146         boolean zip;
    147         FileFilter ff = fc.getFileFilter();
    148         if (zipRequired || joz.equals(ff)) {
    149             zip = true;
    150         } else if (jos.equals(ff)) {
    151             zip = false;
    152         } else {
    153             if (Utils.hasExtension(fn, "joz")) {
    154                 zip = true;
    155             } else {
    156                 zip = false;
    157             }
    158         }
    159         if (fn.indexOf('.') == -1) {
    160             file = new File(file.getPath() + (zip ? ".joz" : ".jos"));
    161             if (!SaveActionBase.confirmOverwrite(file)) {
    162                 throw new UserCancelException();
    163             }
    164         }
    165 
    166         Stream<Layer> layersToSaveStream = layersOut.stream()
    167                 .filter(layer -> layer.isSavable()
    168                         && layer instanceof AbstractModifiableLayer
    169                         && ((AbstractModifiableLayer) layer).requiresSaveToFile()
    170                         && exporters.get(layer) != null
    171                         && !exporters.get(layer).requiresZip());
    172 
    173         if (SAVE_LOCAL_FILES_PROPERTY.get()) {
    174             // individual files must be saved before the session file as the location may change
    175             if (layersToSaveStream
    176                 .map(layer -> SaveAction.getInstance().doSave(layer, true))
    177                 .collect(Collectors.toList()) // force evaluation of all elements
    178                 .contains(false)) {
    179 
    180                 new Notification(tr("Not all local files referenced by the session file could be saved."
    181                         + "<br>Make sure you save them before closing JOSM."))
    182                     .setIcon(JOptionPane.WARNING_MESSAGE)
    183                     .setDuration(Notification.TIME_LONG)
    184                     .show();
    185             }
    186         } else if (layersToSaveStream.anyMatch(l -> true)) {
    187             new Notification(tr("Not all local files referenced by the session file are saved yet."
    188                     + "<br>Make sure you save them before closing JOSM."))
    189                 .setIcon(JOptionPane.INFORMATION_MESSAGE)
    190                 .setDuration(Notification.TIME_LONG)
    191                 .show();
    192         }
    193 
    194         int active = -1;
    195         Layer activeLayer = getLayerManager().getActiveLayer();
    196         if (activeLayer != null) {
    197             active = layersOut.indexOf(activeLayer);
    198         }
    199 
    200         SessionWriter sw = new SessionWriter(layersOut, active, exporters, dependencies, zip);
    201         try {
    202             Notification savingNotification = showSavingNotification(file.getName());
    203             sw.write(file);
    204             SaveActionBase.addToFileOpenHistory(file);
    205             showSavedNotification(savingNotification, file.getName());
    206         } catch (IOException ex) {
    207             Logging.error(ex);
    208             HelpAwareOptionPane.showMessageDialogInEDT(
    209                     MainApplication.getMainFrame(),
    210                     tr("<html>Could not save session file ''{0}''.<br>Error is:<br>{1}</html>",
    211                             file.getName(), Utils.escapeReservedCharactersHTML(ex.getMessage())),
    212                     tr("IO Error"),
    213                     JOptionPane.ERROR_MESSAGE,
    214                     null
    215             );
    216         }
    217     }
    218 
    219     /**
    220      * The "Save Session" dialog
    221      */
    222     public class SessionSaveAsDialog extends ExtendedDialog {
    223 
    224         /**
    225          * Constructs a new {@code SessionSaveAsDialog}.
    226          */
    227         public SessionSaveAsDialog() {
    228             super(MainApplication.getMainFrame(), tr("Save Session"), tr("Save As"), tr("Cancel"));
    229             configureContextsensitiveHelp("Action/SessionSaveAs", true /* show help button */);
    230             initialize();
    231             setButtonIcons("save_as", "cancel");
    232             setDefaultButton(1);
    233             setRememberWindowGeometry(getClass().getName() + ".geometry",
    234                     WindowGeometry.centerInWindow(MainApplication.getMainFrame(), new Dimension(450, 450)));
    235             setContent(build(), false);
    236         }
    237 
    238         /**
    239          * Initializes action.
    240          */
    241         public final void initialize() {
    242             layers = new ArrayList<>(getLayerManager().getLayers());
    243             exporters = new HashMap<>();
    244             dependencies = new MultiMap<>();
    245 
    246             Set<Layer> noExporter = new HashSet<>();
    247 
    248             for (Layer layer : layers) {
    249                 SessionLayerExporter exporter = null;
    250                 try {
    251                     exporter = SessionWriter.getSessionLayerExporter(layer);
    252                 } catch (IllegalArgumentException | JosmRuntimeException e) {
    253                     Logging.error(e);
    254                 }
    255                 if (exporter != null) {
    256                     exporters.put(layer, exporter);
    257                     Collection<Layer> deps = exporter.getDependencies();
    258                     if (deps != null) {
    259                         dependencies.putAll(layer, deps);
    260                     } else {
    261                         dependencies.putVoid(layer);
    262                     }
    263                 } else {
    264                     noExporter.add(layer);
    265                     exporters.put(layer, null);
    266                 }
    267             }
    268 
    269             int numNoExporter = 0;
    270             WHILE: while (numNoExporter != noExporter.size()) {
    271                 numNoExporter = noExporter.size();
    272                 for (Layer layer : layers) {
    273                     if (noExporter.contains(layer)) continue;
    274                     for (Layer depLayer : dependencies.get(layer)) {
    275                         if (noExporter.contains(depLayer)) {
    276                             noExporter.add(layer);
    277                             exporters.put(layer, null);
    278                             break WHILE;
    279                         }
    280                     }
    281                 }
    282             }
    283         }
    284 
    285         protected final Component build() {
    286             JPanel op = new JPanel(new GridBagLayout());
    287             JPanel ip = new JPanel(new GridBagLayout());
    288             for (Layer layer : layers) {
    289                 JPanel wrapper = new JPanel(new GridBagLayout());
    290                 wrapper.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
    291                 Component exportPanel;
    292                 SessionLayerExporter exporter = exporters.get(layer);
    293                 if (exporter == null) {
    294                     if (!exporters.containsKey(layer)) throw new AssertionError();
    295                     exportPanel = getDisabledExportPanel(layer);
    296                 } else {
    297                     exportPanel = exporter.getExportPanel();
    298                 }
    299                 wrapper.add(exportPanel, GBC.std().fill(GBC.HORIZONTAL));
    300                 ip.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(2, 2, 4, 2));
    301             }
    302             ip.add(GBC.glue(0, 1), GBC.eol().fill(GBC.VERTICAL));
    303             JScrollPane sp = new JScrollPane(ip);
    304             sp.setBorder(BorderFactory.createEmptyBorder());
    305             JPanel p = new JPanel(new GridBagLayout());
    306             p.add(sp, GBC.eol().fill());
    307             final JTabbedPane tabs = new JTabbedPane();
    308             tabs.addTab(tr("Layers"), p);
    309             op.add(tabs, GBC.eol().fill());
    310             JCheckBox chkSaveLocal = new JCheckBox(tr("Save all local files to disk"), SAVE_LOCAL_FILES_PROPERTY.get());
    311             chkSaveLocal.addChangeListener(l -> {
    312                 SAVE_LOCAL_FILES_PROPERTY.put(chkSaveLocal.isSelected());
    313             });
    314             op.add(chkSaveLocal);
    315             return op;
    316         }
    317 
    318         protected final Component getDisabledExportPanel(Layer layer) {
    319             JPanel p = new JPanel(new GridBagLayout());
    320             JCheckBox include = new JCheckBox();
    321             include.setEnabled(false);
    322             JLabel lbl = new JLabel(layer.getName(), layer.getIcon(), SwingConstants.LEADING);
    323             lbl.setToolTipText(tr("No exporter for this layer"));
    324             lbl.setLabelFor(include);
    325             lbl.setEnabled(false);
    326             p.add(include, GBC.std());
    327             p.add(lbl, GBC.std());
    328             p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
    329             return p;
    330         }
     54    protected void addListeners() {
     55        MainApplication.addMapFrameListener(this);
    33156    }
    33257
    33358    @Override
    334     protected void updateEnabledState() {
    335         setEnabled(MainApplication.isDisplayingMapView());
    336     }
    337 
    338     @Override
    339     public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
    340         updateEnabledState();
     59    protected void removeListeners() {
     60        MainApplication.removeMapFrameListener(this);
    34161    }
    34262}
Note: See TracChangeset for help on using the changeset viewer.