diff --git a/.gitattributes b/.gitattributes index 6d3cfec2e8a..0e66b9eb5fe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -705,10 +705,6 @@ forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBoxEvent.java -text forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java -text forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java -text forge-gui-desktop/src/main/java/forge/deckchooser/IDecksComboBoxListener.java -text -forge-gui-desktop/src/main/java/forge/download/GuiDownloadPicturesLQ.java -text -forge-gui-desktop/src/main/java/forge/download/GuiDownloadPrices.java -text -forge-gui-desktop/src/main/java/forge/download/GuiDownloadQuestImages.java -text -forge-gui-desktop/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java -text forge-gui-desktop/src/main/java/forge/download/GuiDownloader.java -text forge-gui-desktop/src/main/java/forge/download/package-info.java -text forge-gui-desktop/src/main/java/forge/error/BugReportDialog.java -text @@ -16331,6 +16327,11 @@ forge-gui/src/main/java/forge/deck/DeckgenUtil.java -text forge-gui/src/main/java/forge/deck/io/DeckHtmlSerializer.java -text forge-gui/src/main/java/forge/deck/io/DeckPreferences.java -text forge-gui/src/main/java/forge/deck/io/OldDeckParser.java -text +forge-gui/src/main/java/forge/download/GuiDownloadPicturesLQ.java -text +forge-gui/src/main/java/forge/download/GuiDownloadPrices.java -text +forge-gui/src/main/java/forge/download/GuiDownloadQuestImages.java -text +forge-gui/src/main/java/forge/download/GuiDownloadService.java -text +forge-gui/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java -text forge-gui/src/main/java/forge/error/BugReporter.java -text forge-gui/src/main/java/forge/error/ExceptionHandler.java svneol=native#text/plain forge-gui/src/main/java/forge/error/package-info.java svneol=native#text/plain @@ -16343,6 +16344,7 @@ forge-gui/src/main/java/forge/gauntlet/GauntletIO.java -text forge-gui/src/main/java/forge/interfaces/IButton.java -text forge-gui/src/main/java/forge/interfaces/IGuiBase.java -text forge-gui/src/main/java/forge/interfaces/IProgressBar.java -text +forge-gui/src/main/java/forge/interfaces/ITextField.java -text forge-gui/src/main/java/forge/itemmanager/ColumnDef.java -text forge-gui/src/main/java/forge/itemmanager/GroupDef.java -text forge-gui/src/main/java/forge/itemmanager/IItemManager.java -text diff --git a/forge-gui-desktop/src/main/java/forge/GuiDesktop.java b/forge-gui-desktop/src/main/java/forge/GuiDesktop.java index 3f080741193..e1c3217a9a0 100644 --- a/forge-gui-desktop/src/main/java/forge/GuiDesktop.java +++ b/forge-gui-desktop/src/main/java/forge/GuiDesktop.java @@ -448,4 +448,9 @@ public class GuiDesktop implements IGuiBase { public void startAltSoundSystem(String filename, boolean isSynchronized) { new AltSoundSystem(filename, isSynchronized).start(); } + + @Override + public void clearImageCache() { + ImageCache.clear(); + } } diff --git a/forge-gui-desktop/src/main/java/forge/download/GuiDownloader.java b/forge-gui-desktop/src/main/java/forge/download/GuiDownloader.java index 6656e9ac23b..832f4dfd680 100644 --- a/forge-gui-desktop/src/main/java/forge/download/GuiDownloader.java +++ b/forge-gui-desktop/src/main/java/forge/download/GuiDownloader.java @@ -17,89 +17,51 @@ */ package forge.download; -import com.esotericsoftware.minlog.Log; - import forge.UiCommand; -import forge.ImageCache; import forge.assets.FSkinProp; -import forge.error.BugReporter; import forge.gui.SOverlayUtils; import forge.toolbox.*; -import forge.util.FileUtil; -import forge.util.MyRandom; import net.miginfocom.swing.MigLayout; -import org.apache.commons.lang3.tuple.Pair; - import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; import java.net.*; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; @SuppressWarnings("serial") -public abstract class GuiDownloader extends DefaultBoundedRangeModel implements Runnable { +public class GuiDownloader extends DefaultBoundedRangeModel { public static final Proxy.Type[] TYPES = Proxy.Type.values(); - // Actions and commands - private final ActionListener actStartDownload = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - // invalidate image cache so newly downloaded images will be loaded - ImageCache.clear(); - new Thread(GuiDownloader.this).start(); - btnStart.setEnabled(false); - } - }; - - private final ActionListener actOK = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - close(); - } - }; - - private final UiCommand cmdClose = new UiCommand() { @Override - public void run() { close(); } }; - // Swing components private final FPanel pnlDialog = new FPanel(new MigLayout("insets 0, gap 0, wrap, ax center, ay center")); private final FProgressBar barProgress = new FProgressBar(); private final FButton btnStart = new FButton("Start"); - private final JTextField txfAddr = new JTextField("Proxy Address"); - private final JTextField txfPort = new JTextField("Proxy Port"); + private final FTextField txtAddress = new FTextField.Builder().ghostText("Proxy Address").build(); + private final FTextField txtPort = new FTextField.Builder().ghostText("Proxy Port").build(); + + private final UiCommand cmdClose = new UiCommand() { + @Override + public void run() { + service.setCancel(true); + + // Kill overlay + SOverlayUtils.hideOverlay(); + } + }; private final FLabel btnClose = new FLabel.Builder().text("X") .hoverable(true).fontAlign(SwingConstants.CENTER).cmdClick(cmdClose).build(); - private final JRadioButton radProxyNone = new FRadioButton("No Proxy"); - private final JRadioButton radProxySocks = new FRadioButton("SOCKS Proxy"); - private final JRadioButton radProxyHTTP = new FRadioButton("HTTP Proxy"); + private final FRadioButton radProxyNone = new FRadioButton("No Proxy"); + private final FRadioButton radProxySocks = new FRadioButton("SOCKS Proxy"); + private final FRadioButton radProxyHTTP = new FRadioButton("HTTP Proxy"); - // Proxy info - private int type; + private final GuiDownloadService service; - // Progress variables - private Map cards; // local path -> url - private boolean cancel; - private final long[] times = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private int tptr = 0; - private int skipped = 0; - private long lTime = System.currentTimeMillis(); + public GuiDownloader(GuiDownloadService service0) { + service = service0; - protected GuiDownloader() { String radConstraints = "w 100%!, h 30px!, gap 2% 0 0 10px"; JXButtonPanel grpPanel = new JXButtonPanel(); grpPanel.add(radProxyNone, radConstraints); @@ -121,8 +83,8 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements // Layout pnlDialog.add(grpPanel, "w 50%!"); - pnlDialog.add(txfAddr, "w 95%!, h 30px!, gap 2% 0 0 10px"); - pnlDialog.add(txfPort, "w 95%!, h 30px!, gap 2% 0 0 10px"); + pnlDialog.add(txtAddress, "w 95%!, h 30px!, gap 2% 0 0 10px"); + pnlDialog.add(txtPort, "w 95%!, h 30px!, gap 2% 0 0 10px"); pnlDialog.add(barProgress, "w 95%!, h 40px!, gap 2% 0 20px 0"); pnlDialog.add(btnStart, "w 200px!, h 40px!, gap 0 0 20px 0, ax center"); pnlDialog.add(btnClose, "w 20px!, h 20px!, pos 370px 10px"); @@ -133,218 +95,15 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements pnl.add(pnlDialog, "w 400px!, h 350px!, ax center, ay center"); SOverlayUtils.showOverlay(); - // Free up the EDT by assembling card list in the background - SwingWorker thrGetImages = new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - try { - GuiDownloader.this.cards = GuiDownloader.this.getNeededImages(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - @Override - protected void done() { - GuiDownloader.this.readyToStart(); - } - }; - - thrGetImages.execute(); - } - - private void readyToStart() { - if (this.cards.isEmpty()) { - barProgress.setString("All items have been downloaded."); - btnStart.setText("OK"); - btnStart.addActionListener(actOK); - } else { - barProgress.setMaximum(this.cards.size()); - barProgress.setString(this.cards.size() == 1 ? "1 item found." : this.cards.size() + " items found."); - //for(Entry kv : cards.entrySet()) System.out.printf("Will get %s from %s%n", kv.getKey(), kv.getValue()); - btnStart.addActionListener(actStartDownload); - } - btnStart.setVisible(true); - - SwingUtilities.invokeLater(new Runnable() { + service.initialize(txtAddress, txtPort, barProgress, btnStart, cmdClose, new Runnable() { @Override public void run() { - btnStart.requestFocusInWindow(); + fireStateChanged(); } }); } - private void setCancel(final boolean cancel) { - this.cancel = cancel; - } - - private void close() { - setCancel(true); - - // Kill overlay - SOverlayUtils.hideOverlay(); - } - - protected final int getAverageTimePerObject() { - int numNonzero = 10; - - if (this.tptr > 9) { - this.tptr = 0; - } - - this.times[this.tptr] = System.currentTimeMillis() - this.lTime; - this.lTime = System.currentTimeMillis(); - - int tTime = 0; - for (int i = 0; i < 10; i++) { - tTime += this.times[i]; - if (this.times[i] == 0) { - numNonzero--; - } - } - - this.tptr++; - return tTime / Math.max(1, numNonzero); - } - - private void update(final int card, final File dest) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - GuiDownloader.this.fireStateChanged(); - - final StringBuilder sb = new StringBuilder(); - - final int a = GuiDownloader.this.getAverageTimePerObject(); - - if (card != GuiDownloader.this.cards.size()) { - sb.append(card + "/" + GuiDownloader.this.cards.size() + " - "); - - long t2Go = (GuiDownloader.this.cards.size() - card) * a; - - if (t2Go > 3600000) { - sb.append(String.format("%02d:", t2Go / 3600000)); - t2Go = t2Go % 3600000; - } - if (t2Go > 60000) { - sb.append(String.format("%02d:", t2Go / 60000)); - t2Go = t2Go % 60000; - } else { - sb.append("00:"); - } - - sb.append(String.format("%02d remaining.", t2Go / 1000)); - } else { - sb.append(String.format("%d of %d items finished! Skipped " + skipped + " items. Please close!", - card, GuiDownloader.this.cards.size())); - btnStart.setText("OK"); - btnStart.addActionListener(actOK); - btnStart.setEnabled(true); - btnStart.requestFocusInWindow(); - } - - GuiDownloader.this.barProgress.setString(sb.toString()); - System.out.println(card + "/" + GuiDownloader.this.cards.size() + " - " + dest); - } - }); - } - - @Override - public final void run() { - final Random r = MyRandom.getRandom(); - - Proxy p = null; - if (this.type == 0) { - p = Proxy.NO_PROXY; - } else { - try { - p = new Proxy(GuiDownloader.TYPES[this.type], new InetSocketAddress(this.txfAddr.getText(), - Integer.parseInt(this.txfPort.getText()))); - } catch (final Exception ex) { - BugReporter.reportException(ex, - "Proxy connection could not be established!\nProxy address: %s\nProxy port: %s", - this.txfAddr.getText(), this.txfPort.getText()); - return; - } - } - - int iCard = 0; - for(Entry kv : cards.entrySet()) { - if( cancel ) - break; - - String url = kv.getValue(); - final File fileDest = new File(kv.getKey()); - final File base = fileDest.getParentFile(); - - ReadableByteChannel rbc = null; - FileOutputStream fos = null; - try { - // test for folder existence - if (!base.exists() && !base.mkdir()) { // create folder if not found - System.out.println("Can't create folder" + base.getAbsolutePath()); - } - - URL imageUrl = new URL(url); - HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection(p); - // don't allow redirections here -- they indicate 'file not found' on the server - conn.setInstanceFollowRedirects(false); - conn.connect(); - - if (conn.getResponseCode() != 200) { - conn.disconnect(); - System.out.println("Skipped Download for: " + fileDest.getPath()); - update(++iCard, fileDest); - skipped++; - continue; - } - - rbc = Channels.newChannel(conn.getInputStream()); - fos = new FileOutputStream(fileDest); - fos.getChannel().transferFrom(rbc, 0, 1 << 24); - } catch (final ConnectException ce) { - System.out.println("Connection refused for url: " + url); - } catch (final MalformedURLException mURLe) { - System.out.println("Error - possibly missing URL for: " + fileDest.getName()); - } catch (final FileNotFoundException fnfe) { - String formatStr = "Error - the LQ picture %s could not be found on the server. [%s] - %s"; - System.out.println(String.format(formatStr, fileDest.getName(), url, fnfe.getMessage())); - } catch (final Exception ex) { - Log.error("LQ Pictures", "Error downloading pictures", ex); - } finally { - if (null != rbc) { - try { rbc.close(); } catch (IOException e) { System.out.println("error closing input stream"); } - } - if (null != fos) { - try { fos.close(); } catch (IOException e) { System.out.println("error closing output stream"); } - } - } - - update(++iCard, fileDest); - - // throttle to reduce load on the server - try { - Thread.sleep(r.nextInt(50) + 50); - } catch (final InterruptedException e) { - Log.error("GuiDownloader", "Sleep Error", e); - } - } // for - } - - protected abstract Map getNeededImages(); - - protected static void addMissingItems(Map list, String nameUrlFile, String dir) { - for (Pair nameUrlPair : FileUtil.readNameUrlFile(nameUrlFile)) { - File f = new File(dir, nameUrlPair.getLeft()); - //System.out.println(f.getAbsolutePath()); - if (!f.exists()) { - list.put(f.getAbsolutePath(), nameUrlPair.getRight()); - } - } - } - - protected class ProxyHandler implements ChangeListener { + private class ProxyHandler implements ChangeListener { private final int type; public ProxyHandler(final int type) { @@ -354,9 +113,9 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements @Override public final void stateChanged(final ChangeEvent e) { if (((AbstractButton) e.getSource()).isSelected()) { - GuiDownloader.this.type = this.type; - GuiDownloader.this.txfAddr.setEnabled(this.type != 0); - GuiDownloader.this.txfPort.setEnabled(this.type != 0); + service.setType(this.type); + txtAddress.setEnabled(this.type != 0); + txtPort.setEnabled(this.type != 0); } } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java index 0a8c5c48b48..371ecce6bd5 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuDownloaders.java @@ -5,6 +5,7 @@ import forge.download.GuiDownloadPicturesLQ; import forge.download.GuiDownloadPrices; import forge.download.GuiDownloadQuestImages; import forge.download.GuiDownloadSetPicturesLQ; +import forge.download.GuiDownloader; import forge.error.BugReporter; import forge.gui.ImportDialog; import forge.gui.framework.ICDoc; @@ -24,13 +25,13 @@ public enum CSubmenuDownloaders implements ICDoc { private final UiCommand cmdLicensing = new UiCommand() { @Override public void run() { VSubmenuDownloaders.SINGLETON_INSTANCE.showLicensing(); } }; private final UiCommand cmdPicDownload = new UiCommand() { @Override - public void run() { new GuiDownloadPicturesLQ(); } }; + public void run() { new GuiDownloader(new GuiDownloadPicturesLQ()); } }; private final UiCommand cmdSetDownload = new UiCommand() { @Override - public void run() { new GuiDownloadSetPicturesLQ(); } }; + public void run() { new GuiDownloader(new GuiDownloadSetPicturesLQ()); } }; private final UiCommand cmdQuestImages = new UiCommand() { @Override - public void run() { new GuiDownloadQuestImages(); } }; + public void run() { new GuiDownloader(new GuiDownloadQuestImages()); } }; private final UiCommand cmdDownloadPrices = new UiCommand() { @Override - public void run() { new GuiDownloadPrices(); } }; + public void run() { new GuiDownloader(new GuiDownloadPrices()); } }; private final UiCommand cmdHowToPlay = new UiCommand() { @Override public void run() { VSubmenuDownloaders.SINGLETON_INSTANCE.showHowToPlay(); } }; private final UiCommand cmdImportPictures = new UiCommand() { @Override diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FButton.java b/forge-gui-desktop/src/main/java/forge/toolbox/FButton.java index fb277868d94..3bd164a97d0 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FButton.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FButton.java @@ -17,6 +17,7 @@ */ package forge.toolbox; +import forge.UiCommand; import forge.assets.FSkinProp; import forge.gui.framework.ILocalRepaint; import forge.interfaces.IButton; @@ -237,4 +238,14 @@ public class FButton extends SkinnedButton implements ILocalRepaint, IButton { super.paintComponent(g); } + + @Override + public void setCommand(final UiCommand command) { + addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + command.run(); + } + }); + } } diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FLabel.java b/forge-gui-desktop/src/main/java/forge/toolbox/FLabel.java index a9eb06be0df..5857359e3c9 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FLabel.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FLabel.java @@ -627,4 +627,9 @@ public class FLabel extends SkinnedLabel implements ILocalRepaint, IButton { super.setIcon(new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH))); } } + + @Override + public void setCommand(UiCommand command0) { + cmdClick = command0; + } } diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FTextField.java b/forge-gui-desktop/src/main/java/forge/toolbox/FTextField.java index 983b65bb8de..7c714b9215e 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FTextField.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FTextField.java @@ -1,6 +1,7 @@ package forge.toolbox; import forge.gui.MouseUtil; +import forge.interfaces.ITextField; import forge.toolbox.FSkin.SkinnedTextField; import javax.swing.*; @@ -19,7 +20,7 @@ import java.awt.event.FocusEvent; * */ @SuppressWarnings("serial") -public class FTextField extends SkinnedTextField { +public class FTextField extends SkinnedTextField implements ITextField { /** * Uses the Builder pattern to facilitate/encourage inline styling. * Credit to Effective Java 2 (Joshua Bloch). diff --git a/forge-gui-desktop/src/test/java/forge/GuiDownloadPicturesLQTest.java b/forge-gui-desktop/src/test/java/forge/GuiDownloadPicturesLQTest.java index 8c2da0ff7c3..8c6b49a595c 100644 --- a/forge-gui-desktop/src/test/java/forge/GuiDownloadPicturesLQTest.java +++ b/forge-gui-desktop/src/test/java/forge/GuiDownloadPicturesLQTest.java @@ -1,6 +1,7 @@ package forge; import forge.download.GuiDownloadPicturesLQ; +import forge.download.GuiDownloader; import org.testng.annotations.Test; @@ -15,6 +16,6 @@ public class GuiDownloadPicturesLQTest { */ @Test(enabled = false, timeOut = 1000) public void guiDownloadPicturesTest1() { - new GuiDownloadPicturesLQ(); + new GuiDownloader(new GuiDownloadPicturesLQ()); } } diff --git a/forge-gui-desktop/src/test/java/forge/GuiDownloadSetPicturesLQTest.java b/forge-gui-desktop/src/test/java/forge/GuiDownloadSetPicturesLQTest.java index f92c20c243d..6dd44864aa3 100644 --- a/forge-gui-desktop/src/test/java/forge/GuiDownloadSetPicturesLQTest.java +++ b/forge-gui-desktop/src/test/java/forge/GuiDownloadSetPicturesLQTest.java @@ -1,6 +1,7 @@ package forge; import forge.download.GuiDownloadSetPicturesLQ; +import forge.download.GuiDownloader; import org.testng.annotations.Test; @@ -15,6 +16,6 @@ public class GuiDownloadSetPicturesLQTest { */ @Test(enabled = false, timeOut = 1000) public void g() { - new GuiDownloadSetPicturesLQ(); + new GuiDownloader(new GuiDownloadSetPicturesLQ()); } } diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 20e85624093..d7c3e4d38c3 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -41,6 +41,8 @@ import forge.toolbox.FOverlay; import forge.util.Utils; public class Forge implements ApplicationListener { + public static final String CURRENT_VERSION = "1.5.19.001"; + private static Clipboard clipboard; private static int screenWidth; private static int screenHeight; diff --git a/forge-gui-mobile/src/forge/GuiMobile.java b/forge-gui-mobile/src/forge/GuiMobile.java index e8396600d73..b385edaa01c 100644 --- a/forge-gui-mobile/src/forge/GuiMobile.java +++ b/forge-gui-mobile/src/forge/GuiMobile.java @@ -17,6 +17,7 @@ import forge.assets.FSkin; import forge.assets.FSkinProp; import forge.assets.FTextureImage; import forge.assets.ISkinImage; +import forge.assets.ImageCache; import forge.deck.Deck; import forge.deck.FDeckViewer; import forge.error.BugReportDialog; @@ -373,4 +374,9 @@ public class GuiMobile implements IGuiBase { public void startAltSoundSystem(String filename, boolean isSynchronized) { //TODO: Support alt sound system } + + @Override + public void clearImageCache() { + ImageCache.clear(); + } } diff --git a/forge-gui-mobile/src/forge/toolbox/FButton.java b/forge-gui-mobile/src/forge/toolbox/FButton.java index 48e489bf0a0..f691c3373e2 100644 --- a/forge-gui-mobile/src/forge/toolbox/FButton.java +++ b/forge-gui-mobile/src/forge/toolbox/FButton.java @@ -6,6 +6,7 @@ import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; +import forge.UiCommand; import forge.Forge.Graphics; import forge.assets.FSkinColor; import forge.assets.FSkinColor.Colors; @@ -228,4 +229,20 @@ public class FButton extends FDisplayObject implements IButton { } return false; } + + //use FEventHandler one except when references as IButton + @Override + public void setCommand(final UiCommand command0) { + setCommand(new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + command0.run(); + } + }); + } + + @Override + public boolean requestFocusInWindow() { + return false; + } } diff --git a/forge-gui-mobile/src/forge/toolbox/FLabel.java b/forge-gui-mobile/src/forge/toolbox/FLabel.java index c172c82ee01..7e5188a5241 100644 --- a/forge-gui-mobile/src/forge/toolbox/FLabel.java +++ b/forge-gui-mobile/src/forge/toolbox/FLabel.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; import com.badlogic.gdx.math.Vector2; import forge.Forge.Graphics; +import forge.UiCommand; import forge.assets.FImage; import forge.assets.FSkinColor; import forge.assets.FSkinColor.Colors; @@ -311,4 +312,20 @@ public class FLabel extends FDisplayObject implements IButton { g.endClip(); } } + + //use FEventHandler one except when references as IButton + @Override + public void setCommand(final UiCommand command0) { + setCommand(new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + command0.run(); + } + }); + } + + @Override + public boolean requestFocusInWindow() { + return false; + } } diff --git a/forge-gui-mobile/src/forge/toolbox/FTextField.java b/forge-gui-mobile/src/forge/toolbox/FTextField.java index 314a7b887fd..f4c8225d290 100644 --- a/forge-gui-mobile/src/forge/toolbox/FTextField.java +++ b/forge-gui-mobile/src/forge/toolbox/FTextField.java @@ -9,11 +9,12 @@ import forge.Forge.KeyInputAdapter; import forge.assets.FSkinColor; import forge.assets.FSkinFont; import forge.assets.FSkinColor.Colors; +import forge.interfaces.ITextField; import forge.toolbox.FEvent.FEventHandler; import forge.toolbox.FEvent.FEventType; import forge.util.Utils; -public class FTextField extends FDisplayObject { +public class FTextField extends FDisplayObject implements ITextField { private static final FSkinFont DEFAULT_FONT = FSkinFont.get(14); private static final float BORDER_THICKNESS = Utils.scaleX(1); protected static final float PADDING = Utils.scaleX(5); @@ -330,4 +331,9 @@ public class FTextField extends FDisplayObject { protected float getRightPadding() { return PADDING; } + + @Override + public boolean requestFocusInWindow() { + return false; + } } diff --git a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadPicturesLQ.java b/forge-gui/src/main/java/forge/download/GuiDownloadPicturesLQ.java similarity index 90% rename from forge-gui-desktop/src/main/java/forge/download/GuiDownloadPicturesLQ.java rename to forge-gui/src/main/java/forge/download/GuiDownloadPicturesLQ.java index 4d2dc692720..38047c8e442 100644 --- a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadPicturesLQ.java +++ b/forge-gui/src/main/java/forge/download/GuiDownloadPicturesLQ.java @@ -29,14 +29,9 @@ import java.io.File; import java.util.Map; import java.util.TreeMap; -@SuppressWarnings("serial") -public class GuiDownloadPicturesLQ extends GuiDownloader { - public GuiDownloadPicturesLQ() { - super(); - } - +public class GuiDownloadPicturesLQ extends GuiDownloadService { @Override - protected final Map getNeededImages() { + protected final Map getNeededFiles() { Map downloads = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (PaperCard c : FModel.getMagicDb().getCommonCards().getAllCards()) { diff --git a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadPrices.java b/forge-gui/src/main/java/forge/download/GuiDownloadPrices.java similarity index 81% rename from forge-gui-desktop/src/main/java/forge/download/GuiDownloadPrices.java rename to forge-gui/src/main/java/forge/download/GuiDownloadPrices.java index 2b16679607f..9423661e45b 100644 --- a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadPrices.java +++ b/forge-gui/src/main/java/forge/download/GuiDownloadPrices.java @@ -22,14 +22,9 @@ import forge.properties.ForgeConstants; import java.util.HashMap; import java.util.Map; -@SuppressWarnings("serial") -public class GuiDownloadPrices extends GuiDownloader { - public GuiDownloadPrices() { - super(); - } - +public class GuiDownloadPrices extends GuiDownloadService { @Override - protected Map getNeededImages() { + protected Map getNeededFiles() { Map result = new HashMap(); result.put(ForgeConstants.QUEST_CARD_PRICE_FILE, ForgeConstants.URL_PRICE_DOWNLOAD); return result; diff --git a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadQuestImages.java b/forge-gui/src/main/java/forge/download/GuiDownloadQuestImages.java similarity index 81% rename from forge-gui-desktop/src/main/java/forge/download/GuiDownloadQuestImages.java rename to forge-gui/src/main/java/forge/download/GuiDownloadQuestImages.java index e4d0ad93584..f6aef5e331a 100644 --- a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadQuestImages.java +++ b/forge-gui/src/main/java/forge/download/GuiDownloadQuestImages.java @@ -22,27 +22,9 @@ import forge.properties.ForgeConstants; import java.util.Map; import java.util.TreeMap; -/** */ -@SuppressWarnings("serial") -public class GuiDownloadQuestImages extends GuiDownloader { - /** - *

- * Constructor for GuiDownloadQuestImages. - *

- */ - public GuiDownloadQuestImages() { - super(); - } - - /** - *

- * getNeededCards. - *

- * - * @return an array of {@link forge.download.GuiDownloadSetPicturesLQ} objects. - */ +public class GuiDownloadQuestImages extends GuiDownloadService { @Override - protected final Map getNeededImages() { + protected final Map getNeededFiles() { // read all card names and urls final Map urls = new TreeMap(String.CASE_INSENSITIVE_ORDER); diff --git a/forge-gui/src/main/java/forge/download/GuiDownloadService.java b/forge-gui/src/main/java/forge/download/GuiDownloadService.java new file mode 100644 index 00000000000..3ff03907232 --- /dev/null +++ b/forge-gui/src/main/java/forge/download/GuiDownloadService.java @@ -0,0 +1,303 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.download; + +import com.esotericsoftware.minlog.Log; + +import forge.FThreads; +import forge.GuiBase; +import forge.UiCommand; +import forge.error.BugReporter; +import forge.interfaces.IButton; +import forge.interfaces.IProgressBar; +import forge.interfaces.ITextField; +import forge.util.FileUtil; +import forge.util.MyRandom; +import forge.util.ThreadUtil; + +import org.apache.commons.lang3.tuple.Pair; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.*; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; + +@SuppressWarnings("serial") +public abstract class GuiDownloadService implements Runnable { + public static final Proxy.Type[] TYPES = Proxy.Type.values(); + + //Components passed from GUI component displaying download + private ITextField txtAddress; + private ITextField txtPort; + private IProgressBar barProgress; + private IButton btnStart; + private UiCommand cmdClose; + private Runnable onUpdate; + + private final UiCommand cmdStartDownload = new UiCommand() { + @Override + public void run() { + //invalidate image cache so newly downloaded images will be loaded + GuiBase.getInterface().clearImageCache(); + ThreadUtil.invokeInGameThread(this); + btnStart.setEnabled(false); + } + }; + + // Proxy info + private int type; + + // Progress variables + private Map cards; // local path -> url + private boolean cancel; + private final long[] times = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + private int tptr = 0; + private int skipped = 0; + private long lTime = System.currentTimeMillis(); + + protected GuiDownloadService() { + } + + public void initialize(ITextField txtAddress0, ITextField txtPort0, IProgressBar barProgress0, IButton btnStart0, UiCommand cmdClose0, Runnable onUpdate0) { + txtAddress = txtAddress0; + txtPort = txtPort0; + barProgress = barProgress0; + btnStart = btnStart0; + cmdClose = cmdClose0; + onUpdate = onUpdate0; + + // Free up the EDT by assembling card list on the game thread + ThreadUtil.invokeInGameThread(new Runnable() { + @Override + public void run() { + try { + cards = getNeededFiles(); + } + catch (Exception e) { + e.printStackTrace(); + } + FThreads.invokeInEdtLater(new Runnable() { + @Override + public void run() { + readyToStart(); + } + }); + } + }); + } + + private void readyToStart() { + if (cards.isEmpty()) { + barProgress.setDescription("All items have been downloaded."); + btnStart.setText("OK"); + btnStart.setCommand(cmdClose); + } + else { + barProgress.setMaximum(cards.size()); + barProgress.setDescription(cards.size() == 1 ? "1 item found." : cards.size() + " items found."); + //for(Entry kv : cards.entrySet()) System.out.printf("Will get %s from %s%n", kv.getKey(), kv.getValue()); + btnStart.setCommand(cmdStartDownload); + } + btnStart.setVisible(true); + + FThreads.invokeInEdtLater(new Runnable() { + @Override + public void run() { + btnStart.requestFocusInWindow(); + } + }); + } + + public void setType(int type0) { + type = type0; + } + + public void setCancel(boolean cancel0) { + cancel = cancel0; + } + + protected final int getAverageTimePerObject() { + int numNonzero = 10; + + if (tptr > 9) { + tptr = 0; + } + + times[tptr] = System.currentTimeMillis() - lTime; + lTime = System.currentTimeMillis(); + + int tTime = 0; + for (int i = 0; i < 10; i++) { + tTime += times[i]; + if (times[i] == 0) { + numNonzero--; + } + } + + tptr++; + return tTime / Math.max(1, numNonzero); + } + + private void update(final int card, final File dest) { + FThreads.invokeInEdtLater(new Runnable() { + @Override + public void run() { + onUpdate.run(); + + final StringBuilder sb = new StringBuilder(); + + final int a = getAverageTimePerObject(); + + if (card != cards.size()) { + sb.append(card + "/" + cards.size() + " - "); + + long t2Go = (cards.size() - card) * a; + + if (t2Go > 3600000) { + sb.append(String.format("%02d:", t2Go / 3600000)); + t2Go = t2Go % 3600000; + } + if (t2Go > 60000) { + sb.append(String.format("%02d:", t2Go / 60000)); + t2Go = t2Go % 60000; + } else { + sb.append("00:"); + } + + sb.append(String.format("%02d remaining.", t2Go / 1000)); + } + else { + sb.append(String.format("%d of %d items finished! Skipped " + skipped + " items. Please close!", + card, cards.size())); + btnStart.setText("OK"); + btnStart.setCommand(cmdClose); + btnStart.setEnabled(true); + btnStart.requestFocusInWindow(); + } + + barProgress.setDescription(sb.toString()); + System.out.println(card + "/" + cards.size() + " - " + dest); + } + }); + } + + @Override + public final void run() { + final Random r = MyRandom.getRandom(); + + Proxy p = null; + if (type == 0) { + p = Proxy.NO_PROXY; + } + else { + try { + p = new Proxy(TYPES[type], new InetSocketAddress(txtAddress.getText(), Integer.parseInt(txtPort.getText()))); + } + catch (final Exception ex) { + BugReporter.reportException(ex, + "Proxy connection could not be established!\nProxy address: %s\nProxy port: %s", + txtAddress.getText(), txtPort.getText()); + return; + } + } + + int iCard = 0; + for (Entry kv : cards.entrySet()) { + if (cancel) { break; } + + String url = kv.getValue(); + final File fileDest = new File(kv.getKey()); + final File base = fileDest.getParentFile(); + + ReadableByteChannel rbc = null; + FileOutputStream fos = null; + try { + // test for folder existence + if (!base.exists() && !base.mkdir()) { // create folder if not found + System.out.println("Can't create folder" + base.getAbsolutePath()); + } + + URL imageUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection(p); + // don't allow redirections here -- they indicate 'file not found' on the server + conn.setInstanceFollowRedirects(false); + conn.connect(); + + if (conn.getResponseCode() != 200) { + conn.disconnect(); + System.out.println("Skipped Download for: " + fileDest.getPath()); + update(++iCard, fileDest); + skipped++; + continue; + } + + rbc = Channels.newChannel(conn.getInputStream()); + fos = new FileOutputStream(fileDest); + fos.getChannel().transferFrom(rbc, 0, 1 << 24); + } + catch (final ConnectException ce) { + System.out.println("Connection refused for url: " + url); + } + catch (final MalformedURLException mURLe) { + System.out.println("Error - possibly missing URL for: " + fileDest.getName()); + } + catch (final FileNotFoundException fnfe) { + String formatStr = "Error - the LQ picture %s could not be found on the server. [%s] - %s"; + System.out.println(String.format(formatStr, fileDest.getName(), url, fnfe.getMessage())); + } + catch (final Exception ex) { + Log.error("LQ Pictures", "Error downloading pictures", ex); + } + finally { + if (null != rbc) { + try { rbc.close(); } catch (IOException e) { System.out.println("error closing input stream"); } + } + if (null != fos) { + try { fos.close(); } catch (IOException e) { System.out.println("error closing output stream"); } + } + } + + update(++iCard, fileDest); + + // throttle to reduce load on the server + try { + Thread.sleep(r.nextInt(50) + 50); + } catch (final InterruptedException e) { + Log.error("GuiDownloader", "Sleep Error", e); + } + } + } + + protected abstract Map getNeededFiles(); + + protected static void addMissingItems(Map list, String nameUrlFile, String dir) { + for (Pair nameUrlPair : FileUtil.readNameUrlFile(nameUrlFile)) { + File f = new File(dir, nameUrlPair.getLeft()); + //System.out.println(f.getAbsolutePath()); + if (!f.exists()) { + list.put(f.getAbsolutePath(), nameUrlPair.getRight()); + } + } + } +} diff --git a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java b/forge-gui/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java similarity index 89% rename from forge-gui-desktop/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java rename to forge-gui/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java index 69244c66f82..7639445ba0b 100644 --- a/forge-gui-desktop/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java +++ b/forge-gui/src/main/java/forge/download/GuiDownloadSetPicturesLQ.java @@ -31,14 +31,9 @@ import java.io.File; import java.util.Map; import java.util.TreeMap; -@SuppressWarnings("serial") -public class GuiDownloadSetPicturesLQ extends GuiDownloader { - public GuiDownloadSetPicturesLQ() { - super(); - } - +public class GuiDownloadSetPicturesLQ extends GuiDownloadService { @Override - protected final Map getNeededImages() { + protected final Map getNeededFiles() { Map downloads = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (final PaperCard c : Iterables.concat(FModel.getMagicDb().getCommonCards().getAllCards(), FModel.getMagicDb().getVariantCards().getAllCards())) { diff --git a/forge-gui/src/main/java/forge/interfaces/IButton.java b/forge-gui/src/main/java/forge/interfaces/IButton.java index 87ef7272aee..dc710354d1a 100644 --- a/forge-gui/src/main/java/forge/interfaces/IButton.java +++ b/forge-gui/src/main/java/forge/interfaces/IButton.java @@ -1,10 +1,16 @@ package forge.interfaces; +import forge.UiCommand; + public interface IButton { boolean isEnabled(); void setEnabled(boolean b0); + boolean isVisible(); + void setVisible(boolean b0); String getText(); void setText(String text0); boolean isSelected(); void setSelected(boolean b0); + boolean requestFocusInWindow(); + void setCommand(UiCommand command0); } diff --git a/forge-gui/src/main/java/forge/interfaces/IGuiBase.java b/forge-gui/src/main/java/forge/interfaces/IGuiBase.java index ffde8a697eb..cffaee449e9 100644 --- a/forge-gui/src/main/java/forge/interfaces/IGuiBase.java +++ b/forge-gui/src/main/java/forge/interfaces/IGuiBase.java @@ -87,4 +87,5 @@ public interface IGuiBase { ForgeProfileProperties getProfileProps(); IAudioClip createAudioClip(String filename); void startAltSoundSystem(String filename, boolean isSynchronized); + void clearImageCache(); } \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/interfaces/ITextField.java b/forge-gui/src/main/java/forge/interfaces/ITextField.java new file mode 100644 index 00000000000..7d35f61b9cc --- /dev/null +++ b/forge-gui/src/main/java/forge/interfaces/ITextField.java @@ -0,0 +1,11 @@ +package forge.interfaces; + +public interface ITextField { + boolean isEnabled(); + void setEnabled(boolean b0); + boolean isVisible(); + void setVisible(boolean b0); + String getText(); + void setText(String text0); + boolean requestFocusInWindow(); +}