Skip to content

Commit

Permalink
Improve confirm option how to handle unsaved changes
Browse files Browse the repository at this point in the history
- Clarified button labels of confirmation dialogs.
- Added option to save, discard or cancel to all confirmation dialogs
  unless cancelling is not an available option.
- Improved internal handling of cancelled operations (e.g. no needless
  output of exception stacktraces)
  • Loading branch information
Argent77 committed Nov 22, 2024
1 parent 74a188f commit eb07b69
Show file tree
Hide file tree
Showing 16 changed files with 259 additions and 69 deletions.
43 changes: 43 additions & 0 deletions src/org/infinity/exceptions/AbortException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 Jon Olav Hauglid
// See LICENSE.txt for license information

package org.infinity.exceptions;

/**
* Thrown to indicate that the current operation was intentionally aborted.
*/
public class AbortException extends Exception {
/** Constructs an {@code AbortException} with no detail message. */
public AbortException() {
super();
}

/**
* Constructs an {@code AbortException} with the specified detail message.
*
* @param message the detail message.
*/
public AbortException(String message) {
super(message);
}

/**
* Constructs an {@code AbortException} with the specified detail message and cause.
*
* @param message the detail message.
* @param cause the cause for this exception to be thrown.
*/
public AbortException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs an {@code AbortException} with a cause but no detail message.
*
* @param cause the cause for this exception to be thrown.
*/
public AbortException(Throwable cause) {
super(cause);
}
}
6 changes: 6 additions & 0 deletions src/org/infinity/gui/ChildFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.swing.WindowConstants;

import org.infinity.NearInfinity;
import org.infinity.exceptions.AbortException;
import org.infinity.gui.menu.BrowserMenuBar;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.Closeable;
Expand Down Expand Up @@ -236,6 +237,9 @@ public void actionPerformed(ActionEvent e) {
if (!ChildFrame.this.windowClosing(false)) {
return;
}
} catch (AbortException e2) {
Logger.debug(e2);
return;
} catch (Exception e2) {
Logger.error(e2);
return;
Expand Down Expand Up @@ -411,6 +415,8 @@ private static void closeWindow(ChildFrame frame, WindowEvent event) {
frame.close();
}
frame.dispose();
} catch (AbortException e) {
Logger.debug(e);
} catch (Exception e) {
Logger.error(e);
}
Expand Down
4 changes: 2 additions & 2 deletions src/org/infinity/gui/StructViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,11 @@ public void actionPerformed(ActionEvent event) {
WindowBlocker.blockWindow(wnd, false);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE) == event.getSource()) {
if (ResourceFactory.saveResource((Resource) struct, getTopLevelAncestor())) {
if (ResourceFactory.saveResource((Resource) struct, getTopLevelAncestor()).isTrue()) {
struct.setStructChanged(false);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE_AS) == event.getSource()) {
if (ResourceFactory.saveResourceAs((Resource) struct, getTopLevelAncestor())) {
if (ResourceFactory.saveResourceAs((Resource) struct, getTopLevelAncestor()).isTrue()) {
struct.setStructChanged(false);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.EXPORT_BUTTON) == event.getSource()) {
Expand Down
3 changes: 3 additions & 0 deletions src/org/infinity/gui/converter/BamFilterBaseOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.nio.file.Path;
import java.util.Arrays;

import org.infinity.exceptions.AbortException;
import org.infinity.resource.graphics.DxtEncoder;
import org.infinity.resource.graphics.PseudoBamDecoder;
import org.infinity.util.Logger;
Expand Down Expand Up @@ -86,6 +87,8 @@ public static boolean convertBam(ConvertToBam converter, Path outFileName, Pseud
try {
return decoder.exportBamV2(outFileName, dxtType, pvrzIndex, BamOptionsDialog.getOverwritePvrzIndices(),
converter.getProgressMonitor(), converter.getProgressMonitorStage());
} catch (AbortException e) {
Logger.debug(e);
} catch (Exception e) {
Logger.error(e);
throw e;
Expand Down
106 changes: 67 additions & 39 deletions src/org/infinity/resource/ResourceFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.infinity.datatype.SecTypeBitmap;
import org.infinity.datatype.Song2daBitmap;
import org.infinity.datatype.Summon2daBitmap;
import org.infinity.exceptions.AbortException;
import org.infinity.gui.ChildFrame;
import org.infinity.gui.IdsBrowser;
import org.infinity.gui.menu.BrowserMenuBar;
Expand Down Expand Up @@ -85,7 +86,14 @@
import org.infinity.resource.video.WbmResource;
import org.infinity.resource.wed.WedResource;
import org.infinity.resource.wmp.WmpResource;
import org.infinity.util.*;
import org.infinity.util.CreMapCache;
import org.infinity.util.DynamicArray;
import org.infinity.util.IdsMapCache;
import org.infinity.util.Logger;
import org.infinity.util.Misc;
import org.infinity.util.Platform;
import org.infinity.util.StaticSimpleXorDecryptor;
import org.infinity.util.TriState;
import org.infinity.util.io.FileEx;
import org.infinity.util.io.FileManager;
import org.infinity.util.io.StreamUtils;
Expand Down Expand Up @@ -677,19 +685,27 @@ public static void saveCopyOfResource(ResourceEntry entry) {
}
}

public static boolean saveResource(Resource resource, Component parent) {
public static TriState saveResource(Resource resource, Component parent) {
return saveResource(resource, parent, false);
}

public static TriState saveResource(Resource resource, Component parent, boolean overwrite) {
if (getInstance() != null) {
return getInstance().saveResourceInternal(resource, parent);
return getInstance().saveResourceInternal(resource, parent, overwrite);
} else {
return false;
return TriState.FALSE;
}
}

public static boolean saveResourceAs(Resource resource, Component parent) {
public static TriState saveResourceAs(Resource resource, Component parent) {
return saveResourceAs(resource, parent, false);
}

public static TriState saveResourceAs(Resource resource, Component parent, boolean overwrite) {
if (getInstance() != null) {
return getInstance().saveResourceAsInternal(resource, parent);
return getInstance().saveResourceAsInternal(resource, parent, overwrite);
} else {
return false;
return TriState.FALSE;
}
}

Expand All @@ -703,7 +719,7 @@ public static boolean saveResourceAs(Resource resource, Component parent) {
*
* @throws HeadlessException if {@link GraphicsEnvironment#isHeadless} returns {@code true}
* @throws NullPointerException If any argument is {@code null}
* @throws Exception If save will be cancelled
* @throws AbortException If save will be cancelled
*/
public static void closeResource(Resource resource, ResourceEntry entry, JComponent parent) throws Exception {
final Path output;
Expand All @@ -725,17 +741,19 @@ public static void closeResource(Resource resource, ResourceEntry entry, JCompon
*
* @throws HeadlessException if {@link GraphicsEnvironment#isHeadless} returns {@code true}
* @throws NullPointerException If {@code resource} or {@code parent} is {@code null}
* @throws Exception If save will be cancelled
* @throws AbortException If save will be cancelled
*/
public static void closeResource(Resource resource, Path output, JComponent parent) throws Exception {
if (output != null) {
final String[] options = { "Save changes", "Discard changes", "Cancel" };
final int result = JOptionPane.showOptionDialog(parent, "Save changes to " + output + '?', "Resource changed",
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
if (result == JOptionPane.YES_OPTION) {
saveResource(resource, parent.getTopLevelAncestor());
if (saveResource(resource, parent.getTopLevelAncestor()) == TriState.UNDEFINED) {
throw new AbortException("Save aborted");
}
} else if (result != JOptionPane.NO_OPTION) {
throw new Exception("Save aborted");
throw new AbortException("Save aborted");
}
}
}
Expand Down Expand Up @@ -1539,27 +1557,27 @@ private void saveCopyOfResourceInternal(ResourceEntry entry) {
}
}

private boolean saveResourceAsInternal(Resource resource, Component parent) {
private TriState saveResourceAsInternal(Resource resource, Component parent, boolean overwrite) {
final Path outFile = getExportFileDialogInternal(parent, resource.getResourceEntry().getResourceName(), true);
if (outFile != null) {
return saveResourceInternal(resource, parent, outFile);
return saveResourceInternal(resource, parent, outFile, overwrite);
} else {
return false;
return TriState.FALSE;
}
}

private boolean saveResourceInternal(Resource resource, Component parent) {
return saveResourceInternal(resource, parent, null);
private TriState saveResourceInternal(Resource resource, Component parent, boolean overwrite) {
return saveResourceInternal(resource, parent, null, overwrite);
}

private boolean saveResourceInternal(Resource resource, Component parent, Path outFile) {
private TriState saveResourceInternal(Resource resource, Component parent, Path outFile, boolean overwrite) {
if (!(resource instanceof Writeable)) {
JOptionPane.showMessageDialog(parent, "Resource not savable", "Error", JOptionPane.ERROR_MESSAGE);
return false;
return TriState.FALSE;
}
final ResourceEntry entry = resource.getResourceEntry();
if (entry == null) {
return false;
return TriState.FALSE;
}

Path outPath;
Expand All @@ -1575,7 +1593,7 @@ private boolean saveResourceInternal(Resource resource, Component parent, Path o
JOptionPane.showMessageDialog(parent, "Unable to create override folder.", "Error",
JOptionPane.ERROR_MESSAGE);
Logger.error(e);
return false;
return TriState.FALSE;
}
}
outPath = FileManager.query(overridePath, entry.getResourceName());
Expand All @@ -1592,7 +1610,7 @@ private boolean saveResourceInternal(Resource resource, Component parent, Path o
JOptionPane.showMessageDialog(parent, "Unable to create folder: " + outPath.getParent(), "Error",
JOptionPane.ERROR_MESSAGE);
Logger.error(e);
return false;
return TriState.FALSE;
}
}
}
Expand All @@ -1601,24 +1619,34 @@ private boolean saveResourceInternal(Resource resource, Component parent, Path o

if (FileEx.create(outPath).exists()) {
outPath = outPath.toAbsolutePath();
String[] options = { "Overwrite", "Cancel" };
if (JOptionPane.showOptionDialog(parent, outPath + " exists. Overwrite?", "Save resource",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]) == 0) {
if (BrowserMenuBar.getInstance().getOptions().backupOnSave()) {
try {
Path bakPath = outPath.getParent().resolve(outPath.getFileName() + ".bak");
if (FileEx.create(bakPath).isFile()) {
Files.delete(bakPath);
}
if (!FileEx.create(bakPath).exists()) {
Files.move(outPath, bakPath);
final int result;
if (overwrite) {
result = JOptionPane.YES_OPTION;
} else {
String[] options = { "Overwrite", "Discard", "Cancel" };
result = JOptionPane.showOptionDialog(parent, outPath + " exists. Overwrite?", "Save resource",
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
}
switch (result) {
case JOptionPane.YES_OPTION: // Overwrite
if (BrowserMenuBar.getInstance().getOptions().backupOnSave()) {
try {
Path bakPath = outPath.getParent().resolve(outPath.getFileName() + ".bak");
if (FileEx.create(bakPath).isFile()) {
Files.delete(bakPath);
}
if (!FileEx.create(bakPath).exists()) {
Files.move(outPath, bakPath);
}
} catch (IOException e) {
Logger.error(e);
}
} catch (IOException e) {
Logger.error(e);
}
}
} else {
return false;
break;
case JOptionPane.NO_OPTION: // Discard
return TriState.FALSE;
default: // Cancel
return TriState.UNDEFINED;
}
}

Expand All @@ -1627,7 +1655,7 @@ private boolean saveResourceInternal(Resource resource, Component parent, Path o
} catch (IOException e) {
JOptionPane.showMessageDialog(parent, "Error while saving " + entry, "Error", JOptionPane.ERROR_MESSAGE);
Logger.error(e);
return false;
return TriState.FALSE;
}

JOptionPane.showMessageDialog(parent, "File saved to \"" + outPath.toAbsolutePath() + '\"', "Save complete",
Expand All @@ -1649,6 +1677,6 @@ private boolean saveResourceInternal(Resource resource, Component parent, Path o
} else if (entry.getResourceName().equalsIgnoreCase(SecTypeBitmap.getTableName())) {
SecTypeBitmap.resetTypeTable();
}
return true;
return TriState.TRUE;
}
}
4 changes: 2 additions & 2 deletions src/org/infinity/resource/bcs/BafResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,9 @@ private void save(boolean interactive) {
}
final boolean result;
if (interactive) {
result = ResourceFactory.saveResourceAs(this, panel.getTopLevelAncestor());
result = ResourceFactory.saveResourceAs(this, panel.getTopLevelAncestor()).isTrue();
} else {
result = ResourceFactory.saveResource(this, panel.getTopLevelAncestor());
result = ResourceFactory.saveResource(this, panel.getTopLevelAncestor()).isTrue();
}
if (result) {
bSave.setEnabled(false);
Expand Down
9 changes: 5 additions & 4 deletions src/org/infinity/resource/bcs/BcsResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;

import org.infinity.exceptions.AbortException;
import org.infinity.gui.ButtonPanel;
import org.infinity.gui.ButtonPopupMenu;
import org.infinity.gui.DataMenuItem;
Expand Down Expand Up @@ -360,11 +361,11 @@ public void close() throws Exception {
if (result == JOptionPane.YES_OPTION) {
((JButton) bpDecompile.getControlByType(CTRL_COMPILE)).doClick();
if (bpDecompile.getControlByType(CTRL_ERRORS).isEnabled()) {
throw new Exception("Save aborted");
throw new AbortException("Save aborted");
}
ResourceFactory.saveResource(this, panel.getTopLevelAncestor());
} else if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {
throw new Exception("Save aborted");
throw new AbortException("Save aborted");
}
} else if (codeChanged) {
ResourceFactory.closeResource(this, entry, panel);
Expand Down Expand Up @@ -779,9 +780,9 @@ private void save(boolean interactive) {
}
final boolean result;
if (interactive) {
result = ResourceFactory.saveResourceAs(this, panel.getTopLevelAncestor());
result = ResourceFactory.saveResourceAs(this, panel.getTopLevelAncestor()).isTrue();
} else {
result = ResourceFactory.saveResource(this, panel.getTopLevelAncestor());
result = ResourceFactory.saveResource(this, panel.getTopLevelAncestor()).isTrue();
}
if (result) {
bSave.setEnabled(false);
Expand Down
4 changes: 2 additions & 2 deletions src/org/infinity/resource/graphics/BamResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,11 @@ public void actionPerformed(ActionEvent event) {
} else if (buttonPanel.getControlByType(ButtonPanel.Control.FIND_REFERENCES) == event.getSource()) {
searchReferences(panelMain.getTopLevelAncestor());
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE) == event.getSource()) {
if (ResourceFactory.saveResource(this, panelMain.getTopLevelAncestor())) {
if (ResourceFactory.saveResource(this, panelMain.getTopLevelAncestor()).isTrue()) {
setRawModified(false);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE_AS) == event.getSource()) {
if (ResourceFactory.saveResourceAs(this, panelMain.getTopLevelAncestor())) {
if (ResourceFactory.saveResourceAs(this, panelMain.getTopLevelAncestor()).isTrue()) {
setRawModified(false);
}
} else if (event.getSource() == timer) {
Expand Down
4 changes: 2 additions & 2 deletions src/org/infinity/resource/graphics/PltResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,11 @@ public void actionPerformed(ActionEvent e) {
JOptionPane.ERROR_MESSAGE);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE) == e.getSource()) {
if (ResourceFactory.saveResource(this, panelMain.getTopLevelAncestor())) {
if (ResourceFactory.saveResource(this, panelMain.getTopLevelAncestor()).isTrue()) {
setRawModified(false);
}
} else if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE_AS) == e.getSource()) {
if (ResourceFactory.saveResourceAs(this, panelMain.getTopLevelAncestor())) {
if (ResourceFactory.saveResourceAs(this, panelMain.getTopLevelAncestor()).isTrue()) {
setRawModified(false);
}
}
Expand Down
Loading

0 comments on commit eb07b69

Please sign in to comment.