diff --git a/src/main/java/forge/CardReader.java b/src/main/java/forge/CardReader.java index d91cc2ec2f3..ed9bad3a935 100644 --- a/src/main/java/forge/CardReader.java +++ b/src/main/java/forge/CardReader.java @@ -35,6 +35,8 @@ import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import javax.swing.SwingUtilities; + import net.slightlymagic.braids.util.UtilFunctions; import net.slightlymagic.braids.util.generator.FindNonDirectoriesSkipDotDirectoriesGenerator; import net.slightlymagic.braids.util.generator.GeneratorFunctions; @@ -273,8 +275,11 @@ public class CardReader implements Runnable { } if (barProgress != null) { - barProgress.setMaximum((int) this.estimatedFilesRemaining); - barProgress.setDescription("Preloading card images: "); + barProgress.setMaximum((int) estimatedFilesRemaining); + SwingUtilities.invokeLater(new Runnable() { @Override + public void run() { + barProgress.setDescription("Preloading card images: "); + } }); } for (final File cardTxtFile : this.findNonDirsIterable) { diff --git a/src/main/java/forge/card/cardfactory/PreloadingCardFactory.java b/src/main/java/forge/card/cardfactory/PreloadingCardFactory.java index d61302c1ef2..6240ac81eca 100644 --- a/src/main/java/forge/card/cardfactory/PreloadingCardFactory.java +++ b/src/main/java/forge/card/cardfactory/PreloadingCardFactory.java @@ -30,6 +30,7 @@ import forge.CardReader; import forge.Singletons; import forge.card.CardRules; import forge.error.ErrorViewer; +import forge.gui.GuiUtils; import forge.item.CardDb; import forge.properties.ForgeProps; import forge.properties.NewConstants; @@ -76,6 +77,7 @@ public class PreloadingCardFactory extends AbstractCardFactory { */ public PreloadingCardFactory(final File file) { super(file); + GuiUtils.checkEDT("PreloadingCardFactory$constructor", false); try { this.readCards(file); diff --git a/src/main/java/forge/control/home/ControlSettings.java b/src/main/java/forge/control/home/ControlSettings.java index 2a195c6627a..8d00d06aba9 100644 --- a/src/main/java/forge/control/home/ControlSettings.java +++ b/src/main/java/forge/control/home/ControlSettings.java @@ -155,7 +155,7 @@ public class ControlSettings { FSkin skin = new FSkin(name); - skin.loadFontsAndImages(); + skin.load(); prefs.setPref(FPref.UI_SKIN, name); Singletons.getView().setSkin(skin); diff --git a/src/main/java/forge/gui/GuiUtils.java b/src/main/java/forge/gui/GuiUtils.java index 463f57f1046..1faced72847 100644 --- a/src/main/java/forge/gui/GuiUtils.java +++ b/src/main/java/forge/gui/GuiUtils.java @@ -35,6 +35,7 @@ import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -411,7 +412,8 @@ public final class GuiUtils { } /** Duplicate in DeckEditorQuestMenu and - * probably elsewhere...can streamline at some point. + * probably elsewhere...can streamline at some point + * (probably shouldn't be here). * * @param in   {@link java.lang.String} * @return {@link java.lang.String} @@ -428,4 +430,24 @@ public final class GuiUtils { return out.toString(); } + + /** Checks if calling method uses event dispatch thread. + * Exception thrown if method is on "wrong" thread. + * A boolean is passed to indicate if the method must be EDT or not. + * + * @param methodName   String, part of the custom exception message. + * @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT + */ + public static void checkEDT(String methodName, boolean mustBeEDT) { + boolean isEDT = SwingUtilities.isEventDispatchThread(); + + if (!isEDT && mustBeEDT) { + throw new IllegalStateException( + methodName + " must be accessed from the event dispatch thread."); + } + else if (isEDT && !mustBeEDT) { + throw new IllegalStateException( + methodName + " may not be accessed from the event dispatch thread."); + } + } } diff --git a/src/main/java/forge/view/FView.java b/src/main/java/forge/view/FView.java index 10e945d1742..9b5e469326f 100644 --- a/src/main/java/forge/view/FView.java +++ b/src/main/java/forge/view/FView.java @@ -105,16 +105,15 @@ public class FView { AllZone.getCardFactory(); // Preloads skin components (using progress bar). - FView.this.skin.loadFontsAndImages(); + FView.this.skin.load(); - // Does not use progress bar, due to be deprecated in favor of skin. + // Does not use progress bar, due to be deprecated with battlefield refactoring. CardFaceSymbols.loadImages(); - barProgress.setDescription("Creating display components."); - SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + barProgress.setDescription("Creating display components."); final GuiTopLevel g = new GuiTopLevel(); AllZone.setDisplay(g); g.getController().changeState(FControl.HOME_SCREEN); diff --git a/src/main/java/forge/view/toolbox/FProgressBar.java b/src/main/java/forge/view/toolbox/FProgressBar.java index ffdc5bbe28b..2c4da4412d4 100644 --- a/src/main/java/forge/view/toolbox/FProgressBar.java +++ b/src/main/java/forge/view/toolbox/FProgressBar.java @@ -1,28 +1,25 @@ package forge.view.toolbox; - import java.util.Date; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; +import forge.gui.GuiUtils; + /** * A simple progress bar component using the Forge skin. * * Can show * */ +@SuppressWarnings("serial") public class FProgressBar extends JProgressBar { - private static final long serialVersionUID = 3479715871723156426L; - private int tempVal = 0; - private long startMillis = 0; - private long tempMillis = 0; + private long startMillis = 0, tempMillis = 0; private float timePerUnit = 0; - private int eta = 0; - private boolean isIncrementing = false; + private int tempVal = 0, etaMillis = 0; private int hours, minutes, seconds; - private String desc = ""; - private String str = ""; + private String desc = "", count = "", eta = ""; private boolean showETA = true; private boolean showCount = true; @@ -33,58 +30,43 @@ public class FProgressBar extends JProgressBar { this.setStringPainted(true); } - /** @param s0   A description to prepend before statistics. */ + /** + * Sets description on bar. Must be called from EDT. + * + * @param s0   A description to prepend before statistics. + */ public void setDescription(final String s0) { + GuiUtils.checkEDT("FProgressBar$setDescription", true); this.desc = s0; this.setString(s0); } - /** */ + /** Increments bar, thread safe. Calculations executed on separate thread. */ public void increment() { - if (isIncrementing) { System.out.println("Rejected."); return; } + final Runnable r = new Runnable() { + @Override + public void run() { + tempVal++; + count = (showCount ? " " + tempVal + " of " + getMaximum() : ""); + eta = (showETA ? calculateETA(tempVal) : ""); - isIncrementing = true; - tempVal++; - this.setValue(tempVal); - str = desc; - if (showCount) { calculateCount(tempVal); } - if (showETA) { calculateETA(tempVal); } - updateString(); - isIncrementing = false; + // When calculations finished; EDT can be used. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + FProgressBar.this.setValue(tempVal); + updateString(); + } + }); + } + }; + + r.run(); } - private void calculateCount(int v0) { - str += " " + v0 + " of " + this.getMaximum(); - } - - /** */ - private void calculateETA(int v0) { - tempMillis = new Date().getTime(); - timePerUnit = (tempMillis - startMillis) / (float) v0; - eta = (int) ((this.getMaximum() - v0) * timePerUnit) / 1000; - - seconds = eta; - hours = seconds >= 3600 ? (seconds / 3600) : 0; - seconds = eta % 3600; - minutes = seconds >= 60 ? (seconds / 60) : 0; - seconds = eta % 60 + 1; - - str += ", ETA " + String.format("%02d", hours) + ":" - + String.format("%02d", minutes) + ":" - + String.format("%02d", seconds); - } - - private void updateString() { - this.setString(str); - } - - /** Resets the various values required for this class. */ + /** Resets the various values required for this class. Must be called from EDT. */ public void reset() { - if (!SwingUtilities.isEventDispatchThread()) { - throw new IllegalStateException( - "FProgressBar > reset() must be accessed from an event dispatch thread."); - } - + GuiUtils.checkEDT("FProgressBar$reset", true); this.setIndeterminate(true); this.setValue(0); this.tempVal = 0; @@ -99,18 +81,30 @@ public class FProgressBar extends JProgressBar { this.showETA = b0; } - /** @return b0   Boolean, show the ETA statistic or not */ - public boolean isShowETA() { - return showETA; - } - /** @param b0   Boolean, show the ETA statistic or not */ public void setShowCount(boolean b0) { this.showCount = b0; } - /** @return b0   Boolean, show the ETA statistic or not */ - public boolean isShowCount() { - return showCount; + /** */ + private String calculateETA(int v0) { + GuiUtils.checkEDT("FProgressBar$calculateETA", false); + tempMillis = new Date().getTime(); + timePerUnit = (tempMillis - startMillis) / (float) v0; + etaMillis = (int) ((this.getMaximum() - v0) * timePerUnit) / 1000; + + seconds = etaMillis; + hours = seconds >= 3600 ? (seconds / 3600) : 0; + seconds = etaMillis % 3600; + minutes = seconds >= 60 ? (seconds / 60) : 0; + seconds = etaMillis % 60 + 1; + + return ", ETA " + String.format("%02d", hours) + ":" + + String.format("%02d", minutes) + ":" + + String.format("%02d", seconds); + } + + private void updateString() { + this.setString(desc + count + eta); } } diff --git a/src/main/java/forge/view/toolbox/FSkin.java b/src/main/java/forge/view/toolbox/FSkin.java index 84a325c1b94..9a8d006ed0a 100644 --- a/src/main/java/forge/view/toolbox/FSkin.java +++ b/src/main/java/forge/view/toolbox/FSkin.java @@ -45,16 +45,6 @@ import forge.gui.GuiUtils; */ public class FSkin { - /** Properties of various components that make up the skin. */ - public interface SkinProp { } - /** Add this interface for sub-sprite components, storing their coords. */ - public interface Coords { - /** */ - int[] COORDS = null; - /** @return int[] */ - int[] getCoords(); - } - /** */ public enum Backgrounds implements SkinProp { /** */ BG_SPLASH, /** */ @@ -331,6 +321,16 @@ public class FSkin { public int[] getCoords() { return coords; } } + /** Properties of various components that make up the skin. */ + public interface SkinProp { } + /** Add this interface for sub-sprite components, storing their coords. */ + public interface Coords { + /** */ + int[] COORDS = null; + /** @return int[] */ + int[] getCoords(); + } + private Map icons; private Map images; private Map colors; @@ -375,6 +375,8 @@ public class FSkin { * the skin name */ public FSkin(final String skinName) { + GuiUtils.checkEDT("FSkin$constructor", false); + this.preferredName = skinName; this.preferredDir = FILE_SKINS_DIR + preferredName + "/"; this.defaultDir = FILE_SKINS_DIR + "default/"; @@ -423,7 +425,8 @@ public class FSkin { * preferred takes precedence over default, but if something is * missing, the default picture is retrieved. */ - public void loadFontsAndImages() { + public void load() { + GuiUtils.checkEDT("FSkin$load", false); barProgress = Singletons.getView().getProgressBar(); SwingUtilities.invokeLater(new Runnable() { @@ -431,13 +434,12 @@ public class FSkin { public void run() { barProgress.reset(); barProgress.setShowETA(false); - barProgress.setDescription("Processing fonts and image sprites: "); + barProgress.setDescription("Processing image sprites: "); } }); - barProgress.setMaximum(57); - // Grab and test various sprite files. + barProgress.setMaximum(4); final File f1 = new File(defaultDir + FILE_ICON_SPRITE); final File f2 = new File(preferredDir + FILE_ICON_SPRITE); final File f3 = new File(defaultDir + FILE_CREATURE_SPRITE); @@ -445,10 +447,13 @@ public class FSkin { try { bimDefaultSprite = ImageIO.read(f1); + barProgress.increment(); bimPreferredSprite = ImageIO.read(f2); - + barProgress.increment(); bimCreatures = ImageIO.read(f3); + barProgress.increment(); bimFoils = ImageIO.read(f4); + barProgress.increment(); preferredH = bimPreferredSprite.getHeight(); preferredW = bimPreferredSprite.getWidth(); @@ -458,31 +463,41 @@ public class FSkin { e.printStackTrace(); } + // Images loaded; can start UI init. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + barProgress.setShowETA(false); + barProgress.setShowCount(false); + barProgress.setDescription("Creating display components."); + } + }); + // Pre-derive most fonts (plain, bold, and italic). // Exceptions handled inside method. this.font = GuiUtils.newFont(FILE_SKINS_DIR + preferredName + "/" + FILE_FONT); plainFonts = new HashMap(); - setFontAndIncrement(10); - setFontAndIncrement(11); - setFontAndIncrement(12); - setFontAndIncrement(13); - setFontAndIncrement(14); - setFontAndIncrement(15); - setFontAndIncrement(16); - setFontAndIncrement(18); - setFontAndIncrement(20); - setFontAndIncrement(22); + setFont(10); + setFont(11); + setFont(12); + setFont(13); + setFont(14); + setFont(15); + setFont(16); + setFont(18); + setFont(20); + setFont(22); boldFonts = new HashMap(); - setBoldFontAndIncrement(12); - setBoldFontAndIncrement(14); - setBoldFontAndIncrement(16); - setBoldFontAndIncrement(18); - setBoldFontAndIncrement(20); + setBoldFont(12); + setBoldFont(14); + setBoldFont(16); + setBoldFont(18); + setBoldFont(20); italicFonts = new HashMap(); - setItalicFontAndIncrement(12); - setItalicFontAndIncrement(14); + setItalicFont(12); + setItalicFont(14); // Put various images into map (except sprite and splash). // Exceptions handled inside method. @@ -749,19 +764,16 @@ public class FSkin { this.colors.put(s0, c0); } - private void setFontAndIncrement(int size) { + private void setFont(int size) { plainFonts.put(size, font.deriveFont(Font.PLAIN, size)); - if (barProgress != null) { barProgress.increment(); } } - private void setBoldFontAndIncrement(int size) { + private void setBoldFont(int size) { boldFonts.put(size, font.deriveFont(Font.BOLD, size)); - if (barProgress != null) { barProgress.increment(); } } - private void setItalicFontAndIncrement(int size) { + private void setItalicFont(int size) { italicFonts.put(size, font.deriveFont(Font.ITALIC, size)); - if (barProgress != null) { barProgress.increment(); } } private void setIcon(final SkinProp s0) {