diff --git a/src/main/java/forge/gui/DialogMigrateProfile.java b/src/main/java/forge/gui/DialogMigrateProfile.java index 55e1de5a86e..543ca7f2cfa 100644 --- a/src/main/java/forge/gui/DialogMigrateProfile.java +++ b/src/main/java/forge/gui/DialogMigrateProfile.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; import javax.swing.JComboBox; +import javax.swing.JFileChooser; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; @@ -40,6 +41,8 @@ import javax.swing.SwingWorker; import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.text.DefaultCaret; import net.miginfocom.swing.MigLayout; @@ -47,6 +50,7 @@ import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import forge.Command; import forge.error.BugReporter; import forge.gui.MigrationSourceAnalyzer.OpType; import forge.gui.toolbox.FButton; @@ -61,19 +65,23 @@ import forge.properties.NewConstants; public class DialogMigrateProfile { private final Runnable _onImportSuccessful; private final FButton _btnStart; + private final FLabel _btnChooseDir; private final JPanel _selectionPanel; private volatile boolean _cancel; - public DialogMigrateProfile(String srcDir, boolean showMigrationBlurb, final Runnable onDialogClose) { + @SuppressWarnings("serial") + public DialogMigrateProfile(String forcedSrcDir, final Runnable onDialogClose) { FPanel p = new FPanel(new MigLayout("insets dialog, gap 0, center, wrap")); p.setOpaque(false); p.setBackgroundTexture(FSkin.getIcon(FSkin.Backgrounds.BG_TEXTURE)); - // header - p.add(new FLabel.Builder().text("Migrate profile data").fontSize(15).build(), "center"); + boolean isMigration = !StringUtils.isEmpty(forcedSrcDir); - if (showMigrationBlurb) { + // header + p.add(new FLabel.Builder().text((isMigration ? "Migrate" : "Import") + " profile data").fontSize(15).build(), "center"); + + if (isMigration) { FPanel blurbPanel = new FPanel(new MigLayout("insets dialog, gap 10, center, wrap")); blurbPanel.setOpaque(false); blurbPanel.add(new FLabel.Builder().text("What's this?").build(), "growx"); @@ -106,16 +114,77 @@ public class DialogMigrateProfile { JPanel importSourcePanel = new JPanel(new MigLayout("insets 0, gap 10")); importSourcePanel.setOpaque(false); importSourcePanel.add(new FLabel.Builder().text("Import from:").build()); - boolean emptySrcDir = StringUtils.isEmpty(srcDir); - FTextField txfSrc = new FTextField.Builder().readonly(!emptySrcDir).build(); + final FTextField txfSrc = new FTextField.Builder().readonly().build(); importSourcePanel.add(txfSrc, "pushx, growx"); - if (!emptySrcDir) { - File srcDirFile = new File(srcDir); - txfSrc.setText(srcDirFile.getAbsolutePath()); - } - // TODO: listen for new directory choice and reset analysis - importSourcePanel.add(new FLabel.ButtonBuilder().text("Choose directory...").enabled(emptySrcDir).build(), "h pref+8!, w pref+12!"); - p.add(importSourcePanel, "gaptop 20, growx"); + _btnChooseDir = new FLabel.ButtonBuilder().text("Choose directory...").enabled(!isMigration).build(); + final JFileChooser _fileChooser = new JFileChooser(); + _fileChooser.setMultiSelectionEnabled(false); + _fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + _btnChooseDir.setCommand(new Command() { + @Override public void execute() { + if (JFileChooser.APPROVE_OPTION == _fileChooser.showOpenDialog(null)) { + txfSrc.setText(_fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + importSourcePanel.add(_btnChooseDir, "h pref+8!, w pref+12!"); + txfSrc.getDocument().addDocumentListener(new DocumentListener() { + boolean _analyzerActive; // access synchronized on _onAnalyzerDone + String prevText; + + private final Runnable _onAnalyzerDone = new Runnable() { + public synchronized void run() { + _analyzerActive = false; + notify(); + } + }; + + @Override public void removeUpdate(DocumentEvent e) { } + @Override public void changedUpdate(DocumentEvent e) { } + @Override public void insertUpdate(DocumentEvent e) { + final String text = txfSrc.getText(); + if (text.equals(prevText)) { + return; + } + prevText = text; + + // cancel any active analyzer + _cancel = true; + + if (!text.isEmpty()) { + _btnChooseDir.setEnabled(false); + _btnStart.setEnabled(false); + + SwingWorker analyzerStarter = new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + // wait for active analyzer (if any) to quit + synchronized (_onAnalyzerDone) { + while (_analyzerActive) { + _onAnalyzerDone.wait(); + } + } + + return null; + } + + // executes in gui event loop thread + @Override + protected void done() { + _cancel = false; + synchronized (_onAnalyzerDone) { + _AnalyzerUpdater analyzer = new _AnalyzerUpdater(text, _onAnalyzerDone); + analyzer.execute(); + _analyzerActive = true; + } + _btnChooseDir.setEnabled(true); + } + }; + analyzerStarter.execute(); + } + } + }); + p.add(importSourcePanel, "gaptop 20, pushx, growx"); // prepare import selection panel _selectionPanel = new JPanel(); @@ -148,8 +217,8 @@ public class DialogMigrateProfile { JPanel southPanel = new JPanel(new MigLayout("ax center")); southPanel.setOpaque(false); - southPanel.add(_btnStart, "center, w 40%, h pref+12!"); - southPanel.add(btnCancel, "center, w 40%, h pref+12!"); + southPanel.add(_btnStart, "center, w pref+72!, h pref+12!"); + southPanel.add(btnCancel, "center, w pref+72!, h pref+12!"); p.add(southPanel, "growx"); @@ -163,9 +232,9 @@ public class DialogMigrateProfile { @Override public void run() { btnCancel.requestFocusInWindow(); } }); - if (!emptySrcDir) { - _AnalyzerUpdater analyzer = new _AnalyzerUpdater(srcDir); - analyzer.execute(); + if (isMigration) { + File srcDirFile = new File(forcedSrcDir); + txfSrc.setText(srcDirFile.getAbsolutePath()); } } @@ -191,14 +260,16 @@ public class DialogMigrateProfile { }; private final String _srcDir; + private final Runnable _onAnalyzerDone; private final JComboBox _unknownDeckCombo; private final FCheckBox _moveCheckbox; private final FCheckBox _overwriteCheckbox; private final JTextArea _operationLog; private final JProgressBar _progressBar; - public _AnalyzerUpdater(String srcDir) { - _srcDir = srcDir; + public _AnalyzerUpdater(String srcDir, Runnable onAnalyzerDone) { + _srcDir = srcDir; + _onAnalyzerDone = onAnalyzerDone; _selectionPanel.removeAll(); _selectionPanel.setLayout(new MigLayout("insets 0, gap 5, wrap")); @@ -357,7 +428,7 @@ public class DialogMigrateProfile { MigrationSourceAnalyzer.AnalysisCallback cb = new MigrationSourceAnalyzer.AnalysisCallback() { @Override - public boolean checkCancel() { return _cancel; } + public boolean checkCancel() { try{Thread.sleep(1);}catch(InterruptedException e) {} return _cancel; } @Override public void addOp(OpType type, File src, File dest) { @@ -418,26 +489,29 @@ public class DialogMigrateProfile { // executes in gui event loop thread @Override protected void done() { - if (_cancel) { return; } - - _progressBar.setValue(_progressBar.getMaximum()); - _stateChangedListener.stateChanged(null); - _progressBar.setString("Analysis complete"); - - _btnStart.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent arg0) { - _btnStart.removeActionListener(this); - _btnStart.setEnabled(false); - - _disableAll(); - - _Importer importer = new _Importer( - _selections, _unknownDeckCombo, _operationLog, _progressBar, - _moveCheckbox.isSelected(), _overwriteCheckbox.isSelected()); - importer.execute(); - } - }); - _btnStart.setEnabled(true); + if (!_cancel) { + _progressBar.setValue(_progressBar.getMaximum()); + _stateChangedListener.stateChanged(null); + _progressBar.setString("Analysis complete"); + + _btnStart.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent arg0) { + _btnStart.removeActionListener(this); + _btnStart.setEnabled(false); + _btnChooseDir.setEnabled(false); + + _disableAll(); + + _Importer importer = new _Importer( + _selections, _unknownDeckCombo, _operationLog, _progressBar, + _moveCheckbox.isSelected(), _overwriteCheckbox.isSelected()); + importer.execute(); + } + }); + _btnStart.setEnabled(true); + } + + _onAnalyzerDone.run(); } } diff --git a/src/main/java/forge/gui/home/settings/CSubmenuDownloaders.java b/src/main/java/forge/gui/home/settings/CSubmenuDownloaders.java index 8ae553929f4..6e4cbb21d03 100644 --- a/src/main/java/forge/gui/home/settings/CSubmenuDownloaders.java +++ b/src/main/java/forge/gui/home/settings/CSubmenuDownloaders.java @@ -34,7 +34,7 @@ public enum CSubmenuDownloaders implements ICDoc { private final Command cmdHowToPlay = new Command() { @Override public void execute() { VSubmenuDownloaders.SINGLETON_INSTANCE.showHowToPlay(); } }; private final Command cmdImportPictures = new Command() { @Override - public void execute() { new DialogMigrateProfile(null, false, null); } }; + public void execute() { new DialogMigrateProfile(null, null); } }; private final Command cmdReportBug = new Command() { @Override public void execute() { BugReporter.reportBug(null); } }; diff --git a/src/main/java/forge/gui/toolbox/FTextField.java b/src/main/java/forge/gui/toolbox/FTextField.java index 8d41124a37b..ecfe9b75bde 100644 --- a/src/main/java/forge/gui/toolbox/FTextField.java +++ b/src/main/java/forge/gui/toolbox/FTextField.java @@ -31,6 +31,7 @@ public class FTextField extends JTextField { public Builder maxLength(int i0) { maxLength = i0; return this; } public Builder readonly(boolean b0) { readonly = b0; return this; } + public Builder readonly() { return readonly(true); } public Builder text(String s0) { text = s0; return this; } public Builder tooltip(String s0) { toolTip = s0; return this; } diff --git a/src/main/java/forge/view/FView.java b/src/main/java/forge/view/FView.java index 40bd6f70337..155d15b9fb7 100644 --- a/src/main/java/forge/view/FView.java +++ b/src/main/java/forge/view/FView.java @@ -163,7 +163,7 @@ public enum FView { } if (hasOldData) { - new DialogMigrateProfile("res", true, new Runnable() { + new DialogMigrateProfile("res", new Runnable() { @Override public void run() { // remove known cruft files, yes this is ugly, but it's also temporary for (String cruftFile : Lists.newArrayList("decks/SkieraCube-cards_not_supported_yet.txt", "decks/cube/ArabianExtended.dck", "decks/cube/GtcGuildBoros.dck", "decks/cube/GtcGuildDimir.dck", "decks/cube/GtcGuildGruul.dck", "decks/cube/GtcGuildOrzhov.dck", "decks/cube/GtcGuildSimic.dck", "decks/cube/GtcPromoBoros.dck", "decks/cube/GtcPromoDimir.dck", "decks/cube/GtcPromoGruul.dck", "decks/cube/GtcPromoOrzhov.dck", "decks/cube/GtcPromoSimic.dck", "decks/cube/JuzamjediCube.dck", "decks/cube/RtRGuildAzorius.dck", "decks/cube/RtRGuildGolgari.dck", "decks/cube/RtRGuildIzzet.dck", "decks/cube/RtRGuildRakdos.dck", "decks/cube/RtRGuildSelesnya.dck", "decks/cube/RtRPromoAzorius.dck", "decks/cube/RtRPromoGolgari.dck", "decks/cube/RtRPromoIzzet.dck", "decks/cube/RtRPromoRakdos.dck", "decks/cube/RtRPromoSelesnya.dck", "decks/cube/SkieraCube.dck", "gauntlet/LOCKED_DotP Preconstructed.dat", "gauntlet/LOCKED_Swimming With Sharks.dat", "layouts/editor_default.xml", "layouts/home_default.xml", "layouts/match_default.xml", "pics/snow_covered_forest1.jpg", "pics/snow_covered_forest2.jpg", "pics/snow_covered_forest3.jpg", "pics/snow_covered_island1.jpg", "pics/snow_covered_island2.jpg", "pics/snow_covered_island3.jpg", "pics/snow_covered_mountain1.jpg", "pics/snow_covered_mountain2.jpg", "pics/snow_covered_mountain3.jpg", "pics/snow_covered_plains1.jpg", "pics/snow_covered_plains2.jpg", "pics/snow_covered_plains3.jpg", "pics/snow_covered_swamp1.jpg", "pics/snow_covered_swamp2.jpg", "pics/snow_covered_swamp3.jpg", "pics/VAN/Birds of Paradise Avatar.full.jpg", "pics/VAN/Erhnam Djinn Avatar.full.jpg", "pics/VAN/Goblin Warchief Avatar.full.jpg", "pics/VAN/Grinning Demon Avatar.full.jpg", "pics/VAN/Platinum Angel Avatar.full.jpg", "pics/VAN/Prodigal Sorcerer Avatar.full.jpg", "pics/VAN/Rith, the Awakener Avatar.full.jpg", "pics/VAN/Royal Assassin Avatar.full.jpg", "pics/VAN/Serra Angel Avatar.full.jpg", "pics/VAN/Tradewind Rider Avatar.full.jpg", "pics_product/10E.jpg", "pics_product/2ED.jpg", "pics_product/3ED.jpg", "pics_product/4ED.jpg", "pics_product/5DN.jpg", "pics_product/5ED.jpg", "pics_product/6ED.jpg", "pics_product/7ED.jpg", "pics_product/8ED.jpg", "pics_product/9ED.jpg", "pics_product/ALA.jpg", "pics_product/ALL.jpg", "pics_product/APC.jpg", "pics_product/ARB.jpg", "pics_product/ARN.jpg", "pics_product/ATQ.jpg", "pics_product/BOK.jpg", "pics_product/CFX.jpg", "pics_product/CHK.jpg", "pics_product/CHR.jpg", "pics_product/CSP.jpg", "pics_product/DIS.jpg", "pics_product/DKA.jpg", "pics_product/DRK.jpg", "pics_product/DST.jpg", "pics_product/EVE.jpg", "pics_product/EXO.jpg", "pics_product/FEM.jpg", "pics_product/FUT.jpg", "pics_product/GPT.jpg", "pics_product/HML.jpg", "pics_product/ICE.jpg", "pics_product/INV.jpg", "pics_product/ISD.jpg", "pics_product/JUD.jpg", "pics_product/LEA.jpg", "pics_product/LEB.jpg", "pics_product/LEG.jpg", "pics_product/LGN.jpg", "pics_product/LRW.jpg", "pics_product/M10.jpg", "pics_product/M11.jpg", "pics_product/M12.jpg", "pics_product/MBS.jpg", "pics_product/MIR.jpg", "pics_product/MMQ.jpg", "pics_product/MOR.jpg", "pics_product/MRD.jpg", "pics_product/NMS.jpg", "pics_product/NPH.jpg", "pics_product/ODY.jpg", "pics_product/ONS.jpg", "pics_product/PCY.jpg", "pics_product/PLC.jpg", "pics_product/PLS.jpg", "pics_product/PO2.jpg", "pics_product/POR.jpg", "pics_product/PTK.jpg", "pics_product/RAV.jpg", "pics_product/ROE.jpg", "pics_product/S99.jpg", "pics_product/SCG.jpg", "pics_product/SHM.jpg", "pics_product/SOK.jpg", "pics_product/SOM.jpg", "pics_product/STH.jpg", "pics_product/TMP.jpg", "pics_product/TOR.jpg", "pics_product/TSP.jpg", "pics_product/UDS.jpg", "pics_product/ULG.jpg", "pics_product/USG.jpg", "pics_product/VIS.jpg", "pics_product/WTH.jpg", "pics_product/WWK.jpg", "pics_product/ZEN.jpg", "pics_product/fatpacks/PLS.JPG", "preferences/.project", "preferences/editor.default.preferences", "preferences/main.properties", "quest/quest.preferences", "quest/quest.properties")) {