mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
- don't move by default when import dialog is not in migration mode
- warn the user and allow them to correct the situation if they attempt an incomplete migration
This commit is contained in:
@@ -25,8 +25,10 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -34,6 +36,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
|
|||||||
|
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@@ -71,10 +74,10 @@ import forge.properties.NewConstants;
|
|||||||
* processing done in this class, so most operations are asynchronous.
|
* processing done in this class, so most operations are asynchronous.
|
||||||
*/
|
*/
|
||||||
public class DialogMigrateProfile {
|
public class DialogMigrateProfile {
|
||||||
private final Runnable _onImportSuccessful;
|
private final FButton _btnStart;
|
||||||
private final FButton _btnStart;
|
private final FButton _btnCancel;
|
||||||
private final FLabel _btnChooseDir;
|
private final FLabel _btnChooseDir;
|
||||||
private final JPanel _selectionPanel;
|
private final JPanel _selectionPanel;
|
||||||
|
|
||||||
// volatile since it is checked from multiple threads
|
// volatile since it is checked from multiple threads
|
||||||
private volatile boolean _cancel;
|
private volatile boolean _cancel;
|
||||||
@@ -197,7 +200,7 @@ public class DialogMigrateProfile {
|
|||||||
_cancel = false;
|
_cancel = false;
|
||||||
synchronized (_onAnalyzerDone) {
|
synchronized (_onAnalyzerDone) {
|
||||||
// this will populate the panel with data selection widgets
|
// this will populate the panel with data selection widgets
|
||||||
_AnalyzerUpdater analyzer = new _AnalyzerUpdater(text, _onAnalyzerDone);
|
_AnalyzerUpdater analyzer = new _AnalyzerUpdater(text, _onAnalyzerDone, isMigration);
|
||||||
analyzer.execute();
|
analyzer.execute();
|
||||||
_analyzerActive = true;
|
_analyzerActive = true;
|
||||||
}
|
}
|
||||||
@@ -225,8 +228,8 @@ public class DialogMigrateProfile {
|
|||||||
};
|
};
|
||||||
_btnStart = new FButton("Start import");
|
_btnStart = new FButton("Start import");
|
||||||
_btnStart.setEnabled(false);
|
_btnStart.setEnabled(false);
|
||||||
final FButton btnCancel = new FButton("Cancel");
|
_btnCancel = new FButton("Cancel");
|
||||||
btnCancel.addActionListener(new ActionListener() {
|
_btnCancel.addActionListener(new ActionListener() {
|
||||||
@Override public void actionPerformed(ActionEvent e) {
|
@Override public void actionPerformed(ActionEvent e) {
|
||||||
_cancel = true;
|
_cancel = true;
|
||||||
cleanup.run();
|
cleanup.run();
|
||||||
@@ -236,16 +239,10 @@ public class DialogMigrateProfile {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_onImportSuccessful = new Runnable() {
|
|
||||||
@Override public void run() {
|
|
||||||
btnCancel.setText("Done");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JPanel southPanel = new JPanel(new MigLayout("ax center"));
|
JPanel southPanel = new JPanel(new MigLayout("ax center"));
|
||||||
southPanel.setOpaque(false);
|
southPanel.setOpaque(false);
|
||||||
southPanel.add(_btnStart, "center, w pref+144!, h pref+12!");
|
southPanel.add(_btnStart, "center, w pref+144!, h pref+12!");
|
||||||
southPanel.add(btnCancel, "center, w pref+144!, h pref+12!, gap 72");
|
southPanel.add(_btnCancel, "center, w pref+144!, h pref+12!, gap 72");
|
||||||
p.add(southPanel, "growx");
|
p.add(southPanel, "growx");
|
||||||
|
|
||||||
JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel();
|
JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel();
|
||||||
@@ -255,7 +252,7 @@ public class DialogMigrateProfile {
|
|||||||
|
|
||||||
// focus cancel button after the dialog is shown
|
// focus cancel button after the dialog is shown
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override public void run() { btnCancel.requestFocusInWindow(); }
|
@Override public void run() { _btnCancel.requestFocusInWindow(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
// if our source dir is provided, set the text, which will fire off an analyzer
|
// if our source dir is provided, set the text, which will fire off an analyzer
|
||||||
@@ -294,6 +291,7 @@ public class DialogMigrateProfile {
|
|||||||
|
|
||||||
private final String _srcDir;
|
private final String _srcDir;
|
||||||
private final Runnable _onAnalyzerDone;
|
private final Runnable _onAnalyzerDone;
|
||||||
|
private final boolean _isMigration;
|
||||||
private final JComboBox _unknownDeckCombo;
|
private final JComboBox _unknownDeckCombo;
|
||||||
private final FCheckBox _moveCheckbox;
|
private final FCheckBox _moveCheckbox;
|
||||||
private final FCheckBox _overwriteCheckbox;
|
private final FCheckBox _overwriteCheckbox;
|
||||||
@@ -303,9 +301,10 @@ public class DialogMigrateProfile {
|
|||||||
// updates the _operationLog widget asynchronously to keep the UI responsive
|
// updates the _operationLog widget asynchronously to keep the UI responsive
|
||||||
private final _OperationLogAsyncUpdater _operationLogUpdater;
|
private final _OperationLogAsyncUpdater _operationLogUpdater;
|
||||||
|
|
||||||
public _AnalyzerUpdater(String srcDir, Runnable onAnalyzerDone) {
|
public _AnalyzerUpdater(String srcDir, Runnable onAnalyzerDone, boolean isMigration) {
|
||||||
_srcDir = srcDir;
|
_srcDir = srcDir;
|
||||||
_onAnalyzerDone = onAnalyzerDone;
|
_onAnalyzerDone = onAnalyzerDone;
|
||||||
|
_isMigration = isMigration;
|
||||||
|
|
||||||
_selectionPanel.removeAll();
|
_selectionPanel.removeAll();
|
||||||
_selectionPanel.setLayout(new MigLayout("insets 0, gap 5, wrap"));
|
_selectionPanel.setLayout(new MigLayout("insets 0, gap 5, wrap"));
|
||||||
@@ -364,10 +363,10 @@ public class DialogMigrateProfile {
|
|||||||
JPanel ioOptionPanel = new JPanel(new MigLayout("insets 0, gap 10"));
|
JPanel ioOptionPanel = new JPanel(new MigLayout("insets 0, gap 10"));
|
||||||
ioOptionPanel.setOpaque(false);
|
ioOptionPanel.setOpaque(false);
|
||||||
_moveCheckbox = new FCheckBox("Remove source files after copy");
|
_moveCheckbox = new FCheckBox("Remove source files after copy");
|
||||||
_moveCheckbox.setSelected(true);
|
_moveCheckbox.setSelected(isMigration);
|
||||||
_moveCheckbox.addChangeListener(_stateChangedListener);
|
_moveCheckbox.addChangeListener(_stateChangedListener);
|
||||||
ioOptionPanel.add(_moveCheckbox);
|
ioOptionPanel.add(_moveCheckbox);
|
||||||
_overwriteCheckbox = new FCheckBox("Overwrite existing files");
|
_overwriteCheckbox = new FCheckBox("Overwrite files in destination");
|
||||||
_overwriteCheckbox.addChangeListener(_stateChangedListener);
|
_overwriteCheckbox.addChangeListener(_stateChangedListener);
|
||||||
ioOptionPanel.add(_overwriteCheckbox);
|
ioOptionPanel.add(_overwriteCheckbox);
|
||||||
_selectionPanel.add(ioOptionPanel);
|
_selectionPanel.add(ioOptionPanel);
|
||||||
@@ -530,6 +529,50 @@ public class DialogMigrateProfile {
|
|||||||
// set up the start button to start the prepared import on click
|
// set up the start button to start the prepared import on click
|
||||||
_btnStart.addActionListener(new ActionListener() {
|
_btnStart.addActionListener(new ActionListener() {
|
||||||
@Override public void actionPerformed(ActionEvent arg0) {
|
@Override public void actionPerformed(ActionEvent arg0) {
|
||||||
|
// if this is a migration, warn if active settings will not complete a migration and give the
|
||||||
|
// user an option to fix
|
||||||
|
if (_isMigration) {
|
||||||
|
// assemble a list of selections that need to be selected to complete a full migration
|
||||||
|
List<String> unselectedButShouldBe = new ArrayList<String>();
|
||||||
|
for (Pair<FCheckBox, ? extends Map<File, File>> entry : _selections.values()) {
|
||||||
|
// add name to list if checkbox is unselected, but contains operations
|
||||||
|
FCheckBox cb = entry.getLeft();
|
||||||
|
if (!cb.isSelected() && 0 < entry.getRight().size()) {
|
||||||
|
unselectedButShouldBe.add(cb.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unselectedButShouldBe.isEmpty() || !_moveCheckbox.isSelected()) {
|
||||||
|
StringBuilder sb = new StringBuilder("<html>");
|
||||||
|
if (!unselectedButShouldBe.isEmpty()) {
|
||||||
|
sb.append("It looks like the following options are not selected, which will result in an incomplete migration:");
|
||||||
|
sb.append("<ul>");
|
||||||
|
for (String cbName : unselectedButShouldBe) {
|
||||||
|
sb.append("<li><b>").append(cbName).append("</b></li>");
|
||||||
|
}
|
||||||
|
sb.append("</ul>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_moveCheckbox.isSelected()) {
|
||||||
|
sb.append(unselectedButShouldBe.isEmpty() ? "It " : "It also ").append("looks like the <b>");
|
||||||
|
sb.append(_moveCheckbox.getText()).append("</b> option is not selected.<br><br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("You can continue anyway, but the migration will be incomplete, and the data migration prompt<br>");
|
||||||
|
sb.append("will come up again the next time you start Forge in order to migrate the remaining files<br>");
|
||||||
|
sb.append("unless you move or delete them manually.</html>");
|
||||||
|
|
||||||
|
Object[] options = { "Whoops, let me fix that!", "Continue with the import, I know what I'm doing." };
|
||||||
|
int chosen = JOptionPane.showOptionDialog(_operationLog, sb.toString(), "Migration warning",
|
||||||
|
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
|
||||||
|
|
||||||
|
if (1 != chosen) {
|
||||||
|
// i.e. option 0 was chosen or the dialog was otherwise closed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ensure no other actions (except for cancel) can be taken while the import is in progress
|
// ensure no other actions (except for cancel) can be taken while the import is in progress
|
||||||
_btnStart.setEnabled(false);
|
_btnStart.setEnabled(false);
|
||||||
_btnChooseDir.setEnabled(false);
|
_btnChooseDir.setEnabled(false);
|
||||||
@@ -549,6 +592,8 @@ public class DialogMigrateProfile {
|
|||||||
_selections, _unknownDeckCombo, _operationLog, _progressBar,
|
_selections, _unknownDeckCombo, _operationLog, _progressBar,
|
||||||
_moveCheckbox.isSelected(), _overwriteCheckbox.isSelected());
|
_moveCheckbox.isSelected(), _overwriteCheckbox.isSelected());
|
||||||
importer.execute();
|
importer.execute();
|
||||||
|
|
||||||
|
_btnCancel.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -801,11 +846,12 @@ public class DialogMigrateProfile {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
|
_btnCancel.requestFocusInWindow();
|
||||||
if (_cancel) { return; }
|
if (_cancel) { return; }
|
||||||
|
|
||||||
_progressBar.setValue(_progressBar.getMaximum());
|
_progressBar.setValue(_progressBar.getMaximum());
|
||||||
_progressBar.setString("Import complete");
|
_progressBar.setString("Import complete");
|
||||||
_onImportSuccessful.run();
|
_btnCancel.setText("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual file copy routine. uses java.nio classes for ultra-fast copying
|
// actual file copy routine. uses java.nio classes for ultra-fast copying
|
||||||
|
|||||||
@@ -217,8 +217,8 @@ public enum FView {
|
|||||||
" Now just restart Forge to load the data from its new home!</html>").build());
|
" Now just restart Forge to load the data from its new home!</html>").build());
|
||||||
} else {
|
} else {
|
||||||
p.add(new FLabel.Builder().text("<html>There seem to be a few files left over in your old data" +
|
p.add(new FLabel.Builder().text("<html>There seem to be a few files left over in your old data" +
|
||||||
" directories. They should be deleted or moved somewhere else to avoid having this data" +
|
" directories. They should be deleted or moved somewhere else to avoid having the data" +
|
||||||
" migration message pop up again! If there are any empty directories left over after that," +
|
" migration prompt pop up again! If there are any empty directories left over after that," +
|
||||||
" just run through this migration procedure one more time and that should get them cleared" +
|
" just run through this migration procedure one more time and that should get them cleared" +
|
||||||
" up.</html>").build());
|
" up.</html>").build());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user