mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Refactor out swing-related code from forge-gui into new forge-gui-desktop module
This commit is contained in:
@@ -2,14 +2,9 @@ package forge;
|
||||
|
||||
import forge.util.ThreadUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
|
||||
public class FThreads {
|
||||
private FThreads() { } // no instances supposed
|
||||
|
||||
@@ -29,17 +24,10 @@ public class FThreads {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param runnable
|
||||
*/
|
||||
public static void invokeInEdtLater(Runnable runnable) {
|
||||
SwingUtilities.invokeLater(runnable);
|
||||
GuiBase.getInterface().invokeInEdtLater(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
*/
|
||||
public static void invokeInEdtNowOrLater(Runnable proc) {
|
||||
if (isGuiThread()) {
|
||||
proc.run();
|
||||
@@ -59,32 +47,14 @@ public class FThreads {
|
||||
*
|
||||
* @param proc
|
||||
* the Runnable to run
|
||||
* @see javax.swing.SwingUtilities#invokeLater(Runnable)
|
||||
* @see fgd.SwingUtilities#invokeLater(Runnable)
|
||||
*/
|
||||
public static void invokeInEdtAndWait(final Runnable proc) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
// Just run in the current thread.
|
||||
proc.run();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(proc);
|
||||
}
|
||||
catch (final InterruptedException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
}
|
||||
catch (final InvocationTargetException exn) {
|
||||
throw new RuntimeException(exn);
|
||||
}
|
||||
}
|
||||
GuiBase.getInterface().invokeInEdtAndWait(proc);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGuiThread() {
|
||||
return SwingUtilities.isEventDispatchThread();
|
||||
return GuiBase.getInterface().isGuiThread();
|
||||
}
|
||||
|
||||
public static void delayInEDT(int milliseconds, final Runnable inputUpdater) {
|
||||
|
||||
14
forge-gui/src/main/java/forge/GuiBase.java
Normal file
14
forge-gui/src/main/java/forge/GuiBase.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package forge;
|
||||
|
||||
import forge.interfaces.IGuiBase;
|
||||
|
||||
public class GuiBase {
|
||||
private static IGuiBase guiInterface;
|
||||
|
||||
public static IGuiBase getInterface() {
|
||||
return guiInterface;
|
||||
}
|
||||
public static void setInterface(IGuiBase i0) {
|
||||
guiInterface = i0;
|
||||
}
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.mortennobel.imagescaling.ResampleOp;
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardSplitType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.IHasIcon;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinIcon;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.util.Base64Coder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* This class stores ALL card images in a cache with soft values. this means
|
||||
* that the images may be collected when they are not needed any more, but will
|
||||
* be kept as long as possible.
|
||||
* <p/>
|
||||
* The keys are the following:
|
||||
* <ul>
|
||||
* <li>Keys start with the file name, extension is skipped</li>
|
||||
* <li>The key without suffix belongs to the unmodified image from the file</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ImageCache {
|
||||
// short prefixes to save memory
|
||||
|
||||
private static final Set<String> _missingIconKeys = new HashSet<String>();
|
||||
private static final LoadingCache<String, BufferedImage> _CACHE = CacheBuilder.newBuilder().softValues().build(new ImageLoader());
|
||||
private static final BufferedImage _defaultImage;
|
||||
static {
|
||||
BufferedImage defImage = null;
|
||||
try {
|
||||
defImage = ImageIO.read(new File(NewConstants.NO_CARD_FILE));
|
||||
} catch (Exception ex) {
|
||||
System.err.println("could not load default card image");
|
||||
} finally {
|
||||
_defaultImage = (null == defImage) ? new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB) : defImage;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
_CACHE.invalidateAll();
|
||||
_missingIconKeys.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an image from the cache. returns null if the image is not found in the cache
|
||||
* and cannot be loaded from disk. pass -1 for width and/or height to avoid resizing in that dimension.
|
||||
*/
|
||||
public static BufferedImage getImage(Card card, int width, int height) {
|
||||
final String key;
|
||||
if (!Singletons.getControl().mayShowCard(card) || card.isFaceDown()) {
|
||||
key = ImageKeys.TOKEN_PREFIX + ImageKeys.MORPH_IMAGE;
|
||||
} else {
|
||||
key = card.getImageKey();
|
||||
}
|
||||
return scaleImage(key, width, height, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an image from the cache. returns null if the image is not found in the cache
|
||||
* and cannot be loaded from disk. pass -1 for width and/or height to avoid resizing in that dimension.
|
||||
*/
|
||||
public static BufferedImage getImage(InventoryItem ii, int width, int height) {
|
||||
return scaleImage(ImageKeys.getImageKey(ii, false), width, height, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an icon from the cache. returns the current skin's ICO_UNKNOWN if the icon image is not found
|
||||
* in the cache and cannot be loaded from disk.
|
||||
*/
|
||||
public static SkinIcon getIcon(IHasIcon ihi) {
|
||||
String imageKey = ihi.getIconImageKey();
|
||||
final BufferedImage i;
|
||||
if (_missingIconKeys.contains(imageKey) ||
|
||||
null == (i = scaleImage(ihi.getIconImageKey(), -1, -1, false))) {
|
||||
_missingIconKeys.add(imageKey);
|
||||
return FSkin.getIcon(FSkin.InterfaceIcons.ICO_UNKNOWN);
|
||||
}
|
||||
return new FSkin.UnskinnedIcon(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* This requests the original unscaled image from the cache for the given key.
|
||||
* If the image does not exist then it can return a default image if desired.
|
||||
* <p>
|
||||
* If the requested image is not present in the cache then it attempts to load
|
||||
* the image from file (slower) and then add it to the cache for fast future access.
|
||||
* </p>
|
||||
*/
|
||||
public static BufferedImage getOriginalImage(String imageKey, boolean useDefaultIfNotFound) {
|
||||
if (null == imageKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean altState = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||
if(altState)
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
|
||||
imageKey = getImageKey(getPaperCardFromImageKey(imageKey.substring(2)), altState, true);
|
||||
if (StringUtils.isBlank(imageKey)) {
|
||||
return _defaultImage;
|
||||
}
|
||||
}
|
||||
|
||||
// Load from file and add to cache if not found in cache initially.
|
||||
BufferedImage original = getImage(imageKey);
|
||||
|
||||
// No image file exists for the given key so optionally associate with
|
||||
// a default "not available" image and add to cache for given key.
|
||||
if (original == null) {
|
||||
if (useDefaultIfNotFound) {
|
||||
original = _defaultImage;
|
||||
_CACHE.put(imageKey, _defaultImage);
|
||||
} else {
|
||||
original = null;
|
||||
}
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
private static PaperCard getPaperCardFromImageKey(String key) {
|
||||
if( key == null )
|
||||
return null;
|
||||
|
||||
PaperCard cp = StaticData.instance().getCommonCards().getCard(key);
|
||||
if ( cp == null )
|
||||
cp = StaticData.instance().getVariantCards().getCard(key);
|
||||
return cp;
|
||||
}
|
||||
|
||||
private static BufferedImage scaleImage(String key, final int width, final int height, boolean useDefaultImage) {
|
||||
if (StringUtils.isEmpty(key) || (3 > width && -1 != width) || (3 > height && -1 != height)) {
|
||||
// picture too small or key not defined; return a blank
|
||||
return null;
|
||||
}
|
||||
|
||||
String resizedKey = String.format("%s#%dx%d", key, width, height);
|
||||
|
||||
final BufferedImage cached = _CACHE.getIfPresent(resizedKey);
|
||||
if (null != cached) {
|
||||
//System.out.println("found cached image: " + resizedKey);
|
||||
return cached;
|
||||
}
|
||||
|
||||
BufferedImage original = getOriginalImage(key, useDefaultImage);
|
||||
if (original == null) { return null; }
|
||||
|
||||
// Calculate the scale required to best fit the image into the requested
|
||||
// (width x height) dimensions whilst retaining aspect ratio.
|
||||
double scaleX = (-1 == width ? 1 : (double)width / original.getWidth());
|
||||
double scaleY = (-1 == height? 1 : (double)height / original.getHeight());
|
||||
double bestFitScale = Math.min(scaleX, scaleY);
|
||||
if ((bestFitScale > 1) && !mayEnlarge()) {
|
||||
bestFitScale = 1;
|
||||
}
|
||||
|
||||
BufferedImage result;
|
||||
if (1 == bestFitScale) {
|
||||
result = original;
|
||||
} else {
|
||||
|
||||
int destWidth = (int)(original.getWidth() * bestFitScale);
|
||||
int destHeight = (int)(original.getHeight() * bestFitScale);
|
||||
|
||||
ResampleOp resampler = new ResampleOp(destWidth, destHeight);
|
||||
result = resampler.filter(original, null);
|
||||
}
|
||||
|
||||
//System.out.println("caching image: " + resizedKey);
|
||||
_CACHE.put(resizedKey, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean mayEnlarge() {
|
||||
return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_SCALE_LARGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Image corresponding to the key.
|
||||
*/
|
||||
private static BufferedImage getImage(final String key) {
|
||||
FThreads.assertExecutedByEdt(true);
|
||||
try {
|
||||
return ImageCache._CACHE.get(key);
|
||||
} catch (final ExecutionException ex) {
|
||||
if (ex.getCause() instanceof NullPointerException) {
|
||||
return null;
|
||||
}
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
} catch (final InvalidCacheLoadException ex) {
|
||||
// should be when a card legitimately has no image
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static String getImageRelativePath(PaperCard cp, boolean backFace, boolean includeSet, boolean isDownloadUrl) {
|
||||
final String nameToUse = cp == null ? null : getNameToUse(cp, backFace);
|
||||
if ( null == nameToUse )
|
||||
return null;
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
|
||||
CardRules card = cp.getRules();
|
||||
String edition = cp.getEdition();
|
||||
s.append(ImageCache.toMWSFilename(nameToUse));
|
||||
|
||||
final int cntPictures;
|
||||
final boolean hasManyPictures;
|
||||
final CardDb db = !card.isVariant() ? Singletons.getMagicDb().getCommonCards() : Singletons.getMagicDb().getVariantCards();
|
||||
if (includeSet) {
|
||||
cntPictures = db.getPrintCount(card.getName(), edition);
|
||||
hasManyPictures = cntPictures > 1;
|
||||
} else {
|
||||
// without set number of pictures equals number of urls provided in Svar:Picture
|
||||
String urls = card.getPictureUrl(backFace);
|
||||
cntPictures = StringUtils.countMatches(urls, "\\") + 1;
|
||||
|
||||
// raise the art index limit to the maximum of the sets this card was printed in
|
||||
int maxCntPictures = db.getMaxPrintCount(card.getName());
|
||||
hasManyPictures = maxCntPictures > 1;
|
||||
}
|
||||
|
||||
int artIdx = cp.getArtIndex() - 1;
|
||||
if (hasManyPictures) {
|
||||
if ( cntPictures <= artIdx ) // prevent overflow
|
||||
artIdx = cntPictures == 0 ? 0 : artIdx % cntPictures;
|
||||
s.append(artIdx + 1);
|
||||
}
|
||||
|
||||
// for whatever reason, MWS-named plane cards don't have the ".full" infix
|
||||
if (!card.getType().isPlane() && !card.getType().isPhenomenon()) {
|
||||
s.append(".full");
|
||||
}
|
||||
|
||||
final String fname;
|
||||
if (isDownloadUrl) {
|
||||
s.append(".jpg");
|
||||
fname = Base64Coder.encodeString(s.toString(), true);
|
||||
} else {
|
||||
fname = s.toString();
|
||||
}
|
||||
|
||||
if (includeSet) {
|
||||
String editionAliased = isDownloadUrl ? Singletons.getMagicDb().getEditions().getCode2ByCode(edition) : getSetFolder(edition);
|
||||
return String.format("%s/%s", editionAliased, fname);
|
||||
} else {
|
||||
return fname;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasBackFacePicture(PaperCard cp) {
|
||||
CardSplitType cst = cp.getRules().getSplitType();
|
||||
return cst == CardSplitType.Transform || cst == CardSplitType.Flip;
|
||||
}
|
||||
|
||||
public static String getSetFolder(String edition) {
|
||||
return !NewConstants.CACHE_CARD_PICS_SUBDIR.containsKey(edition)
|
||||
? Singletons.getMagicDb().getEditions().getCode2ByCode(edition) // by default 2-letter codes from MWS are used
|
||||
: NewConstants.CACHE_CARD_PICS_SUBDIR.get(edition); // may use custom paths though
|
||||
}
|
||||
|
||||
private static String getNameToUse(PaperCard cp, boolean backFace) {
|
||||
final CardRules card = cp.getRules();
|
||||
if (backFace ) {
|
||||
if ( hasBackFacePicture(cp) )
|
||||
return card.getOtherPart().getName();
|
||||
else
|
||||
return null;
|
||||
} else if(CardSplitType.Split == cp.getRules().getSplitType()) {
|
||||
return card.getMainPart().getName() + card.getOtherPart().getName();
|
||||
} else {
|
||||
return cp.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getImageKey(PaperCard cp, boolean backFace, boolean includeSet) {
|
||||
return getImageRelativePath(cp, backFace, includeSet, false);
|
||||
}
|
||||
|
||||
public static String getDownloadUrl(PaperCard cp, boolean backFace) {
|
||||
return getImageRelativePath(cp, backFace, true, true);
|
||||
}
|
||||
|
||||
public static String toMWSFilename(String in) {
|
||||
final StringBuffer out = new StringBuffer();
|
||||
char c;
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
c = in.charAt(i);
|
||||
if ((c == '"') || (c == '/') || (c == ':') || (c == '?')) {
|
||||
out.append("");
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package forge;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import forge.error.BugReporter;
|
||||
import forge.properties.NewConstants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
final class ImageLoader extends CacheLoader<String, BufferedImage> {
|
||||
// image file extensions for various formats in order of likelihood
|
||||
// the last, empty, string is for keys that come in with an extension already in place
|
||||
private static final String[] _FILE_EXTENSIONS = { ".jpg", ".png", "" };
|
||||
|
||||
@Override
|
||||
public BufferedImage load(String key) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String path;
|
||||
final String filename;
|
||||
if (key.startsWith(ImageKeys.TOKEN_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.TOKEN_PREFIX.length());
|
||||
path = NewConstants.CACHE_TOKEN_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.ICON_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.ICON_PREFIX.length());
|
||||
path = NewConstants.CACHE_ICON_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.BOOSTER_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.BOOSTER_PREFIX.length());
|
||||
path = NewConstants.CACHE_BOOSTER_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.FATPACK_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.FATPACK_PREFIX.length());
|
||||
path = NewConstants.CACHE_FATPACK_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.PRECON_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.PRECON_PREFIX.length());
|
||||
path = NewConstants.CACHE_PRECON_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.TOURNAMENTPACK_PREFIX)) {
|
||||
filename = key.substring(ImageKeys.TOURNAMENTPACK_PREFIX.length());
|
||||
path = NewConstants.CACHE_TOURNAMENTPACK_PICS_DIR;
|
||||
} else {
|
||||
filename = key;
|
||||
path = NewConstants.CACHE_CARD_PICS_DIR;
|
||||
}
|
||||
|
||||
BufferedImage ret = _findFile(key, path, filename);
|
||||
|
||||
// some S00 cards are really part of 6ED
|
||||
if (null == ret ) {
|
||||
String s2kAlias = ImageCache.getSetFolder("S00");
|
||||
if ( filename.startsWith(s2kAlias) ) {
|
||||
ret = _findFile(key, path, filename.replace(s2kAlias, ImageCache.getSetFolder("6ED")));
|
||||
}
|
||||
}
|
||||
|
||||
// try without set prefix
|
||||
String setlessFilename = null;
|
||||
if (null == ret && filename.contains("/")) {
|
||||
setlessFilename = filename.substring(filename.indexOf('/') + 1);
|
||||
ret = _findFile(key, path, setlessFilename);
|
||||
|
||||
// try lowering the art index to the minimum for regular cards
|
||||
if (null == ret && setlessFilename.contains(".full")) {
|
||||
ret = _findFile(key, path, setlessFilename.replaceAll("[0-9]*[.]full", "1.full"));
|
||||
}
|
||||
}
|
||||
|
||||
if (null == ret) {
|
||||
System.out.println("File not found, no image created: " + key);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static BufferedImage _findFile(String key, String path, String filename) {
|
||||
for (String ext : _FILE_EXTENSIONS) {
|
||||
File file = new File(path, filename + ext);
|
||||
//System.out.println(String.format("Searching for %s at: %s", key, file.getAbsolutePath()));
|
||||
if (file.exists()) {
|
||||
//System.out.println(String.format("Found %s at: %s", key, file.getAbsolutePath()));
|
||||
try {
|
||||
return ImageIO.read(file);
|
||||
} catch (IOException ex) {
|
||||
BugReporter.reportException(ex, "Could not read image file " + file.getAbsolutePath() + " ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,19 @@
|
||||
package forge.gui;
|
||||
package forge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.FThreads;
|
||||
import forge.Singletons;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.item.InventoryItem;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class GuiChoose {
|
||||
|
||||
public class SGuiChoose {
|
||||
/**
|
||||
* Convenience for getChoices(message, 0, 1, choices).
|
||||
*
|
||||
@@ -41,7 +32,7 @@ public class GuiChoose {
|
||||
if ((choices == null) || (choices.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
final List<T> choice = GuiChoose.getChoices(message, 0, 1, choices);
|
||||
final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices);
|
||||
return choice.isEmpty() ? null : choice.get(0);
|
||||
} // getChoiceOptional(String,T...)
|
||||
|
||||
@@ -49,7 +40,7 @@ public class GuiChoose {
|
||||
if ((choices == null) || choices.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final List<T> choice = GuiChoose.getChoices(message, 0, 1, choices);
|
||||
final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices);
|
||||
return choice.isEmpty() ? null : choice.get(0);
|
||||
} // getChoiceOptional(String,T...)
|
||||
|
||||
@@ -68,7 +59,7 @@ public class GuiChoose {
|
||||
* @return a T object.
|
||||
*/
|
||||
public static <T> T one(final String message, final T[] choices) {
|
||||
final List<T> choice = GuiChoose.getChoices(message, 1, 1, choices);
|
||||
final List<T> choice = SGuiChoose.getChoices(message, 1, 1, choices);
|
||||
assert choice.size() == 1;
|
||||
return choice.get(0);
|
||||
}
|
||||
@@ -79,13 +70,13 @@ public class GuiChoose {
|
||||
if( choices.size() == 1)
|
||||
return Iterables.getFirst(choices, null);
|
||||
|
||||
final List<T> choice = GuiChoose.getChoices(message, 1, 1, choices);
|
||||
final List<T> choice = SGuiChoose.getChoices(message, 1, 1, choices);
|
||||
assert choice.size() == 1;
|
||||
return choice.get(0);
|
||||
}
|
||||
|
||||
public static <T> List<T> noneOrMany(final String message, final Collection<T> choices) {
|
||||
return GuiChoose.getChoices(message, 0, choices.size(), choices, null, null);
|
||||
return SGuiChoose.getChoices(message, 0, choices.size(), choices, null, null);
|
||||
}
|
||||
|
||||
// Nothing to choose here. Code uses this to just reveal one or more items
|
||||
@@ -95,10 +86,10 @@ public class GuiChoose {
|
||||
reveal(message, items);
|
||||
}
|
||||
public static <T> void reveal(final String message, final T[] items) {
|
||||
GuiChoose.getChoices(message, -1, -1, items);
|
||||
SGuiChoose.getChoices(message, -1, -1, items);
|
||||
}
|
||||
public static <T> void reveal(final String message, final Collection<T> items) {
|
||||
GuiChoose.getChoices(message, -1, -1, items);
|
||||
SGuiChoose.getChoices(message, -1, -1, items);
|
||||
}
|
||||
|
||||
// Get Integer in range
|
||||
@@ -124,7 +115,7 @@ public class GuiChoose {
|
||||
for (int i = 0; i < count; i++) {
|
||||
choices[i] = Integer.valueOf(i + min);
|
||||
}
|
||||
return GuiChoose.oneOrNone(message, choices);
|
||||
return SGuiChoose.oneOrNone(message, choices);
|
||||
}
|
||||
public static Integer getInteger(final String message, int min, int max, int cutoff) {
|
||||
if (max <= min || cutoff < min) { return min; } //just return min if max <= min or cutoff < min
|
||||
@@ -139,7 +130,7 @@ public class GuiChoose {
|
||||
}
|
||||
choices.add("Other...");
|
||||
|
||||
Object choice = GuiChoose.oneOrNone(message, choices);
|
||||
Object choice = SGuiChoose.oneOrNone(message, choices);
|
||||
if (choice instanceof Integer || choice == null) {
|
||||
return (Integer)choice;
|
||||
}
|
||||
@@ -160,7 +151,7 @@ public class GuiChoose {
|
||||
prompt += ":";
|
||||
|
||||
while (true) {
|
||||
String str = FOptionPane.showInputDialog(prompt, message);
|
||||
String str = SOptionPane.showInputDialog(prompt, message);
|
||||
if (str == null) { return null; } // that is 'cancel'
|
||||
|
||||
if (StringUtils.isNumeric(str)) {
|
||||
@@ -182,59 +173,7 @@ public class GuiChoose {
|
||||
}
|
||||
|
||||
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
|
||||
if (choices == null || choices.isEmpty()) {
|
||||
if (min == 0) {
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
throw new RuntimeException("choice required from empty list");
|
||||
}
|
||||
|
||||
Callable<List<T>> showChoice = new Callable<List<T>>() {
|
||||
@Override
|
||||
public List<T> call() {
|
||||
ListChooser<T> c = new ListChooser<T>(message, min, max, choices, display);
|
||||
final JList<T> list = c.getLstChoices();
|
||||
list.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(final ListSelectionEvent ev) {
|
||||
if (list.getSelectedValue() instanceof Card) {
|
||||
Card card = (Card) list.getSelectedValue();
|
||||
if (card.isFaceDown() && Singletons.getControl().mayShowCard(card)) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(card, true);
|
||||
}
|
||||
else {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(card);
|
||||
}
|
||||
|
||||
GuiUtils.clearPanelSelections();
|
||||
GuiUtils.setPanelSelection(card);
|
||||
}
|
||||
if (list.getSelectedValue() instanceof InventoryItem) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (selected != null) {
|
||||
c.show(selected);
|
||||
}
|
||||
else {
|
||||
c.show();
|
||||
}
|
||||
|
||||
GuiUtils.clearPanelSelections();
|
||||
return c.getSelectedValues();
|
||||
}
|
||||
};
|
||||
|
||||
FutureTask<List<T>> future = new FutureTask<List<T>>(showChoice);
|
||||
FThreads.invokeInEdtAndWait(future);
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) { // should be no exception here
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
return GuiBase.getInterface().getChoices(message, min, max, choices, selected, display);
|
||||
}
|
||||
|
||||
public static <T> List<T> many(final String title, final String topCaption, int cnt, final List<T> sourceChoices, Card referenceCard) {
|
||||
@@ -259,41 +198,7 @@ public class GuiChoose {
|
||||
|
||||
private static <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
|
||||
final List<T> sourceChoices, final List<T> destChoices, final Card referenceCard, final boolean sideboardingMode) {
|
||||
// An input box for handling the order of choices.
|
||||
|
||||
Callable<List<T>> callable = new Callable<List<T>>() {
|
||||
@Override
|
||||
public List<T> call() throws Exception {
|
||||
DualListBox<T> dual = new DualListBox<T>(remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices);
|
||||
dual.setSecondColumnLabelText(top);
|
||||
|
||||
dual.setSideboardMode(sideboardingMode);
|
||||
|
||||
dual.setTitle(title);
|
||||
dual.pack();
|
||||
dual.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
if (referenceCard != null) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(referenceCard);
|
||||
// MARKED FOR UPDATE
|
||||
}
|
||||
dual.setVisible(true);
|
||||
|
||||
List<T> objects = dual.getOrderedList();
|
||||
|
||||
dual.dispose();
|
||||
GuiUtils.clearPanelSelections();
|
||||
return objects;
|
||||
}
|
||||
};
|
||||
|
||||
FutureTask<List<T>> ft = new FutureTask<List<T>>(callable);
|
||||
FThreads.invokeInEdtAndWait(ft);
|
||||
try {
|
||||
return ft.get();
|
||||
} catch (Exception e) { // we have waited enough
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
return GuiBase.getInterface().order(title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode);
|
||||
}
|
||||
|
||||
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
|
||||
@@ -301,7 +206,7 @@ public class GuiChoose {
|
||||
if ((choices == null) || (choices.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
final List<T> choice = GuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
|
||||
final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
|
||||
return choice.isEmpty() ? null : choice.get(0);
|
||||
} // getChoiceOptional(String,T...)
|
||||
|
||||
@@ -310,14 +215,14 @@ public class GuiChoose {
|
||||
if ((choices == null) || choices.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final List<T> choice = GuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
|
||||
final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
|
||||
return choice.isEmpty() ? null : choice.get(0);
|
||||
} // getChoiceOptional(String,T...)
|
||||
|
||||
|
||||
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
|
||||
public static <T> T sortedOne(final String message, final T[] choices, Comparator<T> comparer) {
|
||||
final List<T> choice = GuiChoose.sortedGetChoices(message, 1, 1, choices, comparer);
|
||||
final List<T> choice = SGuiChoose.sortedGetChoices(message, 1, 1, choices, comparer);
|
||||
assert choice.size() == 1;
|
||||
return choice.get(0);
|
||||
} // getChoice()
|
||||
@@ -327,14 +232,14 @@ public class GuiChoose {
|
||||
if ((choices == null) || (choices.size() == 0)) {
|
||||
return null;
|
||||
}
|
||||
final List<T> choice = GuiChoose.sortedGetChoices(message, 1, 1, choices, comparer);
|
||||
final List<T> choice = SGuiChoose.sortedGetChoices(message, 1, 1, choices, comparer);
|
||||
assert choice.size() == 1;
|
||||
return choice.get(0);
|
||||
}
|
||||
|
||||
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
|
||||
public static <T> List<T> sortedNoneOrMany(final String message, final List<T> choices, Comparator<T> comparer) {
|
||||
return GuiChoose.sortedGetChoices(message, 0, choices.size(), choices, comparer);
|
||||
return SGuiChoose.sortedGetChoices(message, 0, choices.size(), choices, comparer);
|
||||
}
|
||||
|
||||
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
|
||||
@@ -350,6 +255,4 @@ public class GuiChoose {
|
||||
Collections.sort(choices, comparer);
|
||||
return getChoices(message, min, max, choices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package forge.gui;
|
||||
package forge;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -14,30 +12,31 @@ import java.util.concurrent.FutureTask;
|
||||
* Holds player interactions using standard windows
|
||||
*
|
||||
*/
|
||||
public class GuiDialog {
|
||||
public class SGuiDialog {
|
||||
private static final String[] defaultConfirmOptions = { "Yes", "No" };
|
||||
|
||||
public static boolean confirm(final Card c, final String question) {
|
||||
return GuiDialog.confirm(c, question, true, null);
|
||||
return SGuiDialog.confirm(c, question, true, null);
|
||||
}
|
||||
public static boolean confirm(final Card c, final String question, final boolean defaultChoice) {
|
||||
return GuiDialog.confirm(c, question, defaultChoice, null);
|
||||
return SGuiDialog.confirm(c, question, defaultChoice, null);
|
||||
}
|
||||
public static boolean confirm(final Card c, final String question, String[] options) {
|
||||
return GuiDialog.confirm(c, question, true, options);
|
||||
return SGuiDialog.confirm(c, question, true, options);
|
||||
}
|
||||
|
||||
public static boolean confirm(final Card c, final String question, final boolean defaultIsYes, final String[] options) {
|
||||
Callable<Boolean> confirmTask = new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
if ( null != c )
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(c);
|
||||
if (c != null) {
|
||||
GuiBase.getInterface().setCard(c);
|
||||
}
|
||||
|
||||
final String title = c == null ? "Question" : c.getName() + " - Ability";
|
||||
String questionToUse = StringUtils.isBlank(question) ? "Activate card's ability?" : question;
|
||||
String[] opts = options == null ? defaultConfirmOptions : options;
|
||||
int answer = FOptionPane.showOptionDialog(questionToUse, title, FOptionPane.QUESTION_ICON, opts, defaultIsYes ? 0 : 1);
|
||||
int answer = SOptionPane.showOptionDialog(questionToUse, title, SOptionPane.QUESTION_ICON, opts, defaultIsYes ? 0 : 1);
|
||||
return answer == 0;
|
||||
}};
|
||||
|
||||
@@ -68,7 +67,7 @@ public class GuiDialog {
|
||||
FThreads.invokeInEdtAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FOptionPane.showMessageDialog(message, title, null);
|
||||
SOptionPane.showMessageDialog(message, title, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
80
forge-gui/src/main/java/forge/SOptionPane.java
Normal file
80
forge-gui/src/main/java/forge/SOptionPane.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package forge;
|
||||
|
||||
import forge.assets.FSkinProp;
|
||||
|
||||
|
||||
public class SOptionPane {
|
||||
public static final FSkinProp QUESTION_ICON = FSkinProp.ICO_QUESTION;
|
||||
public static final FSkinProp INFORMATION_ICON = FSkinProp.ICO_INFORMATION;
|
||||
public static final FSkinProp WARNING_ICON = FSkinProp.ICO_WARNING;
|
||||
public static final FSkinProp ERROR_ICON = FSkinProp.ICO_ERROR;
|
||||
|
||||
public static void showMessageDialog(String message) {
|
||||
showMessageDialog(message, "Forge", INFORMATION_ICON);
|
||||
}
|
||||
|
||||
public static void showMessageDialog(String message, String title) {
|
||||
showMessageDialog(message, title, INFORMATION_ICON);
|
||||
}
|
||||
|
||||
public static void showErrorDialog(String message) {
|
||||
showMessageDialog(message, "Forge", ERROR_ICON);
|
||||
}
|
||||
|
||||
public static void showErrorDialog(String message, String title) {
|
||||
showMessageDialog(message, title, ERROR_ICON);
|
||||
}
|
||||
|
||||
public static void showMessageDialog(String message, String title, FSkinProp icon) {
|
||||
showOptionDialog(message, title, icon, new String[] {"OK"}, 0);
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(String message) {
|
||||
return showConfirmDialog(message, "Forge");
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(String message, String title) {
|
||||
return showConfirmDialog(message, title, "Yes", "No", true);
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(String message, String title, boolean defaultYes) {
|
||||
return showConfirmDialog(message, title, "Yes", "No", defaultYes);
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(String message, String title, String yesButtonText, String noButtonText) {
|
||||
return showConfirmDialog(message, title, yesButtonText, noButtonText, true);
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(String message, String title, String yesButtonText, String noButtonText, boolean defaultYes) {
|
||||
String[] options = {yesButtonText, noButtonText};
|
||||
int reply = SOptionPane.showOptionDialog(message, title, QUESTION_ICON, options, defaultYes ? 0 : 1);
|
||||
return (reply == 0);
|
||||
}
|
||||
|
||||
public static int showOptionDialog(String message, String title, FSkinProp icon, String[] options) {
|
||||
return showOptionDialog(message, title, icon, options, 0);
|
||||
}
|
||||
|
||||
public static int showOptionDialog(String message, String title, FSkinProp icon, String[] options, int defaultOption) {
|
||||
return GuiBase.getInterface().showOptionDialog(message, title, icon, options, defaultOption);
|
||||
}
|
||||
|
||||
public static String showInputDialog(String message, String title) {
|
||||
return showInputDialog(message, title, null, "", null);
|
||||
}
|
||||
|
||||
public static String showInputDialog(String message, String title, FSkinProp icon) {
|
||||
return showInputDialog(message, title, icon, "", null);
|
||||
}
|
||||
|
||||
public static String showInputDialog(String message, String title, FSkinProp icon, String initialInput) {
|
||||
return showInputDialog(message, title, icon, initialInput, null);
|
||||
}
|
||||
|
||||
public static <T> T showInputDialog(String message, String title, FSkinProp icon, T initialInput, T[] inputOptions) {
|
||||
return GuiBase.getInterface().showInputDialog(message, title, icon, initialInput, inputOptions);
|
||||
}
|
||||
|
||||
private SOptionPane() {
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge;
|
||||
|
||||
import forge.control.FControl;
|
||||
import forge.gui.toolbox.FProgressBar;
|
||||
import forge.gui.workshop.CardScriptInfo;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.view.FView;
|
||||
|
||||
/**
|
||||
* Provides global/static access to singleton instances.
|
||||
*/
|
||||
public final class Singletons {
|
||||
private static boolean initialized = false;
|
||||
private static FModel model = null;
|
||||
private static FView view = null;
|
||||
private static FControl control = null;
|
||||
private static StaticData magicDb = null;
|
||||
|
||||
/**
|
||||
* IMPORTANT - does not return view frame! Must call
|
||||
* getFrame() from FView for that.
|
||||
*/
|
||||
public static FView getView() { return view; }
|
||||
public static FControl getControl() { return control; }
|
||||
public static FModel getModel() { return model; }
|
||||
public static StaticData getMagicDb() { return magicDb; }
|
||||
|
||||
public static void initializeOnce(boolean withUi) {
|
||||
FThreads.assertExecutedByEdt(false);
|
||||
|
||||
synchronized (Singletons.class) {
|
||||
if(initialized)
|
||||
throw new IllegalStateException("Singletons.initializeOnce really cannot be called again");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if (withUi) {
|
||||
view = FView.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
CardStorageReader.ProgressObserver progressBarBridge = view == null
|
||||
? CardStorageReader.ProgressObserver.emptyObserver : new CardStorageReader.ProgressObserver() {
|
||||
FProgressBar bar = view.getSplash().getProgressBar();
|
||||
@Override
|
||||
public void setOperationName(final String name, final boolean usePercents) {
|
||||
FThreads.invokeInEdtLater(new Runnable() { @Override public void run() {
|
||||
bar.setDescription(name);
|
||||
bar.setPercentMode(usePercents);
|
||||
} });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(int current, int total) {
|
||||
if ( total != bar.getMaximum())
|
||||
bar.setMaximum(total);
|
||||
bar.setValueThreadSafe(current);
|
||||
}
|
||||
};
|
||||
|
||||
// Loads all cards (using progress bar).
|
||||
final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR, progressBarBridge, CardScriptInfo.readerObserver);
|
||||
magicDb = new StaticData(reader, "res/editions", "res/blockdata");
|
||||
model = FModel.getInstance(withUi);
|
||||
|
||||
if (withUi) {
|
||||
control = FControl.instance;
|
||||
}
|
||||
}
|
||||
|
||||
// disallow instantiation
|
||||
private Singletons() { }
|
||||
}
|
||||
279
forge-gui/src/main/java/forge/assets/FSkinProp.java
Normal file
279
forge-gui/src/main/java/forge/assets/FSkinProp.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.assets;
|
||||
|
||||
/**
|
||||
* Assembles settings from selected or default theme as appropriate. Saves in a
|
||||
* hashtable, access using .get(settingName) method.
|
||||
*
|
||||
*/
|
||||
|
||||
public enum FSkinProp {
|
||||
//backgrounds
|
||||
BG_SPLASH (null, PropType.BACKGROUND),
|
||||
BG_TEXTURE (null, PropType.BACKGROUND),
|
||||
BG_MATCH (null, PropType.BACKGROUND),
|
||||
|
||||
//colors
|
||||
CLR_THEME (new int[] {70, 10}, PropType.COLOR),
|
||||
CLR_BORDERS (new int[] {70, 30}, PropType.COLOR),
|
||||
CLR_ZEBRA (new int[] {70, 50}, PropType.COLOR),
|
||||
CLR_HOVER (new int[] {70, 70}, PropType.COLOR),
|
||||
CLR_ACTIVE (new int[] {70, 90}, PropType.COLOR),
|
||||
CLR_INACTIVE (new int[] {70, 110}, PropType.COLOR),
|
||||
CLR_TEXT (new int[] {70, 130}, PropType.COLOR),
|
||||
CLR_PHASE_INACTIVE_ENABLED (new int[] {70, 150}, PropType.COLOR),
|
||||
CLR_PHASE_INACTIVE_DISABLED (new int[] {70, 170}, PropType.COLOR),
|
||||
CLR_PHASE_ACTIVE_ENABLED (new int[] {70, 190}, PropType.COLOR),
|
||||
CLR_PHASE_ACTIVE_DISABLED (new int[] {70, 210}, PropType.COLOR),
|
||||
CLR_THEME2 (new int[] {70, 230}, PropType.COLOR),
|
||||
CLR_OVERLAY (new int[] {70, 250}, PropType.COLOR),
|
||||
CLR_COMBAT_TARGETING_ARROW (new int[] {70, 270}, PropType.COLOR),
|
||||
CLR_NORMAL_TARGETING_ARROW (new int[] {70, 290}, PropType.COLOR),
|
||||
|
||||
//zone images
|
||||
IMG_ZONE_HAND (new int[] {280, 40, 40, 40}, PropType.IMAGE),
|
||||
IMG_ZONE_LIBRARY (new int[] {280, 0, 40, 40}, PropType.IMAGE),
|
||||
IMG_ZONE_EXILE (new int[] {320, 40, 40, 40}, PropType.IMAGE),
|
||||
IMG_ZONE_FLASHBACK (new int[] {280, 80, 40, 40}, PropType.IMAGE),
|
||||
IMG_ZONE_GRAVEYARD (new int[] {320, 0, 40, 40}, PropType.IMAGE),
|
||||
IMG_ZONE_POISON (new int[] {320, 80, 40, 40}, PropType.IMAGE),
|
||||
|
||||
//mana images
|
||||
IMG_MANA_B (new int[] {360, 160, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_R (new int[] {400, 160, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_COLORLESS (new int[] {440, 160, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_U (new int[] {360, 200, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_G (new int[] {400, 200, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_W (new int[] {440, 200, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_2B (new int[] {360, 400, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_2G (new int[] {400, 400, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_2R (new int[] {440, 400, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_2U (new int[] {440, 360, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_2W (new int[] {400, 360, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_BG (new int[] {360, 240, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_BR (new int[] {400, 240, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_GU (new int[] {360, 280, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_GW (new int[] {440, 280, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_RG (new int[] {360, 320, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_RW (new int[] {400, 320, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_UB (new int[] {440, 240, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_UR (new int[] {440, 320, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_WB (new int[] {400, 280, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_HYBRID_WU (new int[] {360, 360, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_PHRYX_U (new int[] {320, 200, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_PHRYX_W (new int[] {320, 240, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_PHRYX_R (new int[] {320, 280, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_PHRYX_G (new int[] {320, 320, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_PHRYX_B (new int[] {320, 360, 40, 40}, PropType.IMAGE),
|
||||
IMG_MANA_SNOW (new int[] {320, 160, 40, 40}, PropType.IMAGE),
|
||||
|
||||
//colorless mana images
|
||||
IMG_MANA_0 (new int[] {640, 200, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_1 (new int[] {660, 200, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_2 (new int[] {640, 220, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_3 (new int[] {660, 220, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_4 (new int[] {640, 240, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_5 (new int[] {660, 240, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_6 (new int[] {640, 260, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_7 (new int[] {660, 260, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_8 (new int[] {640, 280, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_9 (new int[] {660, 280, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_10 (new int[] {640, 300, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_11 (new int[] {660, 300, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_12 (new int[] {640, 320, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_13 (new int[] {660, 320, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_14 (new int[] {640, 340, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_15 (new int[] {660, 340, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_16 (new int[] {640, 360, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_17 (new int[] {660, 360, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_18 (new int[] {640, 380, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_19 (new int[] {660, 380, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_20 (new int[] {640, 400, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_X (new int[] {660, 400, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_Y (new int[] {640, 420, 20, 20}, PropType.IMAGE),
|
||||
IMG_MANA_Z (new int[] {660, 420, 20, 20}, PropType.IMAGE),
|
||||
|
||||
//gameplay images
|
||||
IMG_TAP (new int[] {640, 440, 20, 20}, PropType.IMAGE),
|
||||
IMG_UNTAP (new int[] {660, 440, 20, 20}, PropType.IMAGE),
|
||||
IMG_CHAOS (new int[] {320, 400, 40, 40}, PropType.IMAGE),
|
||||
IMG_SLASH (new int[] {660, 400, 10, 13}, PropType.IMAGE),
|
||||
IMG_ATTACK (new int[] {160, 320, 80, 80, 32, 32}, PropType.IMAGE),
|
||||
IMG_DEFEND (new int[] {160, 400, 80, 80, 32, 32}, PropType.IMAGE),
|
||||
IMG_SUMMONSICK (new int[] {240, 400, 80, 80, 32, 32}, PropType.IMAGE),
|
||||
IMG_PHASING (new int[] {240, 320, 80, 80, 32, 32}, PropType.IMAGE),
|
||||
IMG_COSTRESERVED (new int[] {240, 240, 80, 80, 40, 40}, PropType.IMAGE),
|
||||
IMG_COUNTERS1 (new int[] {0, 320, 80, 80}, PropType.IMAGE),
|
||||
IMG_COUNTERS2 (new int[] {0, 400, 80, 80}, PropType.IMAGE),
|
||||
IMG_COUNTERS3 (new int[] {80, 320, 80, 80}, PropType.IMAGE),
|
||||
IMG_COUNTERS_MULTI (new int[] {80, 400, 80, 80}, PropType.IMAGE),
|
||||
|
||||
//foils
|
||||
FOIL_01 (new int[] {0, 0, 400, 570}, PropType.FOIL),
|
||||
FOIL_02 (new int[] {400, 0, 400, 570}, PropType.FOIL),
|
||||
FOIL_03 (new int[] {0, 570, 400, 570}, PropType.FOIL),
|
||||
FOIL_04 (new int[] {400, 570, 400, 570}, PropType.FOIL),
|
||||
FOIL_05 (new int[] {0, 1140, 400, 570}, PropType.FOIL),
|
||||
FOIL_06 (new int[] {400, 1140, 400, 570}, PropType.FOIL),
|
||||
FOIL_07 (new int[] {0, 1710, 400, 570}, PropType.FOIL),
|
||||
FOIL_08 (new int[] {400, 1710, 400, 570}, PropType.FOIL),
|
||||
FOIL_09 (new int[] {0, 2280, 400, 570}, PropType.FOIL),
|
||||
FOIL_10 (new int[] {400, 2280, 400, 570}, PropType.FOIL),
|
||||
|
||||
//old foils
|
||||
FOIL_11 (new int[] {0, 0, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_12 (new int[] {400, 0, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_13 (new int[] {0, 570, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_14 (new int[] {400, 570, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_15 (new int[] {0, 1140, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_16 (new int[] {400, 1140, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_17 (new int[] {0, 1710, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_18 (new int[] {400, 1710, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_19 (new int[] {0, 2280, 400, 570}, PropType.OLD_FOIL),
|
||||
FOIL_20 (new int[] {400, 2280, 400, 570}, PropType.OLD_FOIL),
|
||||
|
||||
//dock icons
|
||||
ICO_SHORTCUTS (new int[] {160, 640, 80, 80}, PropType.ICON),
|
||||
ICO_SETTINGS (new int[] {80, 640, 80, 80}, PropType.ICON),
|
||||
ICO_ENDTURN (new int[] {320, 640, 80, 80}, PropType.ICON),
|
||||
ICO_CONCEDE (new int[] {240, 640, 80, 80}, PropType.ICON),
|
||||
ICO_REVERTLAYOUT (new int[] {400, 720, 80, 80}, PropType.ICON),
|
||||
ICO_OPENLAYOUT (new int[] {0, 800, 80, 80}, PropType.ICON),
|
||||
ICO_SAVELAYOUT (new int[] {80, 800, 80, 80}, PropType.ICON),
|
||||
ICO_DECKLIST (new int[] {400, 640, 80, 80}, PropType.ICON),
|
||||
ICO_ALPHASTRIKE (new int[] {160, 800, 80, 80}, PropType.ICON),
|
||||
ICO_ARCSOFF (new int[] {240, 800, 80, 80}, PropType.ICON),
|
||||
ICO_ARCSON (new int[] {320, 800, 80, 80}, PropType.ICON),
|
||||
ICO_ARCSHOVER (new int[] {400, 800, 80, 80}, PropType.ICON),
|
||||
|
||||
//quest icons
|
||||
ICO_QUEST_ZEP (new int[] {0, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_GEAR (new int[] {80, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_GOLD (new int[] {160, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_ELIXIR (new int[] {240, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_BOOK (new int[] {320, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_BOTTLES (new int[] {400, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_BOX (new int[] {480, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_COIN (new int[] {560, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_CHARM (new int[] {480, 800, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_FOX (new int[] {0, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_LEAF (new int[] {80, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_LIFE (new int[] {160, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_COINSTACK (new int[] {240, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_MAP (new int[] {320, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_NOTES (new int[] {400, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_HEART (new int[] {480, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_BREW (new int[] {560, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_STAKES (new int[] {400, 560, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_MINUS (new int[] {560, 640, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_PLUS (new int[] {480, 640, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_PLUSPLUS (new int[] {480, 720, 80, 80}, PropType.ICON),
|
||||
|
||||
//interface icons
|
||||
ICO_QUESTION (new int[] {560, 800, 32, 32}, PropType.ICON),
|
||||
ICO_INFORMATION (new int[] {592, 800, 32, 32}, PropType.ICON),
|
||||
ICO_WARNING (new int[] {560, 832, 32, 32}, PropType.ICON),
|
||||
ICO_ERROR (new int[] {592, 832, 32, 32}, PropType.ICON),
|
||||
ICO_DELETE (new int[] {640, 480, 20, 20}, PropType.ICON),
|
||||
ICO_DELETE_OVER (new int[] {660, 480, 20, 20}, PropType.ICON),
|
||||
ICO_EDIT (new int[] {640, 500, 20, 20}, PropType.ICON),
|
||||
ICO_EDIT_OVER (new int[] {660, 500, 20, 20}, PropType.ICON),
|
||||
ICO_OPEN (new int[] {660, 520, 20, 20}, PropType.ICON),
|
||||
ICO_MINUS (new int[] {660, 620, 20, 20}, PropType.ICON),
|
||||
ICO_NEW (new int[] {660, 540, 20, 20}, PropType.ICON),
|
||||
ICO_PLUS (new int[] {660, 600, 20, 20}, PropType.ICON),
|
||||
ICO_PRINT (new int[] {660, 640, 20, 20}, PropType.ICON),
|
||||
ICO_SAVE (new int[] {660, 560, 20, 20}, PropType.ICON),
|
||||
ICO_SAVEAS (new int[] {660, 580, 20, 20}, PropType.ICON),
|
||||
ICO_CLOSE (new int[] {640, 640, 20, 20}, PropType.ICON),
|
||||
ICO_LIST (new int[] {640, 660, 20, 20}, PropType.ICON),
|
||||
ICO_CARD_IMAGE (new int[] {660, 660, 20, 20}, PropType.ICON),
|
||||
ICO_UNKNOWN (new int[] {0, 720, 80, 80}, PropType.ICON),
|
||||
ICO_LOGO (new int[] {480, 0, 200, 200}, PropType.ICON),
|
||||
ICO_FLIPCARD (new int[] {400, 0, 80, 120}, PropType.ICON),
|
||||
ICO_FAVICON (new int[] {0, 640, 80, 80}, PropType.ICON),
|
||||
|
||||
//layout images
|
||||
IMG_HANDLE (new int[] {320, 450, 80, 20}, PropType.IMAGE),
|
||||
IMG_CUR_L (new int[] {564, 724, 32, 32}, PropType.IMAGE),
|
||||
IMG_CUR_R (new int[] {564, 764, 32, 32}, PropType.IMAGE),
|
||||
IMG_CUR_T (new int[] {604, 724, 32, 32}, PropType.IMAGE),
|
||||
IMG_CUR_B (new int[] {604, 764, 32, 32}, PropType.IMAGE),
|
||||
IMG_CUR_TAB (new int[] {644, 764, 32, 32}, PropType.IMAGE),
|
||||
|
||||
//editor images
|
||||
IMG_STAR_OUTINE (new int[] {640, 460, 20, 20}, PropType.IMAGE),
|
||||
IMG_STAR_FILLED (new int[] {660, 460, 20, 20}, PropType.IMAGE),
|
||||
IMG_ARTIFACT (new int[] {280, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_CREATURE (new int[] {240, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_ENCHANTMENT (new int[] {320, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_INSTANT (new int[] {360, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_LAND (new int[] {120, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_MULTI (new int[] {80, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_PLANESWALKER (new int[] {200, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_PACK (new int[] {80, 760, 40, 40}, PropType.IMAGE),
|
||||
IMG_SORCERY (new int[] {160, 720, 40, 40}, PropType.IMAGE),
|
||||
|
||||
//button images
|
||||
IMG_BTN_START_UP (new int[] {480, 200, 160, 80}, PropType.ICON),
|
||||
IMG_BTN_START_OVER (new int[] {480, 280, 160, 80}, PropType.ICON),
|
||||
IMG_BTN_START_DOWN (new int[] {480, 360, 160, 80}, PropType.ICON),
|
||||
IMG_BTN_UP_LEFT (new int[] {80, 0, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_UP_CENTER (new int[] {120, 0, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_UP_RIGHT (new int[] {160, 0, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_OVER_LEFT (new int[] {80, 40, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_OVER_CENTER (new int[] {120, 40, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_OVER_RIGHT (new int[] {160, 40, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_DOWN_LEFT (new int[] {80, 80, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_DOWN_CENTER (new int[] {120, 80, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_DOWN_RIGHT (new int[] {160, 80, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_FOCUS_LEFT (new int[] {80, 120, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_FOCUS_CENTER (new int[] {120, 120, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_FOCUS_RIGHT (new int[] {160, 120, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_TOGGLE_LEFT (new int[] {80, 160, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_TOGGLE_CENTER (new int[] {120, 160, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_TOGGLE_RIGHT (new int[] {160, 160, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_DISABLED_LEFT (new int[] {80, 200, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_DISABLED_CENTER (new int[] {120, 200, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_DISABLED_RIGHT (new int[] {160, 200, 40, 40}, PropType.ICON);
|
||||
|
||||
private int[] coords;
|
||||
private PropType type;
|
||||
|
||||
private FSkinProp(final int[] coords0, final PropType type0) {
|
||||
coords = coords0;
|
||||
type = type0;
|
||||
}
|
||||
|
||||
public int[] getCoords() {
|
||||
return coords;
|
||||
}
|
||||
public PropType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public enum PropType {
|
||||
BACKGROUND,
|
||||
COLOR,
|
||||
IMAGE,
|
||||
ICON,
|
||||
FOIL,
|
||||
OLD_FOIL
|
||||
}
|
||||
}
|
||||
5
forge-gui/src/main/java/forge/assets/ISkinImage.java
Normal file
5
forge-gui/src/main/java/forge/assets/ISkinImage.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package forge.assets;
|
||||
|
||||
public interface ISkinImage {
|
||||
|
||||
}
|
||||
131
forge-gui/src/main/java/forge/assets/ImageUtil.java
Normal file
131
forge-gui/src/main/java/forge/assets/ImageUtil.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package forge.assets;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardSplitType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.Base64Coder;
|
||||
|
||||
public class ImageUtil {
|
||||
public static PaperCard getPaperCardFromImageKey(String key) {
|
||||
if( key == null )
|
||||
return null;
|
||||
|
||||
PaperCard cp = StaticData.instance().getCommonCards().getCard(key);
|
||||
if ( cp == null )
|
||||
cp = StaticData.instance().getVariantCards().getCard(key);
|
||||
return cp;
|
||||
}
|
||||
|
||||
public static String getImageRelativePath(PaperCard cp, boolean backFace, boolean includeSet, boolean isDownloadUrl) {
|
||||
final String nameToUse = cp == null ? null : getNameToUse(cp, backFace);
|
||||
if ( null == nameToUse )
|
||||
return null;
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
|
||||
CardRules card = cp.getRules();
|
||||
String edition = cp.getEdition();
|
||||
s.append(toMWSFilename(nameToUse));
|
||||
|
||||
final int cntPictures;
|
||||
final boolean hasManyPictures;
|
||||
final CardDb db = !card.isVariant() ? FModel.getMagicDb().getCommonCards() : FModel.getMagicDb().getVariantCards();
|
||||
if (includeSet) {
|
||||
cntPictures = db.getPrintCount(card.getName(), edition);
|
||||
hasManyPictures = cntPictures > 1;
|
||||
} else {
|
||||
// without set number of pictures equals number of urls provided in Svar:Picture
|
||||
String urls = card.getPictureUrl(backFace);
|
||||
cntPictures = StringUtils.countMatches(urls, "\\") + 1;
|
||||
|
||||
// raise the art index limit to the maximum of the sets this card was printed in
|
||||
int maxCntPictures = db.getMaxPrintCount(card.getName());
|
||||
hasManyPictures = maxCntPictures > 1;
|
||||
}
|
||||
|
||||
int artIdx = cp.getArtIndex() - 1;
|
||||
if (hasManyPictures) {
|
||||
if ( cntPictures <= artIdx ) // prevent overflow
|
||||
artIdx = cntPictures == 0 ? 0 : artIdx % cntPictures;
|
||||
s.append(artIdx + 1);
|
||||
}
|
||||
|
||||
// for whatever reason, MWS-named plane cards don't have the ".full" infix
|
||||
if (!card.getType().isPlane() && !card.getType().isPhenomenon()) {
|
||||
s.append(".full");
|
||||
}
|
||||
|
||||
final String fname;
|
||||
if (isDownloadUrl) {
|
||||
s.append(".jpg");
|
||||
fname = Base64Coder.encodeString(s.toString(), true);
|
||||
} else {
|
||||
fname = s.toString();
|
||||
}
|
||||
|
||||
if (includeSet) {
|
||||
String editionAliased = isDownloadUrl ? FModel.getMagicDb().getEditions().getCode2ByCode(edition) : getSetFolder(edition);
|
||||
return String.format("%s/%s", editionAliased, fname);
|
||||
} else {
|
||||
return fname;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean mayEnlarge() {
|
||||
return FModel.getPreferences().getPrefBoolean(FPref.UI_SCALE_LARGER);
|
||||
}
|
||||
|
||||
public static boolean hasBackFacePicture(PaperCard cp) {
|
||||
CardSplitType cst = cp.getRules().getSplitType();
|
||||
return cst == CardSplitType.Transform || cst == CardSplitType.Flip;
|
||||
}
|
||||
|
||||
public static String getSetFolder(String edition) {
|
||||
return !ForgeConstants.CACHE_CARD_PICS_SUBDIR.containsKey(edition)
|
||||
? FModel.getMagicDb().getEditions().getCode2ByCode(edition) // by default 2-letter codes from MWS are used
|
||||
: ForgeConstants.CACHE_CARD_PICS_SUBDIR.get(edition); // may use custom paths though
|
||||
}
|
||||
|
||||
public static String getNameToUse(PaperCard cp, boolean backFace) {
|
||||
final CardRules card = cp.getRules();
|
||||
if (backFace ) {
|
||||
if ( hasBackFacePicture(cp) )
|
||||
return card.getOtherPart().getName();
|
||||
else
|
||||
return null;
|
||||
} else if(CardSplitType.Split == cp.getRules().getSplitType()) {
|
||||
return card.getMainPart().getName() + card.getOtherPart().getName();
|
||||
} else {
|
||||
return cp.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getImageKey(PaperCard cp, boolean backFace, boolean includeSet) {
|
||||
return getImageRelativePath(cp, backFace, includeSet, false);
|
||||
}
|
||||
|
||||
public static String getDownloadUrl(PaperCard cp, boolean backFace) {
|
||||
return getImageRelativePath(cp, backFace, true, true);
|
||||
}
|
||||
|
||||
public static String toMWSFilename(String in) {
|
||||
final StringBuffer out = new StringBuffer();
|
||||
char c;
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
c = in.charAt(i);
|
||||
if ((c == '"') || (c == '/') || (c == ':') || (c == '?')) {
|
||||
out.append("");
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package forge.gui;
|
||||
package forge.card;
|
||||
|
||||
import javax.xml.stream.*;
|
||||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import forge.item.IPaperCard;
|
||||
import forge.properties.ForgeConstants;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -21,21 +25,22 @@ public class CardPreferences {
|
||||
private static final XMLEvent TAB = EVENT_FACTORY.createDTD("\t");
|
||||
private static Map<String, CardPreferences> allPrefs = new HashMap<String, CardPreferences>();
|
||||
|
||||
public static CardPreferences getPrefs(String name) {
|
||||
CardPreferences prefs = allPrefs.get(name);
|
||||
public static CardPreferences getPrefs(IPaperCard card) {
|
||||
String key = card.getName(); //TODO: Consider include art/set in key
|
||||
CardPreferences prefs = allPrefs.get(key);
|
||||
if (prefs == null) {
|
||||
prefs = new CardPreferences();
|
||||
allPrefs.put(name, prefs);
|
||||
allPrefs.put(key, prefs);
|
||||
}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
public static void load(String filename) {
|
||||
public static void load() {
|
||||
allPrefs.clear();
|
||||
|
||||
try {
|
||||
final XMLInputFactory in = XMLInputFactory.newInstance();
|
||||
final XMLEventReader reader = in.createXMLEventReader(new FileInputStream(filename));
|
||||
final XMLEventReader reader = in.createXMLEventReader(new FileInputStream(ForgeConstants.CARD_PREFS_FILE));
|
||||
|
||||
XMLEvent event;
|
||||
StartElement element;
|
||||
@@ -78,10 +83,10 @@ public class CardPreferences {
|
||||
}
|
||||
}
|
||||
|
||||
public static void save(String filename) {
|
||||
public static void save() {
|
||||
try {
|
||||
final XMLOutputFactory out = XMLOutputFactory.newInstance();
|
||||
final XMLEventWriter writer = out.createXMLEventWriter(new FileOutputStream(filename));
|
||||
final XMLEventWriter writer = out.createXMLEventWriter(new FileOutputStream(ForgeConstants.CARD_PREFS_FILE));
|
||||
|
||||
writer.add(EVENT_FACTORY.createStartDocument());
|
||||
writer.add(NEWLINE);
|
||||
@@ -1,8 +1,8 @@
|
||||
package forge.view;
|
||||
package forge.card;
|
||||
|
||||
import forge.CardStorageReader;
|
||||
import forge.card.CardRules;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
|
||||
import java.io.*;
|
||||
@@ -24,7 +24,7 @@ public class CardReaderExperiments {
|
||||
output.add(new ArrayList<String>());
|
||||
}
|
||||
|
||||
final List<File> allFiles = CardStorageReader.collectCardFiles(new ArrayList<File>(), new File(NewConstants.CARD_DATA_DIR));
|
||||
final List<File> allFiles = CardStorageReader.collectCardFiles(new ArrayList<File>(), new File(ForgeConstants.CARD_DATA_DIR));
|
||||
Charset charset = Charset.forName(CardStorageReader.DEFAULT_CHARSET_NAME);
|
||||
final CardRules.Reader rulesReader = new CardRules.Reader();
|
||||
for (File file : allFiles) {
|
||||
@@ -15,11 +15,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.workshop;
|
||||
package forge.card;
|
||||
|
||||
import forge.CardStorageReader;
|
||||
import forge.SOptionPane;
|
||||
import forge.card.CardRules;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -61,7 +62,7 @@ public final class CardScriptInfo {
|
||||
return true;
|
||||
}
|
||||
catch (final Exception ex) {
|
||||
FOptionPane.showErrorDialog("Problem writing file - " + this.file + " : " + ex);
|
||||
SOptionPane.showErrorDialog("Problem writing file - " + this.file + " : " + ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.control;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.ImageCache;
|
||||
import forge.Singletons;
|
||||
import forge.ai.LobbyPlayerAi;
|
||||
import forge.control.KeyboardShortcuts.Shortcut;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameRules;
|
||||
import forge.game.GameType;
|
||||
import forge.game.Match;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.LobbyPlayer;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.gui.CardPreferences;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.framework.*;
|
||||
import forge.gui.home.settings.GamePlayerUtil;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.VMatchUI;
|
||||
import forge.gui.match.controllers.CDock;
|
||||
import forge.gui.match.controllers.CLog;
|
||||
import forge.gui.match.controllers.CPrompt;
|
||||
import forge.gui.match.controllers.CStack;
|
||||
import forge.gui.match.views.VAntes;
|
||||
import forge.gui.match.views.VField;
|
||||
import forge.gui.menus.ForgeMenu;
|
||||
import forge.gui.player.LobbyPlayerHuman;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.net.FServer;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.data.QuestPreferences.QPref;
|
||||
import forge.quest.io.QuestDataIO;
|
||||
import forge.sound.SoundSystem;
|
||||
import forge.view.FFrame;
|
||||
import forge.view.FView;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FControl.
|
||||
* </p>
|
||||
* Controls all Forge UI functionality inside one JFrame. This class switches
|
||||
* between various display screens in that JFrame. Controllers are instantiated
|
||||
* separately by each screen's top level view class.
|
||||
*/
|
||||
public enum FControl implements KeyEventDispatcher {
|
||||
instance;
|
||||
|
||||
private ForgeMenu forgeMenu;
|
||||
private List<Shortcut> shortcuts;
|
||||
private JLayeredPane display;
|
||||
private FScreen currentScreen;
|
||||
private boolean altKeyLastDown;
|
||||
private CloseAction closeAction;
|
||||
|
||||
public static enum CloseAction {
|
||||
NONE,
|
||||
CLOSE_SCREEN,
|
||||
EXIT_FORGE
|
||||
}
|
||||
|
||||
private final SoundSystem soundSystem = new SoundSystem();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FControl.
|
||||
* </p>
|
||||
* Controls all Forge UI functionality inside one JFrame. This class
|
||||
* switches between various display screens in that JFrame. Controllers are
|
||||
* instantiated separately by each screen's top level view class.
|
||||
*/
|
||||
private FControl() {
|
||||
Singletons.getView().getFrame().addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(final WindowEvent e) {
|
||||
switch (closeAction) {
|
||||
case NONE: //prompt user for close action if not previously specified
|
||||
String[] options = {"Close Screen", "Exit Forge", "Cancel"};
|
||||
int reply = FOptionPane.showOptionDialog(
|
||||
"Forge now supports navigation tabs which allow closing and switching between different screens with ease. "
|
||||
+ "As a result, you no longer need to use the X button in the upper right to close the current screen and go back."
|
||||
+ "\n\n"
|
||||
+ "Please select what you want to happen when clicking the X button in the upper right. This choice will be used "
|
||||
+ "going forward and you will not see this message again. You can change this behavior at any time in Preferences.",
|
||||
"Select Your Close Action",
|
||||
FOptionPane.INFORMATION_ICON,
|
||||
options,
|
||||
2);
|
||||
switch (reply) {
|
||||
case 0: //Close Screen
|
||||
setCloseAction(CloseAction.CLOSE_SCREEN);
|
||||
windowClosing(e); //call again to apply chosen close action
|
||||
return;
|
||||
case 1: //Exit Forge
|
||||
setCloseAction(CloseAction.EXIT_FORGE);
|
||||
windowClosing(e); //call again to apply chosen close action
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CLOSE_SCREEN:
|
||||
Singletons.getView().getNavigationBar().closeSelectedTab();
|
||||
break;
|
||||
case EXIT_FORGE:
|
||||
if (exitForge()) { return; }
|
||||
break;
|
||||
}
|
||||
//prevent closing Forge if we reached this point
|
||||
Singletons.getView().getFrame().setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public CloseAction getCloseAction() {
|
||||
return this.closeAction;
|
||||
}
|
||||
|
||||
public void setCloseAction(CloseAction closeAction0) {
|
||||
if (this.closeAction == closeAction0) { return; }
|
||||
this.closeAction = closeAction0;
|
||||
Singletons.getView().getNavigationBar().updateBtnCloseTooltip();
|
||||
|
||||
final ForgePreferences prefs = Singletons.getModel().getPreferences();
|
||||
prefs.setPref(FPref.UI_CLOSE_ACTION, closeAction0.toString());
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
public boolean canExitForge(boolean forRestart) {
|
||||
String action = (forRestart ? "Restart" : "Exit");
|
||||
String userPrompt = "Are you sure you wish to " + (forRestart ? "restart" : "exit") + " Forge?";
|
||||
if (this.game != null) {
|
||||
userPrompt = "A game is currently active. " + userPrompt;
|
||||
}
|
||||
if (!FOptionPane.showConfirmDialog(userPrompt, action + " Forge", action, "Cancel", this.game == null)) { //default Yes if no game active
|
||||
return false;
|
||||
}
|
||||
if (!CDeckEditorUI.SINGLETON_INSTANCE.canSwitchAway(true)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean exitForge() {
|
||||
if (!canExitForge(false)) {
|
||||
return false;
|
||||
}
|
||||
Singletons.getView().getFrame().setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
System.exit(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** After view and model have been initialized, control can start.
|
||||
* @param isHeadlessMode */
|
||||
public void initialize() {
|
||||
// Preloads skin components (using progress bar).
|
||||
FSkin.loadFull(true);
|
||||
|
||||
CardPreferences.load(NewConstants.CARD_PREFS_FILE);
|
||||
DeckPreferences.load(NewConstants.DECK_PREFS_FILE);
|
||||
ItemManagerConfig.load(NewConstants.ITEM_VIEW_PREFS_FILE);
|
||||
|
||||
this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts();
|
||||
this.display = FView.SINGLETON_INSTANCE.getLpnDocument();
|
||||
|
||||
final ForgePreferences prefs = Singletons.getModel().getPreferences();
|
||||
|
||||
this.closeAction = CloseAction.valueOf(prefs.getPref(FPref.UI_CLOSE_ACTION));
|
||||
|
||||
FView.SINGLETON_INSTANCE.setSplashProgessBarMessage("About to load current quest.");
|
||||
// Preload quest data if present
|
||||
final File dirQuests = new File(NewConstants.QUEST_SAVE_DIR);
|
||||
final String questname = Singletons.getModel().getQuestPreferences().getPref(QPref.CURRENT_QUEST);
|
||||
final File data = new File(dirQuests.getPath(), questname);
|
||||
if (data.exists()) {
|
||||
Singletons.getModel().getQuest().load(QuestDataIO.loadData(data));
|
||||
}
|
||||
|
||||
// Handles resizing in null layouts of layers in JLayeredPane as well as saving window layout
|
||||
final FFrame window = Singletons.getView().getFrame();
|
||||
window.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(final ComponentEvent e) {
|
||||
sizeChildren();
|
||||
window.updateNormalBounds();
|
||||
SLayoutIO.saveWindowLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentMoved(final ComponentEvent e) {
|
||||
window.updateNormalBounds();
|
||||
SLayoutIO.saveWindowLayout();
|
||||
}
|
||||
});
|
||||
|
||||
FView.SINGLETON_INSTANCE.getLpnDocument().addMouseListener(SOverflowUtil.getHideOverflowListener());
|
||||
FView.SINGLETON_INSTANCE.getLpnDocument().addComponentListener(SResizingUtil.getWindowResizeListener());
|
||||
|
||||
setGlobalKeyboardHandler();
|
||||
|
||||
FView.SINGLETON_INSTANCE.setSplashProgessBarMessage("Opening main window...");
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Singletons.getView().initialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setGlobalKeyboardHandler() {
|
||||
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
manager.addKeyEventDispatcher(this);
|
||||
}
|
||||
|
||||
public ForgeMenu getForgeMenu() {
|
||||
if (this.forgeMenu == null) {
|
||||
this.forgeMenu = new ForgeMenu();
|
||||
}
|
||||
return this.forgeMenu;
|
||||
}
|
||||
|
||||
public FScreen getCurrentScreen() {
|
||||
return this.currentScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches between display screens in top level JFrame.
|
||||
*/
|
||||
public boolean setCurrentScreen(FScreen screen) {
|
||||
return setCurrentScreen(screen, false);
|
||||
}
|
||||
public boolean setCurrentScreen(FScreen screen, boolean previousScreenClosed) {
|
||||
//TODO: Uncomment the line below if this function stops being used to refresh
|
||||
//the current screen in some places (such as Continue and Restart in the match screen)
|
||||
//if (this.currentScreen == screen) { return; }
|
||||
|
||||
//give previous screen a chance to perform special switch handling and/or cancel switching away from screen
|
||||
if (this.currentScreen != screen && !Singletons.getView().getNavigationBar().canSwitch(screen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentScreen == FScreen.MATCH_SCREEN) { //hide targeting overlay and reset image if was on match screen
|
||||
SOverlayUtils.hideTargetingOverlay();
|
||||
if (isMatchBackgroundImageVisible()) {
|
||||
FView.SINGLETON_INSTANCE.getPnlInsets().setForegroundImage(new ImageIcon());
|
||||
}
|
||||
}
|
||||
|
||||
clearChildren(JLayeredPane.DEFAULT_LAYER);
|
||||
SOverlayUtils.hideOverlay();
|
||||
ImageCache.clear(); //reduce memory usage by clearing image cache when switching screens
|
||||
|
||||
this.currentScreen = screen;
|
||||
|
||||
//load layout for new current screen
|
||||
try {
|
||||
SLayoutIO.loadLayout(null);
|
||||
} catch (InvalidLayoutFileException ex) {
|
||||
GuiDialog.message("Your " + screen.getTabCaption() + " layout file could not be read. It will be deleted after you press OK.\nThe game will proceed with default layout.");
|
||||
if (screen.deleteLayoutFile()) {
|
||||
SLayoutIO.loadLayout(null); //try again
|
||||
}
|
||||
}
|
||||
|
||||
screen.getView().populate();
|
||||
screen.getController().initialize();
|
||||
|
||||
if (screen == FScreen.MATCH_SCREEN) {
|
||||
if (isMatchBackgroundImageVisible()) {
|
||||
FView.SINGLETON_INSTANCE.getPnlInsets().setForegroundImage(FSkin.getIcon(FSkin.Backgrounds.BG_MATCH));
|
||||
}
|
||||
SOverlayUtils.showTargetingOverlay();
|
||||
}
|
||||
|
||||
Singletons.getView().getNavigationBar().updateSelectedTab();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isMatchBackgroundImageVisible() {
|
||||
return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_MATCH_IMAGE_VISIBLE);
|
||||
}
|
||||
|
||||
public boolean ensureScreenActive(FScreen screen) {
|
||||
if (this.currentScreen == screen) { return true; }
|
||||
|
||||
return setCurrentScreen(screen);
|
||||
}
|
||||
|
||||
/** @return List<Shortcut> A list of attached keyboard shortcut descriptions and properties. */
|
||||
public List<Shortcut> getShortcuts() {
|
||||
return this.shortcuts;
|
||||
}
|
||||
|
||||
/** Remove all children from a specified layer. */
|
||||
private void clearChildren(final int layer0) {
|
||||
final Component[] children = FView.SINGLETON_INSTANCE.getLpnDocument().getComponentsInLayer(layer0);
|
||||
|
||||
for (final Component c : children) {
|
||||
display.remove(c);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sizes children of JLayeredPane to fully fit their layers. */
|
||||
private void sizeChildren() {
|
||||
Component[] children = display.getComponentsInLayer(JLayeredPane.DEFAULT_LAYER);
|
||||
if (children.length != 0) { children[0].setSize(display.getSize()); }
|
||||
|
||||
children = display.getComponentsInLayer(FView.TARGETING_LAYER);
|
||||
if (children.length != 0) { children[0].setSize(display.getSize()); }
|
||||
|
||||
children = display.getComponentsInLayer(JLayeredPane.MODAL_LAYER);
|
||||
if (children.length != 0) { children[0].setSize(display.getSize()); }
|
||||
}
|
||||
|
||||
public Player getCurrentPlayer() {
|
||||
// try current priority
|
||||
Player currentPriority = game.getPhaseHandler().getPriorityPlayer();
|
||||
if (null != currentPriority && currentPriority.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer()) {
|
||||
return currentPriority;
|
||||
}
|
||||
|
||||
// otherwise find just any player, belonging to this lobbyplayer
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (p.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean mayShowCard(Card c) {
|
||||
return game == null || !gameHasHumanPlayer || c.canBeShownTo(getCurrentPlayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public SoundSystem getSoundSystem() {
|
||||
return soundSystem;
|
||||
}
|
||||
|
||||
private Game game;
|
||||
private boolean gameHasHumanPlayer;
|
||||
|
||||
public Game getObservedGame() {
|
||||
return game;
|
||||
}
|
||||
|
||||
public final void stopGame() {
|
||||
List<Player> pp = new ArrayList<Player>();
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (p.getOriginalLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer()) {
|
||||
pp.add(p);
|
||||
}
|
||||
}
|
||||
boolean hasHuman = !pp.isEmpty();
|
||||
|
||||
if (pp.isEmpty()) {
|
||||
pp.addAll(game.getPlayers()); // no human? then all players surrender!
|
||||
}
|
||||
|
||||
for (Player p: pp) {
|
||||
p.concede();
|
||||
}
|
||||
|
||||
Player priorityPlayer = game.getPhaseHandler().getPriorityPlayer();
|
||||
boolean humanHasPriority = priorityPlayer == null || priorityPlayer.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer();
|
||||
|
||||
if (hasHuman && humanHasPriority) {
|
||||
game.getAction().checkGameOverCondition();
|
||||
}
|
||||
else {
|
||||
game.isGameOver(); // this is synchronized method - it's used to make Game-0 thread see changes made here
|
||||
inputQueue.onGameOver(false); //release any waiting input, effectively passing priority
|
||||
}
|
||||
|
||||
playbackControl.onGameStopRequested();
|
||||
}
|
||||
|
||||
private InputQueue inputQueue;
|
||||
public InputQueue getInputQueue() {
|
||||
return inputQueue;
|
||||
}
|
||||
|
||||
public final void startGameWithUi(final Match match) {
|
||||
if (this.game != null) {
|
||||
this.setCurrentScreen(FScreen.MATCH_SCREEN);
|
||||
SOverlayUtils.hideOverlay();
|
||||
FOptionPane.showMessageDialog("Cannot start a new game while another game is already in progress.");
|
||||
return; //TODO: See if it's possible to run multiple games at once without crashing
|
||||
}
|
||||
setPlayerName(match.getPlayers());
|
||||
final Game newGame = match.createGame();
|
||||
attachToGame(newGame);
|
||||
|
||||
// It's important to run match in a different thread to allow GUI inputs to be invoked from inside game.
|
||||
// Game is set on pause while gui player takes decisions
|
||||
game.getAction().invoke(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
match.startGame(newGame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public final void endCurrentGame() {
|
||||
if (this.game == null) { return; }
|
||||
|
||||
Singletons.getView().getNavigationBar().closeTab(FScreen.MATCH_SCREEN);
|
||||
this.game = null;
|
||||
}
|
||||
|
||||
private final FControlGameEventHandler fcVisitor = new FControlGameEventHandler(this);
|
||||
private final FControlGamePlayback playbackControl = new FControlGamePlayback(this);
|
||||
private void attachToGame(Game game0) {
|
||||
if (game0.getRules().getGameType() == GameType.Quest) {
|
||||
QuestController qc = Singletons.getModel().getQuest();
|
||||
// Reset new list when the Match round starts, not when each game starts
|
||||
if (game0.getMatch().getPlayedGames().isEmpty()) {
|
||||
qc.getCards().resetNewList();
|
||||
}
|
||||
game0.subscribeToEvents(qc); // this one listens to player's mulligans ATM
|
||||
}
|
||||
|
||||
inputQueue = new InputQueue();
|
||||
|
||||
this.game = game0;
|
||||
game.subscribeToEvents(Singletons.getControl().getSoundSystem());
|
||||
|
||||
LobbyPlayer humanLobbyPlayer = FServer.instance.getLobby().getGuiPlayer();
|
||||
// The UI controls should use these game data as models
|
||||
CMatchUI.SINGLETON_INSTANCE.initMatch(game.getRegisteredPlayers(), humanLobbyPlayer);
|
||||
CDock.SINGLETON_INSTANCE.setModel(game, humanLobbyPlayer);
|
||||
CStack.SINGLETON_INSTANCE.setModel(game.getStack(), humanLobbyPlayer);
|
||||
CLog.SINGLETON_INSTANCE.setModel(game.getGameLog());
|
||||
|
||||
Singletons.getModel().getPreferences().actuateMatchPreferences();
|
||||
VAntes.SINGLETON_INSTANCE.setModel(game.getRegisteredPlayers());
|
||||
|
||||
setCurrentScreen(FScreen.MATCH_SCREEN);
|
||||
SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc());
|
||||
|
||||
CPrompt.SINGLETON_INSTANCE.getInputControl().setGame(game);
|
||||
|
||||
// Listen to DuelOutcome event to show ViewWinLose
|
||||
game.subscribeToEvents(fcVisitor);
|
||||
|
||||
// Add playback controls to match if needed
|
||||
gameHasHumanPlayer = false;
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (p.getController().getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer())
|
||||
gameHasHumanPlayer = true;
|
||||
}
|
||||
|
||||
if (!gameHasHumanPlayer) {
|
||||
game.subscribeToEvents(playbackControl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (final VField field : VMatchUI.SINGLETON_INSTANCE.getFieldViews()) {
|
||||
field.getDetailsPanel().getLblLibrary().setHoverable(ForgePreferences.DEV_MODE);
|
||||
}
|
||||
|
||||
// per player observers were set in CMatchUI.SINGLETON_INSTANCE.initMatch
|
||||
//Set Field shown to current player.
|
||||
VField nextField = CMatchUI.SINGLETON_INSTANCE.getFieldViewFor(game.getPlayers().get(0));
|
||||
SDisplayUtil.showTab(nextField);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.awt.KeyEventDispatcher#dispatchKeyEvent(java.awt.event.KeyEvent)
|
||||
*/
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent e) {
|
||||
// Show Forge menu if Alt key pressed without modifiers and released without pressing any other keys in between
|
||||
if (e.getKeyCode() == KeyEvent.VK_ALT) {
|
||||
if (e.getID() == KeyEvent.KEY_RELEASED) {
|
||||
if (altKeyLastDown) {
|
||||
forgeMenu.show(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (e.getID() == KeyEvent.KEY_PRESSED && e.getModifiers() == KeyEvent.ALT_MASK) {
|
||||
altKeyLastDown = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
altKeyLastDown = false;
|
||||
if (e.getID() == KeyEvent.KEY_PRESSED) {
|
||||
if (forgeMenu.handleKeyEvent(e)) { //give Forge menu the chance to handle the key event
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (e.getID() == KeyEvent.KEY_RELEASED) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) {
|
||||
forgeMenu.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
//Allow the event to be redispatched
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts user for a name that will be used instead of "Human" during gameplay.
|
||||
* <p>
|
||||
* This is a one time only event that is triggered when starting a game and the
|
||||
* PLAYER_NAME setting is blank. Does not apply to a hotseat game.
|
||||
*/
|
||||
private void setPlayerName(List<RegisteredPlayer> players) {
|
||||
final ForgePreferences prefs = Singletons.getModel().getPreferences();
|
||||
if (StringUtils.isBlank(prefs.getPref(FPref.PLAYER_NAME))) {
|
||||
boolean isPlayerOneHuman = players.get(0).getPlayer() instanceof LobbyPlayerHuman;
|
||||
boolean isPlayerTwoComputer = players.get(1).getPlayer() instanceof LobbyPlayerAi;
|
||||
if (isPlayerOneHuman && isPlayerTwoComputer) {
|
||||
GamePlayerUtil.setPlayerName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startMatch(GameType gameType, List<RegisteredPlayer> players) {
|
||||
startMatch(gameType, null, players);
|
||||
}
|
||||
|
||||
public void startMatch(GameType gameType, List<GameType> appliedVariants, List<RegisteredPlayer> players) {
|
||||
boolean useRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
|
||||
for(RegisteredPlayer rp : players) {
|
||||
rp.setRandomFoil(useRandomFoil);
|
||||
}
|
||||
|
||||
GameRules rules = new GameRules(gameType);
|
||||
if (null != appliedVariants && !appliedVariants.isEmpty())
|
||||
rules.setAppliedVariants(appliedVariants);
|
||||
rules.setPlayForAnte(Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE));
|
||||
rules.setManaBurn(Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_MANABURN));
|
||||
rules.canCloneUseTargetsImage = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
|
||||
|
||||
final Match mc = new Match(rules, players);
|
||||
SOverlayUtils.startGameOverlay();
|
||||
SOverlayUtils.showOverlay();
|
||||
FThreads.invokeInEdtLater(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
startGameWithUi(mc);
|
||||
SOverlayUtils.hideOverlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,27 +3,15 @@ package forge.control;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.SGuiChoose;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.event.*;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.gui.framework.SDisplayUtil;
|
||||
import forge.gui.framework.SLayoutIO;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.VMatchUI;
|
||||
import forge.gui.match.ViewWinLose;
|
||||
import forge.gui.match.controllers.CPrompt;
|
||||
import forge.gui.match.controllers.CStack;
|
||||
import forge.gui.match.views.VField;
|
||||
import forge.gui.match.views.VHand;
|
||||
import forge.gui.toolbox.special.PhaseLabel;
|
||||
import forge.net.FServer;
|
||||
import forge.util.Lang;
|
||||
import forge.util.maps.MapOfLists;
|
||||
@@ -35,9 +23,7 @@ import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
private final FControl fc;
|
||||
public FControlGameEventHandler(FControl fc) {
|
||||
this.fc = fc;
|
||||
public FControlGameEventHandler() {
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -50,19 +36,13 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
public Void visit(final GameEventTurnPhase ev) {
|
||||
if (phaseUpdPlanned.getAndSet(true)) return null;
|
||||
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() {
|
||||
PhaseHandler pH = fc.getObservedGame().getPhaseHandler();
|
||||
Player p = pH.getPlayerTurn();
|
||||
PhaseType ph = pH.getPhase();
|
||||
|
||||
phaseUpdPlanned.set(false);
|
||||
|
||||
final CMatchUI matchUi = CMatchUI.SINGLETON_INSTANCE;
|
||||
PhaseLabel lbl = matchUi.getFieldViewFor(p).getPhaseIndicator().getLabelFor(ph);
|
||||
|
||||
matchUi.resetAllPhaseButtons();
|
||||
if (lbl != null) lbl.setActive(true);
|
||||
} });
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
phaseUpdPlanned.set(false);
|
||||
GuiBase.getInterface().updatePhase();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -77,7 +57,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public void run() {
|
||||
combatUpdPlanned.set(false);
|
||||
CMatchUI.SINGLETON_INSTANCE.showCombat(fc.getObservedGame().getCombat());
|
||||
GuiBase.getInterface().showCombat(GuiBase.getInterface().getGame().getCombat());
|
||||
}
|
||||
});
|
||||
return null;
|
||||
@@ -88,15 +68,12 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
public Void visit(final GameEventTurnBegan event) {
|
||||
if (turnUpdPlanned.getAndSet(true)) { return null; }
|
||||
|
||||
final Game game = fc.getObservedGame(); // to make sure control gets a correct game instance
|
||||
final Game game = GuiBase.getInterface().getGame(); // to make sure control gets a correct game instance
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VField nextField = CMatchUI.SINGLETON_INSTANCE.getFieldViewFor(event.turnOwner);
|
||||
SDisplayUtil.showTab(nextField);
|
||||
|
||||
turnUpdPlanned.set(false);
|
||||
CPrompt.SINGLETON_INSTANCE.updateText(game);
|
||||
GuiBase.getInterface().updateTurn(event, game);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
@@ -110,32 +87,27 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
options.add(" -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --");
|
||||
options.add(kv.getValue());
|
||||
}
|
||||
GuiChoose.one("These cards were chosen to ante", options);
|
||||
SGuiChoose.one("These cards were chosen to ante", options);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(GameEventPlayerControl ev) {
|
||||
if (fc.getObservedGame().isGameOver()) {
|
||||
if (GuiBase.getInterface().getGame().isGameOver()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CMatchUI.SINGLETON_INSTANCE.initHandViews(FServer.instance.getLobby().getGuiPlayer());
|
||||
SLayoutIO.loadLayout(null);
|
||||
VMatchUI.SINGLETON_INSTANCE.populate();
|
||||
for (VHand h : VMatchUI.SINGLETON_INSTANCE.getHands()) {
|
||||
h.getLayoutControl().updateHand();
|
||||
}
|
||||
GuiBase.getInterface().updatePlayerControl();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
private final Runnable unlockGameThreadOnGameOver = new Runnable() { @Override public void run() {
|
||||
fc.getInputQueue().onGameOver(true); // this will unlock any game threads waiting for inputs to complete
|
||||
GuiBase.getInterface().getInputQueue().onGameOver(true); // this will unlock any game threads waiting for inputs to complete
|
||||
} };
|
||||
|
||||
@Override
|
||||
@@ -146,17 +118,21 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
@Override
|
||||
public Void visit(GameEventGameFinished ev) {
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() {
|
||||
new ViewWinLose(fc.getObservedGame());
|
||||
SOverlayUtils.showOverlay();
|
||||
} });
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiBase.getInterface().finishGame();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
private final AtomicBoolean stackUpdPlanned = new AtomicBoolean(false);
|
||||
private final Runnable updStack = new Runnable() { @Override public void run() {
|
||||
private final Runnable updStack = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stackUpdPlanned.set(false);
|
||||
CStack.SINGLETON_INSTANCE.update();
|
||||
GuiBase.getInterface().updateStack();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,9 +160,10 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
private final List<Pair<Player, ZoneType>> zonesToUpdate = new Vector<Pair<Player, ZoneType>>();
|
||||
private final Runnable updZones = new Runnable() {
|
||||
@Override public void run() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (zonesToUpdate) {
|
||||
CMatchUI.SINGLETON_INSTANCE.updateZones(zonesToUpdate);
|
||||
GuiBase.getInterface().updateZones(zonesToUpdate);
|
||||
zonesToUpdate.clear();
|
||||
}
|
||||
}
|
||||
@@ -238,7 +215,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (cardsToUpdate) {
|
||||
CMatchUI.SINGLETON_INSTANCE.updateCards(cardsToUpdate);
|
||||
GuiBase.getInterface().updateCards(cardsToUpdate);
|
||||
cardsToUpdate.clear();
|
||||
}
|
||||
}
|
||||
@@ -277,7 +254,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public Void visit(GameEventAttackersDeclared event) {
|
||||
// Skip redraw for GUI player?
|
||||
if (event.player.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer()) {
|
||||
if (event.player.getLobbyPlayer() == FServer.getLobby().getGuiPlayer()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -336,7 +313,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
private final Runnable updManaPool = new Runnable() {
|
||||
@Override public void run() {
|
||||
synchronized (manaPoolUpdate) {
|
||||
CMatchUI.SINGLETON_INSTANCE.updateManaPool(manaPoolUpdate);
|
||||
GuiBase.getInterface().updateManaPool(manaPoolUpdate);
|
||||
manaPoolUpdate.clear();
|
||||
}
|
||||
}
|
||||
@@ -361,7 +338,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
private final Runnable updLives = new Runnable() {
|
||||
@Override public void run() {
|
||||
synchronized (livesUpdate) {
|
||||
CMatchUI.SINGLETON_INSTANCE.updateLives(livesUpdate);
|
||||
GuiBase.getInterface().updateLives(livesUpdate);
|
||||
livesUpdate.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
package forge.control;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.game.event.*;
|
||||
import forge.gui.input.InputPlaybackControl;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.match.input.InputPlaybackControl;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
private final FControl fc;
|
||||
|
||||
private final InputPlaybackControl inputPlayback = new InputPlaybackControl(this);
|
||||
private final AtomicBoolean paused = new AtomicBoolean(false);
|
||||
|
||||
private final CyclicBarrier gameThreadPauser = new CyclicBarrier(2);
|
||||
|
||||
public FControlGamePlayback(FControl fc) {
|
||||
this.fc = fc;
|
||||
public FControlGamePlayback() {
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -52,13 +50,13 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
*/
|
||||
@Override
|
||||
public Void visit(GameEventTurnPhase ev) {
|
||||
boolean isUiToStop = CMatchUI.SINGLETON_INSTANCE.stopAtPhase(ev.playerTurn, ev.phase);
|
||||
boolean isUiToStop = GuiBase.getInterface().stopAtPhase(ev.playerTurn, ev.phase);
|
||||
|
||||
switch(ev.phase) {
|
||||
case COMBAT_END:
|
||||
case COMBAT_DECLARE_ATTACKERS:
|
||||
case COMBAT_DECLARE_BLOCKERS:
|
||||
if (fc.getObservedGame().getPhaseHandler().inCombat()) {
|
||||
if (GuiBase.getInterface().getGame().getPhaseHandler().inCombat()) {
|
||||
pauseForEvent(combatDelay);
|
||||
}
|
||||
break;
|
||||
@@ -77,13 +75,13 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
*/
|
||||
@Override
|
||||
public Void visit(GameEventGameFinished event) {
|
||||
fc.getInputQueue().removeInput(inputPlayback);
|
||||
GuiBase.getInterface().getInputQueue().removeInput(inputPlayback);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(GameEventGameStarted event) {
|
||||
fc.getInputQueue().setInput(inputPlayback);
|
||||
GuiBase.getInterface().getInputQueue().setInput(inputPlayback);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,7 +93,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSpellResolved event) {
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { CMatchUI.SINGLETON_INSTANCE.setCard(event.spell.getHostCard()); } });
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { GuiBase.getInterface().setCard(event.spell.getHostCard()); } });
|
||||
pauseForEvent(resolveDelay);
|
||||
return null;
|
||||
}
|
||||
@@ -105,7 +103,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
*/
|
||||
@Override
|
||||
public Void visit(final GameEventSpellAbilityCast event) {
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { CMatchUI.SINGLETON_INSTANCE.setCard(event.sa.getHostCard()); } });
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { GuiBase.getInterface().setCard(event.sa.getHostCard()); } });
|
||||
pauseForEvent(castDelay);
|
||||
return null;
|
||||
}
|
||||
@@ -139,7 +137,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||
private void releaseGameThread() {
|
||||
// just need to run another thread through the barrier... not edt preferrably :)
|
||||
|
||||
fc.getObservedGame().getAction().invoke(new Runnable() {
|
||||
GuiBase.getInterface().getGame().getAction().invoke(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
package forge.control;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.SDisplayUtil;
|
||||
import forge.gui.home.settings.VSubmenuPreferences.KeyboardShortcutField;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.controllers.CDock;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Consolidates keyboard shortcut assembly into one location
|
||||
* for all shortcuts in the project.
|
||||
*
|
||||
* Just map a new Shortcut object here, set a default in preferences,
|
||||
* and you're done.
|
||||
*/
|
||||
public class KeyboardShortcuts {
|
||||
/**
|
||||
* Attaches all keyboard shortcuts for match UI,
|
||||
* and returns a list of shortcuts with necessary properties for later access.
|
||||
*
|
||||
* @return List<Shortcut> Shortcut objects
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static List<Shortcut> attachKeyboardShortcuts() {
|
||||
final JComponent c = Singletons.getView().getFrame().getLayeredPane();
|
||||
final InputMap im = c.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||
final ActionMap am = c.getActionMap();
|
||||
|
||||
List<Shortcut> list = new ArrayList<Shortcut>();
|
||||
|
||||
//========== Match Shortcuts
|
||||
/** Show stack. */
|
||||
final Action actShowStack = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
||||
}
|
||||
};
|
||||
|
||||
/** Show combat. */
|
||||
final Action actShowCombat = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
SDisplayUtil.showTab(EDocID.REPORT_COMBAT.getDoc());
|
||||
}
|
||||
};
|
||||
|
||||
/** Show console. */
|
||||
final Action actShowConsole = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc());
|
||||
}
|
||||
};
|
||||
|
||||
/** Show players panel. */
|
||||
final Action actShowPlayers = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
SDisplayUtil.showTab(EDocID.REPORT_PLAYERS.getDoc());
|
||||
}
|
||||
};
|
||||
|
||||
/** Show dev panel. */
|
||||
final Action actShowDev = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_MODE_ENABLED)) {
|
||||
SDisplayUtil.showTab(EDocID.DEV_MODE.getDoc());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Concede game. */
|
||||
final Action actConcede = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
CMatchUI.SINGLETON_INSTANCE.concede();
|
||||
}
|
||||
};
|
||||
|
||||
/** End turn. */
|
||||
final Action actEndTurn = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
CDock.SINGLETON_INSTANCE.endTurn();
|
||||
}
|
||||
};
|
||||
|
||||
/** Alpha Strike. */
|
||||
final Action actAllAttack = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
CDock.SINGLETON_INSTANCE.alphaStrike();
|
||||
}
|
||||
};
|
||||
|
||||
/** Targeting visualization overlay. */
|
||||
final Action actTgtOverlay = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (Singletons.getControl().getCurrentScreen() != FScreen.MATCH_SCREEN) { return; }
|
||||
CDock.SINGLETON_INSTANCE.toggleTargeting();
|
||||
}
|
||||
};
|
||||
|
||||
//========== Instantiate shortcut objects and add to list.
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWSTACK, "Match: show stack panel", actShowStack, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWCOMBAT, "Match: show combat panel", actShowCombat, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWCONSOLE, "Match: show console panel", actShowConsole, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWPLAYERS, "Match: show players panel", actShowPlayers, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWDEV, "Match: show dev panel", actShowDev, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_CONCEDE, "Match: concede game", actConcede, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_ENDTURN, "Match: pass priority until EOT or next stack event", actEndTurn, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_ALPHASTRIKE, "Match: Alpha Strike (attack with all available)", actAllAttack, am, im));
|
||||
list.add(new Shortcut(FPref.SHORTCUT_SHOWTARGETING, "Match: toggle targeting visual overlay", actTgtOverlay, am, im));
|
||||
return list;
|
||||
} // End initMatchShortcuts()
|
||||
|
||||
/**
|
||||
*
|
||||
* Instantiates a shortcut key instance with various properties for use
|
||||
* throughout the project.
|
||||
*
|
||||
*/
|
||||
public static class Shortcut {
|
||||
/** */
|
||||
private final FPref prefkeys;
|
||||
/** */
|
||||
private final String description;
|
||||
/** */
|
||||
private final Action handler;
|
||||
/** */
|
||||
private final ActionMap actionMap;
|
||||
/** */
|
||||
private final InputMap inputMap;
|
||||
/** */
|
||||
private KeyStroke key;
|
||||
/** */
|
||||
private String str;
|
||||
|
||||
/**
|
||||
*
|
||||
* Instantiates a shortcut key instance with various properties for use
|
||||
* throughout the project.
|
||||
*
|
||||
* @param prefkey0 String, ident key in forge.preferences
|
||||
* @param description0 String, description of shortcut function
|
||||
* @param handler0 Action, action on execution of shortcut
|
||||
* @param am0 ActionMap, of container targeted by shortcut
|
||||
* @param im0 InputMap, of container targeted by shortcut
|
||||
*/
|
||||
public Shortcut(final FPref prefkey0, final String description0,
|
||||
final Action handler0, final ActionMap am0, final InputMap im0) {
|
||||
|
||||
prefkeys = prefkey0;
|
||||
description = description0;
|
||||
handler = handler0;
|
||||
actionMap = am0;
|
||||
inputMap = im0;
|
||||
attach();
|
||||
}
|
||||
|
||||
/** @return {@link java.lang.String} */
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* String ident key in forge.preferences.
|
||||
* @return {@link java.lang.String}
|
||||
*/
|
||||
public FPref getPrefKey() {
|
||||
return prefkeys;
|
||||
}
|
||||
|
||||
/** */
|
||||
public void attach() {
|
||||
detach();
|
||||
str = Singletons.getModel().getPreferences().getPref(prefkeys);
|
||||
if (!str.isEmpty()) {
|
||||
key = assembleKeystrokes(str.split(" "));
|
||||
|
||||
// Attach key stroke to input map...
|
||||
inputMap.put(key, str);
|
||||
|
||||
// ...then attach actionListener to action map
|
||||
actionMap.put(str, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
public void detach() {
|
||||
inputMap.remove(key);
|
||||
actionMap.remove(str);
|
||||
}
|
||||
} // End class Shortcut
|
||||
|
||||
private static KeyStroke assembleKeystrokes(final String[] keys0) {
|
||||
int[] inputEvents = new int[2];
|
||||
int modifier = 0;
|
||||
int keyEvent = 0;
|
||||
|
||||
inputEvents[0] = 0;
|
||||
inputEvents[1] = 0;
|
||||
|
||||
// If CTRL or SHIFT is pressed, it must be passed as a modifier,
|
||||
// in the form of an input event object. So, first test if these were pressed.
|
||||
// ALT shortcuts will be ignored.
|
||||
for (final String s : keys0) {
|
||||
if (s.equals("16")) { inputEvents[0] = 16; }
|
||||
else if (s.equals("17")) { inputEvents[1] = 17; }
|
||||
else {
|
||||
keyEvent = Integer.valueOf(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Then, convert to InputEvent.
|
||||
if (inputEvents[0] == 16 && inputEvents[1] != 17) {
|
||||
modifier = InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
else if (inputEvents[0] != 16 && inputEvents[1] == 17) {
|
||||
modifier = InputEvent.CTRL_DOWN_MASK;
|
||||
}
|
||||
else if (inputEvents[0] != 0 && inputEvents[1] != 0) {
|
||||
modifier = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
return KeyStroke.getKeyStroke(keyEvent, modifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* - Adds keycode to list stored in name of a text field.
|
||||
* - Code is not added if already in list.
|
||||
* - Backspace removes last code in list.
|
||||
* - Sets text of text field with character equivalent of keycodes.
|
||||
*
|
||||
* @param e   KeyEvent
|
||||
*/
|
||||
public static void addKeyCode(final KeyEvent e) {
|
||||
final KeyboardShortcutField ksf = (KeyboardShortcutField) e.getSource();
|
||||
final String newCode = Integer.toString(e.getKeyCode());
|
||||
final String codestring = ksf.getCodeString();
|
||||
List<String> existingCodes;
|
||||
|
||||
if (codestring != null) {
|
||||
existingCodes = new ArrayList<String>(Arrays.asList(codestring.split(" ")));
|
||||
} else {
|
||||
existingCodes = new ArrayList<String>();
|
||||
}
|
||||
|
||||
// Backspace (8) will remove last code from list.
|
||||
if (e.getKeyCode() == 8) {
|
||||
existingCodes.remove(existingCodes.size() - 1);
|
||||
} else if (!existingCodes.contains(newCode)) {
|
||||
existingCodes.add(newCode);
|
||||
}
|
||||
|
||||
ksf.setCodeString(StringUtils.join(existingCodes, ' '));
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package forge.control;
|
||||
|
||||
import forge.Singletons;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Restarts a java app.
|
||||
* Credit: http://leolewis.website.org/wordpress/2011/07/06/programmatically-restart-a-java-application/
|
||||
*/
|
||||
public class RestartUtil {
|
||||
/**
|
||||
* Sun property pointing the main class and its arguments.
|
||||
* Might not be defined on non Hotspot VM implementations.
|
||||
*/
|
||||
public static final String SUN_JAVA_COMMAND = "sun.java.command";
|
||||
|
||||
/**
|
||||
* Restart the current Java application.
|
||||
* @param runBeforeRestart some custom code to be run before restarting
|
||||
*/
|
||||
public static void restartApplication(final Runnable runBeforeRestart) {
|
||||
if (!Singletons.getControl().canExitForge(true)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// java binary
|
||||
final String java = System.getProperty("java.home")
|
||||
+ File.separator + "bin" + File.separator + "java";
|
||||
|
||||
// vm arguments
|
||||
final List<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
|
||||
final StringBuffer vmArgsOneLine = new StringBuffer();
|
||||
for (final String arg : vmArguments) {
|
||||
// if it's the agent argument : we ignore it otherwise the
|
||||
// address of the old application and the new one will be in conflict
|
||||
if (!arg.contains("-agentlib")) {
|
||||
vmArgsOneLine.append(arg);
|
||||
vmArgsOneLine.append(" ");
|
||||
}
|
||||
}
|
||||
// init the command to execute, add the vm args
|
||||
final StringBuffer cmd = new StringBuffer("\"" + java + "\" " + vmArgsOneLine);
|
||||
|
||||
// program main and program arguments
|
||||
final String[] mainCommand = System.getProperty(SUN_JAVA_COMMAND).split(" ");
|
||||
// program main is a jar
|
||||
if (mainCommand[0].endsWith(".jar")) {
|
||||
// if it's a jar, add -jar mainJar
|
||||
cmd.append("-jar " + new File(mainCommand[0]).getPath());
|
||||
} else {
|
||||
// else it's a .class, add the classpath and mainClass
|
||||
cmd.append("-cp \"" + System.getProperty("java.class.path") + "\" " + mainCommand[0]);
|
||||
}
|
||||
// finally add program arguments
|
||||
for (int i = 1; i < mainCommand.length; i++) {
|
||||
cmd.append(" ");
|
||||
cmd.append(mainCommand[i]);
|
||||
}
|
||||
// execute the command in a shutdown hook, to be sure that all the
|
||||
// resources have been disposed before restarting the application
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Runtime.getRuntime().exec(cmd.toString());
|
||||
} catch (final IOException e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
// execute some custom code before restarting
|
||||
if (runBeforeRestart != null) {
|
||||
runBeforeRestart.run();
|
||||
}
|
||||
// exit
|
||||
System.exit(0);
|
||||
} catch (final Exception ex) {
|
||||
//ErrorViewer.showError(ex, "Restart \"%s\" exception", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
218
forge-gui/src/main/java/forge/deck/DeckGeneratorTheme.java
Normal file
218
forge-gui/src/main/java/forge/deck/DeckGeneratorTheme.java
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.deck;
|
||||
|
||||
import forge.card.ICardDatabase;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.generation.DeckGeneratorBase;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* ThemeDeckGenerator class.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: ThemeDeckGenerator.java 25022 2014-03-02 14:19:50Z teferi $
|
||||
*/
|
||||
public class DeckGeneratorTheme extends DeckGeneratorBase {
|
||||
private int basicLandPercentage = 0;
|
||||
private boolean testing = false;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for ThemeDeckGenerator.
|
||||
* </p>
|
||||
*/
|
||||
public DeckGeneratorTheme(ICardDatabase cardDb) {
|
||||
super(cardDb);
|
||||
this.maxDuplicates = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getThemeNames.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link java.util.ArrayList} object.
|
||||
*/
|
||||
public static final ArrayList<String> getThemeNames() {
|
||||
final ArrayList<String> ltNames = new ArrayList<String>();
|
||||
|
||||
final File file = new File(ForgeConstants.THEMES_DIR);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException("ThemeDeckGenerator : getThemeNames error -- file not found -- filename is "
|
||||
+ file.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (!file.isDirectory()) {
|
||||
throw new RuntimeException("ThemeDeckGenerator : getThemeNames error -- not a directory -- "
|
||||
+ file.getAbsolutePath());
|
||||
}
|
||||
|
||||
final String[] fileList = file.list();
|
||||
for (final String element : fileList) {
|
||||
if (element.endsWith(".thm")) {
|
||||
ltNames.add(element.substring(0, element.indexOf(".thm")));
|
||||
}
|
||||
}
|
||||
|
||||
return ltNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getThemeDeck.
|
||||
* </p>
|
||||
*
|
||||
* @param themeName
|
||||
* a {@link java.lang.String} object.
|
||||
* @param size
|
||||
* a int.
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
public final CardPool getThemeDeck(final String themeName, final int size, final StringBuilder errorBuilder) {
|
||||
String s = "";
|
||||
|
||||
// read theme file
|
||||
final String tFileName = ForgeConstants.THEMES_DIR + themeName + ".thm";
|
||||
List<String> lines = FileUtil.readFile(tFileName);
|
||||
|
||||
final List<Grp> groups = readGroups(lines);
|
||||
|
||||
// begin assigning cards to the deck
|
||||
final Random r = MyRandom.getRandom();
|
||||
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
final Grp g = groups.get(i);
|
||||
final float p = (float) (g.percentage * .01);
|
||||
final int grpCnt = (int) (p * size);
|
||||
final int cnSize = g.cardnames.size();
|
||||
errorBuilder.append("Group" + i + ":" + grpCnt + "\n");
|
||||
|
||||
for (int j = 0; j < grpCnt; j++) {
|
||||
s = g.cardnames.get(r.nextInt(cnSize));
|
||||
|
||||
int lc = 0;
|
||||
while ((cardCounts.get(s) >= g.maxCnt) || (lc > 999)) {
|
||||
// looping
|
||||
// forever
|
||||
s = g.cardnames.get(r.nextInt(cnSize));
|
||||
lc++;
|
||||
}
|
||||
if (lc > 999) {
|
||||
throw new RuntimeException("ThemeDeckGenerator : getThemeDeck -- looped too much -- filename is "
|
||||
+ tFileName);
|
||||
}
|
||||
|
||||
final int n = cardCounts.get(s);
|
||||
tDeck.add(cardDb.getCard(s));
|
||||
cardCounts.put(s, n + 1);
|
||||
errorBuilder.append(s + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
int numBLands;
|
||||
if (basicLandPercentage > 0) { // if theme explicitly defines this
|
||||
numBLands = (int) (size * basicLandPercentage / 100f);
|
||||
}
|
||||
else { // otherwise, just fill in the rest of the deck with basic lands
|
||||
numBLands = size - tDeck.countAll();
|
||||
}
|
||||
|
||||
errorBuilder.append("numBLands:" + numBLands + "\n");
|
||||
|
||||
addBasicLand(numBLands);
|
||||
|
||||
errorBuilder.append("DeckSize:" + tDeck.countAll() + "\n");
|
||||
|
||||
adjustDeckSize(size);
|
||||
|
||||
errorBuilder.append("DeckSize:" + tDeck.countAll() + "\n");
|
||||
if (!testing) {
|
||||
errorBuilder.delete(0, errorBuilder.length()); //clear if not testing
|
||||
}
|
||||
|
||||
return tDeck;
|
||||
}
|
||||
|
||||
private class Grp {
|
||||
/** The Cardnames. */
|
||||
private final ArrayList<String> cardnames = new ArrayList<String>();
|
||||
|
||||
/** The Max cnt. */
|
||||
private int maxCnt;
|
||||
|
||||
/** The Percentage. */
|
||||
private int percentage;
|
||||
}
|
||||
|
||||
private List<Grp> readGroups(List<String> lines) {
|
||||
final List<Grp> groups = new ArrayList<Grp>();
|
||||
|
||||
Grp g = null;
|
||||
for (String s : lines) {
|
||||
if (s.equals("End")) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (s.startsWith("[Group")) {
|
||||
g = new Grp();
|
||||
final String[] ss = s.replaceAll("[\\[\\]]", "").split(" ");
|
||||
for (final String element : ss) {
|
||||
if (element.startsWith("Percentage")) {
|
||||
final String p = element.substring("Percentage".length() + 1);
|
||||
g.percentage = Integer.parseInt(p);
|
||||
}
|
||||
if (element.startsWith("MaxCnt")) {
|
||||
final String m = element.substring("MaxCnt".length() + 1);
|
||||
g.maxCnt = Integer.parseInt(m);
|
||||
}
|
||||
}
|
||||
groups.add(g);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.equals("[/Group]")) {
|
||||
g = null;
|
||||
}
|
||||
|
||||
if (s.startsWith("BasicLandPercentage")) {
|
||||
basicLandPercentage = Integer.parseInt(s.substring("BasicLandPercentage".length() + 1));
|
||||
}
|
||||
else if (s.equals("Testing")) {
|
||||
testing = true;
|
||||
}
|
||||
else if (g != null) {
|
||||
g.cardnames.add(s);
|
||||
cardCounts.put(s, 0);
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package forge.gui.deckeditor;
|
||||
package forge.deck;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.GuiBase;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.ColorSet;
|
||||
@@ -12,13 +12,12 @@ import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.deck.generation.DeckGeneratorTheme;
|
||||
import forge.error.BugReporter;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.GameType;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.PreconDeck;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.QuestEvent;
|
||||
@@ -178,7 +177,7 @@ public class DeckProxy implements InventoryItem {
|
||||
|
||||
public Iterable<GameFormat> getFormats() {
|
||||
if (formats == null) {
|
||||
formats = Singletons.getModel().getFormats().getAllFormatsOfDeck(getDeck());
|
||||
formats = FModel.getFormats().getAllFormatsOfDeck(getDeck());
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
@@ -282,14 +281,14 @@ public class DeckProxy implements InventoryItem {
|
||||
|
||||
@Override
|
||||
public Deck getDeck() {
|
||||
final DeckGeneratorTheme gen = new DeckGeneratorTheme(Singletons.getMagicDb().getCommonCards());
|
||||
final DeckGeneratorTheme gen = new DeckGeneratorTheme(FModel.getMagicDb().getCommonCards());
|
||||
final Deck deck = new Deck();
|
||||
gen.setSingleton(Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||
gen.setUseArtifacts(!Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
gen.setSingleton(FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||
gen.setUseArtifacts(!FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
StringBuilder errorBuilder = new StringBuilder();
|
||||
deck.getMain().addAll(gen.getThemeDeck(this.getName(), 60, errorBuilder));
|
||||
if (errorBuilder.length() > 0) {
|
||||
BugReporter.reportBug(errorBuilder.toString());
|
||||
GuiBase.getInterface().reportBug(errorBuilder.toString());
|
||||
}
|
||||
return deck;
|
||||
}
|
||||
@@ -328,7 +327,7 @@ public class DeckProxy implements InventoryItem {
|
||||
|
||||
public static Iterable<DeckProxy> getAllQuestEventAndChallenges() {
|
||||
ArrayList<DeckProxy> decks = new ArrayList<DeckProxy>();
|
||||
QuestController quest = Singletons.getModel().getQuest();
|
||||
QuestController quest = FModel.getQuest();
|
||||
for (QuestEvent e : quest.getDuelsManager().getAllDuels()) {
|
||||
decks.add(new DeckProxy(e.getEventDeck(), "Quest Event", null, null));
|
||||
}
|
||||
26
forge-gui/src/main/java/forge/deck/DeckType.java
Normal file
26
forge-gui/src/main/java/forge/deck/DeckType.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package forge.deck;
|
||||
|
||||
public enum DeckType {
|
||||
CUSTOM_DECK ("Custom User Decks"),
|
||||
PRECONSTRUCTED_DECK("Preconstructed Decks"),
|
||||
QUEST_OPPONENT_DECK ("Quest Opponent Decks"),
|
||||
COLOR_DECK ("Random Color Decks"),
|
||||
THEME_DECK ("Random Theme Decks");
|
||||
|
||||
private String value;
|
||||
private DeckType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
public static DeckType fromString(String value){
|
||||
for (final DeckType d : DeckType.values()) {
|
||||
if (d.toString().equalsIgnoreCase(value)) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No Enum specified for this string");
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package forge.gui.deckchooser;
|
||||
package forge.deck;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.SOptionPane;
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
@@ -13,9 +13,9 @@ import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.deck.generation.*;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.itemmanager.DeckManager;
|
||||
import forge.item.PaperCard;
|
||||
import forge.itemmanager.IItemManager;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.QuestEvent;
|
||||
@@ -57,7 +57,7 @@ public class DeckgenUtil {
|
||||
String deckName = null;
|
||||
|
||||
DeckGeneratorBase gen = null;
|
||||
CardDb cardDb = Singletons.getMagicDb().getCommonCards();
|
||||
CardDb cardDb = FModel.getMagicDb().getCommonCards();
|
||||
if (selection.size() == 1) {
|
||||
gen = new DeckGeneratorMonoColor(cardDb, selection.get(0));
|
||||
}
|
||||
@@ -71,8 +71,8 @@ public class DeckgenUtil {
|
||||
gen = new DeckGenerator5Color(cardDb);
|
||||
deckName = "5 colors";
|
||||
}
|
||||
gen.setSingleton(Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||
gen.setUseArtifacts(!Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
gen.setSingleton(FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||
gen.setUseArtifacts(!FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
CardPool cards = gen == null ? null : gen.getDeck(60, forAi);
|
||||
|
||||
if (null == deckName) {
|
||||
@@ -87,7 +87,7 @@ public class DeckgenUtil {
|
||||
}
|
||||
|
||||
public static QuestEvent getQuestEvent(final String name) {
|
||||
QuestController qCtrl = Singletons.getModel().getQuest();
|
||||
QuestController qCtrl = FModel.getQuest();
|
||||
for (QuestEventChallenge challenge : qCtrl.getChallenges()) {
|
||||
if (challenge.getTitle().equals(name)) {
|
||||
return challenge;
|
||||
@@ -114,16 +114,15 @@ public class DeckgenUtil {
|
||||
|
||||
/** @return {@link forge.deck.Deck} */
|
||||
public static Deck getRandomCustomDeck() {
|
||||
final IStorage<Deck> allDecks = Singletons.getModel().getDecks().getConstructed();
|
||||
final IStorage<Deck> allDecks = FModel.getDecks().getConstructed();
|
||||
final int rand = (int) (Math.floor(Math.random() * allDecks.size()));
|
||||
final String name = allDecks.getItemNames().toArray(new String[0])[rand];
|
||||
return allDecks.get(name);
|
||||
}
|
||||
|
||||
/** @return {@link forge.deck.Deck} */
|
||||
public static Deck getRandomQuestDeck() {
|
||||
final List<Deck> allQuestDecks = new ArrayList<Deck>();
|
||||
QuestController qCtrl = Singletons.getModel().getQuest();
|
||||
QuestController qCtrl = FModel.getQuest();
|
||||
|
||||
for (final QuestEvent e : qCtrl.getDuelsManager().getAllDuels()) {
|
||||
allQuestDecks.add(e.getEventDeck());
|
||||
@@ -137,7 +136,7 @@ public class DeckgenUtil {
|
||||
return allQuestDecks.get(rand);
|
||||
}
|
||||
|
||||
public static void randomSelectColors(final DeckManager deckManager) {
|
||||
public static void randomSelectColors(final IItemManager<DeckProxy> deckManager) {
|
||||
final int size = deckManager.getItemCount();
|
||||
if (size == 0) { return; }
|
||||
|
||||
@@ -163,7 +162,7 @@ public class DeckgenUtil {
|
||||
deckManager.setSelectedIndices(indices);
|
||||
}
|
||||
|
||||
public static void randomSelect(final DeckManager deckManager) {
|
||||
public static void randomSelect(final IItemManager<DeckProxy> deckManager) {
|
||||
final int size = deckManager.getItemCount();
|
||||
if (size == 0) { return; }
|
||||
|
||||
@@ -181,16 +180,16 @@ public class DeckgenUtil {
|
||||
boolean result = true;
|
||||
|
||||
if (colors0.size() == 4) {
|
||||
FOptionPane.showMessageDialog(
|
||||
SOptionPane.showMessageDialog(
|
||||
"Sorry, four color generated decks aren't supported yet."
|
||||
+ "\n\rPlease use 2, 3, or 5 colors for this deck.",
|
||||
"Generate deck: 4 colors", FOptionPane.ERROR_ICON);
|
||||
"Generate deck: 4 colors", SOptionPane.ERROR_ICON);
|
||||
result = false;
|
||||
}
|
||||
else if (colors0.size() > 5) {
|
||||
FOptionPane.showMessageDialog(
|
||||
SOptionPane.showMessageDialog(
|
||||
"Generate deck: maximum five colors!",
|
||||
"Generate deck: too many colors", FOptionPane.ERROR_ICON);
|
||||
"Generate deck: too many colors", SOptionPane.ERROR_ICON);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
@@ -199,7 +198,7 @@ public class DeckgenUtil {
|
||||
public static CardPool generateSchemeDeck() {
|
||||
CardPool schemes = new CardPool();
|
||||
List<PaperCard> allSchemes = new ArrayList<PaperCard>();
|
||||
for (PaperCard c : Singletons.getMagicDb().getVariantCards().getAllCards()) {
|
||||
for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) {
|
||||
if (c.getRules().getType().isScheme()) {
|
||||
allSchemes.add(c);
|
||||
}
|
||||
@@ -225,7 +224,7 @@ public class DeckgenUtil {
|
||||
public static CardPool generatePlanarDeck() {
|
||||
CardPool res = new CardPool();
|
||||
List<PaperCard> allPlanars = new ArrayList<PaperCard>();
|
||||
for (PaperCard c : Singletons.getMagicDb().getVariantCards().getAllCards()) {
|
||||
for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) {
|
||||
if (c.getRules().getType().isPlane() || c.getRules().getType().isPhenomenon()) {
|
||||
allPlanars.add(c);
|
||||
}
|
||||
@@ -256,7 +255,7 @@ public class DeckgenUtil {
|
||||
/** Generate a 2-color Commander deck. */
|
||||
public static Deck generateCommanderDeck(boolean forAi) {
|
||||
final Deck deck;
|
||||
CardDb cardDb = Singletons.getMagicDb().getCommonCards();
|
||||
CardDb cardDb = FModel.getMagicDb().getCommonCards();
|
||||
PaperCard commander;
|
||||
ColorSet colorID;
|
||||
|
||||
@@ -282,7 +281,7 @@ public class DeckgenUtil {
|
||||
DeckGeneratorBase gen = null;
|
||||
gen = new DeckGenerator2Color(cardDb, comColors.get(0), comColors.get(1));
|
||||
gen.setSingleton(true);
|
||||
gen.setUseArtifacts(!Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
gen.setUseArtifacts(!FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
CardPool cards = gen == null ? null : gen.getDeck(99, forAi);
|
||||
|
||||
// After generating card lists, build deck.
|
||||
@@ -1,9 +1,9 @@
|
||||
package forge.deck.io;
|
||||
|
||||
import forge.ImageCache;
|
||||
import forge.assets.ImageUtil;
|
||||
import forge.deck.Deck;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.properties.ForgeConstants;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapper;
|
||||
import freemarker.template.Template;
|
||||
@@ -71,7 +71,7 @@ public class DeckHtmlSerializer {
|
||||
// System.out.println(card.getSets().get(card.getSets().size() - 1).URL);
|
||||
for (int i = card.getValue().intValue(); i > 0; --i ) {
|
||||
PaperCard r = card.getKey();
|
||||
String url = NewConstants.URL_PIC_DOWNLOAD + ImageCache.getDownloadUrl(r, false);
|
||||
String url = ForgeConstants.URL_PIC_DOWNLOAD + ImageUtil.getDownloadUrl(r, false);
|
||||
list.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import forge.gui.deckeditor.DeckProxy;
|
||||
import forge.deck.DeckProxy;
|
||||
import forge.properties.ForgeConstants;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -19,7 +20,7 @@ import java.util.Map;
|
||||
*
|
||||
*/
|
||||
public class DeckPreferences {
|
||||
private static String currentDeck, filename;
|
||||
private static String currentDeck;
|
||||
private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory.newInstance();
|
||||
private static final XMLEvent NEWLINE = EVENT_FACTORY.createDTD("\n");
|
||||
private static final XMLEvent TAB = EVENT_FACTORY.createDTD("\t");
|
||||
@@ -45,13 +46,12 @@ public class DeckPreferences {
|
||||
return prefs;
|
||||
}
|
||||
|
||||
public static void load(String filename0) {
|
||||
filename = filename0;
|
||||
public static void load() {
|
||||
allPrefs.clear();
|
||||
|
||||
try {
|
||||
final XMLInputFactory in = XMLInputFactory.newInstance();
|
||||
final XMLEventReader reader = in.createXMLEventReader(new FileInputStream(filename));
|
||||
final XMLEventReader reader = in.createXMLEventReader(new FileInputStream(ForgeConstants.DECK_PREFS_FILE));
|
||||
|
||||
XMLEvent event;
|
||||
StartElement element;
|
||||
@@ -107,7 +107,7 @@ public class DeckPreferences {
|
||||
private static void save() {
|
||||
try {
|
||||
final XMLOutputFactory out = XMLOutputFactory.newInstance();
|
||||
final XMLEventWriter writer = out.createXMLEventWriter(new FileOutputStream(filename));
|
||||
final XMLEventWriter writer = out.createXMLEventWriter(new FileOutputStream(ForgeConstants.DECK_PREFS_FILE));
|
||||
|
||||
writer.add(EVENT_FACTORY.createStartDocument());
|
||||
writer.add(NEWLINE);
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
*/
|
||||
package forge.deck.io;
|
||||
|
||||
import forge.SOptionPane;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileSection;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -35,10 +36,7 @@ import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
|
||||
public class OldDeckParser {
|
||||
|
||||
/** Constant <code>BDKFileFilter</code>. */
|
||||
@@ -60,7 +58,7 @@ public class OldDeckParser {
|
||||
*/
|
||||
public OldDeckParser(final IStorage<Deck> constructed2, final IStorage<DeckGroup> draft2,
|
||||
final IStorage<DeckGroup> sealed2, final IStorage<Deck> cube2) {
|
||||
this.deckDir = new File(NewConstants.DECK_BASE_DIR);
|
||||
this.deckDir = new File(ForgeConstants.DECK_BASE_DIR);
|
||||
this.sealed = sealed2;
|
||||
this.constructed = constructed2;
|
||||
this.cube = cube2;
|
||||
@@ -147,7 +145,7 @@ public class OldDeckParser {
|
||||
this.draft.add(d);
|
||||
} else {
|
||||
final String msg = String.format("Draft '%s' lacked some decks.%n%nShould it be deleted?");
|
||||
mayDelete = FOptionPane.showConfirmDialog(msg, "Draft loading error");
|
||||
mayDelete = SOptionPane.showConfirmDialog(msg, "Draft loading error");
|
||||
}
|
||||
|
||||
if (mayDelete) {
|
||||
@@ -183,7 +181,7 @@ public class OldDeckParser {
|
||||
final String msg = String
|
||||
.format("Can not convert deck '%s' for some unsupported cards it contains. %n%s%n%nMay Forge delete all such decks?",
|
||||
name, ex.getMessage());
|
||||
allowDeleteUnsupportedConstructed = FOptionPane.showConfirmDialog(msg, "Problem converting decks");
|
||||
allowDeleteUnsupportedConstructed = SOptionPane.showConfirmDialog(msg, "Problem converting decks");
|
||||
}
|
||||
}
|
||||
if (importedOk || allowDeleteUnsupportedConstructed) {
|
||||
@@ -202,7 +200,7 @@ public class OldDeckParser {
|
||||
final String msg = String
|
||||
.format("Can not convert deck '%s' for some unsupported cards it contains. %n%s%n%nMay Forge delete all such decks?",
|
||||
name, ex.getMessage());
|
||||
allowDeleteUnsupportedConstructed = FOptionPane.showConfirmDialog(msg, "Problem converting decks");
|
||||
allowDeleteUnsupportedConstructed = SOptionPane.showConfirmDialog(msg, "Problem converting decks");
|
||||
}
|
||||
}
|
||||
if (importedOk || allowDeleteUnsupportedConstructed) {
|
||||
@@ -254,7 +252,7 @@ public class OldDeckParser {
|
||||
}
|
||||
sb.append(System.getProperty("line.separator"));
|
||||
sb.append("May Forge delete these decks?");
|
||||
if (FOptionPane.showConfirmDialog(sb.toString(), "Some of your sealed decks are orphaned")) {
|
||||
if (SOptionPane.showConfirmDialog(sb.toString(), "Some of your sealed decks are orphaned")) {
|
||||
for (final Pair<DeckGroup, MutablePair<File, File>> s : sealedDecks.values()) {
|
||||
if (s.getRight().getLeft() != null) {
|
||||
s.getRight().getLeft().delete();
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.error;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.gui.WrapLayout;
|
||||
import forge.gui.toolbox.FHyperlink;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.util.BuildInfo;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* The class ErrorViewer. Enables showing and saving error messages that
|
||||
* occurred in forge.
|
||||
*
|
||||
* @author Clemens Koza
|
||||
* @version V1.0 02.08.2009
|
||||
*/
|
||||
public class BugReporter {
|
||||
private static final int _STACK_OVERFLOW_MAX_MESSAGE_LEN = 16 * 1024;
|
||||
|
||||
private static boolean dialogShown = false;
|
||||
|
||||
/**
|
||||
* Shows exception information in a format ready to post to the forum as a crash report. Uses the exception's message
|
||||
* as the reason if message is null.
|
||||
*/
|
||||
public static void reportException(final Throwable ex, final String message) {
|
||||
if (ex == null) {
|
||||
return;
|
||||
}
|
||||
if (message != null) {
|
||||
System.err.printf("%s > %s%n", FThreads.debugGetCurrThreadId(), message);
|
||||
}
|
||||
System.err.print( FThreads.debugGetCurrThreadId() + " > " );
|
||||
ex.printStackTrace();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Description: [describe what you were doing when the crash occurred]\n\n");
|
||||
_buildSpoilerHeader(sb, ex.getClass().getSimpleName());
|
||||
sb.append("\n\n");
|
||||
if (null != message && !message.isEmpty()) {
|
||||
sb.append(FThreads.debugGetCurrThreadId()).append(" > ").append(message).append("\n");
|
||||
}
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
ex.printStackTrace(pw);
|
||||
|
||||
String swStr = sw.toString();
|
||||
if (ex instanceof StackOverflowError &&
|
||||
_STACK_OVERFLOW_MAX_MESSAGE_LEN <= swStr.length()) {
|
||||
// most likely a cycle. only take first portion so the message
|
||||
// doesn't grow too large to post
|
||||
sb.append(swStr, 0, _STACK_OVERFLOW_MAX_MESSAGE_LEN);
|
||||
sb.append("\n... (truncated)");
|
||||
} else {
|
||||
sb.append(swStr);
|
||||
}
|
||||
|
||||
_buildSpoilerFooter(sb);
|
||||
|
||||
_showDialog("Report a crash", sb.toString(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for reportException(ex, null).
|
||||
*/
|
||||
public static void reportException(final Throwable ex) {
|
||||
reportException(ex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for reportException(ex, String.format(format, args)).
|
||||
*/
|
||||
public static void reportException(final Throwable ex, final String format, final Object... args) {
|
||||
reportException(ex, String.format(format, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a forum post template for reporting a bug.
|
||||
*/
|
||||
public static void reportBug(String details) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Description: [describe the problem]\n\n");
|
||||
_buildSpoilerHeader(sb, "General bug report");
|
||||
if (null != details && !details.isEmpty()) {
|
||||
sb.append("\n\n");
|
||||
sb.append(details);
|
||||
}
|
||||
_buildSpoilerFooter(sb);
|
||||
|
||||
_showDialog("Report a bug", sb.toString(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows thread stack information in a format ready to post to the forum.
|
||||
*/
|
||||
public static void reportThreadStacks(final String message) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Description: [describe what you were doing at the time]\n\n");
|
||||
_buildSpoilerHeader(sb, "Thread stack dump");
|
||||
sb.append("\n\n");
|
||||
if (null != message && !message.isEmpty()) {
|
||||
sb.append(message);
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
final Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
|
||||
for (final Entry<Thread, StackTraceElement[]> e : traces.entrySet()) {
|
||||
pw.println();
|
||||
pw.printf("%s (%s):%n", e.getKey().getName(), e.getKey().getId());
|
||||
for (final StackTraceElement el : e.getValue()) {
|
||||
pw.println(el);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(sw.toString());
|
||||
_buildSpoilerFooter(sb);
|
||||
_showDialog("Thread stack dump", sb.toString(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for reportThreadStacks(String.format(format, args))
|
||||
*/
|
||||
public static void reportThreadStacks(final String format, final Object... args) {
|
||||
reportThreadStacks(String.format(format, args));
|
||||
}
|
||||
|
||||
private static StringBuilder _buildSpoilerHeader(StringBuilder sb, String reportTitle) {
|
||||
sb.append("[spoiler=").append(reportTitle).append("][code]");
|
||||
sb.append("\nForge Version: ").append(BuildInfo.getVersionString());
|
||||
sb.append("\nOperating System: ").append(System.getProperty("os.name"))
|
||||
.append(" ").append(System.getProperty("os.version"))
|
||||
.append(" ").append(System.getProperty("os.arch"));
|
||||
sb.append("\nJava Version: ").append(System.getProperty("java.version"))
|
||||
.append(" ").append(System.getProperty("java.vendor"));
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static StringBuilder _buildSpoilerFooter(StringBuilder sb) {
|
||||
sb.append("[/code][/spoiler]");
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static void _showDialog(String title, String text, boolean showExitAppBtn) {
|
||||
if ( dialogShown )
|
||||
return;
|
||||
|
||||
JTextArea area = new JTextArea(text);
|
||||
area.setFont(new Font("Monospaced", Font.PLAIN, 10));
|
||||
area.setEditable(false);
|
||||
area.setLineWrap(true);
|
||||
area.setWrapStyleWord(true);
|
||||
|
||||
String helpText = "<html>A template for a post in the bug reports forum topic is shown below. Just select 'Copy and go to forum' "
|
||||
+ "and the template will be copied to your system clipboard and the forum page will open in your browser. "
|
||||
+ "Then all you have to do is paste the text into a forum post and edit the description line.</html>";
|
||||
String helpUrlLabel = "Reporting bugs in Forge is very important. We sincerely thank you for your time."
|
||||
+ " For help writing a solid bug report, please see:";
|
||||
String helpUrl = "http://www.slightlymagic.net/forum/viewtopic.php?f=26&p=109925#p109925";
|
||||
JPanel helpPanel = new JPanel(new WrapLayout(FlowLayout.LEFT, 4, 2));
|
||||
for (String word : helpUrlLabel.split(" ")) {
|
||||
helpPanel.add(new FLabel.Builder().text("<html>" + word + "</html>").useSkinColors(false).build());
|
||||
}
|
||||
helpPanel.add(new FHyperlink.Builder().url(helpUrl).text("<html>this post</html>").useSkinColors(false).build());
|
||||
|
||||
JPanel p = new JPanel(new MigLayout("wrap"));
|
||||
p.add(new FLabel.Builder().text(helpText).useSkinColors(false).build(), "gap 5");
|
||||
p.add(helpPanel, "w 600");
|
||||
p.add(new JScrollPane(area), "w 100%, h 100%, gaptop 5");
|
||||
|
||||
// determine proper forum URL
|
||||
String forgeVersion = BuildInfo.getVersionString();
|
||||
final String url;
|
||||
if (StringUtils.containsIgnoreCase(forgeVersion, "svn")
|
||||
|| StringUtils.containsIgnoreCase(forgeVersion, "snapshot")) {
|
||||
url = "http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=6333&start=54564487645#bottom";
|
||||
} else {
|
||||
url = "http://www.slightlymagic.net/forum/viewforum.php?f=26";
|
||||
}
|
||||
|
||||
// Button is not modified, String gets the automatic listener to hide
|
||||
// the dialog
|
||||
ArrayList<Object> options = new ArrayList<Object>();
|
||||
options.add(new JButton(new _CopyAndGo(url, area)));
|
||||
options.add(new JButton(new _SaveAction(area)));
|
||||
options.add("Close");
|
||||
if (showExitAppBtn) {
|
||||
options.add(new JButton(new _ExitAction()));
|
||||
}
|
||||
|
||||
JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE,
|
||||
JOptionPane.DEFAULT_OPTION, null, options.toArray(), options.get(0));
|
||||
JDialog dlg = pane.createDialog(JOptionPane.getRootFrame(), title);
|
||||
dlg.setSize(showExitAppBtn ? 780 : 600, 400);
|
||||
dlg.setResizable(true);
|
||||
dialogShown = true;
|
||||
dlg.setVisible(true);
|
||||
dlg.dispose();
|
||||
dialogShown = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class _CopyAndGo extends AbstractAction {
|
||||
private final String url;
|
||||
private final JTextArea text;
|
||||
|
||||
public _CopyAndGo(String url, JTextArea text) {
|
||||
super("Copy and go to forum");
|
||||
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
|
||||
this.url = url;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
try {
|
||||
// copy text to clipboard
|
||||
StringSelection ss = new StringSelection(text.getText());
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
|
||||
|
||||
// browse to url
|
||||
Desktop.getDesktop().browse(new URI(url));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
FOptionPane.showMessageDialog("Sorry, a problem occurred while opening the forum in your default browser.",
|
||||
"A problem occurred", FOptionPane.ERROR_ICON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class _SaveAction extends AbstractAction {
|
||||
private static JFileChooser c;
|
||||
private final JTextArea area;
|
||||
|
||||
public _SaveAction(final JTextArea areaParam) {
|
||||
super("Save to file");
|
||||
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.area = areaParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (c == null) {
|
||||
c = new JFileChooser();
|
||||
}
|
||||
|
||||
File f;
|
||||
long curTime = System.currentTimeMillis();
|
||||
for (int i = 0;; i++) {
|
||||
final String name = String.format("%TF-%02d.txt", curTime, i);
|
||||
f = new File(name);
|
||||
if (!f.exists()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c.setSelectedFile(f);
|
||||
c.showSaveDialog(null);
|
||||
f = c.getSelectedFile();
|
||||
|
||||
try {
|
||||
final BufferedWriter bw = new BufferedWriter(new FileWriter(f));
|
||||
bw.write(this.area.getText());
|
||||
bw.close();
|
||||
}
|
||||
catch (final IOException ex) {
|
||||
FOptionPane.showMessageDialog("There was an error during saving. Sorry!\n" + ex,
|
||||
"Error saving file", FOptionPane.ERROR_ICON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class _ExitAction extends AbstractAction {
|
||||
public _ExitAction() {
|
||||
super("Exit application");
|
||||
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// disable instantiation
|
||||
private BugReporter() { }
|
||||
}
|
||||
@@ -20,6 +20,8 @@ package forge.error;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
|
||||
import forge.GuiBase;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
@@ -48,7 +50,7 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void uncaughtException(final Thread t, final Throwable ex) {
|
||||
BugReporter.reportException(ex);
|
||||
GuiBase.getInterface().reportException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +61,6 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
|
||||
* a {@link java.lang.Throwable} object.
|
||||
*/
|
||||
public final void handle(final Throwable ex) {
|
||||
BugReporter.reportException(ex);
|
||||
GuiBase.getInterface().reportException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package forge.gui.events;
|
||||
package forge.events;
|
||||
|
||||
public interface IUiEventVisitor<T> {
|
||||
T visit(UiEventBlockerAssigned event);
|
||||
@@ -1,4 +1,4 @@
|
||||
package forge.gui.events;
|
||||
package forge.events;
|
||||
|
||||
|
||||
public abstract class UiEvent {
|
||||
@@ -1,4 +1,4 @@
|
||||
package forge.gui.events;
|
||||
package forge.events;
|
||||
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
@@ -1,4 +1,4 @@
|
||||
package forge.gui.events;
|
||||
package forge.events;
|
||||
|
||||
import forge.game.card.Card;
|
||||
|
||||
@@ -6,12 +6,14 @@ import com.thoughtworks.xstream.converters.MarshallingContext;
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import forge.Singletons;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.CardPool;
|
||||
import forge.error.BugReporter;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.IgnoringXStream;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.*;
|
||||
@@ -37,7 +39,7 @@ public class GauntletIO {
|
||||
}
|
||||
|
||||
public static File getGauntletFile(String name) {
|
||||
return new File(NewConstants.GAUNTLET_DIR.userPrefLoc, name + SUFFIX_DATA);
|
||||
return new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc, name + SUFFIX_DATA);
|
||||
}
|
||||
|
||||
public static File getGauntletFile(GauntletData gd) {
|
||||
@@ -52,7 +54,7 @@ public class GauntletIO {
|
||||
}
|
||||
};
|
||||
|
||||
File folder = new File(NewConstants.GAUNTLET_DIR.userPrefLoc);
|
||||
File folder = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc);
|
||||
return folder.listFiles(filter);
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ public class GauntletIO {
|
||||
}
|
||||
};
|
||||
|
||||
File folder = new File(NewConstants.GAUNTLET_DIR.userPrefLoc);
|
||||
File folder = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc);
|
||||
return folder.listFiles(filter);
|
||||
}
|
||||
|
||||
@@ -76,7 +78,7 @@ public class GauntletIO {
|
||||
}
|
||||
};
|
||||
|
||||
File folder = new File(NewConstants.GAUNTLET_DIR.defaultLoc);
|
||||
File folder = new File(ForgeConstants.GAUNTLET_DIR.defaultLoc);
|
||||
return folder.listFiles(filter);
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ public class GauntletIO {
|
||||
|
||||
return data;
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex, "Error loading Gauntlet Data");
|
||||
GuiBase.getInterface().reportException(ex, "Error loading Gauntlet Data");
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
if (null != zin) {
|
||||
@@ -108,7 +110,7 @@ public class GauntletIO {
|
||||
final XStream xStream = GauntletIO.getSerializer(false);
|
||||
GauntletIO.savePacked(xStream, gd0);
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex, "Error saving Gauntlet Data.");
|
||||
GuiBase.getInterface().reportException(ex, "Error saving Gauntlet Data.");
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
@@ -145,7 +147,7 @@ public class GauntletIO {
|
||||
final String nodename = reader.getNodeName();
|
||||
|
||||
if ("string".equals(nodename)) {
|
||||
result.add(Singletons.getMagicDb().getCommonCards().getCard(reader.getValue()));
|
||||
result.add(FModel.getMagicDb().getCommonCards().getCard(reader.getValue()));
|
||||
} else if ("card".equals(nodename)) { // new format
|
||||
result.add(this.readCardPrinted(reader), cnt);
|
||||
}
|
||||
@@ -173,12 +175,12 @@ public class GauntletIO {
|
||||
final String sIndex = reader.getAttribute("i");
|
||||
final short index = StringUtils.isNumeric(sIndex) ? Short.parseShort(sIndex) : 0;
|
||||
final boolean foil = "1".equals(reader.getAttribute("foil"));
|
||||
PaperCard card = Singletons.getMagicDb().getCommonCards().getCard(name, set, index);
|
||||
PaperCard card = FModel.getMagicDb().getCommonCards().getCard(name, set, index);
|
||||
if ( null == card )
|
||||
card = Singletons.getMagicDb().getCommonCards().getCard(name, set, -1);
|
||||
card = FModel.getMagicDb().getCommonCards().getCard(name, set, -1);
|
||||
if ( null == card )
|
||||
throw new RuntimeException("Unsupported card found in quest save: " + name + " from edition " + set);
|
||||
return foil ? Singletons.getMagicDb().getCommonCards().getFoiled(card) : card;
|
||||
return foil ? FModel.getMagicDb().getCommonCards().getFoiled(card) : card;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package forge.gui;
|
||||
|
||||
import forge.game.card.Card;
|
||||
|
||||
/**
|
||||
* The class CardContainer. A card container is an object that references a
|
||||
* card.
|
||||
*
|
||||
* @author Clemens Koza
|
||||
* @version V0.0 17.02.2010
|
||||
*/
|
||||
public interface CardContainer {
|
||||
/**
|
||||
* <p>
|
||||
* setCard.
|
||||
* </p>
|
||||
*
|
||||
* @param card
|
||||
* a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
void setCard(Card card);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getCard.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
Card getCard();
|
||||
|
||||
}
|
||||
@@ -1,699 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package forge.gui;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.card.CardCharacteristicName;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardType;
|
||||
import forge.card.ColorSet;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.toolbox.*;
|
||||
import forge.gui.toolbox.FSkin.SkinnedPanel;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.InventoryItemFromSet;
|
||||
import forge.item.PreconDeck;
|
||||
import forge.item.SealedProduct;
|
||||
import forge.util.Lang;
|
||||
import forge.view.FDialog;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* The class CardDetailPanel. Shows the details of a card.
|
||||
*
|
||||
* @author Clemens Koza
|
||||
* @version V0.0 17.02.2010
|
||||
*/
|
||||
public class CardDetailPanel extends SkinnedPanel {
|
||||
/** Constant <code>serialVersionUID=-8461473263764812323L</code>. */
|
||||
private static final long serialVersionUID = -8461473263764812323L;
|
||||
|
||||
private static Color purple = new Color(14381203);
|
||||
|
||||
private final FLabel nameCostLabel;
|
||||
private final FLabel typeLabel;
|
||||
private final FLabel powerToughnessLabel;
|
||||
private final FLabel idLabel;
|
||||
private final JLabel setInfoLabel;
|
||||
private final FHtmlViewer cdArea;
|
||||
private final FScrollPane scrArea;
|
||||
|
||||
public CardDetailPanel(final Card card) {
|
||||
super();
|
||||
this.setLayout(null);
|
||||
this.setOpaque(false);
|
||||
|
||||
this.nameCostLabel = new FLabel.Builder().fontAlign(SwingConstants.CENTER).build();
|
||||
this.typeLabel = new FLabel.Builder().fontAlign(SwingConstants.CENTER).build();
|
||||
this.idLabel = new FLabel.Builder().fontAlign(SwingConstants.LEFT).tooltip("Card ID").build();
|
||||
this.powerToughnessLabel = new FLabel.Builder().fontAlign(SwingConstants.CENTER).build();
|
||||
this.setInfoLabel = new JLabel();
|
||||
this.setInfoLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
Font font = new Font("Dialog", 0, 14);
|
||||
this.nameCostLabel.setFont(font);
|
||||
this.typeLabel.setFont(font);
|
||||
this.idLabel.setFont(font);
|
||||
this.powerToughnessLabel.setFont(font);
|
||||
|
||||
this.cdArea = new FHtmlViewer();
|
||||
this.cdArea.setBorder(new EmptyBorder(2, 6, 2, 6));
|
||||
this.cdArea.setOpaque(false);
|
||||
this.scrArea = new FScrollPane(this.cdArea, false);
|
||||
|
||||
this.add(this.nameCostLabel);
|
||||
this.add(this.typeLabel);
|
||||
this.add(this.idLabel);
|
||||
this.add(this.powerToughnessLabel);
|
||||
this.add(this.setInfoLabel);
|
||||
this.add(this.scrArea);
|
||||
|
||||
this.setCard(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doLayout() {
|
||||
int insets = 3;
|
||||
int setInfoWidth = 40;
|
||||
int x = insets;
|
||||
int y = insets;
|
||||
int lineWidth = getWidth() - 2 * insets;
|
||||
int lineHeight = this.nameCostLabel.getPreferredSize().height;
|
||||
int dy = lineHeight + 1;
|
||||
|
||||
this.nameCostLabel.setBounds(x, y, lineWidth, lineHeight);
|
||||
y += dy;
|
||||
|
||||
this.typeLabel.setBounds(x, y, lineWidth, lineHeight);
|
||||
y += dy;
|
||||
|
||||
this.idLabel.setBounds(x, y, this.idLabel.getAutoSizeWidth(), lineHeight);
|
||||
this.powerToughnessLabel.setBounds(x, y, lineWidth, lineHeight);
|
||||
|
||||
//+1 to x,y so set info label right up against border and the baseline matches ID and P/T
|
||||
this.setInfoLabel.setBounds(x + lineWidth - setInfoWidth + 1, y + 1, setInfoWidth, lineHeight);
|
||||
y += dy;
|
||||
|
||||
this.scrArea.setBounds(0, y, getWidth(), getHeight() - y);
|
||||
}
|
||||
|
||||
public String getItemDescription(InventoryItemFromSet i) {
|
||||
if( i instanceof SealedProduct )
|
||||
return ((SealedProduct)i).getDescription();
|
||||
if( i instanceof PreconDeck)
|
||||
return ((PreconDeck) i).getDescription();
|
||||
return i.getName();
|
||||
}
|
||||
|
||||
public final void setItem(InventoryItemFromSet item) {
|
||||
nameCostLabel.setText(item.getName());
|
||||
typeLabel.setVisible(false);
|
||||
powerToughnessLabel.setVisible(false);
|
||||
idLabel.setText("");
|
||||
cdArea.setText(getItemDescription(item));
|
||||
this.updateBorder(item instanceof IPaperCard ? ((IPaperCard)item).getRules().getColor() : null, false);
|
||||
|
||||
String set = item.getEdition();
|
||||
setInfoLabel.setText(set);
|
||||
setInfoLabel.setToolTipText("");
|
||||
if (StringUtils.isEmpty(set)) {
|
||||
setInfoLabel.setOpaque(false);
|
||||
setInfoLabel.setBorder(null);
|
||||
} else {
|
||||
CardEdition edition = Singletons.getMagicDb().getEditions().get(set);
|
||||
if (null != edition) {
|
||||
setInfoLabel.setToolTipText(edition.getName());
|
||||
}
|
||||
|
||||
this.setInfoLabel.setOpaque(true);
|
||||
this.setInfoLabel.setBackground(Color.BLACK);
|
||||
this.setInfoLabel.setForeground(Color.WHITE);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.WHITE));
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrArea.getVerticalScrollBar().setValue(scrArea.getVerticalScrollBar().getMinimum());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public final void setCard(final Card card) {
|
||||
this.nameCostLabel.setText("");
|
||||
this.typeLabel.setVisible(true);
|
||||
this.typeLabel.setText("");
|
||||
this.powerToughnessLabel.setVisible(true);
|
||||
this.powerToughnessLabel.setText("");
|
||||
this.idLabel.setText("");
|
||||
this.setInfoLabel.setText("");
|
||||
this.setInfoLabel.setToolTipText("");
|
||||
this.setInfoLabel.setOpaque(false);
|
||||
this.setInfoLabel.setBorder(null);
|
||||
this.cdArea.setText("");
|
||||
if( card == null ) {
|
||||
this.updateBorder(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean canShowThis = false;
|
||||
|
||||
if (card.isFaceDown()) {
|
||||
if (card.isInZone(ZoneType.Battlefield)) {
|
||||
this.nameCostLabel.setText("Morph");
|
||||
this.typeLabel.setText("Creature");
|
||||
}
|
||||
}
|
||||
else if (Singletons.getControl().mayShowCard(card) || FDialog.isModalOpen()) { //allow showing cards while modal open to account for revealing, picking, and ordering cards
|
||||
canShowThis = true;
|
||||
|
||||
if (card.getManaCost().isNoCost()) {
|
||||
this.nameCostLabel.setText(card.getName());
|
||||
}
|
||||
else {
|
||||
String manaCost = card.getManaCost().toString();
|
||||
if ( card.isSplitCard() && card.getCurState() == CardCharacteristicName.Original) {
|
||||
manaCost = card.getRules().getMainPart().getManaCost().toString() + " // " + card.getRules().getOtherPart().getManaCost().toString();
|
||||
}
|
||||
this.nameCostLabel.setText(FSkin.encodeSymbols(card.getName() + " - " + manaCost, true));
|
||||
}
|
||||
this.typeLabel.setText(formatCardType(card));
|
||||
|
||||
String set = card.getCurSetCode();
|
||||
this.setInfoLabel.setText(set);
|
||||
if (null != set && !set.isEmpty()) {
|
||||
CardEdition edition = Singletons.getMagicDb().getEditions().get(set);
|
||||
if (null == edition) {
|
||||
setInfoLabel.setToolTipText(card.getRarity().name());
|
||||
}
|
||||
else {
|
||||
setInfoLabel.setToolTipText(String.format("%s (%s)", edition.getName(), card.getRarity().name()));
|
||||
}
|
||||
|
||||
this.setInfoLabel.setOpaque(true);
|
||||
switch(card.getRarity()) {
|
||||
case Uncommon:
|
||||
this.setInfoLabel.setBackground(Color.LIGHT_GRAY);
|
||||
this.setInfoLabel.setForeground(Color.BLACK);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
break;
|
||||
|
||||
case Rare:
|
||||
this.setInfoLabel.setBackground(Color.YELLOW);
|
||||
this.setInfoLabel.setForeground(Color.BLACK);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
break;
|
||||
|
||||
case MythicRare:
|
||||
this.setInfoLabel.setBackground(Color.RED);
|
||||
this.setInfoLabel.setForeground(Color.BLACK);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
break;
|
||||
|
||||
case Special:
|
||||
// "Timeshifted" or other Special Rarity Cards
|
||||
this.setInfoLabel.setBackground(CardDetailPanel.purple);
|
||||
this.setInfoLabel.setForeground(Color.BLACK);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
break;
|
||||
|
||||
default: //case BasicLand: + case Common:
|
||||
this.setInfoLabel.setBackground(Color.BLACK);
|
||||
this.setInfoLabel.setForeground(Color.WHITE);
|
||||
this.setInfoLabel.setBorder(BorderFactory.createLineBorder(Color.WHITE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateBorder(CardUtil.getColors(card), canShowThis);
|
||||
|
||||
StringBuilder ptText = new StringBuilder();
|
||||
if (card.isCreature()) {
|
||||
ptText.append(card.getNetAttack()).append(" / ").append(card.getNetDefense());
|
||||
}
|
||||
|
||||
if (card.isPlaneswalker()) {
|
||||
if (ptText.length() > 0) {
|
||||
ptText.insert(0, "P/T: ");
|
||||
ptText.append(" - ").append("Loy: ");
|
||||
} else {
|
||||
ptText.append("Loyalty: ");
|
||||
}
|
||||
|
||||
int loyalty = card.getCounters(CounterType.LOYALTY);
|
||||
if (loyalty == 0) {
|
||||
loyalty = card.getBaseLoyalty();
|
||||
}
|
||||
ptText.append(loyalty);
|
||||
}
|
||||
|
||||
this.powerToughnessLabel.setText(ptText.toString());
|
||||
|
||||
this.idLabel.setText(card.getUniqueNumber() > 0 ? "[" + card.getUniqueNumber() + "]" : "");
|
||||
|
||||
// fill the card text
|
||||
this.cdArea.setText(composeCardText(card, canShowThis));
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrArea.getVerticalScrollBar().setValue(scrArea.getVerticalScrollBar().getMinimum());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String composeCardText(final Card card, final boolean canShow) {
|
||||
final StringBuilder area = new StringBuilder();
|
||||
|
||||
// Token
|
||||
if (card.isToken()) {
|
||||
area.append("Token");
|
||||
}
|
||||
|
||||
if (canShow) {
|
||||
// card text
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
String text = card.getText();
|
||||
// LEVEL [0-9]+-[0-9]+
|
||||
// LEVEL [0-9]+\+
|
||||
|
||||
String regex = "LEVEL [0-9]+-[0-9]+ ";
|
||||
text = text.replaceAll(regex, "$0\r\n");
|
||||
|
||||
regex = "LEVEL [0-9]+\\+ ";
|
||||
text = text.replaceAll(regex, "\r\n$0\r\n");
|
||||
|
||||
// displays keywords that have dots in them a little better:
|
||||
regex = "\\., ";
|
||||
text = text.replaceAll(regex, ".\r\n");
|
||||
|
||||
area.append(text);
|
||||
}
|
||||
|
||||
if (card.isPhasedOut()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Phased Out");
|
||||
}
|
||||
|
||||
// counter text
|
||||
final CounterType[] counters = CounterType.values();
|
||||
for (final CounterType counter : counters) {
|
||||
if (card.getCounters(counter) != 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append(counter.getName() + " counters: ");
|
||||
area.append(card.getCounters(counter));
|
||||
}
|
||||
}
|
||||
|
||||
if (card.isCreature()) {
|
||||
int damage = card.getDamage();
|
||||
if (damage > 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Damage: " + damage);
|
||||
}
|
||||
int assigned = card.getTotalAssignedDamage();
|
||||
if (assigned > 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Assigned Damage: " + assigned);
|
||||
}
|
||||
}
|
||||
if (card.isPlaneswalker()) {
|
||||
int assigned = card.getTotalAssignedDamage();
|
||||
if (assigned > 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Assigned Damage: " + assigned);
|
||||
}
|
||||
}
|
||||
|
||||
// Regeneration Shields
|
||||
final int regenShields = card.getShield().size();
|
||||
if (regenShields > 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Regeneration Shield(s): ").append(regenShields);
|
||||
}
|
||||
|
||||
// Damage Prevention
|
||||
final int preventNextDamage = card.getPreventNextDamageTotalShields();
|
||||
if (preventNextDamage > 0) {
|
||||
area.append("\n");
|
||||
area.append("Prevent the next ").append(preventNextDamage).append(" damage that would be dealt to ");
|
||||
area.append(card.getName()).append(" this turn.");
|
||||
}
|
||||
|
||||
// top revealed
|
||||
if ((card.hasKeyword("Play with the top card of your library revealed.") || card
|
||||
.hasKeyword("Players play with the top card of their libraries revealed."))
|
||||
&& card.getController() != null
|
||||
&& (card.isInZone(ZoneType.Battlefield) || (card.isInZone(ZoneType.Command) && !card.isCommander()))
|
||||
&& !card.getController().getZone(ZoneType.Library).isEmpty()) {
|
||||
area.append("\r\nTop card of your library: ");
|
||||
area.append(card.getController().getCardsIn(ZoneType.Library, 1));
|
||||
if (card.hasKeyword("Players play with the top card of their libraries revealed.")) {
|
||||
for (final Player p : card.getController().getAllOtherPlayers()) {
|
||||
if (p.getZone(ZoneType.Library).isEmpty()) {
|
||||
area.append(p.getName());
|
||||
area.append("'s library is empty.");
|
||||
} else {
|
||||
area.append("\r\nTop card of ");
|
||||
area.append(p.getName());
|
||||
area.append("'s library: ");
|
||||
area.append(p.getCardsIn(ZoneType.Library, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// chosen type
|
||||
if (!card.getChosenType().equals("")) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("(chosen type: ");
|
||||
area.append(card.getChosenType());
|
||||
area.append(")");
|
||||
}
|
||||
|
||||
// chosen color
|
||||
if (!card.getChosenColor().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("(chosen colors: ");
|
||||
area.append(card.getChosenColor());
|
||||
area.append(")");
|
||||
}
|
||||
|
||||
// chosen player
|
||||
if (card.getChosenPlayer() != null) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("(chosen player: " + card.getChosenPlayer() + ")");
|
||||
}
|
||||
|
||||
// named card
|
||||
if (!card.getNamedCard().equals("")) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("(named card: ");
|
||||
area.append(card.getNamedCard());
|
||||
area.append(")");
|
||||
}
|
||||
|
||||
// equipping
|
||||
if (!card.getEquipping().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("=Equipping ");
|
||||
area.append(card.getEquipping().get(0));
|
||||
area.append("=");
|
||||
}
|
||||
|
||||
// equipped by
|
||||
if (!card.getEquippedBy().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("=Equipped by ");
|
||||
for (final Iterator<Card> it = card.getEquippedBy().iterator(); it.hasNext();) {
|
||||
area.append(it.next());
|
||||
if (it.hasNext()) {
|
||||
area.append(", ");
|
||||
}
|
||||
}
|
||||
area.append("=");
|
||||
}
|
||||
|
||||
// enchanting
|
||||
final GameEntity entity = card.getEnchanting();
|
||||
if (entity != null) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("*Enchanting ");
|
||||
|
||||
if (entity instanceof Card) {
|
||||
final Card c = (Card) entity;
|
||||
if (!Singletons.getControl().mayShowCard(c)) {
|
||||
area.append("Morph (");
|
||||
area.append(card.getUniqueNumber());
|
||||
area.append(")");
|
||||
} else {
|
||||
area.append(entity);
|
||||
}
|
||||
} else {
|
||||
area.append(entity);
|
||||
}
|
||||
area.append("*");
|
||||
}
|
||||
|
||||
// enchanted by
|
||||
if (!card.getEnchantedBy().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("*Enchanted by ");
|
||||
for (final Iterator<Card> it = card.getEnchantedBy().iterator(); it.hasNext();) {
|
||||
area.append(it.next());
|
||||
if (it.hasNext()) {
|
||||
area.append(", ");
|
||||
}
|
||||
}
|
||||
area.append("*");
|
||||
}
|
||||
|
||||
// controlling
|
||||
if (card.getGainControlTargets().size() > 0) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("+Controlling: ");
|
||||
for (final Iterator<Card> it = card.getGainControlTargets().iterator(); it.hasNext();) {
|
||||
area.append(it.next());
|
||||
if (it.hasNext()) {
|
||||
area.append(", ");
|
||||
}
|
||||
}
|
||||
area.append("+");
|
||||
}
|
||||
|
||||
// cloned via
|
||||
if (card.getCloneOrigin() != null) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("^Cloned via: ");
|
||||
area.append(card.getCloneOrigin().getName());
|
||||
area.append("^");
|
||||
}
|
||||
|
||||
// Imprint
|
||||
if (!card.getImprinted().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Imprinting: ");
|
||||
for (final Iterator<Card> it = card.getImprinted().iterator(); it.hasNext();) {
|
||||
area.append(it.next());
|
||||
if (it.hasNext()) {
|
||||
area.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Haunt
|
||||
if (!card.getHauntedBy().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Haunted by: ");
|
||||
for (final Iterator<Card> it = card.getHauntedBy().iterator(); it.hasNext();) {
|
||||
area.append(it.next());
|
||||
if (it.hasNext()) {
|
||||
area.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (card.getHaunting() != null) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("Haunting " + card.getHaunting());
|
||||
}
|
||||
|
||||
// must block
|
||||
if (card.getMustBlockCards() != null) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
String mustBlockThese = Lang.joinHomogenous(card.getMustBlockCards());
|
||||
area.append("Must block " + mustBlockThese);
|
||||
}
|
||||
return FSkin.encodeSymbols(area.toString(), true);
|
||||
}
|
||||
|
||||
/** @return FLabel */
|
||||
public FLabel getNameCostLabel() {
|
||||
return this.nameCostLabel;
|
||||
}
|
||||
|
||||
/** @return FLabel */
|
||||
public FLabel getTypeLabel() {
|
||||
return this.typeLabel;
|
||||
}
|
||||
|
||||
/** @return FLabel */
|
||||
public FLabel getPowerToughnessLabel() {
|
||||
return this.powerToughnessLabel;
|
||||
}
|
||||
|
||||
/** @return JLabel */
|
||||
public JLabel getSetInfoLabel() {
|
||||
return this.setInfoLabel;
|
||||
}
|
||||
|
||||
/** @return FHtmlViewer */
|
||||
public FHtmlViewer getCDArea() {
|
||||
return this.cdArea;
|
||||
}
|
||||
|
||||
public static String formatCardType(final Card card) {
|
||||
final ArrayList<String> list = card.getType();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final ArrayList<String> superTypes = new ArrayList<String>();
|
||||
final ArrayList<String> cardTypes = new ArrayList<String>();
|
||||
final ArrayList<String> subTypes = new ArrayList<String>();
|
||||
final boolean allCreatureTypes = list.contains("AllCreatureTypes");
|
||||
|
||||
for (final String t : list) {
|
||||
if (allCreatureTypes && t.equals("AllCreatureTypes")) {
|
||||
continue;
|
||||
}
|
||||
if (CardType.isASuperType(t) && !superTypes.contains(t)) {
|
||||
superTypes.add(t);
|
||||
}
|
||||
if (CardType.isACardType(t) && !cardTypes.contains(t)) {
|
||||
cardTypes.add(t);
|
||||
}
|
||||
if (CardType.isASubType(t) && !subTypes.contains(t) && (!allCreatureTypes || !CardType.isACreatureType(t))) {
|
||||
subTypes.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
for (final String type : superTypes) {
|
||||
sb.append(type).append(" ");
|
||||
}
|
||||
for (final String type : cardTypes) {
|
||||
sb.append(type).append(" ");
|
||||
}
|
||||
if (!subTypes.isEmpty() || allCreatureTypes) {
|
||||
sb.append("- ");
|
||||
}
|
||||
if (allCreatureTypes) {
|
||||
sb.append("All creature types ");
|
||||
}
|
||||
for (final String type : subTypes) {
|
||||
sb.append(type).append(" ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void updateBorder(ColorSet list, final boolean canShow) {
|
||||
// color info
|
||||
if (list == null) {
|
||||
this.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||
scrArea.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
Color color;
|
||||
if (!canShow) {
|
||||
color = Color.gray;
|
||||
} else if (list.isMulticolor()) {
|
||||
color = Color.orange;
|
||||
} else if (list.hasBlack()) {
|
||||
color = Color.black;
|
||||
} else if (list.hasGreen()) {
|
||||
color = new Color(0, 220, 39);
|
||||
} else if (list.hasWhite()) {
|
||||
color = Color.white;
|
||||
} else if (list.hasRed()) {
|
||||
color = Color.red;
|
||||
} else if (list.hasBlue()) {
|
||||
color = Color.blue;
|
||||
} else if (list.isColorless()) {
|
||||
color = Color.gray;
|
||||
} else {
|
||||
color = new Color(200, 0, 230); // If your card has a violet border, something is wrong
|
||||
}
|
||||
|
||||
if (color != Color.gray) {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
|
||||
final int shade = 10;
|
||||
|
||||
r -= shade;
|
||||
g -= shade;
|
||||
b -= shade;
|
||||
|
||||
r = Math.max(0, r);
|
||||
g = Math.max(0, g);
|
||||
b = Math.max(0, b);
|
||||
|
||||
color = new Color(r, g, b);
|
||||
}
|
||||
this.setBorder(BorderFactory.createLineBorder(color, 2));
|
||||
scrArea.setBorder(BorderFactory.createMatteBorder(2, 0, 0, 0, color));
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package forge.gui;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.toolbox.FButton;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FScrollPane;
|
||||
import forge.item.PaperCard;
|
||||
import forge.view.FDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowFocusListener;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple class that shows a list of cards in a dialog with preview in its
|
||||
* right part.
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: ListChooser.java 9708 2011-08-09 19:34:12Z jendave $
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CardListViewer extends FDialog {
|
||||
|
||||
// Data and number of choices for the list
|
||||
private final List<PaperCard> list;
|
||||
|
||||
// initialized before; listeners may be added to it
|
||||
private final JList<PaperCard> jList;
|
||||
private final CardDetailPanel detail;
|
||||
private final CardPicturePanel picture;
|
||||
|
||||
/**
|
||||
* Instantiates a new card list viewer.
|
||||
*
|
||||
* @param title
|
||||
* the title
|
||||
* @param list
|
||||
* the list
|
||||
*/
|
||||
public CardListViewer(final String title, final List<PaperCard> list) {
|
||||
this(title, "", list, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new card list viewer.
|
||||
*
|
||||
* @param title
|
||||
* the title
|
||||
* @param message
|
||||
* the message
|
||||
* @param list
|
||||
* the list
|
||||
*/
|
||||
public CardListViewer(final String title, final String message, final List<PaperCard> list) {
|
||||
this(title, message, list, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new card list viewer.
|
||||
*
|
||||
* @param title
|
||||
* the title
|
||||
* @param message
|
||||
* the message
|
||||
* @param list
|
||||
* the list
|
||||
* @param dialogIcon
|
||||
* the dialog icon
|
||||
*/
|
||||
public CardListViewer(final String title, final String message, final List<PaperCard> list, final Icon dialogIcon) {
|
||||
this.list = Collections.unmodifiableList(list);
|
||||
this.jList = new JList<PaperCard>(new ChooserListModel());
|
||||
this.detail = new CardDetailPanel(null);
|
||||
this.picture = new CardPicturePanel();
|
||||
this.picture.setOpaque(false);
|
||||
|
||||
this.setTitle(title);
|
||||
this.setSize(720, 360);
|
||||
this.addWindowFocusListener(new CardListFocuser());
|
||||
|
||||
FButton btnOK = new FButton("OK");
|
||||
btnOK.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
CardListViewer.this.processWindowEvent(new WindowEvent(CardListViewer.this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
});
|
||||
|
||||
this.add(new FLabel.Builder().text(message).build(), "cell 0 0, spanx 3, gapbottom 4");
|
||||
this.add(new FScrollPane(this.jList, true), "cell 0 1, w 225, growy, pushy, ax c");
|
||||
this.add(this.picture, "cell 1 1, w 225, growy, pushy, ax c");
|
||||
this.add(this.detail, "cell 2 1, w 225, growy, pushy, ax c");
|
||||
this.add(btnOK, "cell 1 2, w 150, h 26, ax c, gaptop 6");
|
||||
|
||||
// selection is here
|
||||
this.jList.getSelectionModel().addListSelectionListener(new SelListener());
|
||||
this.jList.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private class ChooserListModel extends AbstractListModel<PaperCard> {
|
||||
private static final long serialVersionUID = 3871965346333840556L;
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return CardListViewer.this.list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperCard getElementAt(final int index) {
|
||||
return CardListViewer.this.list.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private class CardListFocuser implements WindowFocusListener {
|
||||
@Override
|
||||
public void windowGainedFocus(final WindowEvent e) {
|
||||
CardListViewer.this.jList.grabFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowLostFocus(final WindowEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
private class SelListener implements ListSelectionListener {
|
||||
@Override
|
||||
public void valueChanged(final ListSelectionEvent e) {
|
||||
final int row = CardListViewer.this.jList.getSelectedIndex();
|
||||
// (String) jList.getSelectedValue();
|
||||
if ((row >= 0) && (row < CardListViewer.this.list.size())) {
|
||||
final PaperCard cp = CardListViewer.this.list.get(row);
|
||||
CardListViewer.this.detail.setCard(Card.getCardForUi(cp));
|
||||
CardListViewer.this.picture.setCard(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package forge.gui;
|
||||
|
||||
import forge.ImageCache;
|
||||
import forge.ImageKeys;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardCharacteristicName;
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.toolbox.imaging.FImagePanel;
|
||||
import forge.gui.toolbox.imaging.FImagePanel.AutoSizeImageMode;
|
||||
import forge.gui.toolbox.imaging.FImageUtil;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* Displays image associated with a card or inventory item.
|
||||
*
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public final class CardPicturePanel extends JPanel {
|
||||
/** Constant <code>serialVersionUID=-3160874016387273383L</code>. */
|
||||
private static final long serialVersionUID = -3160874016387273383L;
|
||||
|
||||
private Object displayed;
|
||||
|
||||
private final FImagePanel panel;
|
||||
private BufferedImage currentImage;
|
||||
private boolean mayShowCard;
|
||||
|
||||
public CardPicturePanel() {
|
||||
super(new BorderLayout());
|
||||
|
||||
this.panel = new FImagePanel();
|
||||
this.add(this.panel);
|
||||
}
|
||||
|
||||
public void setCard(final InventoryItem cp) {
|
||||
this.displayed = cp;
|
||||
this.mayShowCard = true;
|
||||
this.setImage();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public void setCard(final Card c, boolean mayShowCard) {
|
||||
this.displayed = c;
|
||||
this.mayShowCard = mayShowCard;
|
||||
this.setImage();
|
||||
}
|
||||
|
||||
public void setCardImage(CardCharacteristicName flipState) {
|
||||
BufferedImage image = FImageUtil.getImage((Card)displayed, flipState);
|
||||
if (image != null && image != this.currentImage) {
|
||||
this.currentImage = image;
|
||||
this.panel.setImage(image, getAutoSizeImageMode());
|
||||
}
|
||||
}
|
||||
|
||||
public void setImage() {
|
||||
BufferedImage image = getImage();
|
||||
if (image != null && image != this.currentImage) {
|
||||
this.currentImage = image;
|
||||
this.panel.setImage(image, getAutoSizeImageMode());
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
if (displayed instanceof InventoryItem) {
|
||||
InventoryItem item = (InventoryItem) displayed;
|
||||
return ImageCache.getOriginalImage(ImageKeys.getImageKey(item, false), true);
|
||||
}
|
||||
else if (displayed instanceof Card) {
|
||||
if (mayShowCard) {
|
||||
return FImageUtil.getImage((Card)displayed);
|
||||
}
|
||||
return ImageCache.getOriginalImage(ImageKeys.TOKEN_PREFIX + ImageKeys.MORPH_IMAGE, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AutoSizeImageMode getAutoSizeImageMode() {
|
||||
return (isUIScaleLarger() ? AutoSizeImageMode.PANEL : AutoSizeImageMode.SOURCE);
|
||||
}
|
||||
|
||||
private boolean isUIScaleLarger() {
|
||||
return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_SCALE_LARGER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,450 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.toolbox.*;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import forge.view.FDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// An input box for handling the order of choices.
|
||||
// Left box has the original choices
|
||||
// Right box has the final order
|
||||
// Top string will be like Top of the Stack or Top of the Library
|
||||
// Bottom string will be like Bottom of the Stack or Bottom of the Library
|
||||
// Single Arrows in between left box and right box for ordering
|
||||
// Multi Arrows for moving everything in order
|
||||
// Up/down arrows on the right of the right box for swapping
|
||||
// Single ok button, disabled until left box has specified number of items remaining
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DualListBox<T> extends FDialog {
|
||||
private final FList<T> sourceList;
|
||||
private final UnsortedListModel<T> sourceListModel;
|
||||
|
||||
private final FList<T> destList;
|
||||
private final UnsortedListModel<T> destListModel;
|
||||
|
||||
private final FButton addButton;
|
||||
private final FButton addAllButton;
|
||||
private final FButton removeButton;
|
||||
private final FButton removeAllButton;
|
||||
private final FButton okButton;
|
||||
private final FButton autoButton;
|
||||
|
||||
private final FLabel orderedLabel;
|
||||
private final FLabel selectOrder;
|
||||
|
||||
private final int targetRemainingSourcesMin;
|
||||
private final int targetRemainingSourcesMax;
|
||||
|
||||
private boolean sideboardingMode = false;
|
||||
private boolean showCard = true;
|
||||
|
||||
public DualListBox(int remainingSources, List<T> sourceElements, List<T> destElements) {
|
||||
this(remainingSources, remainingSources, sourceElements, destElements);
|
||||
}
|
||||
|
||||
public DualListBox(int remainingSourcesMin, int remainingSourcesMax, List<T> sourceElements, List<T> destElements) {
|
||||
targetRemainingSourcesMin = remainingSourcesMin;
|
||||
targetRemainingSourcesMax = remainingSourcesMax;
|
||||
sourceListModel = new UnsortedListModel<T>();
|
||||
sourceList = new FList<T>(sourceListModel);
|
||||
destListModel = new UnsortedListModel<T>();
|
||||
destList = new FList<T>(destListModel);
|
||||
|
||||
final Runnable onAdd = new Runnable() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void run() {
|
||||
if (!addButton.isEnabled()) { return; }
|
||||
|
||||
List<T> selected = new ArrayList<T>();
|
||||
for (Object item : sourceList.getSelectedValuesList()) {
|
||||
selected.add((T)item);
|
||||
}
|
||||
addDestinationElements(selected);
|
||||
clearSourceSelected();
|
||||
sourceList.validate();
|
||||
_setButtonState();
|
||||
}
|
||||
};
|
||||
|
||||
final Runnable onRemove = new Runnable() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void run() {
|
||||
if (!removeButton.isEnabled()) { return; }
|
||||
|
||||
List<T> selected = new ArrayList<T>();
|
||||
for (Object item : destList.getSelectedValuesList()) {
|
||||
selected.add((T)item);
|
||||
}
|
||||
clearDestinationSelected();
|
||||
addSourceElements(selected);
|
||||
_setButtonState();
|
||||
}
|
||||
};
|
||||
|
||||
sourceList.addKeyListener(new KeyAdapter() {
|
||||
@Override public void keyPressed(final KeyEvent e) {
|
||||
_handleListKey(e, onAdd, destList);
|
||||
}
|
||||
});
|
||||
sourceList.addMouseListener(new MouseAdapter() {
|
||||
@Override public void mouseClicked(MouseEvent e) {
|
||||
if (MouseEvent.BUTTON1 == e.getButton() && 2 == e.getClickCount()) { onAdd.run(); }
|
||||
}
|
||||
});
|
||||
|
||||
destList.addKeyListener(new KeyAdapter() {
|
||||
@Override public void keyPressed(final KeyEvent e) {
|
||||
_handleListKey(e, onRemove, sourceList);
|
||||
}
|
||||
});
|
||||
destList.addMouseListener(new MouseAdapter() {
|
||||
@Override public void mouseClicked(MouseEvent e) {
|
||||
if (MouseEvent.BUTTON1 == e.getButton() && 2 == e.getClickCount()) { onRemove.run(); }
|
||||
}
|
||||
});
|
||||
|
||||
// Dual List control buttons
|
||||
addButton = new FButton(">");
|
||||
addButton.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) { onAdd.run(); } });
|
||||
addAllButton = new FButton(">>");
|
||||
addAllButton.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) { _addAll(); } });
|
||||
removeButton = new FButton("<");
|
||||
removeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { onRemove.run(); } });
|
||||
removeAllButton = new FButton("<<");
|
||||
removeAllButton.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) { _removeAll(); } });
|
||||
|
||||
// Dual List Complete Buttons
|
||||
okButton = new FButton("OK");
|
||||
okButton.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) { _finish(); } });
|
||||
autoButton = new FButton("Auto");
|
||||
autoButton.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) { _addAll(); _finish(); } });
|
||||
|
||||
FPanel leftPanel = new FPanel(new BorderLayout());
|
||||
selectOrder = new FLabel.Builder().text("Select Order:").build();
|
||||
leftPanel.add(selectOrder, BorderLayout.NORTH);
|
||||
leftPanel.add(new FScrollPane(sourceList, true), BorderLayout.CENTER);
|
||||
leftPanel.add(okButton, BorderLayout.SOUTH);
|
||||
|
||||
FPanel centerPanel = new FPanel(new GridLayout(6, 1));
|
||||
centerPanel.setBorderToggle(false);
|
||||
JPanel emptyPanel = new JPanel();
|
||||
emptyPanel.setOpaque(false);
|
||||
centerPanel.add(emptyPanel); // empty panel to take up the first slot
|
||||
centerPanel.add(addButton);
|
||||
centerPanel.add(addAllButton);
|
||||
centerPanel.add(removeButton);
|
||||
centerPanel.add(removeAllButton);
|
||||
|
||||
orderedLabel = new FLabel.Builder().build();
|
||||
|
||||
FPanel rightPanel = new FPanel(new BorderLayout());
|
||||
rightPanel.add(orderedLabel, BorderLayout.NORTH);
|
||||
rightPanel.add(new FScrollPane(destList, true), BorderLayout.CENTER);
|
||||
rightPanel.add(autoButton, BorderLayout.SOUTH);
|
||||
|
||||
add(leftPanel, "w 250, h 300");
|
||||
add(centerPanel, "w 100, h 300");
|
||||
add(rightPanel, "w 250, h 300");
|
||||
|
||||
_addListListeners(sourceList);
|
||||
_addListListeners(destList);
|
||||
|
||||
if (destElements != null && !destElements.isEmpty()) {
|
||||
addDestinationElements(destElements);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
destList.setSelectedIndex(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sourceElements != null && !sourceElements.isEmpty()) {
|
||||
addSourceElements(sourceElements);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sourceList.setSelectedIndex(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_setButtonState();
|
||||
|
||||
if (remainingSourcesMin <= sourceElements.size() && remainingSourcesMax >= sourceElements.size()) {
|
||||
//ensure OK button gets initial focus if remainingSources matches source list count
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
okButton.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecondColumnLabelText(String label) {
|
||||
orderedLabel.setText(label);
|
||||
}
|
||||
|
||||
public void setSideboardMode(boolean isSideboardMode) {
|
||||
sideboardingMode = isSideboardMode;
|
||||
if (sideboardingMode) {
|
||||
addAllButton.setVisible(false);
|
||||
removeAllButton.setVisible(false);
|
||||
autoButton.setEnabled(false);
|
||||
selectOrder.setText(String.format("Sideboard (%d):", sourceListModel.getSize()));
|
||||
orderedLabel.setText(String.format("Main Deck (%d):", destListModel.getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
private void _handleListKey(KeyEvent e, Runnable onSpace, FList<T> arrowFocusTarget) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_SPACE:
|
||||
onSpace.run();
|
||||
break;
|
||||
|
||||
case KeyEvent.VK_LEFT:
|
||||
case KeyEvent.VK_RIGHT:
|
||||
arrowFocusTarget.requestFocusInWindow();
|
||||
break;
|
||||
|
||||
case KeyEvent.VK_ENTER:
|
||||
if (okButton.isEnabled()) {
|
||||
okButton.doClick();
|
||||
}
|
||||
else if (autoButton.isEnabled()) {
|
||||
autoButton.doClick();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSourceListModel() {
|
||||
sourceListModel.clear();
|
||||
}
|
||||
|
||||
public void clearDestinationListModel() {
|
||||
destListModel.clear();
|
||||
}
|
||||
|
||||
public void addSourceElements(ListModel<T> newValue) {
|
||||
fillListModel(sourceListModel, newValue);
|
||||
}
|
||||
|
||||
public void setSourceElements(ListModel<T> newValue) {
|
||||
clearSourceListModel();
|
||||
addSourceElements(newValue);
|
||||
}
|
||||
|
||||
public void addDestinationElements(List<T> newValue) {
|
||||
fillListModel(destListModel, newValue);
|
||||
}
|
||||
|
||||
public void addDestinationElements(ListModel<T> newValue) {
|
||||
fillListModel(destListModel, newValue);
|
||||
}
|
||||
|
||||
private void fillListModel(UnsortedListModel<T> model, ListModel<T> newValues) {
|
||||
model.addAll(newValues);
|
||||
}
|
||||
|
||||
public void addSourceElements(List<T> newValue) {
|
||||
fillListModel(sourceListModel, newValue);
|
||||
}
|
||||
|
||||
public void setSourceElements(List<T> newValue) {
|
||||
clearSourceListModel();
|
||||
addSourceElements(newValue);
|
||||
}
|
||||
|
||||
private void fillListModel(UnsortedListModel<T> model, List<T> newValues) {
|
||||
model.addAll(newValues);
|
||||
}
|
||||
|
||||
private void clearSourceSelected() {
|
||||
int[] selected = sourceList.getSelectedIndices();
|
||||
for (int i = selected.length - 1; i >= 0; --i) {
|
||||
sourceListModel.removeElement(selected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDestinationSelected() {
|
||||
int[] selected = destList.getSelectedIndices();
|
||||
for (int i = selected.length - 1; i >= 0; --i) {
|
||||
destListModel.removeElement(selected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> getOrderedList() {
|
||||
this.setVisible(false);
|
||||
return destListModel.model;
|
||||
}
|
||||
|
||||
public List<T> getRemainingSourceList() {
|
||||
return sourceListModel.model;
|
||||
}
|
||||
|
||||
private void showSelectedCard(Object obj) {
|
||||
if (!showCard || null == obj) {
|
||||
return;
|
||||
}
|
||||
Card card = null;
|
||||
if (obj instanceof Card) {
|
||||
card = (Card) obj;
|
||||
} else if (obj instanceof SpellAbility) {
|
||||
card = ((SpellAbility) obj).getHostCard();
|
||||
} else if (obj instanceof PaperCard) {
|
||||
card = Card.getCardForUi((IPaperCard) obj);
|
||||
}
|
||||
|
||||
GuiUtils.clearPanelSelections();
|
||||
if (card != null) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(card);
|
||||
GuiUtils.setPanelSelection(card);
|
||||
}
|
||||
}
|
||||
|
||||
private void _addListListeners(final FList<T> list) {
|
||||
list.getModel().addListDataListener(new ListDataListener() {
|
||||
int callCount = 0;
|
||||
@Override
|
||||
public void intervalRemoved(final ListDataEvent e) {
|
||||
final int callNum = ++callCount;
|
||||
// invoke this later since the list is out of sync with the model
|
||||
// at this moment.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (callNum != callCount) {
|
||||
// don't run stale callbacks
|
||||
return;
|
||||
}
|
||||
|
||||
ListModel<T> model = list.getModel();
|
||||
if (0 == model.getSize()) {
|
||||
// nothing left to show
|
||||
return;
|
||||
}
|
||||
|
||||
int cardIdx = e.getIndex0();
|
||||
if (model.getSize() <= cardIdx) {
|
||||
// the last element got removed, get the one above it
|
||||
cardIdx = model.getSize() - 1;
|
||||
}
|
||||
showCard = false;
|
||||
list.setSelectedIndex(cardIdx);
|
||||
showCard = true;
|
||||
showSelectedCard(model.getElementAt(cardIdx));
|
||||
if (!okButton.isEnabled()) {
|
||||
list.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intervalAdded(final ListDataEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// select just-added items so user can undo the add with a single click
|
||||
int startIdx = Math.min(e.getIndex0(), e.getIndex1());
|
||||
int endIdx = Math.max(e.getIndex0(), e.getIndex1());
|
||||
int[] addedIndices = new int[endIdx - startIdx + 1];
|
||||
for (int idx = startIdx; idx <= endIdx; ++idx) {
|
||||
addedIndices[idx - startIdx] = idx;
|
||||
}
|
||||
// attempt to scroll to just-added item (setSelectedIndices does not scroll)
|
||||
// this will scroll to the wrong item if there are other identical items previously in the list
|
||||
showCard = false;
|
||||
list.setSelectedValue(list.getModel().getElementAt(
|
||||
Math.min(endIdx, startIdx + list.getVisibleRowCount())), true);
|
||||
list.setSelectedIndices(addedIndices);
|
||||
showCard = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contentsChanged(ListDataEvent e) {
|
||||
}
|
||||
});
|
||||
|
||||
list.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent ev) {
|
||||
showSelectedCard(list.getSelectedValue());
|
||||
}
|
||||
});
|
||||
|
||||
list.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
showSelectedCard(list.getSelectedValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void _addAll() {
|
||||
addDestinationElements(sourceListModel);
|
||||
clearSourceListModel();
|
||||
_setButtonState();
|
||||
}
|
||||
|
||||
private void _removeAll() {
|
||||
addSourceElements(destListModel);
|
||||
clearDestinationListModel();
|
||||
_setButtonState();
|
||||
}
|
||||
|
||||
private void _setButtonState() {
|
||||
if (sideboardingMode) {
|
||||
removeAllButton.setVisible(false);
|
||||
addAllButton.setVisible(false);
|
||||
selectOrder.setText(String.format("Sideboard (%d):", sourceListModel.getSize()));
|
||||
orderedLabel.setText(String.format("Main Deck (%d):", destListModel.getSize()));
|
||||
}
|
||||
|
||||
boolean anySize = targetRemainingSourcesMax < 0;
|
||||
boolean canAdd = sourceListModel.getSize() != 0 && (anySize || targetRemainingSourcesMin <= sourceListModel.getSize());
|
||||
boolean canRemove = destListModel.getSize() != 0;
|
||||
boolean targetReached = anySize || targetRemainingSourcesMin <= sourceListModel.getSize() && targetRemainingSourcesMax >= sourceListModel.getSize();
|
||||
|
||||
autoButton.setEnabled(targetRemainingSourcesMax == 0 && !targetReached && !sideboardingMode);
|
||||
|
||||
addButton.setEnabled(canAdd);
|
||||
addAllButton.setEnabled(canAdd);
|
||||
removeButton.setEnabled(canRemove);
|
||||
removeAllButton.setEnabled(canRemove);
|
||||
|
||||
okButton.setEnabled(targetReached);
|
||||
if (targetReached) {
|
||||
okButton.requestFocusInWindow(); //focus OK button if specific target reached
|
||||
}
|
||||
}
|
||||
|
||||
private void _finish() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import forge.control.ChatArea;
|
||||
import forge.gui.toolbox.*;
|
||||
import forge.gui.toolbox.FSkin.SkinnedPanel;
|
||||
import forge.net.FServer;
|
||||
import forge.net.Lobby;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public enum FNetOverlay {
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private final OverlayPanel pnl = new OverlayPanel();
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public SkinnedPanel getPanel() {
|
||||
return this.pnl;
|
||||
}
|
||||
|
||||
private final FTextArea txtLog = new FTextArea();
|
||||
private final FTextField txtInput = new FTextField.Builder().maxLength(60).build();
|
||||
private final FLabel cmdSend = new FLabel.ButtonBuilder().text("Send").build();
|
||||
|
||||
|
||||
//private boolean minimized = false;
|
||||
private int height = 120;
|
||||
private int width = 400;
|
||||
|
||||
private final ActionListener onSend = new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String message = txtInput.getText();
|
||||
txtInput.setText("");
|
||||
if ( StringUtils.isBlank(message) )
|
||||
return;
|
||||
|
||||
Lobby lobby = FServer.instance.getLobby();
|
||||
lobby.speak(ChatArea.Room, lobby.getGuiPlayer(), message);
|
||||
}
|
||||
};
|
||||
|
||||
//private final int minimizedHeight = 30;
|
||||
|
||||
/**
|
||||
* Semi-transparent overlay panel. Should be used with layered panes.
|
||||
*/
|
||||
private FNetOverlay() {
|
||||
pnl.setOpaque(false);
|
||||
pnl.setVisible(false);
|
||||
pnl.setBackground(FSkin.getColor(FSkin.Colors.CLR_ZEBRA));
|
||||
pnl.setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS)));
|
||||
|
||||
pnl.setLayout(new MigLayout("insets 0, gap 0, ax center, wrap 2"));
|
||||
// pnl.add(new FLabel.Builder().text("Loading new game...").fontSize(22).build(), "h 40px!, align center");
|
||||
|
||||
// Block all input events below the overlay
|
||||
|
||||
txtLog.setOpaque(true);
|
||||
txtLog.setFocusable(true);
|
||||
txtLog.setBackground(FSkin.getColor(FSkin.Colors.CLR_ZEBRA));
|
||||
|
||||
FScrollPane _operationLogScroller = new FScrollPane(txtLog, false);
|
||||
_operationLogScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
new SmartScroller(_operationLogScroller);
|
||||
pnl.add(_operationLogScroller, "pushx, hmin 24, pushy, growy, growx, gap 2px 2px 2px 0, sx 2");
|
||||
|
||||
txtInput.setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS)));
|
||||
pnl.add(txtInput, "pushx, growx, h 26px!, gap 2px 2px 2px 0");
|
||||
pnl.add(cmdSend, "w 60px!, h 28px!, gap 0 0 2px 0");
|
||||
|
||||
txtInput.addActionListener(onSend);
|
||||
cmdSend.setCommand(new Runnable() { @Override public void run() { onSend.actionPerformed(null); } });
|
||||
}
|
||||
|
||||
public void showUp(String message) {
|
||||
txtLog.setText(message);
|
||||
pnl.setVisible(true);
|
||||
}
|
||||
|
||||
private class OverlayPanel extends SkinnedPanel {
|
||||
private static final long serialVersionUID = -5056220798272120558L;
|
||||
|
||||
/**
|
||||
* For some reason, the alpha channel background doesn't work properly on
|
||||
* Windows 7, so the paintComponent override is required for a
|
||||
* semi-transparent overlay.
|
||||
*
|
||||
* @param g
|
||||
*   Graphics object
|
||||
*/
|
||||
@Override
|
||||
public void paintComponent(final Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.setColor(this.getBackground());
|
||||
g.fillRect(0, 0, this.getWidth(), this.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param mainBounds
|
||||
*/
|
||||
public void containerResized(Rectangle mainBounds) {
|
||||
int w = Math.max(width, (int)(mainBounds.width * 0.25f));
|
||||
int x = mainBounds.width - w;
|
||||
int y = mainBounds.height - height;
|
||||
getPanel().setBounds(x, y, w, height);
|
||||
getPanel().validate();
|
||||
}
|
||||
|
||||
SimpleDateFormat inFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
public void addMessage(String origin, String message) {
|
||||
String toAdd = String.format("%n[%s] %s: %s", inFormat.format(new Date()), origin, message);
|
||||
txtLog.append(toAdd);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class ForgeAction extends AbstractAction {
|
||||
public static enum MatchConstants {
|
||||
ALWAYSACCEPT ("Always accept this trigger"),
|
||||
ALWAYSDECLINE ("Always decline this trigger"),
|
||||
ALWAYSASK ("Always ask"),
|
||||
HUMANEXILED ("Player's Exile", "Exile:", "Player - View Exile"),
|
||||
HUMANFLASHBACK("Play card with Flashback", "Flashback:", "Player - View Cards with Flashback"),
|
||||
HUMANGRAVEYARD("Player's Graveyard", "Graveyard:", "Player - View Graveyard"),
|
||||
HUMANHAND ("Player's Hand", "Hand:", "Player - View Hand"),
|
||||
HUMANLIBRARY ("Player's Library", "Library:", "Player - View Library");
|
||||
|
||||
public final String title;
|
||||
public final String button;
|
||||
public final String menu;
|
||||
|
||||
private MatchConstants(String title0) {
|
||||
title = title0;
|
||||
button = title0;
|
||||
menu = title0;
|
||||
}
|
||||
private MatchConstants(String title0, String button0, String menu0) {
|
||||
title = title0;
|
||||
button = button0;
|
||||
menu = menu0;
|
||||
}
|
||||
}
|
||||
|
||||
public ForgeAction(MatchConstants property) {
|
||||
super(property.button);
|
||||
this.putValue("buttonText", property.button);
|
||||
this.putValue("menuText", property.menu);
|
||||
}
|
||||
|
||||
public <T extends AbstractButton> T setupButton(final T button) {
|
||||
button.setAction(this);
|
||||
button.setText((String) this.getValue(button instanceof JMenuItem ? "menuText" : "buttonText"));
|
||||
return button;
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import forge.view.FDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gui_ProgressBarWindow class.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
* @since 1.0.15
|
||||
*/
|
||||
public class GuiProgressBarWindow extends FDialog {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 5832740611050396643L;
|
||||
private final JPanel contentPanel = new JPanel();
|
||||
private final JProgressBar progressBar = new JProgressBar();
|
||||
|
||||
/**
|
||||
* Create the dialog.
|
||||
*/
|
||||
public GuiProgressBarWindow() {
|
||||
this.setResizable(false);
|
||||
this.setTitle("Some Progress");
|
||||
final Dimension screen = this.getToolkit().getScreenSize();
|
||||
this.setBounds(screen.width / 3, 100, // position
|
||||
450, 84); // size
|
||||
this.getContentPane().setLayout(null);
|
||||
this.contentPanel.setBounds(0, 0, 442, 58);
|
||||
this.contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
this.getContentPane().add(this.contentPanel);
|
||||
this.contentPanel.setLayout(null);
|
||||
this.progressBar.setValue(50);
|
||||
// progressBar.setBackground(Color.GRAY);
|
||||
// progressBar.setForeground(Color.BLUE);
|
||||
this.progressBar.setBounds(12, 12, 418, 32);
|
||||
this.contentPanel.add(this.progressBar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* setProgressRange.
|
||||
* </p>
|
||||
*
|
||||
* @param min
|
||||
* a int.
|
||||
* @param max
|
||||
* a int.
|
||||
*/
|
||||
public final void setProgressRange(final int min, final int max) {
|
||||
this.progressBar.setMinimum(min);
|
||||
this.progressBar.setMaximum(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* increment.
|
||||
* </p>
|
||||
*/
|
||||
public final void increment() {
|
||||
this.progressBar.setValue(this.progressBar.getValue() + 1);
|
||||
|
||||
if ((this.progressBar.getValue() % 10) == 0) {
|
||||
this.contentPanel.paintImmediately(this.progressBar.getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the progressBar for fine tuning (e.g., adding text).
|
||||
*
|
||||
* @return the progressBar
|
||||
*/
|
||||
public final JProgressBar getProgressBar() {
|
||||
return this.progressBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the progress back to zero units completed.
|
||||
*
|
||||
* It is not certain whether this method actually works the way it was
|
||||
* intended.
|
||||
*/
|
||||
public final void reset() {
|
||||
this.getProgressBar().setValue(0);
|
||||
this.contentPanel.paintImmediately(this.getProgressBar().getBounds());
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.match.VMatchUI;
|
||||
import forge.gui.match.views.VField;
|
||||
import forge.view.arcane.CardPanel;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* GuiUtils class.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class GuiUtils {
|
||||
private GuiUtils() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a font from a filename. Concise error reported if
|
||||
* exceptions found.
|
||||
*
|
||||
* @param filename
|
||||
* String
|
||||
* @return Font
|
||||
*/
|
||||
public static Font newFont(final String filename) {
|
||||
final File file = new File(filename);
|
||||
Font ttf = null;
|
||||
|
||||
try {
|
||||
ttf = Font.createFont(Font.TRUETYPE_FONT, file);
|
||||
} catch (final FontFormatException e) {
|
||||
System.err.println("GuiUtils > newFont: bad font format \"" + filename + "\"");
|
||||
} catch (final IOException e) {
|
||||
System.err.println("GuiUtils > newFont: can't find \"" + filename + "\"");
|
||||
}
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all visually highlighted card panels on the battlefield.
|
||||
*/
|
||||
public static void clearPanelSelections() {
|
||||
List<VField> view = VMatchUI.SINGLETON_INSTANCE.getFieldViews();
|
||||
for (VField v : view) {
|
||||
for (CardPanel p : v.getTabletop().getCardPanels()) {
|
||||
p.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight a card on the playfield.
|
||||
*
|
||||
* @param c
|
||||
* a card to be highlighted
|
||||
*/
|
||||
public static void setPanelSelection(final Card c) {
|
||||
mainLoop:
|
||||
for (VField v : VMatchUI.SINGLETON_INSTANCE.getFieldViews()) {
|
||||
List<CardPanel> panels = v.getTabletop().getCardPanels();
|
||||
for (CardPanel p : panels) {
|
||||
if (p.getCard().equals(c)) {
|
||||
p.setSelected(true);
|
||||
break mainLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int minItemWidth = 100;
|
||||
private static final int itemHeight = 25;
|
||||
|
||||
public static void setMenuItemSize(JMenuItem item) {
|
||||
item.setPreferredSize(new Dimension(Math.max(item.getPreferredSize().width, minItemWidth), itemHeight));
|
||||
}
|
||||
|
||||
public static JMenu createMenu(String label) {
|
||||
if (label.startsWith("<html>")) { //adjust label if HTML
|
||||
label = "<html>" + "<div style='height: " + itemHeight + "px; margin-top: 6px;'>" + label.substring(6, label.length() - 7) + "</div></html>";
|
||||
}
|
||||
JMenu menu = new JMenu(label);
|
||||
setMenuItemSize(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static JMenuItem createMenuItem(String label, KeyStroke accelerator, final Runnable onClick, boolean enabled, boolean bold) {
|
||||
if (label.startsWith("<html>")) { //adjust label if HTML
|
||||
label = "<html>" + "<div style='height: " + itemHeight + "px; margin-top: 6px;'>" + label.substring(6, label.length() - 7) + "</div></html>";
|
||||
}
|
||||
JMenuItem item = new JMenuItem(label);
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
if (null != onClick) {
|
||||
onClick.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
item.setEnabled(enabled);
|
||||
item.setAccelerator(accelerator);
|
||||
if (bold) {
|
||||
item.setFont(item.getFont().deriveFont(Font.BOLD));
|
||||
}
|
||||
setMenuItemSize(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, true, false));
|
||||
}
|
||||
|
||||
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, true, false));
|
||||
}
|
||||
|
||||
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, enabled, false));
|
||||
}
|
||||
|
||||
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, enabled, false));
|
||||
}
|
||||
|
||||
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled, boolean bold) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, enabled, bold));
|
||||
}
|
||||
|
||||
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled, boolean bold) {
|
||||
parent.add(createMenuItem(label, accelerator, onClick, enabled, bold));
|
||||
}
|
||||
|
||||
public static void addSeparator(JPopupMenu parent) {
|
||||
parent.add(new JSeparator());
|
||||
}
|
||||
|
||||
public static void addSeparator(JMenuItem parent) {
|
||||
parent.add(new JSeparator());
|
||||
}
|
||||
}
|
||||
@@ -1,985 +0,0 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (c) 2013 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.error.BugReporter;
|
||||
import forge.gui.ImportSourceAnalyzer.OpType;
|
||||
import forge.gui.toolbox.*;
|
||||
import forge.properties.NewConstants;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
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 java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
/**
|
||||
* This class implements an overlay-based dialog that imports data from a user-selected directory
|
||||
* into the correct locations in the user and cache directories. There is a lot of I/O and data
|
||||
* processing done in this class, so most operations are asynchronous.
|
||||
*/
|
||||
public class ImportDialog {
|
||||
private final FButton _btnStart;
|
||||
private final FButton _btnCancel;
|
||||
private final FLabel _btnChooseDir;
|
||||
private final FPanel _topPanel;
|
||||
private final JPanel _selectionPanel;
|
||||
|
||||
// volatile since it is checked from multiple threads
|
||||
private volatile boolean _cancel;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public ImportDialog(String forcedSrcDir, final Runnable onDialogClose) {
|
||||
_topPanel = new FPanel(new MigLayout("insets dialog, gap 0, center, wrap, fill"));
|
||||
_topPanel.setOpaque(false);
|
||||
_topPanel.setBackgroundTexture(FSkin.getIcon(FSkin.Backgrounds.BG_TEXTURE));
|
||||
|
||||
final boolean isMigration = !StringUtils.isEmpty(forcedSrcDir);
|
||||
|
||||
// header
|
||||
_topPanel.add(new FLabel.Builder().text((isMigration ? "Migrate" : "Import") + " profile data").fontSize(15).build(), "center");
|
||||
|
||||
// add some help text if this is for the initial data migration
|
||||
if (isMigration) {
|
||||
FPanel blurbPanel = new FPanel(new MigLayout("insets panel, gap 10, fill"));
|
||||
blurbPanel.setOpaque(false);
|
||||
JPanel blurbPanelInterior = new JPanel(new MigLayout("insets dialog, gap 10, center, wrap, fill"));
|
||||
blurbPanelInterior.setOpaque(false);
|
||||
blurbPanelInterior.add(new FLabel.Builder().text("<html><b>What's this?</b></html>").build(), "growx, w 50:50:");
|
||||
blurbPanelInterior.add(new FLabel.Builder().text(
|
||||
"<html>Over the last several years, people have had to jump through a lot of hoops to" +
|
||||
" update to the most recent version. We hope to reduce this workload to a point where a new" +
|
||||
" user will find that it is fairly painless to update. In order to make this happen, Forge" +
|
||||
" has changed where it stores your data so that it is outside of the program installation directory." +
|
||||
" This way, when you upgrade, you will no longer need to import your data every time to get things" +
|
||||
" working. There are other benefits to having user data separate from program data, too, and it" +
|
||||
" lays the groundwork for some cool new features.</html>").build(), "growx, w 50:50:");
|
||||
blurbPanelInterior.add(new FLabel.Builder().text("<html><b>So where's my data going?</b></html>").build(), "growx, w 50:50:");
|
||||
blurbPanelInterior.add(new FLabel.Builder().text(
|
||||
"<html>Forge will now store your data in the same place as other applications on your system." +
|
||||
" Specifically, your personal data, like decks, quest progress, and program preferences will be" +
|
||||
" stored in <b>" + NewConstants.USER_DIR + "</b> and all downloaded content, such as card pictures," +
|
||||
" skins, and quest world prices will be under <b>" + NewConstants.CACHE_DIR + "</b>. If, for whatever" +
|
||||
" reason, you need to set different paths, cancel out of this dialog, exit Forge, and find the <b>" +
|
||||
NewConstants.PROFILE_TEMPLATE_FILE + "</b> file in the program installation directory. Copy or rename" +
|
||||
" it to <b>" + NewConstants.PROFILE_FILE + "</b> and edit the paths inside it. Then restart Forge and use" +
|
||||
" this dialog to move your data to the paths that you set. Keep in mind that if you install a future" +
|
||||
" version of Forge into a different directory, you'll need to copy this file over so Forge will know" +
|
||||
" where to find your data.</html>").build(), "growx, w 50:50:");
|
||||
blurbPanelInterior.add(new FLabel.Builder().text(
|
||||
"<html><b>Remember, your data won't be available until you complete this step!</b></html>").build(), "growx, w 50:50:");
|
||||
|
||||
FScrollPane blurbScroller = new FScrollPane(blurbPanelInterior, true,
|
||||
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
blurbPanel.add(blurbScroller, "hmin 150, growy, growx, center, gap 0 0 5 5");
|
||||
_topPanel.add(blurbPanel, "gap 10 10 20 0, growy, growx, w 50:50:");
|
||||
}
|
||||
|
||||
// import source widgets
|
||||
JPanel importSourcePanel = new JPanel(new MigLayout("insets 0, gap 10"));
|
||||
importSourcePanel.setOpaque(false);
|
||||
importSourcePanel.add(new FLabel.Builder().text("Import from:").build());
|
||||
final FTextField txfSrc = new FTextField.Builder().readonly().build();
|
||||
importSourcePanel.add(txfSrc, "pushx, 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 UiCommand() {
|
||||
@Override public void run() {
|
||||
// bring up a file open dialog and, if the OK button is selected, apply the filename
|
||||
// to the import source text field
|
||||
if (JFileChooser.APPROVE_OPTION == _fileChooser.showOpenDialog(JOptionPane.getRootFrame())) {
|
||||
File f = _fileChooser.getSelectedFile();
|
||||
if (!f.canRead()) {
|
||||
FOptionPane.showErrorDialog("Cannot access selected directory (Permission denied).");
|
||||
}
|
||||
else {
|
||||
txfSrc.setText(f.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
importSourcePanel.add(_btnChooseDir, "h pref+8!, w pref+12!");
|
||||
|
||||
// add change handler to the import source text field that starts up a
|
||||
// new analyzer. it also interacts with the current active analyzer,
|
||||
// if any, to make sure it cancels out before the new one is initiated
|
||||
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) {
|
||||
// text field is read-only, so the only time this will get updated
|
||||
// is when _btnChooseDir does it
|
||||
final String text = txfSrc.getText();
|
||||
if (text.equals(prevText)) {
|
||||
// only restart the analyzer if the directory has changed
|
||||
return;
|
||||
}
|
||||
prevText = text;
|
||||
|
||||
// cancel any active analyzer
|
||||
_cancel = true;
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
// ensure we don't get two instances of this function running at the same time
|
||||
_btnChooseDir.setEnabled(false);
|
||||
|
||||
// re-disable the start button. it will be enabled if the previous analyzer has
|
||||
// already successfully finished
|
||||
_btnStart.setEnabled(false);
|
||||
|
||||
// we have to wait in a background thread since we can't block in the GUI thread
|
||||
SwingWorker<Void, Void> analyzerStarter = new SwingWorker<Void, Void>() {
|
||||
@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) {
|
||||
// this will populate the panel with data selection widgets
|
||||
_AnalyzerUpdater analyzer = new _AnalyzerUpdater(text, _onAnalyzerDone, isMigration);
|
||||
analyzer.run();
|
||||
_analyzerActive = true;
|
||||
}
|
||||
if (!isMigration) {
|
||||
// only enable the directory choosing button if this is not a migration dialog
|
||||
// since in that case we're permanently locked to the starting directory
|
||||
_btnChooseDir.setEnabled(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
analyzerStarter.execute();
|
||||
}
|
||||
}
|
||||
});
|
||||
_topPanel.add(importSourcePanel, "gaptop 20, pushx, growx");
|
||||
|
||||
// prepare import selection panel (will be cleared and filled in later by an analyzer)
|
||||
_selectionPanel = new JPanel();
|
||||
_selectionPanel.setOpaque(false);
|
||||
_topPanel.add(_selectionPanel, "growx, growy, gaptop 10");
|
||||
|
||||
// action button widgets
|
||||
final Runnable cleanup = new Runnable() {
|
||||
@Override public void run() { SOverlayUtils.hideOverlay(); }
|
||||
};
|
||||
_btnStart = new FButton("Start import");
|
||||
_btnStart.setEnabled(false);
|
||||
_btnCancel = new FButton("Cancel");
|
||||
_btnCancel.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
_cancel = true;
|
||||
cleanup.run();
|
||||
if (null != onDialogClose) {
|
||||
onDialogClose.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JPanel southPanel = new JPanel(new MigLayout("ax center"));
|
||||
southPanel.setOpaque(false);
|
||||
southPanel.add(_btnStart, "center, w pref+144!, h pref+12!");
|
||||
southPanel.add(_btnCancel, "center, w pref+144!, h pref+12!, gap 72");
|
||||
_topPanel.add(southPanel, "growx");
|
||||
|
||||
JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel();
|
||||
overlay.setLayout(new MigLayout("insets 0, gap 0, wrap, ax center, ay center"));
|
||||
overlay.add(_topPanel, "w 500::90%, h 100::90%");
|
||||
SOverlayUtils.showOverlay();
|
||||
|
||||
// focus cancel button after the dialog is shown
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() { _btnCancel.requestFocusInWindow(); }
|
||||
});
|
||||
|
||||
// if our source dir is provided, set the text, which will fire off an analyzer
|
||||
if (isMigration) {
|
||||
File srcDirFile = new File(forcedSrcDir);
|
||||
txfSrc.setText(srcDirFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
// encapsulates the choices in the combobox for choosing the destination paths for
|
||||
// decks of unknown type
|
||||
private class _UnknownDeckChoice {
|
||||
public final String name;
|
||||
public final String path;
|
||||
|
||||
public _UnknownDeckChoice(String name0, String path0) {
|
||||
name = name0;
|
||||
path = path0;
|
||||
}
|
||||
|
||||
@Override public String toString() { return name; }
|
||||
}
|
||||
|
||||
// this class owns the import selection widgets and bridges them with the running
|
||||
// MigrationSourceAnalyzer instance
|
||||
private class _AnalyzerUpdater extends SwingWorker<Void, Void> {
|
||||
// associates a file operation type with its enablement checkbox and the set
|
||||
// of file move/copy operations that enabling it would entail
|
||||
private final Map<OpType, Pair<FCheckBox, ? extends Map<File, File>>> _selections =
|
||||
new HashMap<OpType, Pair<FCheckBox, ? extends Map<File, File>>>();
|
||||
|
||||
// attached to all changeable widgets to keep the UI in sync
|
||||
private final ChangeListener _stateChangedListener = new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent arg0) { _updateUI(); }
|
||||
};
|
||||
|
||||
private final String _srcDir;
|
||||
private final Runnable _onAnalyzerDone;
|
||||
private final boolean _isMigration;
|
||||
private final FLabel _unknownDeckLabel;
|
||||
private final FComboBoxWrapper<_UnknownDeckChoice> _unknownDeckCombo;
|
||||
private final FCheckBox _moveCheckbox;
|
||||
private final FCheckBox _overwriteCheckbox;
|
||||
private final JTextArea _operationLog;
|
||||
private final JScrollPane _operationLogScroller;
|
||||
private final JProgressBar _progressBar;
|
||||
|
||||
// updates the _operationLog widget asynchronously to keep the UI responsive
|
||||
private final _OperationLogAsyncUpdater _operationLogUpdater;
|
||||
|
||||
public _AnalyzerUpdater(String srcDir, Runnable onAnalyzerDone, boolean isMigration) {
|
||||
_srcDir = srcDir;
|
||||
_onAnalyzerDone = onAnalyzerDone;
|
||||
_isMigration = isMigration;
|
||||
|
||||
_selectionPanel.removeAll();
|
||||
_selectionPanel.setLayout(new MigLayout("insets 0, gap 5, wrap, fill"));
|
||||
|
||||
JPanel cbPanel = new JPanel(new MigLayout("insets 0, gap 5"));
|
||||
cbPanel.setOpaque(false);
|
||||
|
||||
// add deck selections
|
||||
JPanel knownDeckPanel = new JPanel(new MigLayout("insets 0, gap 5, wrap 2"));
|
||||
knownDeckPanel.setOpaque(false);
|
||||
knownDeckPanel.add(new FLabel.Builder().text("Decks").build(), "wrap");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.CONSTRUCTED_DECK, "Constructed decks");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.DRAFT_DECK, "Draft decks");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.PLANAR_DECK, "Planar decks");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.SCHEME_DECK, "Scheme decks");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.SEALED_DECK, "Sealed decks");
|
||||
_addSelectionWidget(knownDeckPanel, OpType.UNKNOWN_DECK, "Unknown decks");
|
||||
JPanel unknownDeckPanel = new JPanel(new MigLayout("insets 0, gap 5"));
|
||||
unknownDeckPanel.setOpaque(false);
|
||||
_unknownDeckCombo = new FComboBoxWrapper<_UnknownDeckChoice>();
|
||||
_unknownDeckCombo.addItem(new _UnknownDeckChoice("Constructed", NewConstants.DECK_CONSTRUCTED_DIR));
|
||||
_unknownDeckCombo.addItem(new _UnknownDeckChoice("Draft", NewConstants.DECK_DRAFT_DIR));
|
||||
_unknownDeckCombo.addItem(new _UnknownDeckChoice("Planar", NewConstants.DECK_PLANE_DIR));
|
||||
_unknownDeckCombo.addItem(new _UnknownDeckChoice("Scheme", NewConstants.DECK_SCHEME_DIR));
|
||||
_unknownDeckCombo.addItem(new _UnknownDeckChoice("Sealed", NewConstants.DECK_SEALED_DIR));
|
||||
_unknownDeckCombo.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent arg0) { _updateUI(); }
|
||||
});
|
||||
_unknownDeckLabel = new FLabel.Builder().text("Treat unknown decks as:").build();
|
||||
unknownDeckPanel.add(_unknownDeckLabel);
|
||||
_unknownDeckCombo.addTo(unknownDeckPanel);
|
||||
knownDeckPanel.add(unknownDeckPanel, "span");
|
||||
cbPanel.add(knownDeckPanel, "aligny top");
|
||||
|
||||
// add other userDir data elements
|
||||
JPanel dataPanel = new JPanel(new MigLayout("insets 0, gap 5, wrap"));
|
||||
dataPanel.setOpaque(false);
|
||||
dataPanel.add(new FLabel.Builder().text("Other data").build());
|
||||
_addSelectionWidget(dataPanel, OpType.GAUNTLET_DATA, "Gauntlet data");
|
||||
_addSelectionWidget(dataPanel, OpType.QUEST_DATA, "Quest saves");
|
||||
_addSelectionWidget(dataPanel, OpType.PREFERENCE_FILE, "Preference files");
|
||||
cbPanel.add(dataPanel, "aligny top");
|
||||
|
||||
// add cacheDir data elements
|
||||
JPanel cachePanel = new JPanel(new MigLayout("insets 0, gap 5, wrap 2"));
|
||||
cachePanel.setOpaque(false);
|
||||
cachePanel.add(new FLabel.Builder().text("Cached data").build(), "wrap");
|
||||
_addSelectionWidget(cachePanel, OpType.DEFAULT_CARD_PIC, "Default card pics");
|
||||
_addSelectionWidget(cachePanel, OpType.SET_CARD_PIC, "Set-specific card pics");
|
||||
_addSelectionWidget(cachePanel, OpType.TOKEN_PIC, "Card token pics");
|
||||
_addSelectionWidget(cachePanel, OpType.QUEST_PIC, "Quest-related pics");
|
||||
_addSelectionWidget(cachePanel, OpType.DB_FILE, "Database files", true, null, "wrap");
|
||||
|
||||
_addSelectionWidget(cachePanel, OpType.POSSIBLE_SET_CARD_PIC,
|
||||
"Import possible set pics from as-yet unsupported cards", false,
|
||||
"<html>Picture files that are not recognized as belonging to any known card.<br>" +
|
||||
"It could be that these pictures belong to cards that are not yet supported<br>" +
|
||||
"by Forge. If you know this to be the case and want the pictures imported for<br>" +
|
||||
"future use, select this option.<html>", "span");
|
||||
cbPanel.add(cachePanel, "aligny top");
|
||||
_selectionPanel.add(cbPanel, "center");
|
||||
|
||||
// add move/copy and overwrite checkboxes
|
||||
JPanel ioOptionPanel = new JPanel(new MigLayout("insets 0, gap 10"));
|
||||
ioOptionPanel.setOpaque(false);
|
||||
_moveCheckbox = new FCheckBox("Remove source files after copy");
|
||||
_moveCheckbox.setToolTipText("Move files into the data directories instead of just copying them");
|
||||
_moveCheckbox.setSelected(isMigration);
|
||||
_moveCheckbox.addChangeListener(_stateChangedListener);
|
||||
ioOptionPanel.add(_moveCheckbox);
|
||||
_overwriteCheckbox = new FCheckBox("Overwrite files in destination");
|
||||
_overwriteCheckbox.setToolTipText("Overwrite existing data with the imported data");
|
||||
_overwriteCheckbox.addChangeListener(_stateChangedListener);
|
||||
ioOptionPanel.add(_overwriteCheckbox);
|
||||
_selectionPanel.add(ioOptionPanel);
|
||||
|
||||
// add operation summary textfield
|
||||
_operationLog = new JTextArea();
|
||||
_operationLog.setFont(new Font("Monospaced", Font.PLAIN, 10));
|
||||
_operationLog.setOpaque(false);
|
||||
_operationLog.setWrapStyleWord(true);
|
||||
_operationLog.setLineWrap(true);
|
||||
_operationLog.setEditable(false);
|
||||
// autoscroll when we set/add text unless the user has intentionally scrolled somewhere else
|
||||
_operationLogScroller = new JScrollPane(_operationLog);
|
||||
_operationLogScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
new SmartScroller(_operationLogScroller);
|
||||
_selectionPanel.add(_operationLogScroller, "w 400:400:, hmin 60, growy, growx");
|
||||
|
||||
// add progress bar
|
||||
_progressBar = new JProgressBar();
|
||||
_progressBar.setString("Preparing to analyze source directory...");
|
||||
_progressBar.setStringPainted(true);
|
||||
_selectionPanel.add(_progressBar, "w 100%!");
|
||||
|
||||
// start the op log updater
|
||||
_operationLogUpdater = new _OperationLogAsyncUpdater(_selections, _operationLog);
|
||||
_operationLogUpdater.start();
|
||||
|
||||
// set initial checkbox labels
|
||||
_updateUI();
|
||||
|
||||
// resize the panel properly now that the _selectionPanel is filled in
|
||||
_selectionPanel.getParent().validate();
|
||||
_selectionPanel.getParent().invalidate();
|
||||
}
|
||||
|
||||
private void _addSelectionWidget(JPanel parent, OpType type, String name) {
|
||||
_addSelectionWidget(parent, type, name, true, null, null);
|
||||
}
|
||||
|
||||
private void _addSelectionWidget(JPanel parent, OpType type, String name, boolean selected,
|
||||
String tooltip, String constraints) {
|
||||
FCheckBox cb = new FCheckBox();
|
||||
cb.setName(name);
|
||||
cb.setSelected(selected);
|
||||
cb.setToolTipText(tooltip);
|
||||
cb.addChangeListener(_stateChangedListener);
|
||||
|
||||
// use a skip list map instead of a regular hashmap so that the files are sorted
|
||||
// alphabetically in the logs. note that this is a concurrent data structure
|
||||
// since it will be modified and read simultaneously by different threads
|
||||
_selections.put(type, Pair.of(cb, new ConcurrentSkipListMap<File, File>()));
|
||||
parent.add(cb, constraints);
|
||||
}
|
||||
|
||||
// must be called from GUI event loop thread
|
||||
private void _updateUI() {
|
||||
// update checkbox text labels with current totals
|
||||
Set<OpType> selectedOptions = new HashSet<OpType>();
|
||||
for (Map.Entry<OpType, Pair<FCheckBox, ? extends Map<File, File>>> entry : _selections.entrySet()) {
|
||||
Pair<FCheckBox, ? extends Map<File, File>> selection = entry.getValue();
|
||||
FCheckBox cb = selection.getLeft();
|
||||
|
||||
if (cb.isSelected()) {
|
||||
selectedOptions.add(entry.getKey());
|
||||
}
|
||||
|
||||
cb.setText(String.format("%s (%d)", cb.getName(), selection.getRight().size()));
|
||||
}
|
||||
|
||||
// asynchronously update the text in the op log, which may be many tens of thousands of lines long
|
||||
// if this were done synchronously the UI would slow to a crawl
|
||||
_operationLogUpdater.requestUpdate(selectedOptions, (_UnknownDeckChoice)_unknownDeckCombo.getSelectedItem(),
|
||||
_moveCheckbox.isSelected(), _overwriteCheckbox.isSelected());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
Timer timer = null;
|
||||
|
||||
try {
|
||||
Map<OpType, Map<File, File>> selections = new HashMap<OpType, Map<File, File>>();
|
||||
for (Map.Entry<OpType, Pair<FCheckBox, ? extends Map<File, File>>> entry : _selections.entrySet()) {
|
||||
selections.put(entry.getKey(), entry.getValue().getRight());
|
||||
}
|
||||
|
||||
ImportSourceAnalyzer.AnalysisCallback cb = new ImportSourceAnalyzer.AnalysisCallback() {
|
||||
@Override
|
||||
public boolean checkCancel() { return _cancel; }
|
||||
|
||||
@Override
|
||||
public void addOp(OpType type, File src, File dest) {
|
||||
// add to concurrent map
|
||||
_selections.get(type).getRight().put(src, dest);
|
||||
}
|
||||
};
|
||||
|
||||
final ImportSourceAnalyzer msa = new ImportSourceAnalyzer(_srcDir, cb);
|
||||
final int numFilesToAnalyze = msa.getNumFilesToAnalyze();
|
||||
|
||||
// update only once every half-second so we're not flooding the UI with updates
|
||||
timer = new Timer(500, null);
|
||||
timer.setInitialDelay(100);
|
||||
final Timer finalTimer = timer;
|
||||
timer.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent arg0) {
|
||||
if (_cancel) {
|
||||
finalTimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// timers run in the gui event loop, so it's ok to interact with widgets
|
||||
_progressBar.setValue(msa.getNumFilesAnalyzed());
|
||||
_updateUI();
|
||||
|
||||
// allow the the panel to resize to accommodate additional text
|
||||
_selectionPanel.getParent().validate();
|
||||
_selectionPanel.getParent().invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
// update the progress bar widget from the GUI event loop
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (_cancel) { return; }
|
||||
_progressBar.setString("Analyzing...");
|
||||
_progressBar.setMaximum(numFilesToAnalyze);
|
||||
_progressBar.setValue(0);
|
||||
_progressBar.setIndeterminate(false);
|
||||
|
||||
// start update timer
|
||||
finalTimer.start();
|
||||
}
|
||||
});
|
||||
|
||||
// does not return until analysis is complete or has been canceled
|
||||
msa.doAnalysis();
|
||||
} catch (final Exception e) {
|
||||
_cancel = true;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
_progressBar.setString("Error");
|
||||
BugReporter.reportException(e);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
// ensure the UI update timer is stopped after analysis is complete
|
||||
if (null != timer) {
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// executes in gui event loop thread
|
||||
@Override
|
||||
protected void done() {
|
||||
if (!_cancel) {
|
||||
_progressBar.setValue(_progressBar.getMaximum());
|
||||
_updateUI();
|
||||
_progressBar.setString("Analysis complete");
|
||||
|
||||
// clear any previously-set action listeners on the start button
|
||||
// in case we've previously completed an analysis but changed the directory
|
||||
// instead of starting the import
|
||||
for (ActionListener a : _btnStart.getActionListeners()) {
|
||||
_btnStart.removeActionListener(a);
|
||||
}
|
||||
|
||||
// deselect and disable all options that have 0 operations associated with
|
||||
// them to highlight the important options
|
||||
for (Pair<FCheckBox, ? extends Map<File, File>> p : _selections.values()) {
|
||||
FCheckBox cb = p.getLeft();
|
||||
if (0 == p.getRight().size()) {
|
||||
cb.removeChangeListener(_stateChangedListener);
|
||||
cb.setSelected(false);
|
||||
cb.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == _selections.get(OpType.UNKNOWN_DECK).getRight().size()) {
|
||||
_unknownDeckLabel.setEnabled(false);
|
||||
_unknownDeckCombo.setEnabled(false);
|
||||
}
|
||||
|
||||
// set up the start button to start the prepared import on click
|
||||
_btnStart.addActionListener(new ActionListener() {
|
||||
@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 (Map.Entry<OpType, Pair<FCheckBox, ? extends Map<File, File>>> entry : _selections.entrySet()) {
|
||||
if (OpType.POSSIBLE_SET_CARD_PIC == entry.getKey()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add name to list if checkbox is unselected, but contains operations
|
||||
Pair<FCheckBox, ? extends Map<File, File>> p = entry.getValue();
|
||||
FCheckBox cb = p.getLeft();
|
||||
if (!cb.isSelected() && 0 < p.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>");
|
||||
|
||||
String[] options = { "Whoops, let me fix that!", "Continue with the import, I know what I'm doing." };
|
||||
int chosen = FOptionPane.showOptionDialog(sb.toString(), "Migration warning", FOptionPane.WARNING_ICON, options);
|
||||
|
||||
if (chosen != 1) {
|
||||
// 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
|
||||
_btnStart.setEnabled(false);
|
||||
_btnChooseDir.setEnabled(false);
|
||||
|
||||
for (Pair<FCheckBox, ? extends Map<File, File>> selection : _selections.values()) {
|
||||
selection.getLeft().setEnabled(false);
|
||||
}
|
||||
_unknownDeckCombo.setEnabled(false);
|
||||
_moveCheckbox.setEnabled(false);
|
||||
_overwriteCheckbox.setEnabled(false);
|
||||
|
||||
// stop updating the operation log -- the importer needs it now
|
||||
_operationLogUpdater.requestStop();
|
||||
|
||||
// jump to the bottom of the log text area so it starts autoscrolling again
|
||||
// note that since it is controlled by a SmartScroller, just setting the caret position will not work
|
||||
JScrollBar scrollBar = _operationLogScroller.getVerticalScrollBar();
|
||||
scrollBar.setValue(scrollBar.getMaximum());
|
||||
|
||||
// start importing!
|
||||
_Importer importer = new _Importer(
|
||||
_srcDir, _selections, _unknownDeckCombo, _operationLog, _progressBar,
|
||||
_moveCheckbox.isSelected(), _overwriteCheckbox.isSelected());
|
||||
importer.run();
|
||||
|
||||
_btnCancel.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// import ready to proceed: enable the start button
|
||||
_btnStart.setEnabled(true);
|
||||
}
|
||||
|
||||
// report to the Choose Directory button that this analysis run has stopped
|
||||
_onAnalyzerDone.run();
|
||||
}
|
||||
}
|
||||
|
||||
// asynchronously iterates through the given concurrent maps and populates the operation log with
|
||||
// the proposed operations
|
||||
private class _OperationLogAsyncUpdater extends Thread {
|
||||
final Map<OpType, Map<File, File>> _selections;
|
||||
final JTextArea _operationLog; // safe to set text from another thread
|
||||
|
||||
// synchronized-access data
|
||||
private int _updateCallCnt = 0;
|
||||
private Set<OpType> _selectedOptions;
|
||||
private _UnknownDeckChoice _unknownDeckChoice;
|
||||
private boolean _isMove;
|
||||
private boolean _isOverwrite;
|
||||
private boolean _stop;
|
||||
|
||||
// only accessed from the event loop thread
|
||||
int _maxLogLength = 0;
|
||||
|
||||
public _OperationLogAsyncUpdater(Map<OpType, Pair<FCheckBox, ? extends Map<File, File>>> selections, JTextArea operationLog) {
|
||||
super("OperationLogUpdater");
|
||||
setDaemon(true);
|
||||
|
||||
_selections = new HashMap<OpType, Map<File, File>>();
|
||||
_operationLog = operationLog;
|
||||
|
||||
// remove references to FCheckBox when populating map -- we can't safely access it from a thread
|
||||
// anyway and it's better to keep our data structure clean to prevent mistakes
|
||||
for (Map.Entry<OpType, Pair<FCheckBox, ? extends Map<File, File>>> entry : selections.entrySet()) {
|
||||
_selections.put(entry.getKey(), entry.getValue().getRight());
|
||||
}
|
||||
}
|
||||
|
||||
// updates the synchronized data with values for the next iteration in _run
|
||||
public synchronized void requestUpdate(
|
||||
Set<OpType> selectedOptions, _UnknownDeckChoice unknownDeckChoice, boolean isMove, boolean isOverwrite) {
|
||||
++_updateCallCnt;
|
||||
_selectedOptions = selectedOptions;
|
||||
_unknownDeckChoice = unknownDeckChoice;
|
||||
_isMove = isMove;
|
||||
_isOverwrite = isOverwrite;
|
||||
|
||||
// notify waiter
|
||||
notify();
|
||||
}
|
||||
|
||||
public synchronized void requestStop() {
|
||||
_stop = true;
|
||||
|
||||
// notify waiter
|
||||
notify();
|
||||
}
|
||||
|
||||
private void _run() throws InterruptedException {
|
||||
int lastUpdateCallCnt = _updateCallCnt;
|
||||
Set<OpType> selectedOptions;
|
||||
_UnknownDeckChoice unknownDeckChoice;
|
||||
boolean isMove;
|
||||
boolean isOverwrite;
|
||||
|
||||
while (true) {
|
||||
synchronized (this) {
|
||||
// can't check _stop in the while condition since we have to do it in a synchronized block
|
||||
if (_stop) { break; }
|
||||
|
||||
// if we're stopped while looping here, run through the update one last time
|
||||
// before returning
|
||||
while (lastUpdateCallCnt == _updateCallCnt && !_stop) {
|
||||
wait();
|
||||
}
|
||||
|
||||
// safely copy synchronized data to local values that we will use for this runthrough
|
||||
lastUpdateCallCnt = _updateCallCnt;
|
||||
selectedOptions = _selectedOptions;
|
||||
unknownDeckChoice = _unknownDeckChoice;
|
||||
isMove = _isMove;
|
||||
isOverwrite = _isOverwrite;
|
||||
}
|
||||
|
||||
// build operation log
|
||||
final StringBuilder log = new StringBuilder();
|
||||
int totalOps = 0;
|
||||
for (OpType opType : selectedOptions) {
|
||||
Map<File, File> ops = _selections.get(opType);
|
||||
totalOps += ops.size();
|
||||
|
||||
for (Map.Entry<File, File> op : ops.entrySet()) {
|
||||
File dest = op.getValue();
|
||||
if (OpType.UNKNOWN_DECK == opType) {
|
||||
dest = new File(unknownDeckChoice.path, dest.getName());
|
||||
}
|
||||
log.append(op.getKey().getAbsolutePath()).append(" -> ");
|
||||
log.append(dest.getAbsolutePath()).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// append summary
|
||||
if (0 < totalOps) {
|
||||
log.append("\n");
|
||||
}
|
||||
log.append("Prepared to ").append(isMove ? "move" : "copy");
|
||||
log.append(" ").append(totalOps).append(" files\n");
|
||||
log.append(isOverwrite ? "O" : "Not o").append("verwriting existing files");
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String logText = log.toString();
|
||||
|
||||
// setText is thread-safe, but the resizing is not, so might as well do this in the swing event loop thread
|
||||
_operationLog.setText(log.toString());
|
||||
|
||||
if (_maxLogLength < logText.length()) {
|
||||
_maxLogLength = logText.length();
|
||||
|
||||
// resize the panel properly for the new log contents
|
||||
_selectionPanel.getParent().validate();
|
||||
_selectionPanel.getParent().invalidate();
|
||||
_topPanel.getParent().validate();
|
||||
_topPanel.getParent().invalidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try { _run(); } catch (final InterruptedException e) {
|
||||
_cancel = true;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
// we never interrupt the thread, so this is not expected to happen
|
||||
BugReporter.reportException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// asynchronously completes the specified I/O operations and updates the progress bar and operation log
|
||||
private class _Importer extends SwingWorker<Void, Void> {
|
||||
private final String _srcDir;
|
||||
private final Map<File, File> _operations;
|
||||
private final JTextArea _operationLog;
|
||||
private final JProgressBar _progressBar;
|
||||
private final boolean _move;
|
||||
private final boolean _overwrite;
|
||||
|
||||
public _Importer(String srcDir, Map<OpType, Pair<FCheckBox, ? extends Map<File, File>>> selections, FComboBoxWrapper<_UnknownDeckChoice> unknownDeckCombo,
|
||||
JTextArea operationLog, JProgressBar progressBar, boolean move, boolean overwrite) {
|
||||
_srcDir = srcDir;
|
||||
_operationLog = operationLog;
|
||||
_progressBar = progressBar;
|
||||
_move = move;
|
||||
_overwrite = overwrite;
|
||||
|
||||
// build local operations map that only includes data that we can access from the background thread
|
||||
// use a tree map to maintain alphabetical order
|
||||
_operations = new TreeMap<File, File>();
|
||||
for (Map.Entry<OpType, Pair<FCheckBox, ? extends Map<File, File>>> entry : selections.entrySet()) {
|
||||
Pair<FCheckBox, ? extends Map<File, File>> selection = entry.getValue();
|
||||
if (selection.getLeft().isSelected()) {
|
||||
if (OpType.UNKNOWN_DECK != entry.getKey()) {
|
||||
_operations.putAll(selection.getRight());
|
||||
} else {
|
||||
// map unknown decks to selected directory
|
||||
for (Map.Entry<File, File> op : selection.getRight().entrySet()) {
|
||||
_UnknownDeckChoice choice = (_UnknownDeckChoice)unknownDeckCombo.getSelectedItem();
|
||||
_operations.put(op.getKey(), new File(choice.path, op.getValue().getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set progress bar bounds
|
||||
_progressBar.setString(_move ? "Moving files..." : "Copying files...");
|
||||
_progressBar.setMinimum(0);
|
||||
_progressBar.setMaximum(_operations.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
try {
|
||||
// working with textbox text is thread safe
|
||||
_operationLog.setText("");
|
||||
|
||||
// only update the text box once very half second, but make the first
|
||||
// update after only 100ms
|
||||
final long updateIntervalMs = 500;
|
||||
long lastUpdateTimestampMs = System.currentTimeMillis() - 400;
|
||||
StringBuffer opLogBuf = new StringBuffer();
|
||||
|
||||
// only update the progress bar when we expect the visual value to change
|
||||
final long progressInterval = Math.max(1, _operations.size() / _progressBar.getWidth());
|
||||
|
||||
// the length of the prefix to remove from source paths
|
||||
final int srcPathPrefixLen;
|
||||
if (_srcDir.endsWith("/") || _srcDir.endsWith(File.separator)) {
|
||||
srcPathPrefixLen = _srcDir.length();
|
||||
} else
|
||||
{
|
||||
srcPathPrefixLen = _srcDir.length() + 1;
|
||||
}
|
||||
|
||||
// stats maintained during import sequence
|
||||
int numOps = 0;
|
||||
int numExisting = 0;
|
||||
int numSucceeded = 0;
|
||||
int numFailed = 0;
|
||||
for (Map.Entry<File, File> op : _operations.entrySet()) {
|
||||
if (_cancel) { break; }
|
||||
|
||||
final int curOpNum = ++numOps;
|
||||
if (0 == curOpNum % progressInterval) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (_cancel) { return; }
|
||||
_progressBar.setValue(curOpNum);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
long curTimeMs = System.currentTimeMillis();
|
||||
if (updateIntervalMs <= curTimeMs - lastUpdateTimestampMs) {
|
||||
lastUpdateTimestampMs = curTimeMs;
|
||||
|
||||
// working with textbox text is thread safe
|
||||
_operationLog.append(opLogBuf.toString());
|
||||
opLogBuf.setLength(0);
|
||||
}
|
||||
|
||||
File srcFile = op.getKey();
|
||||
File destFile = op.getValue();
|
||||
|
||||
try {
|
||||
// simplify logged source path and log next attempted operation
|
||||
String srcPath = srcFile.getAbsolutePath();
|
||||
// I doubt that the srcPath will start with anything other than _srcDir, even with symlinks,
|
||||
// hardlinks, or Windows junctioned nodes, but it's better to be safe than to have malformed output
|
||||
if (srcPath.startsWith(_srcDir)) {
|
||||
srcPath = srcPath.substring(srcPathPrefixLen);
|
||||
}
|
||||
opLogBuf.append(_move ? "Moving " : "Copying ").append(srcPath).append(" -> ");
|
||||
opLogBuf.append(destFile.getAbsolutePath()).append("\n");
|
||||
|
||||
if (!destFile.exists()) {
|
||||
_copyFile(srcFile, destFile, _move);
|
||||
} else {
|
||||
if (_overwrite) {
|
||||
opLogBuf.append(" Destination file exists; overwriting\n");
|
||||
_copyFile(srcFile, destFile, _move);
|
||||
} else {
|
||||
opLogBuf.append(" Destination file exists; skipping copy\n");
|
||||
}
|
||||
++numExisting;
|
||||
}
|
||||
|
||||
if (_move) {
|
||||
// source file may have been deleted already if _copyFile was called
|
||||
srcFile.delete();
|
||||
opLogBuf.append(" Removed source file after successful copy\n");
|
||||
}
|
||||
|
||||
++numSucceeded;
|
||||
} catch (IOException e) {
|
||||
opLogBuf.append(" Operation failed: ").append(e.getMessage()).append("\n");
|
||||
++numFailed;
|
||||
}
|
||||
}
|
||||
|
||||
// append summary footer
|
||||
opLogBuf.append("\nImport complete: ");
|
||||
opLogBuf.append(numSucceeded).append(" operation").append(1 == numSucceeded ? "" : "s").append(" succeeded, ");
|
||||
opLogBuf.append(numFailed).append(" error").append(1 == numFailed ? "" : "s");
|
||||
if (0 < numExisting) {
|
||||
opLogBuf.append(", ").append(numExisting);
|
||||
if (_overwrite) {
|
||||
opLogBuf.append(" existing destination files overwritten");
|
||||
} else {
|
||||
opLogBuf.append(" copy operations skipped due to existing destination files");
|
||||
}
|
||||
}
|
||||
_operationLog.append(opLogBuf.toString());
|
||||
} catch (final Exception e) {
|
||||
_cancel = true;
|
||||
|
||||
// report any exceptions in a standard dialog
|
||||
// note that regular I/O errors don't throw, they'll just be mentioned in the log
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
_progressBar.setString("Error");
|
||||
BugReporter.reportException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
_btnCancel.requestFocusInWindow();
|
||||
if (_cancel) { return; }
|
||||
|
||||
_progressBar.setValue(_progressBar.getMaximum());
|
||||
_progressBar.setString("Import complete");
|
||||
_btnCancel.setText("Done");
|
||||
}
|
||||
}
|
||||
|
||||
// when copying is required, uses java nio classes for ultra-fast I/O
|
||||
private static void _copyFile(File srcFile, File destFile, boolean deleteSrcAfter) throws IOException {
|
||||
destFile.getParentFile().mkdirs();
|
||||
|
||||
// if this is a move, try a simple rename first
|
||||
if (deleteSrcAfter) {
|
||||
if (srcFile.renameTo(destFile)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!destFile.exists()) {
|
||||
destFile.createNewFile();
|
||||
}
|
||||
|
||||
FileChannel src = null;
|
||||
FileChannel dest = null;
|
||||
try {
|
||||
src = new FileInputStream(srcFile).getChannel();
|
||||
dest = new FileOutputStream(destFile).getChannel();
|
||||
dest.transferFrom(src, 0, src.size());
|
||||
} finally {
|
||||
if (src != null) { src.close(); }
|
||||
if (dest != null) { dest.close(); }
|
||||
}
|
||||
|
||||
if (deleteSrcAfter) {
|
||||
srcFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,656 +0,0 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (c) 2013 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ImageCache;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRules;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.NewConstants;
|
||||
import forge.util.FileUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ImportSourceAnalyzer {
|
||||
public static enum OpType {
|
||||
CONSTRUCTED_DECK,
|
||||
DRAFT_DECK,
|
||||
PLANAR_DECK,
|
||||
SCHEME_DECK,
|
||||
SEALED_DECK,
|
||||
UNKNOWN_DECK,
|
||||
DEFAULT_CARD_PIC,
|
||||
SET_CARD_PIC,
|
||||
POSSIBLE_SET_CARD_PIC,
|
||||
TOKEN_PIC,
|
||||
QUEST_PIC,
|
||||
GAUNTLET_DATA,
|
||||
QUEST_DATA,
|
||||
PREFERENCE_FILE,
|
||||
DB_FILE
|
||||
}
|
||||
|
||||
public static interface AnalysisCallback {
|
||||
boolean checkCancel();
|
||||
void addOp(OpType type, File src, File dest);
|
||||
}
|
||||
|
||||
private final File _source;
|
||||
private final AnalysisCallback _cb;
|
||||
private final int _numFilesToAnalyze;
|
||||
|
||||
private int _numFilesAnalyzed;
|
||||
|
||||
public ImportSourceAnalyzer(String source, AnalysisCallback cb) {
|
||||
_source = new File(source);
|
||||
_cb = cb;
|
||||
|
||||
_numFilesToAnalyze = _countFiles(_source);
|
||||
}
|
||||
|
||||
public int getNumFilesToAnalyze() { return _numFilesToAnalyze; }
|
||||
public int getNumFilesAnalyzed() { return _numFilesAnalyzed; }
|
||||
|
||||
public void doAnalysis() {
|
||||
_identifyAndAnalyze(_source);
|
||||
}
|
||||
|
||||
private void _identifyAndAnalyze(File root) {
|
||||
// see if we can figure out the likely identity of the source folder and
|
||||
// dispatch to the best analysis subroutine to handle it
|
||||
String dirname = root.getName();
|
||||
|
||||
if ("res".equalsIgnoreCase(dirname)) { _analyzeOldResDir(root); }
|
||||
else if ("constructed".equalsIgnoreCase(dirname)) { _analyzeConstructedDeckDir(root); }
|
||||
else if ("draft".equalsIgnoreCase(dirname)) { _analyzeDraftDeckDir(root); }
|
||||
else if ("plane".equalsIgnoreCase(dirname) || "planar".equalsIgnoreCase(dirname)) { _analyzePlanarDeckDir(root); }
|
||||
else if ("scheme".equalsIgnoreCase(dirname)) { _analyzeSchemeDeckDir(root); }
|
||||
else if ("sealed".equalsIgnoreCase(dirname)) { _analyzeSealedDeckDir(root); }
|
||||
else if (StringUtils.containsIgnoreCase(dirname, "deck")) { _analyzeDecksDir(root); }
|
||||
else if ("gauntlet".equalsIgnoreCase(dirname)) { _analyzeGauntletDataDir(root); }
|
||||
else if ("layouts".equalsIgnoreCase(dirname)) { _analyzeLayoutsDir(root); }
|
||||
else if ("pics".equalsIgnoreCase(dirname)) { _analyzeCardPicsDir(root); }
|
||||
else if ("pics_product".equalsIgnoreCase(dirname)) { _analyzeProductPicsDir(root); }
|
||||
else if ("preferences".equalsIgnoreCase(dirname)) { _analyzePreferencesDir(root); }
|
||||
else if ("quest".equalsIgnoreCase(dirname)) { _analyzeQuestDir(root); }
|
||||
else if (null != Singletons.getMagicDb().getEditions().get(dirname)) { _analyzeCardPicsSetDir(root); }
|
||||
else {
|
||||
// look at files in directory and make a semi-educated guess based on file extensions
|
||||
int numUnhandledFiles = 0;
|
||||
for (File file : root.listFiles()) {
|
||||
if (_cb.checkCancel()) { return; }
|
||||
|
||||
if (file.isFile()) {
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, ".dck")) {
|
||||
_analyzeDecksDir(root);
|
||||
numUnhandledFiles = 0;
|
||||
break;
|
||||
} else if (StringUtils.endsWithIgnoreCase(filename, ".jpg")) {
|
||||
_analyzeCardPicsDir(root);
|
||||
numUnhandledFiles = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
++numUnhandledFiles;
|
||||
} else if (file.isDirectory()) {
|
||||
_identifyAndAnalyze(file);
|
||||
}
|
||||
}
|
||||
_numFilesAnalyzed += numUnhandledFiles;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// pre-profile res dir
|
||||
//
|
||||
|
||||
private void _analyzeOldResDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override boolean onDir(File dir) {
|
||||
String dirname = dir.getName();
|
||||
if ("decks".equalsIgnoreCase(dirname)) {
|
||||
_analyzeDecksDir(dir);
|
||||
} else if ("gauntlet".equalsIgnoreCase(dirname)) {
|
||||
_analyzeGauntletDataDir(dir);
|
||||
} else if ("layouts".equalsIgnoreCase(dirname)) {
|
||||
_analyzeLayoutsDir(dir);
|
||||
} else if ("pics".equalsIgnoreCase(dirname)) {
|
||||
_analyzeCardPicsDir(dir);
|
||||
} else if ("pics_product".equalsIgnoreCase(dirname)) {
|
||||
_analyzeProductPicsDir(dir);
|
||||
} else if ("preferences".equalsIgnoreCase(dirname)) {
|
||||
_analyzePreferencesDir(dir);
|
||||
} else if ("quest".equalsIgnoreCase(dirname)) {
|
||||
_analyzeQuestDir(dir);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// decks
|
||||
//
|
||||
|
||||
private void _analyzeDecksDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
// we don't really expect any files in here, but if we find a .dck file, add it to the unknown list
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, ".dck")) {
|
||||
File targetFile = new File(_lcaseExt(filename));
|
||||
_cb.addOp(OpType.UNKNOWN_DECK, file, targetFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override boolean onDir(File dir) {
|
||||
String dirname = dir.getName();
|
||||
if ("constructed".equalsIgnoreCase(dirname)) {
|
||||
_analyzeConstructedDeckDir(dir);
|
||||
} else if ("cube".equalsIgnoreCase(dirname)) {
|
||||
return false;
|
||||
} else if ("draft".equalsIgnoreCase(dirname)) {
|
||||
_analyzeDraftDeckDir(dir);
|
||||
} else if ("plane".equalsIgnoreCase(dirname) || "planar".equalsIgnoreCase(dirname)) {
|
||||
_analyzePlanarDeckDir(dir);
|
||||
} else if ("scheme".equalsIgnoreCase(dirname)) {
|
||||
_analyzeSchemeDeckDir(dir);
|
||||
} else if ("sealed".equalsIgnoreCase(dirname)) {
|
||||
_analyzeSealedDeckDir(dir);
|
||||
} else {
|
||||
_analyzeKnownDeckDir(dir, null, OpType.UNKNOWN_DECK);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void _analyzeConstructedDeckDir(File root) {
|
||||
_analyzeKnownDeckDir(root, NewConstants.DECK_CONSTRUCTED_DIR, OpType.CONSTRUCTED_DECK);
|
||||
}
|
||||
|
||||
private void _analyzeDraftDeckDir(File root) {
|
||||
_analyzeKnownDeckDir(root, NewConstants.DECK_DRAFT_DIR, OpType.DRAFT_DECK);
|
||||
}
|
||||
|
||||
private void _analyzePlanarDeckDir(File root) {
|
||||
_analyzeKnownDeckDir(root, NewConstants.DECK_PLANE_DIR, OpType.PLANAR_DECK);
|
||||
}
|
||||
|
||||
private void _analyzeSchemeDeckDir(File root) {
|
||||
_analyzeKnownDeckDir(root, NewConstants.DECK_SCHEME_DIR, OpType.SCHEME_DECK);
|
||||
}
|
||||
|
||||
private void _analyzeSealedDeckDir(File root) {
|
||||
_analyzeKnownDeckDir(root, NewConstants.DECK_SEALED_DIR, OpType.SEALED_DECK);
|
||||
}
|
||||
|
||||
private void _analyzeKnownDeckDir(File root, final String targetDir, final OpType opType) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, ".dck")) {
|
||||
File targetFile = new File(targetDir, _lcaseExt(filename));
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(opType, file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override boolean onDir(File dir) {
|
||||
// if there's a dir beneath a known directory, assume the same kind of decks are in there
|
||||
_analyzeKnownDeckDir(dir, targetDir, opType);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// gauntlet
|
||||
//
|
||||
|
||||
private void _analyzeGauntletDataDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
// find *.dat files, but exclude LOCKED_*
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, ".dat") && !filename.startsWith("LOCKED_")) {
|
||||
File targetFile = new File(NewConstants.GAUNTLET_DIR.userPrefLoc, _lcaseExt(filename));
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(OpType.GAUNTLET_DATA, file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// layouts
|
||||
//
|
||||
|
||||
private void _analyzeLayoutsDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
// find *_preferred.xml files
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, "_preferred.xml")) {
|
||||
File targetFile = new File(NewConstants.USER_PREFS_DIR,
|
||||
file.getName().toLowerCase(Locale.ENGLISH).replace("_preferred", ""));
|
||||
_cb.addOp(OpType.PREFERENCE_FILE, file, targetFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// default card pics
|
||||
//
|
||||
|
||||
private static String _oldCleanString(String in) {
|
||||
final StringBuffer out = new StringBuffer();
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
char c = in.charAt(i);
|
||||
if ((c == ' ') || (c == '-')) {
|
||||
out.append('_');
|
||||
} else if (Character.isLetterOrDigit(c) || (c == '_')) {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// usually we would want to pass Locale.ENGLISH to the toLowerCase() method to prevent unintentional
|
||||
// character mangling on some system locales, but we want to replicate the old code here exactly
|
||||
return out.toString().toLowerCase();
|
||||
}
|
||||
|
||||
private void _addDefaultPicNames(PaperCard c, boolean backFace) {
|
||||
CardRules card = c.getRules();
|
||||
String urls = card.getPictureUrl(backFace);
|
||||
if (StringUtils.isEmpty(urls)) { return; }
|
||||
|
||||
int numPics = 1 + StringUtils.countMatches(urls, "\\");
|
||||
if ( c.getArtIndex() > numPics )
|
||||
return;
|
||||
|
||||
String filenameBase = ImageCache.getImageKey(c, backFace, false);
|
||||
String filename = filenameBase + ".jpg";
|
||||
boolean alreadyHadIt = null != _defaultPicNames.put(filename, filename);
|
||||
if ( alreadyHadIt ) return;
|
||||
|
||||
// Do you shift artIndex by one here?
|
||||
String newLastSymbol = 0 == c.getArtIndex() ? "" : String.valueOf(c.getArtIndex() /* + 1 */);
|
||||
String oldFilename = _oldCleanString(filenameBase.replaceAll("[0-9]?(\\.full)?$", "")) + newLastSymbol + ".jpg";
|
||||
//if ( numPics > 1 )
|
||||
//System.out.printf("Will move %s -> %s%n", oldFilename, filename);
|
||||
_defaultPicOldNameToCurrentName.put(oldFilename, filename);
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> _defaultPicNames;
|
||||
private Map<String, String> _defaultPicOldNameToCurrentName;
|
||||
private void _analyzeCardPicsDir(File root) {
|
||||
if (null == _defaultPicNames) {
|
||||
_defaultPicNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
_defaultPicOldNameToCurrentName = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
for (PaperCard c : Singletons.getMagicDb().getCommonCards().getAllCards()) {
|
||||
_addDefaultPicNames(c, false);
|
||||
if (ImageCache.hasBackFacePicture(c))
|
||||
_addDefaultPicNames(c, true);
|
||||
}
|
||||
|
||||
for (PaperCard c : Singletons.getMagicDb().getVariantCards().getAllCards()) {
|
||||
_addDefaultPicNames(c, false);
|
||||
// variants never have backfaces
|
||||
}
|
||||
}
|
||||
|
||||
_analyzeListedDir(root, NewConstants.CACHE_CARD_PICS_DIR, new _ListedAnalyzer() {
|
||||
@Override public String map(String filename) {
|
||||
if (_defaultPicOldNameToCurrentName.containsKey(filename)) {
|
||||
return _defaultPicOldNameToCurrentName.get(filename);
|
||||
}
|
||||
return _defaultPicNames.get(filename);
|
||||
}
|
||||
|
||||
@Override public OpType getOpType(String filename) { return OpType.DEFAULT_CARD_PIC; }
|
||||
|
||||
@Override boolean onDir(File dir) {
|
||||
if ("icons".equalsIgnoreCase(dir.getName())) {
|
||||
_analyzeIconsPicsDir(dir);
|
||||
} else if ("tokens".equalsIgnoreCase(dir.getName())) {
|
||||
_analyzeTokenPicsDir(dir);
|
||||
} else {
|
||||
_analyzeCardPicsSetDir(dir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// set card pics
|
||||
//
|
||||
|
||||
private static void _addSetCards(Map<String, String> cardFileNames, Iterable<PaperCard> library, Predicate<PaperCard> filter) {
|
||||
for (PaperCard c : Iterables.filter(library, filter)) {
|
||||
String filename = ImageCache.getImageKey(c, false, true) + ".jpg";
|
||||
cardFileNames.put(filename, filename);
|
||||
if (ImageCache.hasBackFacePicture(c)) {
|
||||
filename = ImageCache.getImageKey(c, true, true) + ".jpg";
|
||||
cardFileNames.put(filename, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Map<String, String>> _cardFileNamesBySet;
|
||||
Map<String, String> _nameUpdates;
|
||||
private void _analyzeCardPicsSetDir(File root) {
|
||||
if (null == _cardFileNamesBySet) {
|
||||
_cardFileNamesBySet = new TreeMap<String, Map<String, String>>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (CardEdition ce : Singletons.getMagicDb().getEditions()) {
|
||||
Map<String, String> cardFileNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
Predicate<PaperCard> filter = IPaperCard.Predicates.printedInSet(ce.getCode());
|
||||
_addSetCards(cardFileNames, Singletons.getMagicDb().getCommonCards().getAllCards(), filter);
|
||||
_addSetCards(cardFileNames, Singletons.getMagicDb().getVariantCards().getAllCards(), filter);
|
||||
_cardFileNamesBySet.put(ce.getCode2(), cardFileNames);
|
||||
}
|
||||
|
||||
// planar cards now don't have the ".full" part in their filenames
|
||||
_nameUpdates = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
Predicate<PaperCard> predPlanes = new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard arg0) {
|
||||
return arg0.getRules().getType().isPlane() || arg0.getRules().getType().isPhenomenon();
|
||||
}
|
||||
};
|
||||
|
||||
for (PaperCard c : Iterables.filter(Singletons.getMagicDb().getVariantCards().getAllCards(), predPlanes)) {
|
||||
String baseName = ImageCache.getImageKey(c,false, true);
|
||||
_nameUpdates.put(baseName + ".full.jpg", baseName + ".jpg");
|
||||
if (ImageCache.hasBackFacePicture(c)) {
|
||||
baseName = ImageCache.getImageKey(c, true, true);
|
||||
_nameUpdates.put(baseName + ".full.jpg", baseName + ".jpg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardEdition.Collection editions = Singletons.getMagicDb().getEditions();
|
||||
String editionCode = root.getName();
|
||||
CardEdition edition = editions.get(editionCode);
|
||||
if (null == edition) {
|
||||
// not a valid set name, skip
|
||||
_numFilesAnalyzed += _countFiles(root);
|
||||
return;
|
||||
}
|
||||
|
||||
final String editionCode2 = edition.getCode2();
|
||||
final Map<String, String> validFilenames = _cardFileNamesBySet.get(editionCode2);
|
||||
_analyzeListedDir(root, NewConstants.CACHE_CARD_PICS_DIR, new _ListedAnalyzer() {
|
||||
@Override public String map(String filename) {
|
||||
filename = editionCode2 + "/" + filename;
|
||||
if (_nameUpdates.containsKey(filename)) {
|
||||
filename = _nameUpdates.get(filename);
|
||||
}
|
||||
if (validFilenames.containsKey(filename)) {
|
||||
return validFilenames.get(filename);
|
||||
} else if (StringUtils.endsWithIgnoreCase(filename, ".jpg")
|
||||
|| StringUtils.endsWithIgnoreCase(filename, ".png")) {
|
||||
return filename;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override public OpType getOpType(String filename) {
|
||||
return validFilenames.containsKey(filename) ? OpType.SET_CARD_PIC : OpType.POSSIBLE_SET_CARD_PIC;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// other image dirs
|
||||
//
|
||||
|
||||
Map<String, String> _iconFileNames;
|
||||
private void _analyzeIconsPicsDir(File root) {
|
||||
if (null == _iconFileNames) {
|
||||
_iconFileNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (Pair<String, String> nameurl : FileUtil.readNameUrlFile(NewConstants.IMAGE_LIST_QUEST_OPPONENT_ICONS_FILE)) {
|
||||
_iconFileNames.put(nameurl.getLeft(), nameurl.getLeft());
|
||||
}
|
||||
for (Pair<String, String> nameurl : FileUtil.readNameUrlFile(NewConstants.IMAGE_LIST_QUEST_PET_SHOP_ICONS_FILE)) {
|
||||
_iconFileNames.put(nameurl.getLeft(), nameurl.getLeft());
|
||||
}
|
||||
}
|
||||
|
||||
_analyzeListedDir(root, NewConstants.CACHE_ICON_PICS_DIR, new _ListedAnalyzer() {
|
||||
@Override public String map(String filename) { return _iconFileNames.containsKey(filename) ? _iconFileNames.get(filename) : null; }
|
||||
@Override public OpType getOpType(String filename) { return OpType.QUEST_PIC; }
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, String> _tokenFileNames;
|
||||
Map<String, String> _questTokenFileNames;
|
||||
private void _analyzeTokenPicsDir(File root) {
|
||||
if (null == _tokenFileNames) {
|
||||
_tokenFileNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
_questTokenFileNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (Pair<String, String> nameurl : FileUtil.readNameUrlFile(NewConstants.IMAGE_LIST_TOKENS_FILE)) {
|
||||
_tokenFileNames.put(nameurl.getLeft(), nameurl.getLeft());
|
||||
}
|
||||
for (Pair<String, String> nameurl : FileUtil.readNameUrlFile(NewConstants.IMAGE_LIST_QUEST_TOKENS_FILE)) {
|
||||
_questTokenFileNames.put(nameurl.getLeft(), nameurl.getLeft());
|
||||
}
|
||||
}
|
||||
|
||||
_analyzeListedDir(root, NewConstants.CACHE_TOKEN_PICS_DIR, new _ListedAnalyzer() {
|
||||
@Override public String map(String filename) {
|
||||
if (_questTokenFileNames.containsKey(filename)) { return _questTokenFileNames.get(filename); }
|
||||
if (_tokenFileNames.containsKey(filename)) { return _tokenFileNames.get(filename); }
|
||||
return null;
|
||||
}
|
||||
@Override public OpType getOpType(String filename) {
|
||||
return _questTokenFileNames.containsKey(filename) ? OpType.QUEST_PIC : OpType.TOKEN_PIC;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void _analyzeProductPicsDir(File root) {
|
||||
// we don't care about the files in the root dir -- the new booster files are .png, not the current .jpg ones
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override boolean onDir(File dir) {
|
||||
String dirName = dir.getName();
|
||||
if ("booster".equalsIgnoreCase(dirName)) {
|
||||
_analyzeSimpleListedDir(dir, NewConstants.IMAGE_LIST_QUEST_BOOSTERS_FILE, NewConstants.CACHE_BOOSTER_PICS_DIR, OpType.QUEST_PIC);
|
||||
} else if ("fatpacks".equalsIgnoreCase(dirName)) {
|
||||
_analyzeSimpleListedDir(dir, NewConstants.IMAGE_LIST_QUEST_FATPACKS_FILE, NewConstants.CACHE_FATPACK_PICS_DIR, OpType.QUEST_PIC);
|
||||
} else if ("precons".equalsIgnoreCase(dirName)) {
|
||||
_analyzeSimpleListedDir(dir, NewConstants.IMAGE_LIST_QUEST_PRECONS_FILE, NewConstants.CACHE_PRECON_PICS_DIR, OpType.QUEST_PIC);
|
||||
} else if ("tournamentpacks".equalsIgnoreCase(dirName)) {
|
||||
_analyzeSimpleListedDir(dir, NewConstants.IMAGE_LIST_QUEST_TOURNAMENTPACKS_FILE, NewConstants.CACHE_TOURNAMENTPACK_PICS_DIR, OpType.QUEST_PIC);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// preferences
|
||||
//
|
||||
|
||||
private void _analyzePreferencesDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
String filename = file.getName();
|
||||
if ("editor.preferences".equalsIgnoreCase(filename) || "forge.preferences".equalsIgnoreCase(filename)) {
|
||||
File targetFile = new File(NewConstants.USER_PREFS_DIR, filename.toLowerCase(Locale.ENGLISH));
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(OpType.PREFERENCE_FILE, file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// quest data
|
||||
//
|
||||
|
||||
private void _analyzeQuestDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
String filename = file.getName();
|
||||
if ("all-prices.txt".equalsIgnoreCase(filename)) {
|
||||
File targetFile = new File(NewConstants.DB_DIR, filename.toLowerCase(Locale.ENGLISH));
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(OpType.DB_FILE, file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override boolean onDir(File dir) {
|
||||
if ("data".equalsIgnoreCase(dir.getName())) {
|
||||
_analyzeQuestDataDir(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void _analyzeQuestDataDir(File root) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
String filename = file.getName();
|
||||
if (StringUtils.endsWithIgnoreCase(filename, ".dat")) {
|
||||
File targetFile = new File(NewConstants.QUEST_SAVE_DIR, _lcaseExt(filename));
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(OpType.QUEST_DATA, file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// utility functions
|
||||
//
|
||||
|
||||
private class _Analyzer {
|
||||
void onFile(File file) { }
|
||||
|
||||
// returns whether the directory has been handled
|
||||
boolean onDir(File dir) { return false; }
|
||||
}
|
||||
|
||||
private void _analyzeDir(File root, _Analyzer analyzer) {
|
||||
for (File file : root.listFiles()) {
|
||||
if (_cb.checkCancel()) { return; }
|
||||
|
||||
if (file.isFile()) {
|
||||
++_numFilesAnalyzed;
|
||||
analyzer.onFile(file);
|
||||
} else if (file.isDirectory()) {
|
||||
if (!analyzer.onDir(file)) {
|
||||
_numFilesAnalyzed += _countFiles(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Map<String, String>> _fileNameDb = new HashMap<String, Map<String, String>>();
|
||||
private void _analyzeSimpleListedDir(File root, String listFile, String targetDir, final OpType opType) {
|
||||
if (!_fileNameDb.containsKey(listFile)) {
|
||||
Map<String, String> fileNames = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (Pair<String, String> nameurl : FileUtil.readNameUrlFile(listFile)) {
|
||||
// we use a map instead of a set since we need to match case-insensitively but still map to the correct case
|
||||
fileNames.put(nameurl.getLeft(), nameurl.getLeft());
|
||||
}
|
||||
_fileNameDb.put(listFile, fileNames);
|
||||
}
|
||||
|
||||
final Map<String, String> fileDb = _fileNameDb.get(listFile);
|
||||
_analyzeListedDir(root, targetDir, new _ListedAnalyzer() {
|
||||
@Override public String map(String filename) { return fileDb.containsKey(filename) ? fileDb.get(filename) : null; }
|
||||
@Override public OpType getOpType(String filename) { return opType; }
|
||||
});
|
||||
}
|
||||
|
||||
private abstract class _ListedAnalyzer {
|
||||
abstract String map(String filename);
|
||||
abstract OpType getOpType(String filename);
|
||||
|
||||
// returns whether the directory has been handled
|
||||
boolean onDir(File dir) { return false; }
|
||||
}
|
||||
|
||||
private void _analyzeListedDir(File root, final String targetDir, final _ListedAnalyzer listedAnalyzer) {
|
||||
_analyzeDir(root, new _Analyzer() {
|
||||
@Override void onFile(File file) {
|
||||
String filename = listedAnalyzer.map(file.getName());
|
||||
if (null != filename) {
|
||||
File targetFile = new File(targetDir, filename);
|
||||
if (!file.equals(targetFile)) {
|
||||
_cb.addOp(listedAnalyzer.getOpType(filename), file, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override boolean onDir(File dir) { return listedAnalyzer.onDir(dir); }
|
||||
});
|
||||
}
|
||||
|
||||
private int _countFiles(File root) {
|
||||
int count = 0;
|
||||
for (File file : root.listFiles()) {
|
||||
if (_cb.checkCancel()) { return 0; }
|
||||
|
||||
if (file.isFile()) {
|
||||
++count;
|
||||
} else if (file.isDirectory()) {
|
||||
count += _countFiles(file);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private String _lcaseExt(String filename) {
|
||||
int lastDotIdx = filename.lastIndexOf('.');
|
||||
if (0 > lastDotIdx) {
|
||||
return filename;
|
||||
}
|
||||
String basename = filename.substring(0, lastDotIdx);
|
||||
String ext = filename.substring(lastDotIdx).toLowerCase(Locale.ENGLISH);
|
||||
if (filename.endsWith(ext)) {
|
||||
return filename;
|
||||
}
|
||||
return basename + ext;
|
||||
}
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package forge.gui;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.gui.toolbox.FList;
|
||||
import forge.gui.toolbox.FMouseAdapter;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.FScrollPane;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple class that shows a list of choices in a dialog. Two properties
|
||||
* influence the behavior of a list chooser: minSelection and maxSelection.
|
||||
* These two give the allowed number of selected items for the dialog to be
|
||||
* closed. A negative value for minSelection suggests that the list is revealed
|
||||
* and the choice doesn't matter.
|
||||
* <ul>
|
||||
* <li>If minSelection is 0, there will be a Cancel button.</li>
|
||||
* <li>If minSelection is -1, 0 or 1, double-clicking a choice will also close the
|
||||
* dialog.</li>
|
||||
* <li>If the number of selections is out of bounds, the "OK" button is
|
||||
* disabled.</li>
|
||||
* <li>The dialog was "committed" if "OK" was clicked or a choice was double
|
||||
* clicked.</li>
|
||||
* <li>The dialog was "canceled" if "Cancel" or "X" was clicked.</li>
|
||||
* <li>If the dialog was canceled, the selection will be empty.</li>
|
||||
* <li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <T>
|
||||
* the generic type
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ListChooser<T> {
|
||||
// Data and number of choices for the list
|
||||
private List<T> list;
|
||||
private int minChoices, maxChoices;
|
||||
|
||||
// Flag: was the dialog already shown?
|
||||
private boolean called;
|
||||
|
||||
// initialized before; listeners may be added to it
|
||||
private FList<T> lstChoices;
|
||||
private FOptionPane optionPane;
|
||||
|
||||
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list, final Function<T, String> display) {
|
||||
FThreads.assertExecutedByEdt(true);
|
||||
this.minChoices = minChoices;
|
||||
this.maxChoices = maxChoices;
|
||||
this.list = list.getClass().isInstance(List.class) ? (List<T>)list : Lists.newArrayList(list);
|
||||
this.lstChoices = new FList<T>(new ChooserListModel());
|
||||
|
||||
String[] options;
|
||||
if (minChoices == 0) {
|
||||
options = new String[] {"OK","Cancel"};
|
||||
}
|
||||
else {
|
||||
options = new String[] {"OK"};
|
||||
}
|
||||
|
||||
if (maxChoices == 1 || minChoices == -1) {
|
||||
this.lstChoices.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
}
|
||||
|
||||
if (display != null) {
|
||||
this.lstChoices.setCellRenderer(new TransformedCellRenderer(display));
|
||||
}
|
||||
|
||||
FScrollPane listScroller = new FScrollPane(this.lstChoices, true);
|
||||
int minWidth = this.lstChoices.getAutoSizeWidth();
|
||||
if (this.lstChoices.getModel().getSize() > this.lstChoices.getVisibleRowCount()) {
|
||||
minWidth += listScroller.getVerticalScrollBar().getPreferredSize().width;
|
||||
}
|
||||
listScroller.setMinimumSize(new Dimension(minWidth, listScroller.getMinimumSize().height));
|
||||
|
||||
this.optionPane = new FOptionPane(null, title, null, listScroller, options, minChoices < 0 ? 0 : -1);
|
||||
this.optionPane.setButtonEnabled(0, minChoices <= 0);
|
||||
|
||||
if (minChoices != -1) {
|
||||
this.optionPane.setDefaultFocus(this.lstChoices);
|
||||
}
|
||||
|
||||
if (minChoices > 0) {
|
||||
this.optionPane.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
}
|
||||
|
||||
if (minChoices != -1) {
|
||||
this.lstChoices.getSelectionModel().addListSelectionListener(new SelListener());
|
||||
}
|
||||
|
||||
this.lstChoices.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
ListChooser.this.commit();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.lstChoices.addMouseListener(new FMouseAdapter() {
|
||||
@Override
|
||||
public void onLeftClick(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
ListChooser.this.commit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FList used in the list chooser. this is useful for
|
||||
* registering listeners before showing the dialog.
|
||||
*
|
||||
* @return a {@link javax.swing.JList} object.
|
||||
*/
|
||||
public FList<T> getLstChoices() {
|
||||
return this.lstChoices;
|
||||
}
|
||||
|
||||
/** @return boolean */
|
||||
public boolean show() {
|
||||
return show(list.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog and returns after the dialog was closed.
|
||||
*
|
||||
* @param index0 index to select when shown
|
||||
* @return a boolean.
|
||||
*/
|
||||
public boolean show(final T item) {
|
||||
if (this.called) {
|
||||
throw new IllegalStateException("Already shown");
|
||||
}
|
||||
int result;
|
||||
do {
|
||||
SwingUtilities.invokeLater(new Runnable() { //invoke later so selected item not set until dialog open
|
||||
@Override
|
||||
public void run() {
|
||||
if (list.contains(item)) {
|
||||
lstChoices.setSelectedValue(item, true);
|
||||
}
|
||||
else {
|
||||
lstChoices.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.optionPane.setVisible(true);
|
||||
result = this.optionPane.getResult();
|
||||
if (result != 0) {
|
||||
this.lstChoices.clearSelection();
|
||||
}
|
||||
// can't stop closing by ESC, so repeat if cancelled
|
||||
} while (this.minChoices > 0 && result != 0);
|
||||
|
||||
this.optionPane.dispose();
|
||||
|
||||
// this assert checks if we really don't return on a cancel if input is mandatory
|
||||
assert (this.minChoices <= 0) || (result == 0);
|
||||
this.called = true;
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the dialog was closed by pressing "OK" or double clicking an
|
||||
* option the last time.
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public boolean isCommitted() {
|
||||
if (!this.called) {
|
||||
throw new IllegalStateException("not yet shown");
|
||||
}
|
||||
return (this.optionPane.getResult() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected indices as a list of integers.
|
||||
*
|
||||
* @return a {@link java.util.List} object.
|
||||
*/
|
||||
public int[] getSelectedIndices() {
|
||||
if (!this.called) {
|
||||
throw new IllegalStateException("not yet shown");
|
||||
}
|
||||
return this.lstChoices.getSelectedIndices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected values as a list of objects. no casts are necessary
|
||||
* when retrieving the objects.
|
||||
*
|
||||
* @return a {@link java.util.List} object.
|
||||
*/
|
||||
public List<T> getSelectedValues() {
|
||||
if (!this.called) {
|
||||
throw new IllegalStateException("not yet shown");
|
||||
}
|
||||
return this.lstChoices.getSelectedValuesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (minimum) selected index, or -1.
|
||||
*
|
||||
* @return a int.
|
||||
*/
|
||||
public int getSelectedIndex() {
|
||||
if (!this.called) {
|
||||
throw new IllegalStateException("not yet shown");
|
||||
}
|
||||
return this.lstChoices.getSelectedIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (first) selected value, or null.
|
||||
*
|
||||
* @return a T object.
|
||||
*/
|
||||
public T getSelectedValue() {
|
||||
if (!this.called) {
|
||||
throw new IllegalStateException("not yet shown");
|
||||
}
|
||||
return (T) this.lstChoices.getSelectedValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* commit.
|
||||
* </p>
|
||||
*/
|
||||
private void commit() {
|
||||
if (this.optionPane.isButtonEnabled(0)) {
|
||||
optionPane.setResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
private class ChooserListModel extends AbstractListModel<T> {
|
||||
private static final long serialVersionUID = 3871965346333840556L;
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return ListChooser.this.list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getElementAt(final int index) {
|
||||
return ListChooser.this.list.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private class SelListener implements ListSelectionListener {
|
||||
@Override
|
||||
public void valueChanged(final ListSelectionEvent e) {
|
||||
final int num = ListChooser.this.lstChoices.getSelectedIndices().length;
|
||||
ListChooser.this.optionPane.setButtonEnabled(0, (num >= ListChooser.this.minChoices) && (num <= ListChooser.this.maxChoices));
|
||||
}
|
||||
}
|
||||
|
||||
private class TransformedCellRenderer implements ListCellRenderer<T> {
|
||||
public final Function<T, String> transformer;
|
||||
public final DefaultListCellRenderer defRenderer;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for Constructor.
|
||||
*/
|
||||
public TransformedCellRenderer(final Function<T, String> t1) {
|
||||
transformer = t1;
|
||||
defRenderer = new DefaultListCellRenderer();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
// TODO Auto-generated method stub
|
||||
return defRenderer.getListCellRendererComponent(list, transformer.apply(value), index, isSelected, cellHasFocus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import forge.view.FView;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public final class MouseUtil {
|
||||
private static Cursor cursor;
|
||||
private static int cursorLockCount;
|
||||
|
||||
/**
|
||||
* Lock cursor as it is currently displayed until unlockCursor called
|
||||
*/
|
||||
public static void lockCursor() {
|
||||
cursorLockCount++;
|
||||
}
|
||||
public static void unlockCursor() {
|
||||
if (cursorLockCount == 0) { return; }
|
||||
if (--cursorLockCount == 0) {
|
||||
//update displayed cursor after cursor unlocked
|
||||
FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The only reliable way to ensure the mouse cursor is set properly in Forge.
|
||||
*
|
||||
* @param mouseCursor one of the predefined {@code Cursor} types.
|
||||
*/
|
||||
public static void resetCursor() {
|
||||
setCursor(Cursor.getDefaultCursor());
|
||||
}
|
||||
public static void setCursor(int cursorType) {
|
||||
setCursor(Cursor.getPredefinedCursor(cursorType));
|
||||
}
|
||||
public static void setCursor(Cursor cursor0) {
|
||||
if (cursor == cursor0) { return; }
|
||||
cursor = cursor0;
|
||||
if (cursorLockCount > 0) { return; }
|
||||
FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(cursor);
|
||||
}
|
||||
|
||||
public static void setComponentCursor(final Component comp, final int cursorType) {
|
||||
setComponentCursor(comp, Cursor.getPredefinedCursor(cursorType));
|
||||
}
|
||||
public static void setComponentCursor(final Component comp, final Cursor cursor0) {
|
||||
comp.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
setCursor(cursor0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
resetCursor();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* A {@link JLabel} with support for multi-line text that wraps when the line
|
||||
* doesn't fit in the available width. Multi-line text support is handled by the
|
||||
* {@link MultiLineLabelUI}, the default UI delegate of this component. The text
|
||||
* in the label can be horizontally and vertically aligned, relative to the
|
||||
* bounds of the component.
|
||||
*
|
||||
* @author Samuel Sjoberg, http://samuelsjoberg.com
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class MultiLineLabel extends JLabel {
|
||||
|
||||
/**
|
||||
* Default serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Horizontal text alignment.
|
||||
*/
|
||||
private int halign = SwingConstants.LEFT;
|
||||
|
||||
/**
|
||||
* Vertical text alignment.
|
||||
*/
|
||||
private int valign = SwingConstants.CENTER;
|
||||
|
||||
/**
|
||||
* Cache to save heap allocations.
|
||||
*/
|
||||
private Rectangle bounds;
|
||||
|
||||
/**
|
||||
* Creates a new empty label.
|
||||
*/
|
||||
public MultiLineLabel() {
|
||||
super();
|
||||
this.setUI(MultiLineLabelUI.getLabelUI());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new label with <code>text</code> value.
|
||||
*
|
||||
* @param text
|
||||
* the value of the label
|
||||
*/
|
||||
public MultiLineLabel(final String text) {
|
||||
this();
|
||||
this.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return a {@link java.awt.Rectangle} object.
|
||||
*/
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
if (this.bounds == null) {
|
||||
this.bounds = new Rectangle();
|
||||
}
|
||||
return super.getBounds(this.bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the vertical text alignment.
|
||||
*
|
||||
* @param alignment
|
||||
* vertical alignment
|
||||
*/
|
||||
public void setVerticalTextAlignment(final int alignment) {
|
||||
this.firePropertyChange("verticalTextAlignment", this.valign, alignment);
|
||||
this.valign = alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the horizontal text alignment.
|
||||
*
|
||||
* @param alignment
|
||||
* horizontal alignment
|
||||
*/
|
||||
public void setHorizontalTextAlignment(final int alignment) {
|
||||
this.firePropertyChange("horizontalTextAlignment", this.halign, alignment);
|
||||
this.halign = alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertical text alignment.
|
||||
*
|
||||
* @return vertical text alignment
|
||||
*/
|
||||
public int getVerticalTextAlignment() {
|
||||
return this.valign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the horizontal text alignment.
|
||||
*
|
||||
* @return horizontal text alignment
|
||||
*/
|
||||
public int getHorizontalTextAlignment() {
|
||||
return this.halign;
|
||||
}
|
||||
}
|
||||
@@ -1,655 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.LabelUI;
|
||||
import javax.swing.plaf.basic.BasicLabelUI;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Label UI delegate that supports multiple lines and line wrapping. Hard line
|
||||
* breaks (<code>\n</code>) are preserved. If the dimensions of the label is too
|
||||
* small to fit all content, the string will be clipped and "..." appended to
|
||||
* the end of the visible text (similar to the default behavior of
|
||||
* <code>JLabel</code>). If used in conjunction with a {@link MultiLineLabel},
|
||||
* text alignment (horizontal and vertical) is supported. The UI delegate can be
|
||||
* used on a regular <code>JLabel</code> if text alignment isn't required. The
|
||||
* default alignment, left and vertically centered, will then be used.
|
||||
* <p/>
|
||||
* Example of usage:
|
||||
* <p/>
|
||||
*
|
||||
* <pre>
|
||||
* JLabel myLabel = new JLabel();
|
||||
* myLabel.setUI(MultiLineLabelUI.labelUI);
|
||||
* myLabel.setText("A long label that will wrap automatically.");
|
||||
* </pre>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* The line and wrapping support is implemented without using a
|
||||
* <code>View</code> to make it easy for subclasses to add custom text effects
|
||||
* by overriding {@link #paintEnabledText(JLabel, Graphics, String, int, int)}
|
||||
* and {@link #paintDisabledText(JLabel, Graphics, String, int, int)}. This
|
||||
* class is designed to be easily extended by subclasses.
|
||||
*
|
||||
* @author Samuel Sjoberg, http://samuelsjoberg.com
|
||||
* @version 1.3.0
|
||||
*/
|
||||
public class MultiLineLabelUI extends BasicLabelUI implements ComponentListener {
|
||||
|
||||
/**
|
||||
* Shared instance of the UI delegate.
|
||||
*/
|
||||
private static LabelUI labelUI = new MultiLineLabelUI();
|
||||
|
||||
/**
|
||||
* Client property key used to store the calculated wrapped lines on the
|
||||
* JLabel.
|
||||
*/
|
||||
public static final String PROPERTY_KEY = "WrappedText";
|
||||
|
||||
// Static references to avoid heap allocations.
|
||||
/** Constant <code>paintIconR</code>. */
|
||||
private static Rectangle paintIconR = new Rectangle();
|
||||
|
||||
/** Constant <code>paintTextR</code>. */
|
||||
private static Rectangle paintTextR = new Rectangle();
|
||||
|
||||
/** Constant <code>paintViewR</code>. */
|
||||
private static Rectangle paintViewR = new Rectangle();
|
||||
|
||||
/** Constant <code>paintViewInsets</code>. */
|
||||
private static Insets paintViewInsets = new Insets(0, 0, 0, 0);
|
||||
|
||||
/**
|
||||
* Font metrics of the JLabel being rendered.
|
||||
*/
|
||||
private FontMetrics metrics;
|
||||
|
||||
/**
|
||||
* Default size of the lines list.
|
||||
*/
|
||||
private static int defaultSize = 4;
|
||||
|
||||
/**
|
||||
* Get the shared UI instance.
|
||||
*
|
||||
* @param c
|
||||
* the c
|
||||
* @return a ComponentUI
|
||||
*/
|
||||
public static ComponentUI createUI(final JComponent c) {
|
||||
return MultiLineLabelUI.getLabelUI();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void uninstallDefaults(final JLabel c) {
|
||||
super.uninstallDefaults(c);
|
||||
this.clearCache(c);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void installListeners(final JLabel c) {
|
||||
super.installListeners(c);
|
||||
c.addComponentListener(this);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void uninstallListeners(final JLabel c) {
|
||||
super.uninstallListeners(c);
|
||||
c.removeComponentListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the wrapped line cache.
|
||||
*
|
||||
* @param l
|
||||
* the label containing a cached value
|
||||
*/
|
||||
protected void clearCache(final JLabel l) {
|
||||
l.putClientProperty(MultiLineLabelUI.PROPERTY_KEY, null);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void propertyChange(final PropertyChangeEvent e) {
|
||||
super.propertyChange(e);
|
||||
final String name = e.getPropertyName();
|
||||
if (name.equals("text") || "font".equals(name)) {
|
||||
this.clearCache((JLabel) e.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the paint rectangles for the icon and text for the passed
|
||||
* label.
|
||||
*
|
||||
* @param l
|
||||
* a label
|
||||
* @param fm
|
||||
* the font metrics to use, or <code>null</code> to get the font
|
||||
* metrics from the label
|
||||
* @param width
|
||||
* label width
|
||||
* @param height
|
||||
* label height
|
||||
*/
|
||||
protected void updateLayout(final JLabel l, FontMetrics fm, final int width, final int height) {
|
||||
if (fm == null) {
|
||||
fm = l.getFontMetrics(l.getFont());
|
||||
}
|
||||
this.metrics = fm;
|
||||
|
||||
final String text = l.getText();
|
||||
final Icon icon = l.getIcon();
|
||||
final Insets insets = l.getInsets(MultiLineLabelUI.paintViewInsets);
|
||||
|
||||
MultiLineLabelUI.paintViewR.x = insets.left;
|
||||
MultiLineLabelUI.paintViewR.y = insets.top;
|
||||
MultiLineLabelUI.paintViewR.width = width - (insets.left + insets.right);
|
||||
MultiLineLabelUI.paintViewR.height = height - (insets.top + insets.bottom);
|
||||
|
||||
MultiLineLabelUI.paintIconR.x = 0;
|
||||
MultiLineLabelUI.paintIconR.y = 0;
|
||||
MultiLineLabelUI.paintIconR.width = 0;
|
||||
MultiLineLabelUI.paintIconR.height = 0;
|
||||
MultiLineLabelUI.paintTextR.x = 0;
|
||||
MultiLineLabelUI.paintTextR.y = 0;
|
||||
MultiLineLabelUI.paintTextR.width = 0;
|
||||
MultiLineLabelUI.paintTextR.height = 0;
|
||||
|
||||
this.layoutCL(l, fm, text, icon, MultiLineLabelUI.paintViewR, MultiLineLabelUI.paintIconR,
|
||||
MultiLineLabelUI.paintTextR);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* prepareGraphics.
|
||||
* </p>
|
||||
*
|
||||
* @param g
|
||||
* a {@link java.awt.Graphics} object.
|
||||
*/
|
||||
protected void prepareGraphics(final Graphics g) {
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void paint(final Graphics g, final JComponent c) {
|
||||
|
||||
// parent's update method fills the background
|
||||
this.prepareGraphics(g);
|
||||
|
||||
final JLabel label = (JLabel) c;
|
||||
final String text = label.getText();
|
||||
final Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
|
||||
|
||||
if ((icon == null) && (text == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final FontMetrics fm = g.getFontMetrics();
|
||||
|
||||
this.updateLayout(label, fm, c.getWidth(), c.getHeight());
|
||||
|
||||
if (icon != null) {
|
||||
icon.paintIcon(c, g, MultiLineLabelUI.paintIconR.x, MultiLineLabelUI.paintIconR.y);
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
final View v = (View) c.getClientProperty("html");
|
||||
if (v != null) {
|
||||
// HTML view disables multi-line painting.
|
||||
v.paint(g, MultiLineLabelUI.paintTextR);
|
||||
} else {
|
||||
// Paint the multi line text
|
||||
this.paintTextLines(g, label, fm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint the wrapped text lines.
|
||||
*
|
||||
* @param g
|
||||
* graphics component to paint on
|
||||
* @param label
|
||||
* the label being painted
|
||||
* @param fm
|
||||
* font metrics for current font
|
||||
*/
|
||||
protected void paintTextLines(final Graphics g, final JLabel label, final FontMetrics fm) {
|
||||
final List<String> lines = this.getTextLines(label);
|
||||
|
||||
// Available component height to paint on.
|
||||
final int height = this.getAvailableHeight(label);
|
||||
|
||||
int textHeight = lines.size() * fm.getHeight();
|
||||
while (textHeight > height) {
|
||||
// Remove one line until no. of visible lines is found.
|
||||
textHeight -= fm.getHeight();
|
||||
}
|
||||
MultiLineLabelUI.paintTextR.height = Math.min(textHeight, height);
|
||||
MultiLineLabelUI.paintTextR.y = this.alignmentY(label, fm, MultiLineLabelUI.paintTextR);
|
||||
|
||||
final int textX = MultiLineLabelUI.paintTextR.x;
|
||||
int textY = MultiLineLabelUI.paintTextR.y;
|
||||
|
||||
for (final Iterator<String> it = lines.iterator(); it.hasNext()
|
||||
&& MultiLineLabelUI.paintTextR.contains(textX, textY + MultiLineLabelUI.getAscent(fm)); textY += fm
|
||||
.getHeight()) {
|
||||
|
||||
String text = it.next().trim();
|
||||
|
||||
if (it.hasNext()
|
||||
&& !MultiLineLabelUI.paintTextR.contains(textX,
|
||||
textY + fm.getHeight() + MultiLineLabelUI.getAscent(fm))) {
|
||||
// The last visible row, add a clip indication.
|
||||
text = this.clip(text, fm, MultiLineLabelUI.paintTextR);
|
||||
}
|
||||
|
||||
final int x = this.alignmentX(label, fm, text, MultiLineLabelUI.paintTextR);
|
||||
|
||||
if (label.isEnabled()) {
|
||||
this.paintEnabledText(label, g, text, x, textY);
|
||||
} else {
|
||||
this.paintDisabledText(label, g, text, x, textY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available height to paint text on. This is the height of the
|
||||
* passed component with insets subtracted.
|
||||
*
|
||||
* @param l
|
||||
* a component
|
||||
* @return the available height
|
||||
*/
|
||||
protected int getAvailableHeight(final JLabel l) {
|
||||
l.getInsets(MultiLineLabelUI.paintViewInsets);
|
||||
return l.getHeight() - MultiLineLabelUI.paintViewInsets.top - MultiLineLabelUI.paintViewInsets.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a clip indication to the string. It is important that the string
|
||||
* length does not exceed the length or the original string.
|
||||
*
|
||||
* @param text
|
||||
* the to be painted
|
||||
* @param fm
|
||||
* font metrics
|
||||
* @param bounds
|
||||
* the text bounds
|
||||
* @return the clipped string
|
||||
*/
|
||||
protected String clip(final String text, final FontMetrics fm, final Rectangle bounds) {
|
||||
// Fast and lazy way to insert a clip indication is to simply replace
|
||||
// the last characters in the string with the clip indication.
|
||||
// A better way would be to use metrics and calculate how many (if any)
|
||||
// characters that need to be replaced.
|
||||
if (text.length() < 3) {
|
||||
return "...";
|
||||
}
|
||||
return text.substring(0, text.length() - 3) + "...";
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish the vertical text alignment. The default alignment is to center
|
||||
* the text in the label.
|
||||
*
|
||||
* @param label
|
||||
* the label to paint
|
||||
* @param fm
|
||||
* font metrics
|
||||
* @param bounds
|
||||
* the text bounds rectangle
|
||||
* @return the vertical text alignment, defaults to CENTER.
|
||||
*/
|
||||
protected int alignmentY(final JLabel label, final FontMetrics fm, final Rectangle bounds) {
|
||||
final int height = this.getAvailableHeight(label);
|
||||
final int textHeight = bounds.height;
|
||||
|
||||
if (label instanceof MultiLineLabel) {
|
||||
final int align = ((MultiLineLabel) label).getVerticalTextAlignment();
|
||||
switch (align) {
|
||||
case SwingConstants.TOP:
|
||||
return MultiLineLabelUI.getAscent(fm) + MultiLineLabelUI.paintViewInsets.top;
|
||||
case SwingConstants.BOTTOM:
|
||||
return (((MultiLineLabelUI.getAscent(fm) + height) - MultiLineLabelUI.paintViewInsets.top) + MultiLineLabelUI.paintViewInsets.bottom)
|
||||
- textHeight;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Center alignment
|
||||
final int textY = MultiLineLabelUI.paintViewInsets.top + ((height - textHeight) / 2)
|
||||
+ MultiLineLabelUI.getAscent(fm);
|
||||
return Math.max(textY, MultiLineLabelUI.getAscent(fm) + MultiLineLabelUI.paintViewInsets.top);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAscent.
|
||||
* </p>
|
||||
*
|
||||
* @param fm
|
||||
* a {@link java.awt.FontMetrics} object.
|
||||
* @return a int.
|
||||
*/
|
||||
private static int getAscent(final FontMetrics fm) {
|
||||
return fm.getAscent() + fm.getLeading();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish the horizontal text alignment. The default alignment is left
|
||||
* aligned text.
|
||||
*
|
||||
* @param label
|
||||
* the label to paint
|
||||
* @param fm
|
||||
* font metrics
|
||||
* @param s
|
||||
* the string to paint
|
||||
* @param bounds
|
||||
* the text bounds rectangle
|
||||
* @return the x-coordinate to use when painting for proper alignment
|
||||
*/
|
||||
protected int alignmentX(final JLabel label, final FontMetrics fm, final String s, final Rectangle bounds) {
|
||||
if (label instanceof MultiLineLabel) {
|
||||
final int align = ((MultiLineLabel) label).getHorizontalTextAlignment();
|
||||
switch (align) {
|
||||
case SwingConstants.RIGHT:
|
||||
return (bounds.x + MultiLineLabelUI.paintViewR.width) - fm.stringWidth(s);
|
||||
case SwingConstants.CENTER:
|
||||
return (bounds.x + (MultiLineLabelUI.paintViewR.width / 2)) - (fm.stringWidth(s) / 2);
|
||||
default:
|
||||
return bounds.x;
|
||||
}
|
||||
}
|
||||
return bounds.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given string to see if it should be rendered as HTML. Code
|
||||
* based on implementation found in
|
||||
* <code>BasicHTML.isHTMLString(String)</code> in future JDKs.
|
||||
*
|
||||
* @param s
|
||||
* the string
|
||||
* @return <code>true</code> if string is HTML, otherwise <code>false</code>
|
||||
*/
|
||||
private static boolean isHTMLString(final String s) {
|
||||
if (s != null) {
|
||||
if ((s.length() >= 6) && (s.charAt(0) == '<') && (s.charAt(5) == '>')) {
|
||||
final String tag = s.substring(1, 5);
|
||||
return tag.equalsIgnoreCase("html");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Dimension getPreferredSize(final JComponent c) {
|
||||
final Dimension d = super.getPreferredSize(c);
|
||||
final JLabel label = (JLabel) c;
|
||||
|
||||
if (MultiLineLabelUI.isHTMLString(label.getText())) {
|
||||
return d; // HTML overrides everything and we don't need to process
|
||||
}
|
||||
|
||||
// Width calculated by super is OK. The preferred width is the width of
|
||||
// the unwrapped content as long as it does not exceed the width of the
|
||||
// parent container.
|
||||
|
||||
if (c.getParent() != null) {
|
||||
// Ensure that preferred width never exceeds the available width
|
||||
// (including its border insets) of the parent container.
|
||||
final Insets insets = c.getParent().getInsets();
|
||||
final Dimension size = c.getParent().getSize();
|
||||
if (size.width > 0) {
|
||||
// If width isn't set component shouldn't adjust.
|
||||
d.width = size.width - insets.left - insets.right;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateLayout(label, null, d.width, d.height);
|
||||
|
||||
// The preferred height is either the preferred height of the text
|
||||
// lines, or the height of the icon.
|
||||
d.height = Math.max(d.height, this.getPreferredHeight(label));
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* The preferred height of the label is the height of the lines with added
|
||||
* top and bottom insets.
|
||||
*
|
||||
* @param label
|
||||
* the label
|
||||
* @return the preferred height of the wrapped lines.
|
||||
*/
|
||||
protected int getPreferredHeight(final JLabel label) {
|
||||
final int numOfLines = this.getTextLines(label).size();
|
||||
final Insets insets = label.getInsets(MultiLineLabelUI.paintViewInsets);
|
||||
return (numOfLines * this.metrics.getHeight()) + insets.top + insets.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lines of text contained in the text label. The prepared lines is
|
||||
* cached as a client property, accessible via {@link #PROPERTY_KEY}.
|
||||
*
|
||||
* @param l
|
||||
* the label
|
||||
* @return the text lines of the label.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List<String> getTextLines(final JLabel l) {
|
||||
List<String> lines = (List<String>) l.getClientProperty(MultiLineLabelUI.PROPERTY_KEY);
|
||||
if (lines == null) {
|
||||
lines = this.prepareLines(l);
|
||||
l.putClientProperty(MultiLineLabelUI.PROPERTY_KEY, lines);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void componentHidden(final ComponentEvent e) {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void componentMoved(final ComponentEvent e) {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void componentResized(final ComponentEvent e) {
|
||||
this.clearCache((JLabel) e.getSource());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void componentShown(final ComponentEvent e) {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the text lines for rendering. The lines are wrapped to fit in the
|
||||
* current available space for text. Explicit line breaks are preserved.
|
||||
*
|
||||
* @param l
|
||||
* the label to render
|
||||
* @return a list of text lines to render
|
||||
*/
|
||||
protected List<String> prepareLines(final JLabel l) {
|
||||
final List<String> lines = new ArrayList<String>(MultiLineLabelUI.defaultSize);
|
||||
final String text = l.getText();
|
||||
if (text == null) {
|
||||
return null; // Null guard
|
||||
}
|
||||
final PlainDocument doc = new PlainDocument();
|
||||
try {
|
||||
doc.insertString(0, text, null);
|
||||
} catch (final BadLocationException e) {
|
||||
return null;
|
||||
}
|
||||
final Element root = doc.getDefaultRootElement();
|
||||
for (int i = 0, j = root.getElementCount(); i < j; i++) {
|
||||
this.wrap(lines, root.getElement(i));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* If necessary, wrap the text into multiple lines.
|
||||
*
|
||||
* @param lines
|
||||
* line array in which to store the wrapped lines
|
||||
* @param elem
|
||||
* the document element containing the text content
|
||||
*/
|
||||
protected void wrap(final List<String> lines, final Element elem) {
|
||||
final int p1 = elem.getEndOffset();
|
||||
final Document doc = elem.getDocument();
|
||||
for (int p0 = elem.getStartOffset(); p0 < p1;) {
|
||||
final int p = this.calculateBreakPosition(doc, p0, p1);
|
||||
try {
|
||||
lines.add(doc.getText(p0, p - p0));
|
||||
} catch (final BadLocationException e) {
|
||||
throw new Error("Can't get line text. p0=" + p0 + " p=" + p);
|
||||
}
|
||||
p0 = (p == p0) ? p1 : p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the position on which to break (wrap) the line.
|
||||
*
|
||||
* @param doc
|
||||
* the document
|
||||
* @param p0
|
||||
* start position
|
||||
* @param p1
|
||||
* end position
|
||||
* @return the actual end position, will be <code>p1</code> if content does
|
||||
* not need to wrap, otherwise it will be less than <code>p1</code>.
|
||||
*/
|
||||
protected int calculateBreakPosition(final Document doc, final int p0, final int p1) {
|
||||
final Segment segment = SegmentCache.getSegment();
|
||||
try {
|
||||
doc.getText(p0, p1 - p0, segment);
|
||||
} catch (final BadLocationException e) {
|
||||
throw new Error("Can't get line text");
|
||||
}
|
||||
|
||||
final int width = MultiLineLabelUI.paintTextR.width;
|
||||
final int p = p0 + Utilities.getBreakLocation(segment, this.metrics, 0, width, null, p0);
|
||||
SegmentCache.releaseSegment(segment);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label ui.
|
||||
*
|
||||
* @return the labelUI
|
||||
*/
|
||||
public static LabelUI getLabelUI() {
|
||||
return MultiLineLabelUI.labelUI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label ui.
|
||||
*
|
||||
* @param labelUI
|
||||
* the new label ui
|
||||
*/
|
||||
public static void setLabelUI(final LabelUI labelUI) {
|
||||
MultiLineLabelUI.labelUI = labelUI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static singleton {@link Segment} cache.
|
||||
*
|
||||
* @author Samuel Sjoberg
|
||||
* @see javax.swing.text.SegmentCache
|
||||
*/
|
||||
protected static final class SegmentCache {
|
||||
|
||||
/**
|
||||
* Reused segments.
|
||||
*/
|
||||
private final ArrayList<Segment> segments = new ArrayList<Segment>(2);
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*/
|
||||
private static SegmentCache cache = new SegmentCache();
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private SegmentCache() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>Segment</code>. When done, the <code>Segment</code>
|
||||
* should be recycled by invoking {@link #releaseSegment(Segment)}.
|
||||
*
|
||||
* @return a <code>Segment</code>.
|
||||
*/
|
||||
public static Segment getSegment() {
|
||||
final int size = SegmentCache.cache.segments.size();
|
||||
if (size > 0) {
|
||||
return SegmentCache.cache.segments.remove(size - 1);
|
||||
}
|
||||
return new Segment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a <code>Segment</code>. A segment should not be used after
|
||||
* it is released, and a segment should never be released more than
|
||||
* once.
|
||||
*
|
||||
* @param segment
|
||||
* the segment
|
||||
*/
|
||||
public static void releaseSegment(final Segment segment) {
|
||||
segment.array = null;
|
||||
segment.count = 0;
|
||||
SegmentCache.cache.segments.add(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.gui.match.TargetingOverlay;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FOverlay;
|
||||
import forge.gui.toolbox.FPanel;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinnedButton;
|
||||
import forge.gui.toolbox.FSkin.SkinnedLabel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.FocusManager;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* All overlay interaction is handled here.
|
||||
*
|
||||
* <br><br><i>(S at beginning of class name denotes a static factory.)</i>
|
||||
*/
|
||||
public final class SOverlayUtils {
|
||||
private static int counter = 0;
|
||||
|
||||
/**
|
||||
* A standardized overlay for a game start condition.
|
||||
*/
|
||||
public static void startGameOverlay() {
|
||||
final JPanel overlay = SOverlayUtils.genericOverlay();
|
||||
final int w = overlay.getWidth();
|
||||
final int h = overlay.getHeight();
|
||||
final int pnlW = 400;
|
||||
final int pnlH = 300;
|
||||
|
||||
// Adds the "loading" panel to generic overlay container
|
||||
// (which is preset with null layout and close button)
|
||||
final FPanel pnl = new FPanel();
|
||||
pnl.setLayout(new MigLayout("insets 0, gap 0, ax center, wrap"));
|
||||
pnl.setBackground(FSkin.getColor(FSkin.Colors.CLR_ACTIVE));
|
||||
pnl.setBounds(new Rectangle(((w - pnlW) / 2), ((h - pnlH) / 2), pnlW, pnlH));
|
||||
|
||||
pnl.add(new FLabel.Builder().icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_LOGO)).build(),
|
||||
"h 200px!, align center");
|
||||
pnl.add(new FLabel.Builder().text("Loading new game...")
|
||||
.fontSize(22).build(), "h 40px!, align center");
|
||||
|
||||
overlay.add(pnl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A standardized overlay for a loading condition (note: thread issues, as of 1-Mar-12).
|
||||
* @param msg0   {@link java.lang.String}
|
||||
* @return {@link javax.swing.JPanel}
|
||||
*/
|
||||
// NOTE: This animation happens on the EDT; if the EDT is tied up doing something
|
||||
// else, the animation is effectively frozen. So, this needs some work.
|
||||
public static JPanel loadingOverlay(final String msg0) {
|
||||
final JPanel overlay = SOverlayUtils.genericOverlay();
|
||||
final FPanel pnlLoading = new FPanel();
|
||||
final int w = overlay.getWidth();
|
||||
final int h = overlay.getHeight();
|
||||
|
||||
final SkinnedLabel lblLoading = new SkinnedLabel("");
|
||||
lblLoading.setOpaque(true);
|
||||
lblLoading.setBackground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
lblLoading.setMinimumSize(new Dimension(0, 20));
|
||||
|
||||
pnlLoading.setBounds(((w - 170) / 2), ((h - 80) / 2), 170, 80);
|
||||
pnlLoading.setLayout(new MigLayout("wrap, align center"));
|
||||
pnlLoading.add(new FLabel.Builder().fontSize(18)
|
||||
.text(msg0).build(), "h 20px!, w 140px!, gap 0 0 5px 0");
|
||||
pnlLoading.add(lblLoading, "gap 0 0 0 10px");
|
||||
|
||||
overlay.add(pnlLoading);
|
||||
|
||||
SOverlayUtils.counter = 0;
|
||||
final Timer timer = new Timer(300, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
lblLoading.setMinimumSize(new Dimension(10 * (SOverlayUtils.counter++), 20));
|
||||
lblLoading.revalidate();
|
||||
if (SOverlayUtils.counter > 13) { SOverlayUtils.counter = 0; }
|
||||
}
|
||||
});
|
||||
timer.start();
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* A template overlay with close button, null layout, ready for anything.
|
||||
* @return {@link javax.swing.JPanel}
|
||||
*/
|
||||
public static JPanel genericOverlay() {
|
||||
final JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel();
|
||||
final int w = overlay.getWidth();
|
||||
|
||||
final SkinnedButton btnCloseTopRight = new SkinnedButton("X");
|
||||
btnCloseTopRight.setBounds(w - 25, 10, 15, 15);
|
||||
btnCloseTopRight.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
btnCloseTopRight.setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_TEXT)));
|
||||
btnCloseTopRight.setOpaque(false);
|
||||
btnCloseTopRight.setBackground(new Color(0, 0, 0));
|
||||
btnCloseTopRight.setFocusPainted(false);
|
||||
btnCloseTopRight.addActionListener(new ActionListener() { @Override
|
||||
public void actionPerformed(ActionEvent arg0) { SOverlayUtils.hideOverlay(); } });
|
||||
|
||||
overlay.removeAll();
|
||||
overlay.setLayout(null);
|
||||
overlay.add(btnCloseTopRight);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
private static boolean _overlayHasFocus;
|
||||
public static boolean overlayHasFocus() {
|
||||
return _overlayHasFocus;
|
||||
}
|
||||
|
||||
private static Component prevFocusOwner;
|
||||
public static void showOverlay() {
|
||||
Singletons.getView().getNavigationBar().setEnabled(false);
|
||||
prevFocusOwner = FocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
|
||||
FOverlay.SINGLETON_INSTANCE.getPanel().setVisible(true);
|
||||
// ensure no background element has focus
|
||||
FOverlay.SINGLETON_INSTANCE.getPanel().requestFocusInWindow();
|
||||
_overlayHasFocus = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes child components and closes overlay.
|
||||
*/
|
||||
public static void hideOverlay() {
|
||||
Singletons.getView().getNavigationBar().setEnabled(true);
|
||||
FOverlay.SINGLETON_INSTANCE.getPanel().removeAll();
|
||||
FOverlay.SINGLETON_INSTANCE.getPanel().setVisible(false);
|
||||
if (null != prevFocusOwner) {
|
||||
prevFocusOwner.requestFocusInWindow();
|
||||
prevFocusOwner = null;
|
||||
}
|
||||
_overlayHasFocus = false;
|
||||
}
|
||||
|
||||
public static void showTargetingOverlay() {
|
||||
TargetingOverlay.SINGLETON_INSTANCE.getPanel().setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes child components and closes overlay.
|
||||
*/
|
||||
public static void hideTargetingOverlay() {
|
||||
TargetingOverlay.SINGLETON_INSTANCE.getPanel().setVisible(false);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsortedListModel<T> extends AbstractListModel<T> {
|
||||
List<T> model;
|
||||
|
||||
public UnsortedListModel() {
|
||||
model = new ArrayList<T>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return model.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getElementAt(int index) {
|
||||
return model.get(index);
|
||||
}
|
||||
|
||||
public void add(T element) {
|
||||
model.add(element);
|
||||
fireIntervalAdded(this, getSize() - 1, getSize() - 1);
|
||||
fireContentsChanged(this, 0, getSize() - 1);
|
||||
}
|
||||
|
||||
public void addAll(T[] elements) {
|
||||
for (T e : elements) {
|
||||
model.add(e);
|
||||
}
|
||||
fireIntervalAdded(this, getSize() - elements.length, getSize() - 1);
|
||||
fireContentsChanged(this, 0, getSize() - 1);
|
||||
}
|
||||
|
||||
public void addAll(Collection<T> elements) {
|
||||
if (elements.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
model.addAll(elements);
|
||||
fireIntervalAdded(this, getSize() - elements.size(), getSize() - 1);
|
||||
fireContentsChanged(this, 0, getSize() - 1);
|
||||
}
|
||||
|
||||
public void addAll(ListModel<T> otherModel) {
|
||||
Collection<T> elements = new ArrayList<T>();
|
||||
int size = otherModel.getSize();
|
||||
for (int i = 0; size > i; ++i) {
|
||||
elements.add((T)otherModel.getElementAt(i));
|
||||
}
|
||||
addAll(elements);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
int prevSize = getSize();
|
||||
model.clear();
|
||||
fireIntervalRemoved(this, 0, prevSize - 1);
|
||||
fireContentsChanged(this, 0, prevSize - 1);
|
||||
}
|
||||
|
||||
public boolean contains(Object element) {
|
||||
return model.contains(element);
|
||||
}
|
||||
|
||||
public Iterator<T> iterator() {
|
||||
return model.iterator();
|
||||
}
|
||||
|
||||
public void removeElement(int idx) {
|
||||
model.remove(idx);
|
||||
fireIntervalRemoved(this, idx, idx);
|
||||
fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package forge.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* FlowLayout subclass that fully supports wrapping of components.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class WrapLayout extends FlowLayout {
|
||||
/**
|
||||
* Constructs a new <code>WrapLayout</code> with a left
|
||||
* alignment and a default 5-unit horizontal and vertical gap.
|
||||
*/
|
||||
public WrapLayout() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>FlowLayout</code> with the specified
|
||||
* alignment and a default 5-unit horizontal and vertical gap.
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>FlowLayout.LEFT</code>, <code>FlowLayout.CENTER</code>,
|
||||
* or <code>FlowLayout.RIGHT</code>.
|
||||
* @param align the alignment value
|
||||
*/
|
||||
public WrapLayout(int align) {
|
||||
super(align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new flow layout manager with the indicated alignment
|
||||
* and the indicated horizontal and vertical gaps.
|
||||
* <p>
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>FlowLayout.LEFT</code>, <code>FlowLayout.CENTER</code>,
|
||||
* or <code>FlowLayout.RIGHT</code>.
|
||||
* @param align the alignment value
|
||||
* @param hgap the horizontal gap between components
|
||||
* @param vgap the vertical gap between components
|
||||
*/
|
||||
public WrapLayout(int align, int hgap, int vgap) {
|
||||
super(align, hgap, vgap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred dimensions for this layout given the
|
||||
* <i>visible</i> components in the specified target container.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the preferred dimensions to lay out the
|
||||
* subcomponents of the specified container
|
||||
*/
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container target) {
|
||||
return layoutSize(target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum dimensions needed to layout the <i>visible</i>
|
||||
* components contained in the specified target container.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the minimum dimensions to lay out the
|
||||
* subcomponents of the specified container
|
||||
*/
|
||||
@Override
|
||||
public Dimension minimumLayoutSize(Container target) {
|
||||
Dimension minimum = layoutSize(target, false);
|
||||
minimum.width -= (getHgap() + 1);
|
||||
return minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum or preferred dimension needed to layout the target
|
||||
* container.
|
||||
*
|
||||
* @param target target to get layout size for
|
||||
* @param preferred should preferred size be calculated
|
||||
* @return the dimension to layout the target container
|
||||
*/
|
||||
private Dimension layoutSize(Container target, boolean preferred) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
// Each row must fit with the width allocated to the container.
|
||||
// When the container width = 0, the preferred width of the container
|
||||
// has not yet been calculated so we use a width guaranteed to be less
|
||||
// than we need so that it gets recalculated later when the widget is
|
||||
// shown.
|
||||
|
||||
int hgap = getHgap();
|
||||
Insets insets = target.getInsets();
|
||||
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
|
||||
int targetWidth = Math.max(horizontalInsetsAndGap, target.getSize().width);
|
||||
int maxWidth = targetWidth - horizontalInsetsAndGap;
|
||||
|
||||
// Fit components into the allowed width
|
||||
Dimension dim = new Dimension(0, 0);
|
||||
int rowWidth = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
final int nmembers = target.getComponentCount();
|
||||
for (int i = 0; i < nmembers; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
|
||||
if (!m.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
|
||||
|
||||
// can't add the component to current row. Start a new row if
|
||||
// there's at least one component in this row.
|
||||
if (0 < rowWidth && rowWidth + d.width > maxWidth) {
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
rowWidth = 0;
|
||||
rowHeight = 0;
|
||||
}
|
||||
|
||||
// Add a horizontal gap for all components after the first
|
||||
if (rowWidth != 0) {
|
||||
rowWidth += hgap;
|
||||
}
|
||||
|
||||
rowWidth += d.width;
|
||||
rowHeight = Math.max(rowHeight, d.height);
|
||||
}
|
||||
|
||||
// add last row
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
|
||||
dim.width += horizontalInsetsAndGap;
|
||||
dim.height += insets.top + insets.bottom + getVgap() * 2;
|
||||
|
||||
// When using a scroll pane or the DecoratedLookAndFeel we need to
|
||||
// make sure the preferred size is less than the size of the
|
||||
// target container so shrinking the container size works
|
||||
// correctly. Removing the horizontal gap is an easy way to do this.
|
||||
|
||||
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
|
||||
|
||||
if (scrollPane != null) {
|
||||
dim.width -= (hgap + 1);
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A new row has been completed. Use the dimensions of this row
|
||||
* to update the preferred size for the container.
|
||||
*
|
||||
* @param dim update the width and height when appropriate
|
||||
* @param rowWidth the width of the row to add
|
||||
* @param rowHeight the height of the row to add
|
||||
*/
|
||||
private void addRow(Dimension dim, int rowWidth, int rowHeight) {
|
||||
dim.width = Math.max(dim.width, rowWidth);
|
||||
|
||||
if (dim.height > 0) {
|
||||
dim.height += getVgap();
|
||||
}
|
||||
dim.height += rowHeight;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package forge.gui.bazaar;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.UiCommand;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.quest.bazaar.QuestBazaarManager;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public enum CBazaarUI implements ICDoc {
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
/**
|
||||
* Controls top-level instance of bazaar.
|
||||
* @param v0   {@link forge.gui.bazaar.VBazaarUI}
|
||||
* @param bazaar
|
||||
*/
|
||||
private CBazaarUI() {
|
||||
}
|
||||
|
||||
/** Populate all stalls, and select first one. */
|
||||
public void initBazaar(QuestBazaarManager bazaar) {
|
||||
VBazaarUI.SINGLETON_INSTANCE.populateStalls();
|
||||
((FLabel) VBazaarUI.SINGLETON_INSTANCE.getPnlAllStalls().getComponent(0)).setSelected(true);
|
||||
showStall(Iterables.get(bazaar.getStallNames(), 0), bazaar);
|
||||
}
|
||||
|
||||
/** @param s0   {@link java.lang.String} */
|
||||
public void showStall(final String s0, final QuestBazaarManager bazaar) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VBazaarUI.SINGLETON_INSTANCE.getPnlSingleStall().setStall(bazaar.getStall(s0));
|
||||
VBazaarUI.SINGLETON_INSTANCE.getPnlSingleStall().updateStall();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package forge.gui.bazaar;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.IVTopLevelUI;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FPanel;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.quest.bazaar.QuestBazaarManager;
|
||||
import forge.quest.gui.ViewStall;
|
||||
import forge.view.FView;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
|
||||
/** Lays out containers and borders for resizeable layout and
|
||||
* instantiates top-level controller for bazaar UI. */
|
||||
public enum VBazaarUI implements IVTopLevelUI {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private JPanel pnlAllStalls;
|
||||
private ViewStall pnlSingleStall;
|
||||
private FLabel previousSelected;
|
||||
private QuestBazaarManager bazaar;
|
||||
|
||||
/** Lays out containers and borders for resizeable layout and
|
||||
* instantiates top-level controller for bazaar UI.
|
||||
* @param bazaar0 */
|
||||
private VBazaarUI() {
|
||||
}
|
||||
|
||||
/** */
|
||||
@SuppressWarnings("serial")
|
||||
public void populateStalls() {
|
||||
for (final String s : bazaar.getStallNames()) {
|
||||
|
||||
final FLabel lbl = new FLabel.ButtonBuilder().text(s + " ")
|
||||
.fontAlign(SwingConstants.RIGHT).iconInBackground(true)
|
||||
.fontSize(16).icon(FSkin.getIcon(bazaar.getStall(s).getIcon())).build();
|
||||
|
||||
pnlAllStalls.add(lbl, "h 80px!, w 90%!, gap 0 0 10px 10px");
|
||||
|
||||
lbl.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (previousSelected != null) { previousSelected.setSelected(false); }
|
||||
lbl.setSelected(true);
|
||||
previousSelected = lbl;
|
||||
CBazaarUI.SINGLETON_INSTANCE.showStall(s, bazaar);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
public void refreshLastInstance() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
pnlSingleStall.updateStall();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return {@link javax.swing.JPanel}
|
||||
*/
|
||||
public JPanel getPnlAllStalls() {
|
||||
return this.pnlAllStalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return {@link forge.quest.gui.ViewStall}
|
||||
*/
|
||||
public ViewStall getPnlSingleStall() {
|
||||
return this.pnlSingleStall;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#instantiate()
|
||||
*/
|
||||
@Override
|
||||
public void instantiate() {
|
||||
// Final inits
|
||||
this.pnlAllStalls = new JPanel();
|
||||
this.pnlSingleStall = new ViewStall(this);
|
||||
this.bazaar = Singletons.getModel().getQuest().getBazaar();
|
||||
|
||||
pnlAllStalls.setOpaque(false);
|
||||
pnlAllStalls.setLayout(new MigLayout("insets 0, gap 0, wrap, align center"));
|
||||
|
||||
// Instantiate control
|
||||
CBazaarUI.SINGLETON_INSTANCE.initBazaar(this.bazaar);
|
||||
previousSelected = ((FLabel) pnlAllStalls.getComponent(0));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
FPanel pnl = FView.SINGLETON_INSTANCE.getPnlInsets();
|
||||
pnl.setBorder((Border)null);
|
||||
pnl.setLayout(new MigLayout("insets 0, gap 0"));
|
||||
pnl.setBackgroundTexture(FSkin.getIcon(FSkin.Backgrounds.BG_TEXTURE));
|
||||
|
||||
pnl.add(pnlAllStalls, "w 25%!, h 100%!");
|
||||
pnl.add(pnlSingleStall, "w 75%!, h 100%!");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#onSwitching(forge.gui.framework.FScreen)
|
||||
*/
|
||||
@Override
|
||||
public boolean onSwitching(FScreen fromScreen, FScreen toScreen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.view.FNavigationBar.INavigationTabData#onClosingTab()
|
||||
*/
|
||||
@Override
|
||||
public boolean onClosing(FScreen screen) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package forge.gui.deckchooser;
|
||||
|
||||
import forge.gui.MouseUtil;
|
||||
import forge.gui.deckchooser.DecksComboBox.DeckType;
|
||||
import forge.gui.toolbox.FComboBox.TextAlignment;
|
||||
import forge.gui.toolbox.FComboBoxWrapper;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DecksComboBox extends FComboBoxWrapper<DeckType> {
|
||||
|
||||
public enum DeckType {
|
||||
CUSTOM_DECK ("Custom User Decks"),
|
||||
PRECONSTRUCTED_DECK("Preconstructed Decks"),
|
||||
QUEST_OPPONENT_DECK ("Quest Opponent Decks"),
|
||||
COLOR_DECK ("Random Color Decks"),
|
||||
THEME_DECK ("Random Theme Decks");
|
||||
|
||||
private String value;
|
||||
private DeckType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
public static DeckType fromString(String value){
|
||||
for (final DeckType d : DeckType.values()) {
|
||||
if (d.toString().equalsIgnoreCase(value)) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No Enum specified for this string");
|
||||
}
|
||||
};
|
||||
|
||||
private List<IDecksComboBoxListener> _listeners = new ArrayList<>();
|
||||
private DeckType selectedDeckType = null;
|
||||
|
||||
public DecksComboBox() {
|
||||
setSkinFont(FSkin.getBoldFont(14));
|
||||
setTextAlignment(TextAlignment.CENTER);
|
||||
addActionListener(getDeckTypeComboListener());
|
||||
}
|
||||
|
||||
public void refresh(DeckType deckType) {
|
||||
setModel(new DefaultComboBoxModel<DeckType>(DeckType.values()));
|
||||
setSelectedItem(deckType);
|
||||
}
|
||||
|
||||
private ActionListener getDeckTypeComboListener() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MouseUtil.setCursor(Cursor.WAIT_CURSOR);
|
||||
DeckType newDeckType = (DeckType)getSelectedItem();
|
||||
if (newDeckType != selectedDeckType) {
|
||||
notifyDeckTypeSelected(newDeckType);
|
||||
selectedDeckType = newDeckType;
|
||||
}
|
||||
MouseUtil.resetCursor();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized void addListener(IDecksComboBoxListener obj) {
|
||||
_listeners.add(obj);
|
||||
}
|
||||
|
||||
public synchronized void removeListener(IDecksComboBoxListener obj) {
|
||||
_listeners.remove(obj);
|
||||
}
|
||||
|
||||
private synchronized void notifyDeckTypeSelected(DeckType deckType) {
|
||||
if (deckType != null) {
|
||||
for (IDecksComboBoxListener listener : _listeners) {
|
||||
listener.deckTypeSelected(new DecksComboBoxEvent(this, deckType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DeckType getDeckType() {
|
||||
return selectedDeckType;
|
||||
}
|
||||
|
||||
public void setDeckType(DeckType valueOf) {
|
||||
selectedDeckType = valueOf;
|
||||
setSelectedItem(selectedDeckType);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package forge.gui.deckchooser;
|
||||
|
||||
import forge.gui.deckchooser.DecksComboBox.DeckType;
|
||||
|
||||
import java.util.EventObject;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DecksComboBoxEvent extends EventObject {
|
||||
|
||||
private final DeckType _deckType;
|
||||
|
||||
public DecksComboBoxEvent(Object source, DeckType deckType) {
|
||||
super(source);
|
||||
_deckType = deckType;
|
||||
}
|
||||
|
||||
public DeckType getDeckType() {
|
||||
return _deckType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
package forge.gui.deckchooser;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.GameType;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.gui.deckchooser.DecksComboBox.DeckType;
|
||||
import forge.gui.deckeditor.DeckProxy;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.itemmanager.DeckManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerContainer;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.QuestEvent;
|
||||
import forge.quest.QuestEventChallenge;
|
||||
import forge.quest.QuestUtil;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
private DecksComboBox decksComboBox;
|
||||
private DeckType selectedDeckType;
|
||||
private ItemManagerContainer lstDecksContainer;
|
||||
|
||||
private final DeckManager lstDecks = new DeckManager(GameType.Constructed);
|
||||
private final FLabel btnViewDeck = new FLabel.ButtonBuilder().text("View Deck").fontSize(14).build();
|
||||
private final FLabel btnRandom = new FLabel.ButtonBuilder().fontSize(14).build();
|
||||
|
||||
private boolean isAi;
|
||||
|
||||
private final ForgePreferences prefs = Singletons.getModel().getPreferences();
|
||||
private FPref stateSetting = null;
|
||||
|
||||
public FDeckChooser(boolean forAi) {
|
||||
setOpaque(false);
|
||||
isAi = forAi;
|
||||
UiCommand cmdViewDeck = new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK) {
|
||||
FDeckViewer.show(getDeck());
|
||||
}
|
||||
}
|
||||
};
|
||||
lstDecks.setItemActivateCommand(cmdViewDeck);
|
||||
btnViewDeck.setCommand(cmdViewDeck);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
initialize(DeckType.COLOR_DECK);
|
||||
}
|
||||
public void initialize(DeckType defaultDeckType) {
|
||||
initialize(null, defaultDeckType);
|
||||
}
|
||||
public void initialize(FPref savedStateSetting, DeckType defaultDeckType) {
|
||||
stateSetting = savedStateSetting;
|
||||
selectedDeckType = defaultDeckType;
|
||||
}
|
||||
|
||||
public DeckType getSelectedDeckType() { return selectedDeckType; }
|
||||
public void setSelectedDeckType(DeckType selectedDeckType0) {
|
||||
refreshDecksList(selectedDeckType0, false, null);
|
||||
}
|
||||
|
||||
public DeckManager getLstDecks() { return lstDecks; }
|
||||
|
||||
private void updateCustom() {
|
||||
lstDecks.setAllowMultipleSelections(false);
|
||||
|
||||
lstDecks.setPool(DeckProxy.getAllConstructedDecks(Singletons.getModel().getDecks().getConstructed()));
|
||||
lstDecks.setup(ItemManagerConfig.CONSTRUCTED_DECKS);
|
||||
|
||||
btnRandom.setText("Random Deck");
|
||||
btnRandom.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
});
|
||||
|
||||
lstDecks.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private class ColorDeckGenerator extends DeckProxy implements Comparable<ColorDeckGenerator> {
|
||||
private String name;
|
||||
private int index;
|
||||
|
||||
public ColorDeckGenerator(String name0, int index0) {
|
||||
super();
|
||||
name = name0;
|
||||
this.index = index0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(final ColorDeckGenerator d) {
|
||||
return d instanceof ColorDeckGenerator ? Integer.compare(this.index, ((ColorDeckGenerator)d).index) : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deck getDeck() {
|
||||
List<String> selection = new ArrayList<String>();
|
||||
for (DeckProxy deck : lstDecks.getSelectedItems()) {
|
||||
selection.add(deck.getName());
|
||||
}
|
||||
if (DeckgenUtil.colorCheck(selection)) {
|
||||
return DeckgenUtil.buildColorDeck(selection, isAi);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeneratedDeck() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateColors() {
|
||||
lstDecks.setAllowMultipleSelections(true);
|
||||
|
||||
String[] colors = new String[] { "Random 1", "Random 2", "Random 3",
|
||||
"White", "Blue", "Black", "Red", "Green" };
|
||||
ArrayList<DeckProxy> decks = new ArrayList<DeckProxy>();
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
decks.add(new ColorDeckGenerator(colors[i], i));
|
||||
}
|
||||
|
||||
lstDecks.setPool(decks);
|
||||
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
|
||||
|
||||
btnRandom.setText("Random Colors");
|
||||
btnRandom.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
DeckgenUtil.randomSelectColors(lstDecks);
|
||||
}
|
||||
});
|
||||
|
||||
// default selection = basic two color deck
|
||||
lstDecks.setSelectedIndices(new Integer[]{0, 1});
|
||||
}
|
||||
|
||||
private void updateThemes() {
|
||||
lstDecks.setAllowMultipleSelections(false);
|
||||
|
||||
lstDecks.setPool(DeckProxy.getAllThemeDecks());
|
||||
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
|
||||
|
||||
btnRandom.setText("Random Deck");
|
||||
btnRandom.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
});
|
||||
|
||||
lstDecks.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private void updatePrecons() {
|
||||
lstDecks.setAllowMultipleSelections(false);
|
||||
|
||||
lstDecks.setPool(DeckProxy.getAllPreconstructedDecks(QuestController.getPrecons()));
|
||||
lstDecks.setup(ItemManagerConfig.PRECON_DECKS);
|
||||
|
||||
btnRandom.setText("Random Deck");
|
||||
btnRandom.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
});
|
||||
|
||||
lstDecks.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private void updateQuestEvents() {
|
||||
lstDecks.setAllowMultipleSelections(false);
|
||||
|
||||
lstDecks.setPool(DeckProxy.getAllQuestEventAndChallenges());
|
||||
lstDecks.setup(ItemManagerConfig.QUEST_EVENT_DECKS);
|
||||
|
||||
btnRandom.setText("Random Deck");
|
||||
btnRandom.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
});
|
||||
|
||||
lstDecks.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
public Deck getDeck() {
|
||||
DeckProxy proxy = lstDecks.getSelectedItem();
|
||||
return proxy.getDeck();
|
||||
}
|
||||
|
||||
/** Generates deck from current list selection(s). */
|
||||
public RegisteredPlayer getPlayer() {
|
||||
if (lstDecks.getSelectedIndex() < 0) { return null; }
|
||||
|
||||
// Special branch for quest events
|
||||
if (selectedDeckType == DeckType.QUEST_OPPONENT_DECK) {
|
||||
QuestEvent event = DeckgenUtil.getQuestEvent(lstDecks.getSelectedItem().getName());
|
||||
RegisteredPlayer result = new RegisteredPlayer(event.getEventDeck());
|
||||
if (event instanceof QuestEventChallenge) {
|
||||
result.setStartingLife(((QuestEventChallenge) event).getAiLife());
|
||||
}
|
||||
result.setCardsOnBattlefield(QuestUtil.getComputerStartingCards(event));
|
||||
return result;
|
||||
}
|
||||
|
||||
return new RegisteredPlayer(getDeck());
|
||||
}
|
||||
|
||||
public void populate() {
|
||||
if (decksComboBox == null) { //initialize components with delayed initialization the first time this is populated
|
||||
decksComboBox = new DecksComboBox();
|
||||
lstDecksContainer = new ItemManagerContainer(lstDecks);
|
||||
restoreSavedState();
|
||||
decksComboBox.addListener(this);
|
||||
}
|
||||
else {
|
||||
removeAll();
|
||||
restoreSavedState(); //ensure decks refreshed and state restored in case any deleted or added since last loaded
|
||||
}
|
||||
this.setLayout(new MigLayout("insets 0, gap 0"));
|
||||
decksComboBox.addTo(this, "w 100%, h 30px!, gapbottom 5px, spanx 2, wrap");
|
||||
this.add(lstDecksContainer, "w 100%, growy, pushy, spanx 2, wrap");
|
||||
this.add(btnViewDeck, "w 50%-3px, h 30px!, gaptop 5px, gapright 6px");
|
||||
this.add(btnRandom, "w 50%-3px, h 30px!, gaptop 5px");
|
||||
if (isShowing()) {
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isAi() {
|
||||
return isAi;
|
||||
}
|
||||
|
||||
public void setIsAi(boolean isAiDeck) {
|
||||
this.isAi = isAiDeck;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckchooser.IDecksComboBoxListener#deckTypeSelected(forge.gui.deckchooser.DecksComboBoxEvent)
|
||||
*/
|
||||
@Override
|
||||
public void deckTypeSelected(DecksComboBoxEvent ev) {
|
||||
refreshDecksList(ev.getDeckType(), false, ev);
|
||||
}
|
||||
|
||||
private void refreshDecksList(DeckType deckType, boolean forceRefresh, DecksComboBoxEvent ev) {
|
||||
if (selectedDeckType == deckType && !forceRefresh) { return; }
|
||||
selectedDeckType = deckType;
|
||||
|
||||
if (ev == null) {
|
||||
decksComboBox.refresh(deckType);
|
||||
}
|
||||
lstDecks.setCaption(deckType.toString());
|
||||
|
||||
switch (deckType) {
|
||||
case CUSTOM_DECK:
|
||||
updateCustom();
|
||||
break;
|
||||
case COLOR_DECK:
|
||||
updateColors();
|
||||
break;
|
||||
case THEME_DECK:
|
||||
updateThemes();
|
||||
break;
|
||||
case QUEST_OPPONENT_DECK:
|
||||
updateQuestEvents();
|
||||
break;
|
||||
case PRECONSTRUCTED_DECK:
|
||||
updatePrecons();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private final String SELECTED_DECK_DELIMITER = "::";
|
||||
|
||||
public void saveState() {
|
||||
if (stateSetting == null) {
|
||||
throw new NullPointerException("State setting missing. Specify first using the initialize() method.");
|
||||
}
|
||||
prefs.setPref(stateSetting, getState());
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
private String getState() {
|
||||
String deckType = decksComboBox.getDeckType().name();
|
||||
StringBuilder state = new StringBuilder(deckType);
|
||||
state.append(";");
|
||||
joinSelectedDecks(state, SELECTED_DECK_DELIMITER);
|
||||
return state.toString();
|
||||
}
|
||||
|
||||
private void joinSelectedDecks(StringBuilder state, String delimiter) {
|
||||
Iterable<DeckProxy> selectedDecks = lstDecks.getSelectedItems();
|
||||
boolean isFirst = true;
|
||||
if (selectedDecks != null) {
|
||||
for (DeckProxy deck : selectedDecks) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
}
|
||||
else {
|
||||
state.append(delimiter);
|
||||
}
|
||||
state.append(deck.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a clean name from the state that can be used for labels. */
|
||||
public final String getStateForLabel() {
|
||||
String deckType = decksComboBox.getDeckType().toString();
|
||||
StringBuilder state = new StringBuilder(deckType);
|
||||
state.append(": ");
|
||||
joinSelectedDecks(state, ", ");
|
||||
return state.toString();
|
||||
}
|
||||
|
||||
private void restoreSavedState() {
|
||||
if (stateSetting == null) {
|
||||
//if can't restore saved state, just refresh deck list
|
||||
refreshDecksList(selectedDeckType, true, null);
|
||||
return;
|
||||
}
|
||||
|
||||
String savedState = prefs.getPref(stateSetting);
|
||||
refreshDecksList(getDeckTypeFromSavedState(savedState), true, null);
|
||||
lstDecks.setSelectedStrings(getSelectedDecksFromSavedState(savedState));
|
||||
}
|
||||
|
||||
private DeckType getDeckTypeFromSavedState(String savedState) {
|
||||
try {
|
||||
if (StringUtils.isBlank(savedState)) {
|
||||
return selectedDeckType;
|
||||
}
|
||||
else {
|
||||
return DeckType.valueOf(savedState.split(";")[0]);
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
System.err.println(ex.getMessage() + ". Using default : " + selectedDeckType);
|
||||
return selectedDeckType;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getSelectedDecksFromSavedState(String savedState) {
|
||||
try {
|
||||
if (StringUtils.isBlank(savedState)) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
else {
|
||||
return Arrays.asList(savedState.split(";")[1].split(SELECTED_DECK_DELIMITER));
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
System.err.println(ex + " [savedState=" + savedState + "]");
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
|
||||
public DecksComboBox getDecksComboBox() {
|
||||
return decksComboBox;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package forge.gui.deckchooser;
|
||||
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.card.Card;
|
||||
import forge.gui.CardDetailPanel;
|
||||
import forge.gui.CardPicturePanel;
|
||||
import forge.gui.toolbox.FButton;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerContainer;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerModel;
|
||||
import forge.gui.toolbox.itemmanager.views.*;
|
||||
import forge.item.PaperCard;
|
||||
import forge.view.FDialog;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FDeckViewer extends FDialog {
|
||||
private final Deck deck;
|
||||
private final List<DeckSection> sections = new ArrayList<DeckSection>();
|
||||
private final CardManager cardManager;
|
||||
private DeckSection currentSection;
|
||||
|
||||
private final CardDetailPanel cardDetail = new CardDetailPanel(null);
|
||||
private final CardPicturePanel cardPicture = new CardPicturePanel();
|
||||
private final FButton btnCopyToClipboard = new FButton("Copy to Clipboard");
|
||||
private final FButton btnChangeSection = new FButton("Change Section");
|
||||
private final FButton btnClose = new FButton("Close");
|
||||
|
||||
public static void show(final Deck deck) {
|
||||
if (deck == null) { return; }
|
||||
|
||||
FDeckViewer deckViewer = new FDeckViewer(deck);
|
||||
deckViewer.setVisible(true);
|
||||
deckViewer.dispose();
|
||||
}
|
||||
|
||||
private FDeckViewer(Deck deck0) {
|
||||
this.deck = deck0;
|
||||
this.setTitle(deck.getName());
|
||||
this.cardManager = new CardManager(false) {
|
||||
@Override //show hovered card in Image View in dialog instead of main Detail/Picture panes
|
||||
protected ImageView<PaperCard> createImageView(final ItemManagerModel<PaperCard> model0) {
|
||||
return new ImageView<PaperCard>(this, model0) {
|
||||
@Override
|
||||
protected void showHoveredItem(PaperCard item) {
|
||||
Card card = Card.getCardForUi(item);
|
||||
if (card == null) { return; }
|
||||
|
||||
cardDetail.setCard(card);
|
||||
cardPicture.setCard(card, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
this.cardManager.setPool(deck.getMain());
|
||||
this.cardManager.addSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
PaperCard paperCard = cardManager.getSelectedItem();
|
||||
if (paperCard == null) { return; }
|
||||
|
||||
Card card = Card.getCardForUi(paperCard);
|
||||
if (card == null) { return; }
|
||||
|
||||
cardDetail.setCard(card);
|
||||
cardPicture.setCard(card, true);
|
||||
}
|
||||
});
|
||||
|
||||
for (Entry<DeckSection, CardPool> entry : deck) {
|
||||
this.sections.add(entry.getKey());
|
||||
}
|
||||
this.currentSection = DeckSection.Main;
|
||||
updateCaption();
|
||||
|
||||
this.btnCopyToClipboard.setFocusable(false);
|
||||
this.btnCopyToClipboard.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
FDeckViewer.this.copyToClipboard();
|
||||
}
|
||||
});
|
||||
this.btnChangeSection.setFocusable(false);
|
||||
if (this.sections.size() > 1) {
|
||||
this.btnChangeSection.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
FDeckViewer.this.changeSection();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.btnChangeSection.setEnabled(false);
|
||||
}
|
||||
this.btnClose.setFocusable(false);
|
||||
this.btnClose.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
FDeckViewer.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
final int width = 800;
|
||||
final int height = 600;
|
||||
this.setPreferredSize(new Dimension(width, height));
|
||||
this.setSize(width, height);
|
||||
|
||||
this.cardPicture.setOpaque(false);
|
||||
|
||||
JPanel cardPanel = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
|
||||
cardPanel.setOpaque(false);
|
||||
cardPanel.add(this.cardDetail, "w 225px, h 240px, gapbottom 10px");
|
||||
cardPanel.add(this.cardPicture, "w 225px, h 350px, gapbottom 10px");
|
||||
|
||||
JPanel buttonPanel = new JPanel(new MigLayout("insets 0, gap 0"));
|
||||
buttonPanel.setOpaque(false);
|
||||
buttonPanel.add(this.btnCopyToClipboard, "w 200px!, h 26px!, gapright 5px");
|
||||
buttonPanel.add(this.btnChangeSection, "w 200px!, h 26px!");
|
||||
|
||||
this.add(new ItemManagerContainer(this.cardManager), "push, grow, gapright 10px, gapbottom 10px");
|
||||
this.add(cardPanel, "wrap");
|
||||
this.add(buttonPanel);
|
||||
this.add(this.btnClose, "w 120px!, h 26px!, ax right");
|
||||
|
||||
this.cardManager.setup(ItemManagerConfig.DECK_VIEWER);
|
||||
this.setDefaultFocus(this.cardManager.getCurrentView().getComponent());
|
||||
}
|
||||
|
||||
private void changeSection() {
|
||||
int index = sections.indexOf(currentSection);
|
||||
index = (index + 1) % sections.size();
|
||||
currentSection = sections.get(index);
|
||||
this.cardManager.setPool(this.deck.get(currentSection));
|
||||
updateCaption();
|
||||
}
|
||||
|
||||
private void updateCaption() {
|
||||
this.cardManager.setCaption(deck.getName() + " - " + currentSection.name());
|
||||
}
|
||||
|
||||
private void copyToClipboard() {
|
||||
final String nl = System.getProperty("line.separator");
|
||||
final StringBuilder deckList = new StringBuilder();
|
||||
final String dName = deck.getName();
|
||||
deckList.append(dName == null ? "" : dName + nl + nl);
|
||||
|
||||
for (DeckSection s : DeckSection.values()){
|
||||
CardPool cp = deck.get(s);
|
||||
if (cp == null || cp.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
deckList.append(s.toString()).append(": ");
|
||||
if (s.isSingleCard()) {
|
||||
deckList.append(cp.get(0).getName()).append(nl);
|
||||
}
|
||||
else {
|
||||
deckList.append(nl);
|
||||
for (final Entry<PaperCard, Integer> ev : cp) {
|
||||
deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl);
|
||||
}
|
||||
}
|
||||
deckList.append(nl);
|
||||
}
|
||||
|
||||
final StringSelection ss = new StringSelection(deckList.toString());
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
|
||||
FOptionPane.showMessageDialog("Deck list for '" + deck.getName() + "' copied to clipboard.");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package forge.gui.deckchooser;
|
||||
|
||||
public interface IDecksComboBoxListener {
|
||||
public void deckTypeSelected(DecksComboBoxEvent ev);
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.gui.deckeditor.controllers.*;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VCardCatalog;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.match.controllers.CDetail;
|
||||
import forge.gui.match.controllers.CPicture;
|
||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Constructs instance of deck editor UI controller, used as a single point of
|
||||
* top-level control for child UIs. Tasks targeting the view of individual
|
||||
* components are found in a separate controller for that component and
|
||||
* should not be included here.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*/
|
||||
public enum CDeckEditorUI implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private final HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>> screenChildControllers;
|
||||
private ACEditorBase<? extends InventoryItem, ? extends DeckBase> childController;
|
||||
|
||||
private CDeckEditorUI() {
|
||||
screenChildControllers = new HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Pack, for when Packs can be shown in the CardPicturePanel.
|
||||
* @param item
|
||||
*/
|
||||
public void setCard(final InventoryItem item) {
|
||||
CDetail.SINGLETON_INSTANCE.showCard(item);
|
||||
CPicture.SINGLETON_INSTANCE.showImage(item);
|
||||
}
|
||||
|
||||
public boolean hasChanges() {
|
||||
if (this.childController == null) { return false; }
|
||||
|
||||
final DeckController<?> deckController = this.childController.getDeckController();
|
||||
if (deckController == null) { return false; }
|
||||
|
||||
return !deckController.isSaved();
|
||||
}
|
||||
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
if (this.childController != null) {
|
||||
if (!this.childController.canSwitchAway(isClosing)) {
|
||||
return false;
|
||||
}
|
||||
this.childController.resetUIChanges();
|
||||
if (isClosing) {
|
||||
screenChildControllers.remove(this.childController.getScreen());
|
||||
this.childController = null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//========= Accessor/mutator methods
|
||||
/**
|
||||
* @return ACEditorBase<?, ?>
|
||||
*/
|
||||
public ACEditorBase<? extends InventoryItem, ? extends DeckBase> getCurrentEditorController() {
|
||||
return childController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set controller for a given editor screen.
|
||||
*/
|
||||
public void setEditorController(ACEditorBase<? extends InventoryItem, ? extends DeckBase> childController0) {
|
||||
FScreen screen = childController0.getScreen();
|
||||
screenChildControllers.put(screen, childController0);
|
||||
if (screen == Singletons.getControl().getCurrentScreen()) {
|
||||
setCurrentEditorController(childController0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends InventoryItem> void incrementDeckQuantity(T item, int delta) {
|
||||
if (item == null || delta == 0) { return; }
|
||||
|
||||
if (delta > 0) { //add items
|
||||
int qty = Math.min(delta, ((ItemManager<T>)childController.getCatalogManager()).getItemCount(item));
|
||||
if (qty == 0) { return; }
|
||||
((ACEditorBase<T, ?>)childController).addItem(item, qty, false);
|
||||
}
|
||||
else { //remove items
|
||||
int qty = Math.min(-delta, ((ItemManager<T>)childController.getDeckManager()).getItemCount(item));
|
||||
if (qty == 0) { return; }
|
||||
((ACEditorBase<T, ?>)childController).removeItem(item, qty, false);
|
||||
}
|
||||
|
||||
CStatistics.SINGLETON_INSTANCE.update();
|
||||
CProbabilities.SINGLETON_INSTANCE.update();
|
||||
}
|
||||
|
||||
private interface _MoveAction {
|
||||
public <T extends InventoryItem> void move(Iterable<Entry<T, Integer>> items);
|
||||
}
|
||||
|
||||
private <T extends InventoryItem> void moveSelectedItems(ItemManager<T> itemManager, _MoveAction moveAction, int maxQty) {
|
||||
if (maxQty == 0) { return; }
|
||||
|
||||
ItemPool<T> items = new ItemPool<T>(itemManager.getGenericType());
|
||||
for (T item : itemManager.getSelectedItems()) {
|
||||
int qty = Math.min(maxQty, itemManager.getItemCount(item));
|
||||
if (qty > 0) {
|
||||
items.add(item, qty);
|
||||
}
|
||||
}
|
||||
|
||||
if (items.isEmpty()) { return; }
|
||||
|
||||
moveAction.move(items);
|
||||
CStatistics.SINGLETON_INSTANCE.update();
|
||||
CProbabilities.SINGLETON_INSTANCE.update();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addSelectedCards(final boolean toAlternate, int number) {
|
||||
moveSelectedItems(childController.getCatalogManager(), new _MoveAction() {
|
||||
@Override
|
||||
public <T extends InventoryItem> void move(Iterable<Entry<T, Integer>> items) {
|
||||
((ACEditorBase<T, ?>)childController).addItems(items, toAlternate);
|
||||
}
|
||||
}, number);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void removeSelectedCards(final boolean toAlternate, int number) {
|
||||
moveSelectedItems(childController.getDeckManager(), new _MoveAction() {
|
||||
@Override
|
||||
public <T extends InventoryItem> void move(Iterable<Entry<T, Integer>> items) {
|
||||
((ACEditorBase<T, ?>)childController).removeItems(items, toAlternate);
|
||||
}
|
||||
}, number);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void removeAllCards(final boolean toAlternate) {
|
||||
ItemManager<?> v = childController.getDeckManager();
|
||||
v.selectAll();
|
||||
moveSelectedItems(v, new _MoveAction() {
|
||||
@Override
|
||||
public <T extends InventoryItem> void move(Iterable<Entry<T, Integer>> items) {
|
||||
((ACEditorBase<T, ?>)childController).removeItems(items, toAlternate);
|
||||
}
|
||||
}, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current editor controller
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private void setCurrentEditorController(ACEditorBase<? extends InventoryItem, ? extends DeckBase> childController0) {
|
||||
this.childController = childController0;
|
||||
Singletons.getControl().getForgeMenu().setProvider(childController0);
|
||||
|
||||
if (childController == null) { return; }
|
||||
|
||||
final ItemManager<? extends InventoryItem> catView = childController.getCatalogManager();
|
||||
final ItemManager<? extends InventoryItem> deckView = childController.getDeckManager();
|
||||
|
||||
VCardCatalog.SINGLETON_INSTANCE.setItemManager(catView);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.setItemManager(deckView);
|
||||
|
||||
if (!childController.listenersHooked) { //hook listeners the first time the controller is updated
|
||||
catView.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (!catView.isIncrementalSearchActive() && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
||||
addSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
||||
}
|
||||
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
||||
deckView.focus();
|
||||
e.consume(); //prevent losing selection
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
deckView.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (!catView.isIncrementalSearchActive() && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
||||
removeSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
||||
}
|
||||
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
||||
catView.focus();
|
||||
e.consume(); //prevent losing selection
|
||||
}
|
||||
else if (KeyEvent.VK_F == e.getKeyCode()) {
|
||||
// let ctrl/cmd-F set focus to the text filter box
|
||||
if (e.isControlDown() || e.isMetaDown()) {
|
||||
deckView.focusSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
catView.setItemActivateCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
addSelectedCards(false, 1);
|
||||
}
|
||||
});
|
||||
deckView.setItemActivateCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
removeSelectedCards(false, 1);
|
||||
}
|
||||
});
|
||||
|
||||
catView.setContextMenuBuilder(childController.createContextMenuBuilder(true));
|
||||
deckView.setContextMenuBuilder(childController.createContextMenuBuilder(false));
|
||||
|
||||
//set card when selection changes
|
||||
catView.addSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
setCard(catView.getSelectedItem());
|
||||
}
|
||||
});
|
||||
|
||||
deckView.addSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
setCard(deckView.getSelectedItem());
|
||||
}
|
||||
});
|
||||
|
||||
catView.setAllowMultipleSelections(true);
|
||||
deckView.setAllowMultipleSelections(true);
|
||||
|
||||
childController.listenersHooked = true;
|
||||
}
|
||||
|
||||
childController.update();
|
||||
|
||||
catView.applyFilters();
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
catView.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
//change to previously open child controller based on screen
|
||||
FScreen screen = Singletons.getControl().getCurrentScreen();
|
||||
ACEditorBase<? extends InventoryItem, ? extends DeckBase> screenChildController = screenChildControllers.get(screen);
|
||||
if (screenChildController != null) {
|
||||
setCurrentEditorController(screenChildController);
|
||||
}
|
||||
else if (screen == FScreen.DECK_EDITOR_CONSTRUCTED) {
|
||||
setEditorController(new CEditorConstructed()); //ensure Constructed deck editor controller initialized
|
||||
|
||||
String currentDeckStr = DeckPreferences.getCurrentDeck();
|
||||
if (currentDeckStr != null) {
|
||||
DeckProxy deck = VAllDecks.SINGLETON_INSTANCE.getLstDecks().stringToItem(currentDeckStr);
|
||||
if (deck != null) {
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedItem(deck);
|
||||
childController.getDeckController().load(deck.getPath(), deck.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() { }
|
||||
}
|
||||
|
||||
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.DeckRecognizer;
|
||||
import forge.deck.DeckRecognizer.TokenType;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.controllers.ACEditorBase;
|
||||
import forge.gui.toolbox.*;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.view.FDialog;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Element;
|
||||
import javax.swing.text.ElementIterator;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* Dialog for quick import of decks.
|
||||
*
|
||||
* @param <TItem>
|
||||
* @param <TModel>
|
||||
*/
|
||||
public class DeckImport<TItem extends InventoryItem, TModel extends DeckBase> extends FDialog {
|
||||
private static final long serialVersionUID = -5837776824284093004L;
|
||||
|
||||
private final FTextArea txtInput = new FTextArea();
|
||||
private static final String STYLESHEET = "<style>"
|
||||
+ "body, h1, h2, h3, h4, h5, h6, table, tr, td, p {margin: 3px 1px; padding: 0; font-weight: "
|
||||
+ "normal; font-style: normal; text-decoration: none; font-family: Arial; font-size: 10px; background-color: white;} "
|
||||
+
|
||||
// "h1 {border-bottom: solid 1px black; color: blue; font-size: 12px; margin: 3px 0 9px 0; } "
|
||||
// +
|
||||
".comment {color: #666666;} " + ".knowncard {color: #009900;} " + ".unknowncard {color: #990000;} "
|
||||
+ ".section {padding: 3px 10px; margin: 3px 0; font-weight: 700; background-color: #DDDDDD; } "
|
||||
+ "</style>";
|
||||
private static final String HTML_WELCOME_TEXT = "<html>"
|
||||
+ DeckImport.STYLESHEET
|
||||
+ "<h3>You'll see recognized cards here</h3>"
|
||||
+ "<div class='section'>Legend</div>"
|
||||
+ "<ul>"
|
||||
+ "<li class='knowncard'>Recognized cards will be shown in green. These cards will be auto-imported into a new deck<BR></li>"
|
||||
+ "<li class='unknowncard'>Lines which seem to be cards but are either misspelled or unsupported by Forge, are shown in dark-red<BR></li>"
|
||||
+ "<li class='comment'>Lines that appear unsignificant will be shown in gray<BR><BR></li>" + "</ul>"
|
||||
+ "<div class='section'>Choosing source</div>"
|
||||
+ "<p>In most cases when you paste from clipboard a carefully selected area of a webpage, it works perfectly.</p>"
|
||||
+ "<p>Sometimes to filter out unneeded data you may have to export deck in MTGO format, and paste here downloaded file contents.</p>"
|
||||
+ "<p>Sideboard recognition is supported. Make sure that the sideboard cards are listed after a line that contains the word 'Sideboard'</p>"
|
||||
+ "</html>";
|
||||
|
||||
private final FHtmlViewer htmlOutput = new FHtmlViewer(DeckImport.HTML_WELCOME_TEXT);
|
||||
private final FScrollPane scrollInput = new FScrollPane(this.txtInput, false);
|
||||
private final FScrollPane scrollOutput = new FScrollPane(this.htmlOutput, false);
|
||||
private final FLabel summaryMain = new FLabel.Builder().text("Imported deck summary will appear here").build();
|
||||
private final FLabel summarySide = new FLabel.Builder().text("Line for sideboard summary").build();
|
||||
private final FButton cmdAccept = new FButton("Import Deck");
|
||||
private final FButton cmdCancel = new FButton("Cancel");
|
||||
private final FCheckBox newEditionCheck = new FCheckBox("Import latest version of card", true);
|
||||
private final FCheckBox dateTimeCheck = new FCheckBox("Use only sets released before:", false);
|
||||
private final FCheckBox onlyCoreExpCheck = new FCheckBox("Use only core and expansion sets", true);
|
||||
|
||||
private final FComboBox<String> monthDropdown = new FComboBox<String>(); //don't need wrappers since skin can't change while this dialog is open
|
||||
private final FComboBox<Integer> yearDropdown = new FComboBox<Integer>();
|
||||
|
||||
/** The tokens. */
|
||||
private final List<DeckRecognizer.Token> tokens = new ArrayList<DeckRecognizer.Token>();
|
||||
|
||||
private final ACEditorBase<TItem, TModel> host;
|
||||
|
||||
/**
|
||||
* Instantiates a new deck import.
|
||||
*
|
||||
* @param g
|
||||
* the g
|
||||
*/
|
||||
public DeckImport(final ACEditorBase<TItem, TModel> g) {
|
||||
this.host = g;
|
||||
|
||||
final int wWidth = 700;
|
||||
final int wHeight = 600;
|
||||
|
||||
this.setPreferredSize(new java.awt.Dimension(wWidth, wHeight));
|
||||
this.setSize(wWidth, wHeight);
|
||||
this.setTitle("Deck Importer");
|
||||
|
||||
txtInput.setFocusable(true);
|
||||
txtInput.setEditable(true);
|
||||
|
||||
FSkin.SkinColor foreColor = FSkin.getColor(FSkin.Colors.CLR_TEXT);
|
||||
this.scrollInput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), "Paste or type a decklist", foreColor));
|
||||
this.scrollOutput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), "Expect the recognized lines to appear", foreColor));
|
||||
this.scrollInput.setViewportBorder(BorderFactory.createLoweredBevelBorder());
|
||||
this.scrollOutput.setViewportBorder(BorderFactory.createLoweredBevelBorder());
|
||||
|
||||
this.add(this.scrollInput, "cell 0 0, w 50%, growy, pushy");
|
||||
this.add(this.newEditionCheck, "cell 0 1, w 50%, ax c");
|
||||
this.add(this.dateTimeCheck, "cell 0 2, w 50%, ax c");
|
||||
|
||||
this.add(monthDropdown, "cell 0 3, w 20%, ax left, split 2, pad 0 4 0 0");
|
||||
this.add(yearDropdown, "w 15%");
|
||||
fillDateDropdowns();
|
||||
|
||||
this.add(this.onlyCoreExpCheck, "cell 0 4, w 50%, ax c");
|
||||
|
||||
this.add(this.scrollOutput, "cell 1 0, w 50%, growy, pushy");
|
||||
this.add(this.summaryMain, "cell 1 1, label");
|
||||
this.add(this.summarySide, "cell 1 2, label");
|
||||
|
||||
this.add(this.cmdAccept, "cell 1 4, split 2, w 150, align r, h 26");
|
||||
this.add(this.cmdCancel, "w 150, h 26");
|
||||
|
||||
this.cmdCancel.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
DeckImport.this.processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
});
|
||||
|
||||
this.cmdAccept.addActionListener(new ActionListener() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
final String warning = "This will replace contents of your currently open deck with whatever you are importing. Proceed?";
|
||||
if (!FOptionPane.showConfirmDialog(warning, "Replacing old deck")) {
|
||||
return;
|
||||
}
|
||||
final Deck toSet = DeckImport.this.buildDeck();
|
||||
DeckImport.this.host.getDeckController().setModel((TModel) toSet);
|
||||
DeckImport.this.processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
});
|
||||
|
||||
ActionListener updateDateCheck = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
boolean isSel = dateTimeCheck.isSelected();
|
||||
monthDropdown.setEnabled(isSel);
|
||||
yearDropdown.setEnabled(isSel);
|
||||
parseAndDisplay();
|
||||
}
|
||||
};
|
||||
this.dateTimeCheck.addActionListener(updateDateCheck);
|
||||
|
||||
ActionListener reparse = new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) { parseAndDisplay(); }
|
||||
};
|
||||
this.newEditionCheck.addActionListener(reparse);
|
||||
this.onlyCoreExpCheck.addActionListener(reparse);
|
||||
this.yearDropdown.addActionListener(reparse);
|
||||
this.monthDropdown.addActionListener(reparse);
|
||||
updateDateCheck.actionPerformed(null); // update actual state
|
||||
|
||||
this.txtInput.getDocument().addDocumentListener(new OnChangeTextUpdate());
|
||||
this.cmdAccept.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
*/
|
||||
private void fillDateDropdowns() {
|
||||
DateFormatSymbols dfs = new DateFormatSymbols();
|
||||
monthDropdown.removeAllItems();
|
||||
String[] months = dfs.getMonths();
|
||||
for(String monthName : months)
|
||||
if(!StringUtils.isBlank(monthName))
|
||||
monthDropdown.addItem(monthName);
|
||||
int yearNow = Calendar.getInstance().get(Calendar.YEAR);
|
||||
for(int i = yearNow; i >= 1993; i--)
|
||||
yearDropdown.addItem(Integer.valueOf(i));
|
||||
}
|
||||
|
||||
private void readInput() {
|
||||
this.tokens.clear();
|
||||
final ElementIterator it = new ElementIterator(this.txtInput.getDocument().getDefaultRootElement());
|
||||
Element e;
|
||||
|
||||
DeckRecognizer recognizer = new DeckRecognizer(newEditionCheck.isSelected(), onlyCoreExpCheck.isSelected(), Singletons.getMagicDb().getCommonCards());
|
||||
if (dateTimeCheck.isSelected()) {
|
||||
recognizer.setDateConstraint(monthDropdown.getSelectedIndex(), (Integer)yearDropdown.getSelectedItem());
|
||||
}
|
||||
while ((e = it.next()) != null) {
|
||||
if (!e.isLeaf()) {
|
||||
continue;
|
||||
}
|
||||
final int rangeStart = e.getStartOffset();
|
||||
final int rangeEnd = e.getEndOffset();
|
||||
try {
|
||||
final String line = this.txtInput.getText(rangeStart, rangeEnd - rangeStart);
|
||||
this.tokens.add(recognizer.recognizeLine(line));
|
||||
}
|
||||
catch (final BadLocationException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayTokens() {
|
||||
if(this.tokens.isEmpty())
|
||||
this.htmlOutput.setText(HTML_WELCOME_TEXT);
|
||||
else {
|
||||
final StringBuilder sbOut = new StringBuilder("<html>");
|
||||
sbOut.append(DeckImport.STYLESHEET);
|
||||
for (final DeckRecognizer.Token t : this.tokens) {
|
||||
sbOut.append(this.makeHtmlViewOfToken(t));
|
||||
}
|
||||
sbOut.append("</html>");
|
||||
this.htmlOutput.setText(sbOut.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSummaries() {
|
||||
final int[] cardsOk = new int[2];
|
||||
final int[] cardsUnknown = new int[2];
|
||||
int idx = 0;
|
||||
for (final DeckRecognizer.Token t : this.tokens) {
|
||||
if (t.getType() == TokenType.KnownCard) {
|
||||
cardsOk[idx] += t.getNumber();
|
||||
}
|
||||
if (t.getType() == TokenType.UnknownCard) {
|
||||
cardsUnknown[idx] += t.getNumber();
|
||||
}
|
||||
if ((t.getType() == TokenType.SectionName) && t.getText().toLowerCase().contains("side")) {
|
||||
idx = 1;
|
||||
}
|
||||
}
|
||||
this.summaryMain.setText(String.format("Main: %d cards recognized, %d unknown cards", cardsOk[0], cardsUnknown[0]));
|
||||
this.summarySide.setText(String.format("Sideboard: %d cards recognized, %d unknown cards", cardsOk[1], cardsUnknown[1]));
|
||||
this.cmdAccept.setEnabled(cardsOk[0] > 0);
|
||||
}
|
||||
|
||||
private Deck buildDeck() {
|
||||
final Deck result = new Deck();
|
||||
boolean isMain = true;
|
||||
for (final DeckRecognizer.Token t : this.tokens) {
|
||||
final DeckRecognizer.TokenType type = t.getType();
|
||||
if ((type == DeckRecognizer.TokenType.SectionName) && t.getText().toLowerCase().contains("side")) {
|
||||
isMain = false;
|
||||
}
|
||||
if (type != DeckRecognizer.TokenType.KnownCard) {
|
||||
continue;
|
||||
}
|
||||
final PaperCard crd = t.getCard();
|
||||
if (isMain) {
|
||||
result.getMain().add(crd, t.getNumber());
|
||||
}
|
||||
else {
|
||||
result.getOrCreate(DeckSection.Sideboard).add(crd, t.getNumber());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void parseAndDisplay() {
|
||||
readInput();
|
||||
displayTokens();
|
||||
updateSummaries();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Class OnChangeTextUpdate.
|
||||
*/
|
||||
protected class OnChangeTextUpdate implements DocumentListener {
|
||||
private void onChange() {
|
||||
DeckImport.this.parseAndDisplay();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* javax.swing.event.DocumentListener#insertUpdate(javax.swing.event
|
||||
* .DocumentEvent)
|
||||
*/
|
||||
@Override
|
||||
public final void insertUpdate(final DocumentEvent e) {
|
||||
this.onChange();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* javax.swing.event.DocumentListener#removeUpdate(javax.swing.event
|
||||
* .DocumentEvent)
|
||||
*/
|
||||
@Override
|
||||
public final void removeUpdate(final DocumentEvent e) {
|
||||
this.onChange();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* javax.swing.event.DocumentListener#changedUpdate(javax.swing.event
|
||||
* .DocumentEvent)
|
||||
*/
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
} // Happend only on ENTER pressed
|
||||
}
|
||||
|
||||
private String makeHtmlViewOfToken(final DeckRecognizer.Token token) {
|
||||
switch (token.getType()) {
|
||||
case KnownCard:
|
||||
return String.format("<div class='knowncard'>%s * %s [%s] %s</div>", token.getNumber(), token.getCard()
|
||||
.getName(), token.getCard().getEdition(), token.getCard().isFoil() ? "<i>foil</i>" : "");
|
||||
case UnknownCard:
|
||||
return String.format("<div class='unknowncard'>%s * %s</div>", token.getNumber(), token.getText());
|
||||
case SectionName:
|
||||
return String.format("<div class='section'>%s</div>", token.getText());
|
||||
case UnknownText:
|
||||
case Comment:
|
||||
return String.format("<div class='comment'>%s</div>", token.getText());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package forge.gui.deckeditor;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.gui.deckeditor.controllers.CAllDecks;
|
||||
import forge.gui.deckeditor.controllers.DeckController;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles editor preferences saving and loading.
|
||||
*
|
||||
* <br><br><i>(S at beginning of class name denotes a static factory.)</i>
|
||||
*/
|
||||
public class SEditorIO {
|
||||
/**
|
||||
* Saves the current deck, with various prompts depending on the
|
||||
* current save environment.
|
||||
*
|
||||
* @return boolean, true if success
|
||||
*/
|
||||
public static boolean saveDeck() {
|
||||
final DeckController<?> controller = CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController();
|
||||
final String name = VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().getText();
|
||||
final String deckStr = DeckProxy.getDeckString(controller.getModelPath(), name);
|
||||
|
||||
// Warn if no name
|
||||
if (name == null || name.isEmpty()) {
|
||||
FOptionPane.showMessageDialog("Please name your deck using the 'Title' box.",
|
||||
"Save Error!", FOptionPane.ERROR_ICON);
|
||||
return false;
|
||||
}
|
||||
// Confirm if overwrite
|
||||
else if (controller.fileExists(name)) {
|
||||
boolean confirmResult = true;
|
||||
if (!StringUtils.equals(name, controller.getModelName())) { // prompt only if name was changed
|
||||
confirmResult = FOptionPane.showConfirmDialog(
|
||||
"There is already a deck named '" + name + "'. Overwrite?",
|
||||
"Overwrite Deck?");
|
||||
}
|
||||
|
||||
if (confirmResult) {
|
||||
controller.save();
|
||||
DeckProxy deck = VAllDecks.SINGLETON_INSTANCE.getLstDecks().stringToItem(deckStr);
|
||||
if (deck != null) { //reload DeckProxy to pull changes into deck list
|
||||
deck.reloadFromStorage();
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedItem(deck);
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Confirm if a new deck will be created
|
||||
else if (FOptionPane.showConfirmDialog("This will create a new deck named '" +
|
||||
name + "'. Continue?", "Create Deck?")) {
|
||||
controller.saveAs(name);
|
||||
CAllDecks.SINGLETON_INSTANCE.refresh(); //pull new deck into deck list and select it
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().setSelectedString(deckStr);
|
||||
}
|
||||
|
||||
if (Singletons.getControl().getCurrentScreen() == FScreen.DECK_EDITOR_CONSTRUCTED) {
|
||||
DeckPreferences.setCurrentDeck(deckStr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts to save changes if necessary.
|
||||
*
|
||||
* @return boolean, true if success
|
||||
*/
|
||||
public static boolean confirmSaveChanges(FScreen screen, boolean isClosing) {
|
||||
if (CDeckEditorUI.SINGLETON_INSTANCE.hasChanges()) {
|
||||
//ensure Deck Editor is active before showing dialog
|
||||
if (!Singletons.getControl().ensureScreenActive(screen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int choice = FOptionPane.showOptionDialog("Save changes to current deck?", "Save Changes?",
|
||||
FOptionPane.QUESTION_ICON, new String[] {"Save", "Don't Save", "Cancel"});
|
||||
|
||||
if (choice == -1 || choice == 2) { return false; }
|
||||
|
||||
if (choice == 0 && !saveDeck()) { return false; }
|
||||
|
||||
//reload deck if user chose not to save changes and deck isn't being closed
|
||||
if (!isClosing) {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController().reload();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package forge.gui.deckeditor;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.gui.deckeditor.views.VCardCatalog;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.IVTopLevelUI;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Top level view class; instantiates and assembles
|
||||
* tabs used in deck editor UI drag layout.<br>
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum VDeckEditorUI implements IVTopLevelUI {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#instantiate()
|
||||
*/
|
||||
@Override
|
||||
public void instantiate() {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VCardCatalog.SINGLETON_INSTANCE.getItemManager().focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#onSwitching(forge.gui.framework.FScreen)
|
||||
*/
|
||||
@Override
|
||||
public boolean onSwitching(FScreen fromScreen, FScreen toScreen) {
|
||||
//ensure deck saved before switching
|
||||
return CDeckEditorUI.SINGLETON_INSTANCE.canSwitchAway(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVTopLevelUI#onClosing()
|
||||
*/
|
||||
@Override
|
||||
public boolean onClosing(FScreen screen) {
|
||||
if (screen == FScreen.DECK_EDITOR_CONSTRUCTED) {
|
||||
//don't close tab if Constructed editor, but return to home screen if this called
|
||||
Singletons.getControl().setCurrentScreen(FScreen.HOME_SCREEN);
|
||||
return false;
|
||||
}
|
||||
return CDeckEditorUI.SINGLETON_INSTANCE.canSwitchAway(true);
|
||||
}
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.menus.CDeckEditorUIMenus;
|
||||
import forge.gui.framework.*;
|
||||
import forge.gui.menus.IMenuProvider;
|
||||
import forge.gui.toolbox.ContextMenuBuilder;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.ItemPool;
|
||||
import forge.view.FView;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Maintains a generically typed architecture for various editing
|
||||
* environments. A basic editor instance requires a card catalog, the
|
||||
* current deck being edited, and optional filters on the catalog.
|
||||
* <br><br>
|
||||
* These requirements are collected in this class and manipulated
|
||||
* in subclasses for different environments. There are two generic
|
||||
* types for all card display and filter predicates.
|
||||
*
|
||||
* <br><br><i>(A at beginning of class name denotes an abstract class.)</i>
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @param <TItem> extends {@link forge.item.InventoryItem}
|
||||
* @param <TModel> extends {@link forge.deck.DeckBase}
|
||||
*/
|
||||
public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends DeckBase> implements IMenuProvider {
|
||||
public boolean listenersHooked;
|
||||
private final FScreen screen;
|
||||
private ItemManager<TItem> catalogManager;
|
||||
private ItemManager<TItem> deckManager;
|
||||
protected DeckSection sectionMode = DeckSection.Main;
|
||||
|
||||
// card transfer buttons
|
||||
private final FLabel btnAdd = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Add card")
|
||||
.tooltip("Add selected card to current deck (or double click the row or hit the spacebar)")
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_PLUS))
|
||||
.iconScaleAuto(false).hoverable().build();
|
||||
private final FLabel btnAdd4 = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Add 4 of card")
|
||||
.tooltip("Add up to 4 of selected card to current deck")
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_PLUS))
|
||||
.iconScaleAuto(false).hoverable().build();
|
||||
|
||||
private final FLabel btnRemove = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Remove card")
|
||||
.tooltip("Remove selected card from current deck (or double click the row or hit the spacebar)")
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_MINUS))
|
||||
.iconScaleAuto(false).hoverable().build();
|
||||
|
||||
private final FLabel btnRemove4 = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Remove 4 of card")
|
||||
.tooltip("Remove up to 4 of selected card to current deck")
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_MINUS))
|
||||
.iconScaleAuto(false).hoverable().build();
|
||||
|
||||
private final FLabel btnCycleSection = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Change Section")
|
||||
.tooltip("Toggle between editing the deck and the sideboard/planar/scheme/vanguard parts of this deck")
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_EDIT))
|
||||
.iconScaleAuto(false).hoverable().build();
|
||||
|
||||
protected ACEditorBase(FScreen screen0) {
|
||||
this.screen = screen0;
|
||||
}
|
||||
|
||||
public FScreen getScreen() {
|
||||
return this.screen;
|
||||
}
|
||||
|
||||
public DeckSection getSectionMode() {
|
||||
return this.sectionMode;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.menubar.IMenuProvider#getMenus()
|
||||
*/
|
||||
@Override
|
||||
public List<JMenu> getMenus() {
|
||||
if (this.getDeckController() == null) {
|
||||
return null;
|
||||
}
|
||||
return new CDeckEditorUIMenus().getMenus();
|
||||
}
|
||||
|
||||
public final void addItem(TItem item) {
|
||||
onAddItems(createPoolForItem(item, 1), false);
|
||||
}
|
||||
public final void addItem(TItem item, int qty) {
|
||||
onAddItems(createPoolForItem(item, qty), false);
|
||||
}
|
||||
public final void addItem(TItem item, int qty, boolean toAlternate) {
|
||||
onAddItems(createPoolForItem(item, qty), toAlternate);
|
||||
}
|
||||
|
||||
public final void removeItem(TItem item) {
|
||||
onRemoveItems(createPoolForItem(item, 1), false);
|
||||
}
|
||||
public final void removeItem(TItem item, int qty) {
|
||||
onRemoveItems(createPoolForItem(item, qty), false);
|
||||
}
|
||||
public final void removeItem(TItem item, int qty, boolean toAlternate) {
|
||||
onRemoveItems(createPoolForItem(item, qty), toAlternate);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ItemPool<TItem> createPoolForItem(final TItem item, final int qty) {
|
||||
if (item == null || qty <= 0) { return null; }
|
||||
|
||||
ItemPool<TItem> pool = new ItemPool<TItem>((Class<TItem>)item.getClass());
|
||||
pool.add(item, qty);
|
||||
return pool;
|
||||
}
|
||||
|
||||
public final void addItems(Iterable<Entry<TItem, Integer>> items, boolean toAlternate) {
|
||||
if (items == null || !items.iterator().hasNext()) { return; } //do nothing if no items
|
||||
onAddItems(items, toAlternate);
|
||||
}
|
||||
|
||||
public final void removeItems(Iterable<Entry<TItem, Integer>> items, boolean toAlternate) {
|
||||
if (items == null || !items.iterator().hasNext()) { return; } //do nothing if no items
|
||||
onRemoveItems(items, toAlternate);
|
||||
}
|
||||
|
||||
public enum CardLimit {
|
||||
Singleton,
|
||||
Default,
|
||||
None
|
||||
}
|
||||
|
||||
private static final List<String> limitExceptions = Arrays.asList(
|
||||
new String[]{"Relentless Rats", "Shadowborn Apostle"});
|
||||
|
||||
/**
|
||||
* @return pool of additions allowed to deck
|
||||
*/
|
||||
protected ItemPool<TItem> getAllowedAdditions(Iterable<Entry<TItem, Integer>> itemsToAdd) {
|
||||
ItemPool<TItem> additions = new ItemPool<TItem>(getCatalogManager().getGenericType());
|
||||
CardLimit limit = getCardLimit();
|
||||
DeckController<TModel> controller = getDeckController();
|
||||
Deck deck = controller != null && controller.getModel() instanceof Deck ? (Deck)controller.getModel() : null;
|
||||
|
||||
for (Entry<TItem, Integer> itemEntry : itemsToAdd) {
|
||||
TItem item = itemEntry.getKey();
|
||||
PaperCard card = item instanceof PaperCard ? (PaperCard)item : null;
|
||||
int qty = itemEntry.getValue();
|
||||
|
||||
int max;
|
||||
if (deck == null || card == null || card.getRules().getType().isBasic() ||
|
||||
limit == CardLimit.None || limitExceptions.contains(card.getName())) {
|
||||
max = Integer.MAX_VALUE;
|
||||
}
|
||||
else {
|
||||
max = (limit == CardLimit.Singleton ? 1 : Singletons.getModel().getPreferences().getPrefInt(FPref.DECK_DEFAULT_CARD_LIMIT));
|
||||
max -= deck.getMain().count(card);
|
||||
if (deck.has(DeckSection.Sideboard)) {
|
||||
max -= deck.get(DeckSection.Sideboard).count(card);
|
||||
}
|
||||
if (deck.has(DeckSection.Commander)) {
|
||||
max -= deck.get(DeckSection.Commander).count(card);
|
||||
}
|
||||
}
|
||||
if (qty > max) {
|
||||
qty = max;
|
||||
}
|
||||
if (qty > 0) {
|
||||
additions.add(item, qty);
|
||||
}
|
||||
}
|
||||
|
||||
return additions;
|
||||
}
|
||||
|
||||
protected abstract CardLimit getCardLimit();
|
||||
|
||||
/**
|
||||
* Operation to add selected items to current deck.
|
||||
*/
|
||||
protected abstract void onAddItems(Iterable<Entry<TItem, Integer>> items, boolean toAlternate);
|
||||
|
||||
/**
|
||||
* Operation to remove selected item from current deck.
|
||||
*/
|
||||
protected abstract void onRemoveItems(Iterable<Entry<TItem, Integer>> items, boolean toAlternate);
|
||||
|
||||
protected abstract void buildAddContextMenu(EditorContextMenuBuilder cmb);
|
||||
protected abstract void buildRemoveContextMenu(EditorContextMenuBuilder cmb);
|
||||
|
||||
/**
|
||||
* Resets the cards in the catalog table and current deck table.
|
||||
*/
|
||||
public abstract void resetTables();
|
||||
|
||||
/**
|
||||
* Gets controller responsible for the current deck being edited.
|
||||
*
|
||||
* @return {@link forge.gui.deckeditor.controllers.DeckController}
|
||||
*/
|
||||
public abstract DeckController<TModel> getDeckController();
|
||||
|
||||
/**
|
||||
* Called when switching away from or closing the editor wants to exit. Should confirm save options.
|
||||
*
|
||||
* @return boolean   true if safe to exit
|
||||
*/
|
||||
public abstract boolean canSwitchAway(boolean isClosing);
|
||||
|
||||
/**
|
||||
* Resets and initializes the current editor.
|
||||
*/
|
||||
public abstract void update();
|
||||
|
||||
/**
|
||||
* Reset UI changes made in update
|
||||
*/
|
||||
public abstract void resetUIChanges();
|
||||
|
||||
/**
|
||||
* Gets the ItemManager holding the cards in the current deck.
|
||||
*
|
||||
* @return {@link forge.gui.toolbox.itemmanager.ItemManager}
|
||||
*/
|
||||
public ItemManager<TItem> getDeckManager() {
|
||||
return this.deckManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ItemManager holding the cards in the current deck.
|
||||
*
|
||||
* @param itemManager   {@link forge.gui.toolbox.itemmanager.ItemManager}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public void setDeckManager(final ItemManager<TItem> itemManager) {
|
||||
this.deckManager = itemManager;
|
||||
|
||||
btnRemove.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1);
|
||||
}
|
||||
});
|
||||
btnRemove4.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 4);
|
||||
}
|
||||
});
|
||||
itemManager.getPnlButtons().add(btnRemove, "w 30%!, h 30px!, gapx 5");
|
||||
itemManager.getPnlButtons().add(btnRemove4, "w 30%!, h 30px!, gapx 5");
|
||||
itemManager.getPnlButtons().add(btnCycleSection, "w 30%!, h 30px!, gapx 5");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ItemManager holding the cards in the current catalog.
|
||||
*
|
||||
* @return {@link forge.gui.toolbox.itemmanager.ItemManager}
|
||||
*/
|
||||
public ItemManager<TItem> getCatalogManager() {
|
||||
return this.catalogManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ItemManager holding the cards in the current catalog.
|
||||
*
|
||||
* @param itemManager   {@link forge.gui.toolbox.itemmanager.ItemManager}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public void setCatalogManager(final ItemManager<TItem> itemManager) {
|
||||
this.catalogManager = itemManager;
|
||||
|
||||
btnAdd.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.addSelectedCards(false, 1);
|
||||
}
|
||||
});
|
||||
btnAdd4.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.addSelectedCards(false, 4);
|
||||
}
|
||||
});
|
||||
itemManager.getPnlButtons().add(btnAdd, "w 30%!, h 30px!, h 30px!, gapx 5");
|
||||
itemManager.getPnlButtons().add(btnAdd4, "w 30%!, h 30px!, h 30px!, gapx 5");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified tab and returns its parent for later re-adding
|
||||
*/
|
||||
protected DragCell removeTab (IVDoc<? extends ICDoc> tab) {
|
||||
final DragCell parent;
|
||||
if (tab.getParentCell() == null) {
|
||||
parent = null;
|
||||
} else {
|
||||
parent = tab.getParentCell();
|
||||
parent.removeDoc(tab);
|
||||
tab.setParentCell(null);
|
||||
|
||||
if (parent.getDocs().size() > 0) {
|
||||
// if specified tab was first child of its parent, the new first tab needs re-selecting.
|
||||
parent.setSelected(parent.getDocs().get(0));
|
||||
} else {
|
||||
// if the parent is now childless, fill in the resultant gap
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SRearrangingUtil.fillGap(parent);
|
||||
FView.SINGLETON_INSTANCE.removeDragCell(parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public FLabel getBtnAdd() { return btnAdd; }
|
||||
public FLabel getBtnAdd4() { return btnAdd4; }
|
||||
public FLabel getBtnRemove() { return btnRemove; }
|
||||
public FLabel getBtnRemove4() { return btnRemove4; }
|
||||
public FLabel getBtnCycleSection() { return btnCycleSection; }
|
||||
|
||||
public ContextMenuBuilder createContextMenuBuilder(boolean isAddContextMenu0) {
|
||||
return new EditorContextMenuBuilder(isAddContextMenu0);
|
||||
}
|
||||
|
||||
protected class EditorContextMenuBuilder implements ContextMenuBuilder {
|
||||
private final boolean isAddContextMenu;
|
||||
private JPopupMenu menu;
|
||||
|
||||
private EditorContextMenuBuilder(boolean isAddContextMenu0) {
|
||||
isAddContextMenu = isAddContextMenu0;
|
||||
}
|
||||
|
||||
private ItemManager<TItem> getItemManager() {
|
||||
return isAddContextMenu ? catalogManager : deckManager;
|
||||
}
|
||||
|
||||
private ItemManager<TItem> getNextItemManager() {
|
||||
return isAddContextMenu ? deckManager : catalogManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildContextMenu(JPopupMenu menu) {
|
||||
this.menu = menu; //cache menu while controller populates menu
|
||||
if (isAddContextMenu) {
|
||||
buildAddContextMenu(this);
|
||||
}
|
||||
else {
|
||||
buildRemoveContextMenu(this);
|
||||
}
|
||||
this.menu = null;
|
||||
|
||||
if (menu.getComponentCount() > 0) {
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
GuiUtils.addMenuItem(menu, "Jump to previous table",
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getNextItemManager().focus();
|
||||
}
|
||||
});
|
||||
GuiUtils.addMenuItem(menu, "Jump to next table",
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getNextItemManager().focus();
|
||||
}
|
||||
});
|
||||
GuiUtils.addMenuItem(menu, "Jump to text filter",
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getItemManager().focusSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addItem(String verb, String dest, final boolean toAlternate, final int qty, int shortcutModifiers) {
|
||||
String label = verb + " " + SItemManagerUtil.getItemDisplayString(getItemManager().getSelectedItems(), qty, false);
|
||||
if (dest != null && !dest.isEmpty()) {
|
||||
label += " " + dest;
|
||||
}
|
||||
GuiUtils.addMenuItem(menu, label,
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, shortcutModifiers), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Integer quantity = qty;
|
||||
if (quantity < 0) {
|
||||
quantity = GuiChoose.getInteger("Choose a value for X", 1, -quantity, 20);
|
||||
if (quantity == null) { return; }
|
||||
}
|
||||
if (isAddContextMenu) {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.addSelectedCards(toAlternate, quantity);
|
||||
}
|
||||
else {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(toAlternate, quantity);
|
||||
}
|
||||
}
|
||||
}, true, shortcutModifiers == 0);
|
||||
}
|
||||
|
||||
private int getMaxMoveQuantity() {
|
||||
ItemPool<TItem> selectedItemPool = getItemManager().getSelectedItemPool();
|
||||
if (isAddContextMenu) {
|
||||
selectedItemPool = getAllowedAdditions(selectedItemPool);
|
||||
}
|
||||
if (selectedItemPool.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int max = Integer.MAX_VALUE;
|
||||
for (Entry<TItem, Integer> itemEntry : selectedItemPool) {
|
||||
if (itemEntry.getValue() < max) {
|
||||
max = itemEntry.getValue();
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private void addItems(String verb, String dest, boolean toAlternate, int shortcutModifiers1, int shortcutModifiers2, int shortcutModifiers3) {
|
||||
int max = getMaxMoveQuantity();
|
||||
if (max == 0) { return; }
|
||||
|
||||
addItem(verb, dest, toAlternate, 1, shortcutModifiers1);
|
||||
if (max == 1) { return; }
|
||||
|
||||
int qty = Singletons.getModel().getPreferences().getPrefInt(FPref.DECK_DEFAULT_CARD_LIMIT);
|
||||
if (qty > max) {
|
||||
qty = max;
|
||||
}
|
||||
|
||||
addItem(verb, dest, toAlternate, qty, shortcutModifiers2);
|
||||
if (max == 2) { return; }
|
||||
|
||||
addItem(verb, dest, toAlternate, -max, shortcutModifiers3); //pass -max as quantity to indicate to prompt for specific quantity
|
||||
}
|
||||
|
||||
public void addMoveItems(String verb, String dest) {
|
||||
addItems(verb, dest, false, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.ALT_MASK);
|
||||
}
|
||||
|
||||
public void addMoveAlternateItems(String verb, String dest) {
|
||||
if (this.menu.getComponentCount() > 0) {
|
||||
this.menu.addSeparator();
|
||||
}
|
||||
//yes, CTRL_DOWN_MASK and not getMenuShortcutKeyMask(). On OSX, cmd-space is hard-coded to bring up Spotlight
|
||||
addItems(verb, dest, true, KeyEvent.CTRL_DOWN_MASK,
|
||||
//getMenuShortcutKeyMask() instead of CTRL_DOWN_MASK since on OSX, ctrl-shift-space brings up the window manager
|
||||
KeyEvent.SHIFT_DOWN_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(),
|
||||
KeyEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.gui.deckeditor.DeckProxy;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
|
||||
/**
|
||||
* Controls the "all decks" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CAllDecks implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().setPool(DeckProxy.getAllConstructedDecks(Singletons.getModel().getDecks().getConstructed()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
VAllDecks.SINGLETON_INSTANCE.getLstDecks().setup(ItemManagerConfig.CONSTRUCTED_DECKS);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.gui.framework.ICDoc;
|
||||
|
||||
/**
|
||||
* Controls the "card catalog" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CCardCatalog implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private CCardCatalog() {
|
||||
}
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.io.DeckHtmlSerializer;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.error.BugReporter;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.DeckImport;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.properties.NewConstants;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
import java.awt.Dialog.ModalityType;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Controls the "current deck" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CCurrentDeck implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private static File previousDirectory;
|
||||
|
||||
private JFileChooser fileChooser = new JFileChooser(NewConstants.DECK_BASE_DIR);
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
private CCurrentDeck() {
|
||||
FileFilter[] defaultFilters = fileChooser.getChoosableFileFilters();
|
||||
for(FileFilter defFilter : defaultFilters)
|
||||
{
|
||||
fileChooser.removeChoosableFileFilter(defFilter);
|
||||
}
|
||||
|
||||
FileFilter DCK_FILTER = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(final File f) {
|
||||
return f.getName().endsWith(DeckStorage.FILE_EXTENSION) || f.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Simple Deck File .dck";
|
||||
}
|
||||
};
|
||||
|
||||
fileChooser.addChoosableFileFilter(DCK_FILTER);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("serial")
|
||||
public void initialize() {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSave().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
SEditorIO.saveDeck();
|
||||
}
|
||||
});
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSaveAs().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
exportDeck();
|
||||
}
|
||||
});
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnPrintProxies().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
printProxies();
|
||||
}
|
||||
});
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnOpen().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
openDeck();
|
||||
}
|
||||
});
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnNew().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
newDeck();
|
||||
}
|
||||
});
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnImport().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
importDeck();
|
||||
}
|
||||
});
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (Character.isLetterOrDigit(e.getKeyChar())) {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController().notifyModelChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dialog for importing a deck from a different MTG software.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private <TItem extends InventoryItem, TModel extends DeckBase> void importDeck() {
|
||||
final ACEditorBase<TItem, TModel> ed = (ACEditorBase<TItem, TModel>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
final DeckImport dImport = new DeckImport(ed);
|
||||
dImport.setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
dImport.setVisible(true);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void newDeck() {
|
||||
if (!SEditorIO.confirmSaveChanges(Singletons.getControl().getCurrentScreen(), true)) { return; }
|
||||
|
||||
try {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((DeckController<DeckBase>) CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController()).newModel();
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("Error creating new deck. " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
@SuppressWarnings("unchecked")
|
||||
private void openDeck() {
|
||||
if (!SEditorIO.confirmSaveChanges(Singletons.getControl().getCurrentScreen(), true)) { return; }
|
||||
|
||||
final File file = this.getImportFilename();
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
((DeckController<DeckBase>) CDeckEditorUI.SINGLETON_INSTANCE
|
||||
.getCurrentEditorController().getDeckController())
|
||||
.setModel(DeckSerializer.fromFile(file));
|
||||
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("Error importing deck." + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
private File getImportFilename() {
|
||||
fileChooser.setDialogTitle("Import Deck");
|
||||
fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
|
||||
if (previousDirectory != null) {
|
||||
fileChooser.setCurrentDirectory(previousDirectory);
|
||||
}
|
||||
|
||||
final int returnVal = fileChooser.showOpenDialog(null);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
final File file = fileChooser.getSelectedFile();
|
||||
previousDirectory = file.getParentFile();
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** */
|
||||
@SuppressWarnings("unchecked")
|
||||
private void exportDeck() {
|
||||
DeckController<Deck> controller = (DeckController<Deck>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController();
|
||||
|
||||
final File filename = this.getExportFilename();
|
||||
if (filename == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//create copy of deck to save under new name
|
||||
String name = filename.getName();
|
||||
name = name.substring(0, name.lastIndexOf(".")); //remove extension
|
||||
Deck deck = (Deck)controller.getModel().copyTo(name);
|
||||
|
||||
try {
|
||||
DeckSerializer.writeDeck(deck, filename);
|
||||
controller.setModel(DeckSerializer.fromFile(filename)); //reload deck from file so everything is in sync
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("Error exporting deck." + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
@SuppressWarnings("unchecked")
|
||||
private void printProxies() {
|
||||
final File filename = this.getPrintProxiesFilename();
|
||||
if (filename == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DeckHtmlSerializer.writeDeckHtml(
|
||||
((DeckController<Deck>) CDeckEditorUI.SINGLETON_INSTANCE
|
||||
.getCurrentEditorController().getDeckController()).getModel(), filename);
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("Error exporting deck." + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private File getExportFilename() {
|
||||
fileChooser.setDialogTitle("Export Deck");
|
||||
fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
|
||||
if (previousDirectory != null) {
|
||||
fileChooser.setCurrentDirectory(previousDirectory);
|
||||
}
|
||||
|
||||
if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
final File file = fileChooser.getSelectedFile();
|
||||
final String check = file.getAbsolutePath();
|
||||
|
||||
previousDirectory = file.getParentFile();
|
||||
|
||||
return check.endsWith(".dck") ? file : new File(check + ".dck");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getPrintProxiesFilename() {
|
||||
final JFileChooser save = new JFileChooser(previousDirectory);
|
||||
save.setDialogTitle("Print Proxies");
|
||||
save.setDialogType(JFileChooser.SAVE_DIALOG);
|
||||
save.setFileFilter(HTML_FILTER);
|
||||
|
||||
if (save.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
final File file = save.getSelectedFile();
|
||||
final String check = file.getAbsolutePath();
|
||||
|
||||
previousDirectory = file.getParentFile();
|
||||
|
||||
return check.endsWith(".html") ? file : new File(check + ".html");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The Constant HTML_FILTER. */
|
||||
public static final FileFilter HTML_FILTER = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(final File f) {
|
||||
return f.getName().endsWith(".html") || f.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Proxy File .html";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.MagicColor;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.generation.*;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
|
||||
/**
|
||||
* Controls the "analysis" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CDeckgen implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Override
|
||||
public void initialize() {
|
||||
VDeckgen.SINGLETON_INSTANCE.getBtnRandCardpool().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
newRandomConstructed();
|
||||
}
|
||||
});
|
||||
VDeckgen.SINGLETON_INSTANCE.getBtnRandDeck2().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
newGenerateConstructed(2);
|
||||
}
|
||||
});
|
||||
VDeckgen.SINGLETON_INSTANCE.getBtnRandDeck3().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
newGenerateConstructed(3);
|
||||
}
|
||||
});
|
||||
VDeckgen.SINGLETON_INSTANCE.getBtnRandDeck5().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
newGenerateConstructed(5);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
|
||||
//========== Other methods
|
||||
@SuppressWarnings("unchecked")
|
||||
private <TItem extends InventoryItem, TModel extends DeckBase> void newRandomConstructed() {
|
||||
if (!SEditorIO.confirmSaveChanges(Singletons.getControl().getCurrentScreen(), true)) { return; }
|
||||
|
||||
final Deck randomDeck = new Deck();
|
||||
|
||||
Predicate<PaperCard> notBasicLand = Predicates.not(Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
|
||||
Iterable<PaperCard> source = Iterables.filter(Singletons.getMagicDb().getCommonCards().getUniqueCards(), notBasicLand);
|
||||
randomDeck.getMain().addAllFlat(Aggregates.random(source, 15 * 5));
|
||||
|
||||
for(String landName : MagicColor.Constant.BASIC_LANDS) {
|
||||
randomDeck.getMain().add(landName, 1);
|
||||
}
|
||||
randomDeck.getMain().add("Terramorphic Expanse", 1);
|
||||
|
||||
final ACEditorBase<TItem, TModel> ed = (ACEditorBase<TItem, TModel>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
ed.getDeckController().setModel((TModel) randomDeck);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <TItem extends InventoryItem, TModel extends DeckBase> void newGenerateConstructed(final int colorCount0) {
|
||||
if (!SEditorIO.confirmSaveChanges(Singletons.getControl().getCurrentScreen(), true)) { return; }
|
||||
|
||||
final Deck genConstructed = new Deck();
|
||||
CardDb cardDb = Singletons.getMagicDb().getCommonCards();
|
||||
DeckGeneratorBase gen = null;
|
||||
switch (colorCount0) {
|
||||
case 1: gen = new DeckGeneratorMonoColor(cardDb, null); break;
|
||||
case 2: gen = new DeckGenerator2Color(cardDb, null, null); break;
|
||||
case 3: gen = new DeckGenerator3Color(cardDb, null, null, null); break;
|
||||
case 5: gen = new DeckGenerator5Color(cardDb); break;
|
||||
}
|
||||
|
||||
if( null != gen ) {
|
||||
gen.setSingleton(Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||
gen.setUseArtifacts(!Singletons.getModel().getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||
genConstructed.getMain().addAll(gen.getDeck(60, false));
|
||||
}
|
||||
|
||||
final ACEditorBase<TItem, TModel> ed = (ACEditorBase<TItem, TModel>) CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
ed.getDeckController().setModel((TModel) genConstructed);
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: CEditorCommander.java 18430 2012-11-27 22:42:36Z Hellfish $
|
||||
*/
|
||||
public final class CEditorCommander extends ACEditorBase<PaperCard, Deck> {
|
||||
private final DeckController<Deck> controller;
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
|
||||
private List<DeckSection> allSections = new ArrayList<DeckSection>();
|
||||
private final ItemPool<PaperCard> commanderPool;
|
||||
private final ItemPool<PaperCard> normalPool;
|
||||
|
||||
//=========== Constructor
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*/
|
||||
public CEditorCommander() {
|
||||
super(FScreen.DECK_EDITOR_COMMANDER);
|
||||
allSections.add(DeckSection.Main);
|
||||
allSections.add(DeckSection.Sideboard);
|
||||
allSections.add(DeckSection.Commander);
|
||||
|
||||
commanderPool = ItemPool.createFrom(Singletons.getMagicDb().getCommonCards().getAllCards(Predicates.compose(Predicates.and(CardRulesPredicates.Presets.IS_CREATURE, CardRulesPredicates.Presets.IS_LEGENDARY), PaperCard.FN_GET_RULES)),PaperCard.class);
|
||||
normalPool = ItemPool.createFrom(Singletons.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);
|
||||
|
||||
CardManager catalogManager = new CardManager(true);
|
||||
CardManager deckManager = new CardManager(true);
|
||||
|
||||
catalogManager.setCaption("Catalog");
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
|
||||
final Supplier<Deck> newCreator = new Supplier<Deck>() {
|
||||
@Override
|
||||
public Deck get() {
|
||||
return new Deck();
|
||||
}
|
||||
};
|
||||
this.controller = new DeckController<Deck>(Singletons.getModel().getDecks().getCommander(), this, newCreator);
|
||||
}
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
return CardLimit.Singleton;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
CEditorConstructed.onAddItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
CEditorConstructed.onRemoveItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildAddContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
CEditorConstructed.buildAddContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildRemoveContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
CEditorConstructed.buildRemoveContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#updateView()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
this.sectionMode = DeckSection.Main;
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Main));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<Deck> getDeckController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getDeckManager().setup(ItemManagerConfig.DECK_EDITOR);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
this.getBtnRemove4().setVisible(false);
|
||||
this.getBtnAdd4().setVisible(false);
|
||||
this.getBtnCycleSection().setVisible(true);
|
||||
this.getBtnCycleSection().setCommand(new UiCommand() {
|
||||
private static final long serialVersionUID = -9082606944024479599L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
cycleEditorMode();
|
||||
}
|
||||
});
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
|
||||
this.controller.refreshModel();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
return SEditorIO.confirmSaveChanges(FScreen.DECK_EDITOR_COMMANDER, isClosing);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between the main deck and the sideboard editor.
|
||||
*/
|
||||
public void cycleEditorMode() {
|
||||
int curindex = allSections.indexOf(sectionMode);
|
||||
curindex = (curindex + 1) % allSections.size();
|
||||
sectionMode = allSections.get(curindex);
|
||||
|
||||
switch(sectionMode) {
|
||||
case Main:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
break;
|
||||
case Sideboard:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard));
|
||||
break;
|
||||
case Commander:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.COMMANDER_POOL);
|
||||
this.getCatalogManager().setPool(commanderPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Commander));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.controller.updateCaptions();
|
||||
}
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class CEditorConstructed extends ACEditorBase<PaperCard, Deck> {
|
||||
private final DeckController<Deck> controller;
|
||||
private final List<DeckSection> allSections = new ArrayList<DeckSection>();
|
||||
private final ItemPool<PaperCard> normalPool, avatarPool, planePool, schemePool;
|
||||
|
||||
//=========== Constructor
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*/
|
||||
public CEditorConstructed() {
|
||||
super(FScreen.DECK_EDITOR_CONSTRUCTED);
|
||||
|
||||
allSections.add(DeckSection.Main);
|
||||
allSections.add(DeckSection.Sideboard);
|
||||
allSections.add(DeckSection.Avatar);
|
||||
allSections.add(DeckSection.Schemes);
|
||||
allSections.add(DeckSection.Planes);
|
||||
|
||||
normalPool = ItemPool.createFrom(Singletons.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);
|
||||
avatarPool = ItemPool.createFrom(Singletons.getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_VANGUARD, PaperCard.FN_GET_RULES)),PaperCard.class);
|
||||
planePool = ItemPool.createFrom(Singletons.getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_PLANE_OR_PHENOMENON, PaperCard.FN_GET_RULES)),PaperCard.class);
|
||||
schemePool = ItemPool.createFrom(Singletons.getMagicDb().getVariantCards().getAllCards(Predicates.compose(CardRulesPredicates.Presets.IS_SCHEME, PaperCard.FN_GET_RULES)),PaperCard.class);
|
||||
|
||||
CardManager catalogManager = new CardManager(false); // TODO: restore the functionality of the "want uniques only" toggle
|
||||
CardManager deckManager = new CardManager(false); // IMPORTANT: must *always* show all cards in the deck, otherwise cards with different art get ignored!
|
||||
|
||||
catalogManager.setCaption("Catalog");
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
|
||||
final Supplier<Deck> newCreator = new Supplier<Deck>() {
|
||||
@Override
|
||||
public Deck get() {
|
||||
return new Deck();
|
||||
}
|
||||
};
|
||||
|
||||
this.controller = new DeckController<Deck>(Singletons.getModel().getDecks().getConstructed(), this, newCreator);
|
||||
}
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
if (sectionMode == DeckSection.Avatar) {
|
||||
return CardLimit.Singleton;
|
||||
}
|
||||
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
return CardLimit.Default;
|
||||
}
|
||||
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
||||
}
|
||||
|
||||
public static void onAddItems(ACEditorBase<PaperCard, Deck> editor, Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
DeckSection sectionMode = editor.sectionMode;
|
||||
DeckController<Deck> controller = editor.getDeckController();
|
||||
|
||||
if (sectionMode == DeckSection.Commander || sectionMode == DeckSection.Avatar) {
|
||||
editor.getDeckManager().removeAllItems();
|
||||
}
|
||||
|
||||
ItemPool<PaperCard> itemsToAdd = editor.getAllowedAdditions(items);
|
||||
if (itemsToAdd.isEmpty()) { return; }
|
||||
|
||||
if (toAlternate) {
|
||||
switch (sectionMode) {
|
||||
case Main:
|
||||
controller.getModel().getOrCreate(DeckSection.Sideboard).addAll(itemsToAdd);
|
||||
break;
|
||||
default:
|
||||
return; //no other sections should support toAlternate
|
||||
}
|
||||
}
|
||||
else {
|
||||
editor.getDeckManager().addItems(itemsToAdd);
|
||||
}
|
||||
|
||||
if (editor.getCatalogManager().isInfinite()) {
|
||||
//select all added cards in Catalog if infinite
|
||||
editor.getCatalogManager().selectItemEntrys(itemsToAdd);
|
||||
}
|
||||
else {
|
||||
//remove all added cards from Catalog if not infinite
|
||||
editor.getCatalogManager().removeItems(items);
|
||||
}
|
||||
|
||||
controller.notifyModelChanged();
|
||||
}
|
||||
|
||||
public static void onRemoveItems(ACEditorBase<PaperCard, Deck> editor, Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
DeckSection sectionMode = editor.sectionMode;
|
||||
DeckController<Deck> controller = editor.getDeckController();
|
||||
|
||||
if (toAlternate) {
|
||||
switch (sectionMode) {
|
||||
case Main:
|
||||
controller.getModel().getOrCreate(DeckSection.Sideboard).addAll(items);
|
||||
break;
|
||||
case Sideboard:
|
||||
controller.getModel().get(DeckSection.Main).addAll(items);
|
||||
break;
|
||||
default:
|
||||
break; //no other sections should support toAlternate
|
||||
}
|
||||
}
|
||||
else {
|
||||
editor.getCatalogManager().addItems(items);
|
||||
}
|
||||
editor.getDeckManager().removeItems(items);
|
||||
|
||||
controller.notifyModelChanged();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
onAddItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
onRemoveItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
public static void buildAddContextMenu(EditorContextMenuBuilder cmb, DeckSection sectionMode) {
|
||||
switch (sectionMode) {
|
||||
case Main:
|
||||
cmb.addMoveItems("Add", "to deck");
|
||||
cmb.addMoveAlternateItems("Add", "to sideboard");
|
||||
break;
|
||||
case Sideboard:
|
||||
cmb.addMoveItems("Add", "to sideboard");
|
||||
break;
|
||||
case Commander:
|
||||
cmb.addMoveItems("Set", "as commander");
|
||||
break;
|
||||
case Avatar:
|
||||
cmb.addMoveItems("Set", "as avatar");
|
||||
break;
|
||||
case Schemes:
|
||||
cmb.addMoveItems("Add", "to scheme deck");
|
||||
break;
|
||||
case Planes:
|
||||
cmb.addMoveItems("Add", "to planar deck");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void buildRemoveContextMenu(EditorContextMenuBuilder cmb, DeckSection sectionMode) {
|
||||
switch (sectionMode) {
|
||||
case Main:
|
||||
cmb.addMoveItems("Remove", "from deck");
|
||||
cmb.addMoveAlternateItems("Move", "to sideboard");
|
||||
break;
|
||||
case Sideboard:
|
||||
cmb.addMoveItems("Remove", "from sideboard");
|
||||
cmb.addMoveAlternateItems("Move", "to deck");
|
||||
break;
|
||||
case Commander:
|
||||
cmb.addMoveItems("Remove", "as commander");
|
||||
break;
|
||||
case Avatar:
|
||||
cmb.addMoveItems("Remove", "as avatar");
|
||||
break;
|
||||
case Schemes:
|
||||
cmb.addMoveItems("Remove", "from scheme deck");
|
||||
break;
|
||||
case Planes:
|
||||
cmb.addMoveItems("Remove", "from planar deck");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildAddContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
buildAddContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildRemoveContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
buildRemoveContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#resetTables()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
// Constructed mode can use all cards, no limitations.
|
||||
this.sectionMode = DeckSection.Main;
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<Deck> getDeckController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between the main deck and the sideboard editor.
|
||||
*/
|
||||
public void cycleEditorMode() {
|
||||
int curindex = allSections.indexOf(sectionMode);
|
||||
curindex = (curindex + 1) % allSections.size();
|
||||
sectionMode = allSections.get(curindex);
|
||||
|
||||
switch(sectionMode) {
|
||||
case Main:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
break;
|
||||
case Sideboard:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getCatalogManager().setPool(normalPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard));
|
||||
break;
|
||||
case Avatar:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.AVATAR_POOL);
|
||||
this.getCatalogManager().setPool(avatarPool, true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Avatar));
|
||||
break;
|
||||
case Planes:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.PLANAR_POOL);
|
||||
this.getCatalogManager().setPool(planePool,true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Planes));
|
||||
break;
|
||||
case Schemes:
|
||||
this.getCatalogManager().setup(ItemManagerConfig.SCHEME_POOL);
|
||||
this.getCatalogManager().setPool(schemePool,true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Schemes));
|
||||
break;
|
||||
case Commander:
|
||||
break; //do nothing for Commander here
|
||||
}
|
||||
|
||||
this.controller.updateCaptions();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Override
|
||||
public void update() {
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getDeckManager().setup(ItemManagerConfig.DECK_EDITOR);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
this.getBtnCycleSection().setVisible(true);
|
||||
this.getBtnCycleSection().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
cycleEditorMode();
|
||||
}
|
||||
});
|
||||
|
||||
this.controller.refreshModel();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
return SEditorIO.confirmSaveChanges(FScreen.DECK_EDITOR_CONSTRUCTED, false); //ignore isClosing since screen can't close
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.card.MagicColor;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.home.sanctioned.CSubmenuDraft;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.item.PaperCard;
|
||||
import forge.limited.BoosterDraft;
|
||||
import forge.limited.IBoosterDraft;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.ItemPool;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Updates the deck editor UI as necessary draft selection mode.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CEditorDraftingProcess extends ACEditorBase<PaperCard, DeckGroup> {
|
||||
private IBoosterDraft boosterDraft;
|
||||
|
||||
private String ccAddLabel = "Add card";
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
private boolean saved = false;
|
||||
|
||||
//========== Constructor
|
||||
|
||||
/**
|
||||
* Updates the deck editor UI as necessary draft selection mode.
|
||||
*/
|
||||
public CEditorDraftingProcess() {
|
||||
super(FScreen.DRAFTING_PROCESS);
|
||||
|
||||
final CardManager catalogManager = new CardManager(false);
|
||||
final CardManager deckManager = new CardManager(false);
|
||||
|
||||
//hide filters and options panel so more of pack is visible by default
|
||||
catalogManager.setHideViewOptions(1, true);
|
||||
|
||||
deckManager.setCaption("Draft Picks");
|
||||
|
||||
catalogManager.setAlwaysNonUnique(true);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show gui.
|
||||
*
|
||||
* @param inBoosterDraft
|
||||
* the in_booster draft
|
||||
*/
|
||||
public final void showGui(final IBoosterDraft inBoosterDraft) {
|
||||
this.boosterDraft = inBoosterDraft;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
if (toAlternate) { return; }
|
||||
|
||||
// can only draft one at a time, regardless of the requested quantity
|
||||
PaperCard card = items.iterator().next().getKey();
|
||||
this.getDeckManager().addItem(card, 1);
|
||||
|
||||
// get next booster pack
|
||||
this.boosterDraft.setChoice(card);
|
||||
|
||||
if (this.boosterDraft.hasNextChoice()) {
|
||||
this.showChoices(this.boosterDraft.nextChoice());
|
||||
}
|
||||
else {
|
||||
this.boosterDraft.finishedDrafting();
|
||||
this.saveDraft();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
cmb.addMoveItems("Draft", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
// no valid remove options
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* showChoices.
|
||||
* </p>
|
||||
*
|
||||
* @param list
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
private void showChoices(final ItemPool<PaperCard> list) {
|
||||
int packNumber = ((BoosterDraft) boosterDraft).getCurrentBoosterIndex() + 1;
|
||||
|
||||
this.getCatalogManager().setCaption("Pack " + packNumber + " - Cards");
|
||||
this.getCatalogManager().setPool(list);
|
||||
} // showChoices()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getPlayersDeck.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.deck.Deck} object.
|
||||
*/
|
||||
private Deck getPlayersDeck() {
|
||||
final Deck deck = new Deck();
|
||||
|
||||
// add sideboard to deck
|
||||
deck.getOrCreate(DeckSection.Sideboard).addAll(this.getDeckManager().getPool());
|
||||
|
||||
final String landSet = IBoosterDraft.LAND_SET_CODE[0].getCode();
|
||||
final boolean isZendikarSet = landSet.equals("ZEN"); // we want to generate one kind of Zendikar lands at a time only
|
||||
final boolean zendikarSetMode = MyRandom.getRandom().nextBoolean();
|
||||
|
||||
final int landsCount = 10;
|
||||
|
||||
for(String landName : MagicColor.Constant.BASIC_LANDS) {
|
||||
int numArt = Singletons.getMagicDb().getCommonCards().getArtCount(landName, landSet);
|
||||
int minArtIndex = isZendikarSet ? (zendikarSetMode ? 1 : 5) : 1;
|
||||
int maxArtIndex = isZendikarSet ? minArtIndex + 3 : numArt;
|
||||
|
||||
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_ART_IN_POOLS)) {
|
||||
|
||||
for (int i = minArtIndex; i <= maxArtIndex; i++) {
|
||||
deck.get(DeckSection.Sideboard).add(landName, landSet, i, numArt > 1 ? landsCount : 30);
|
||||
}
|
||||
} else {
|
||||
deck.get(DeckSection.Sideboard).add(landName, landSet, 30);
|
||||
}
|
||||
}
|
||||
|
||||
return deck;
|
||||
} // getPlayersDeck()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* saveDraft.
|
||||
* </p>
|
||||
*/
|
||||
private void saveDraft() {
|
||||
String s = FOptionPane.showInputDialog("Save this draft as:", "Save Draft", FOptionPane.QUESTION_ICON);
|
||||
|
||||
// Cancel button will be null; OK will return string.
|
||||
// Must check for null value first, then string length.
|
||||
// Recurse, if either null or empty string.
|
||||
if (s == null || s.length() == 0) {
|
||||
saveDraft();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for overwrite case
|
||||
for (DeckGroup d : Singletons.getModel().getDecks().getDraft()) {
|
||||
if (s.equalsIgnoreCase(d.getName())) {
|
||||
if (!FOptionPane.showConfirmDialog(
|
||||
"There is already a deck named '" + s + "'. Overwrite?",
|
||||
"Overwrite Deck?", false)) {
|
||||
// If no overwrite, recurse.
|
||||
saveDraft();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
saved = true;
|
||||
|
||||
// Construct computer's decks and save draft
|
||||
final Deck[] computer = this.boosterDraft.getDecks();
|
||||
|
||||
final DeckGroup finishedDraft = new DeckGroup(s);
|
||||
finishedDraft.setHumanDeck((Deck) this.getPlayersDeck().copyTo(s));
|
||||
finishedDraft.addAiDecks(computer);
|
||||
|
||||
Singletons.getModel().getDecks().getDraft().add(finishedDraft);
|
||||
CSubmenuDraft.SINGLETON_INSTANCE.update();
|
||||
FScreen.DRAFTING_PROCESS.close();
|
||||
|
||||
//open draft pool in Draft Deck Editor right away
|
||||
Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_DRAFT);
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.setEditorController(new CEditorLimited(Singletons.getModel().getDecks().getDraft(), FScreen.DECK_EDITOR_DRAFT));
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckController().load(null, s);
|
||||
}
|
||||
|
||||
//========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
return CardLimit.None;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<DeckGroup> getDeckController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#updateView()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.getCatalogManager().setup(ItemManagerConfig.DRAFT_PACK);
|
||||
this.getDeckManager().setup(ItemManagerConfig.DRAFT_POOL);
|
||||
|
||||
ccAddLabel = this.getBtnAdd().getText();
|
||||
|
||||
if (this.getDeckManager().getPool() == null) { //avoid showing next choice or resetting pool if just switching back to Draft screen
|
||||
this.showChoices(this.boosterDraft.nextChoice());
|
||||
this.getDeckManager().setPool((Iterable<PaperCard>) null);
|
||||
}
|
||||
else {
|
||||
this.showChoices(this.getCatalogManager().getPool());
|
||||
}
|
||||
|
||||
//Remove buttons
|
||||
this.getBtnAdd().setVisible(false);
|
||||
this.getBtnAdd4().setVisible(false);
|
||||
this.getBtnRemove().setVisible(false);
|
||||
this.getBtnRemove4().setVisible(false);
|
||||
|
||||
this.getBtnCycleSection().setVisible(false);
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getPnlHeader().setVisible(false);
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
|
||||
// set catalog table to single-selection only mode
|
||||
getCatalogManager().setAllowMultipleSelections(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
if (isClosing && !saved) {
|
||||
String userPrompt =
|
||||
"This will end the current draft and you will not be able to resume.\n\n" +
|
||||
"Leave anyway?";
|
||||
return FOptionPane.showConfirmDialog(userPrompt, "Leave Draft?", "Leave", "Cancel", false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
//Re-rename buttons
|
||||
this.getBtnAdd().setText(ccAddLabel);
|
||||
|
||||
//Re-add buttons
|
||||
this.getBtnAdd4().setVisible(true);
|
||||
this.getBtnRemove().setVisible(true);
|
||||
this.getBtnRemove4().setVisible(true);
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getPnlHeader().setVisible(true);
|
||||
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
|
||||
// set catalog table back to free-selection mode
|
||||
getCatalogManager().setAllowMultipleSelections(true);
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.home.sanctioned.CSubmenuDraft;
|
||||
import forge.gui.home.sanctioned.CSubmenuSealed;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Child controller for limited deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: DeckEditorCommon.java 12850 2011-12-26 14:55:09Z slapshot5 $
|
||||
*/
|
||||
public final class CEditorLimited extends ACEditorBase<PaperCard, DeckGroup> {
|
||||
|
||||
private final DeckController<DeckGroup> controller;
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
|
||||
//========== Constructor
|
||||
|
||||
/**
|
||||
* Child controller for limited deck editor UI.
|
||||
*
|
||||
* @param deckMap0   {@link forge.deck.DeckGroup}<{@link forge.util.storage.IStorage}>
|
||||
*/
|
||||
public CEditorLimited(final IStorage<DeckGroup> deckMap0, FScreen screen0) {
|
||||
super(screen0);
|
||||
|
||||
final CardManager catalogManager = new CardManager(false);
|
||||
final CardManager deckManager = new CardManager(false);
|
||||
|
||||
catalogManager.setCaption("Sideboard");
|
||||
|
||||
catalogManager.setAlwaysNonUnique(true);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
|
||||
final Supplier<DeckGroup> newCreator = new Supplier<DeckGroup>() {
|
||||
@Override
|
||||
public DeckGroup get() {
|
||||
return new DeckGroup("");
|
||||
}
|
||||
};
|
||||
this.controller = new DeckController<DeckGroup>(deckMap0, this, newCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
private Deck getSelectedDeck(final DeckGroup model) {
|
||||
return model.getHumanDeck();
|
||||
}
|
||||
|
||||
//========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
return CardLimit.None;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
if (toAlternate) { return; }
|
||||
|
||||
// update view
|
||||
this.getDeckManager().addItems(items);
|
||||
this.getCatalogManager().removeItems(items);
|
||||
this.getDeckController().notifyModelChanged();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
if (toAlternate) { return; }
|
||||
|
||||
// update view
|
||||
this.getCatalogManager().addItems(items);
|
||||
this.getDeckManager().removeItems(items);
|
||||
this.getDeckController().notifyModelChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
cmb.addMoveItems("Move", "to deck");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
cmb.addMoveItems("Move", "to sideboard");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#updateView()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
final Deck toEdit = this.getSelectedDeck(this.controller.getModel());
|
||||
this.getCatalogManager().setPool(toEdit.getOrCreate(DeckSection.Sideboard));
|
||||
this.getDeckManager().setPool(toEdit.getMain());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<DeckGroup> getDeckController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.getCatalogManager().setup(getScreen() == FScreen.DECK_EDITOR_DRAFT ? ItemManagerConfig.DRAFT_POOL : ItemManagerConfig.SEALED_POOL);
|
||||
this.getDeckManager().setup(ItemManagerConfig.DECK_EDITOR);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnPrintProxies().setVisible(false);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSaveAs().setVisible(false);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnNew().setVisible(false);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnOpen().setVisible(false);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().setEnabled(false);
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
return SEditorIO.confirmSaveChanges(getScreen(), isClosing);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
CSubmenuDraft.SINGLETON_INSTANCE.update();
|
||||
CSubmenuSealed.SINGLETON_INSTANCE.update();
|
||||
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.home.quest.CSubmenuQuestDecks;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.gui.toolbox.itemmanager.views.ColumnDef;
|
||||
import forge.gui.toolbox.itemmanager.views.ItemColumn;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.quest.QuestController;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
//import forge.quest.data.QuestBoosterPack;
|
||||
|
||||
/**
|
||||
* Child controller for quest deck editor UI.
|
||||
* <br><br>
|
||||
* Card catalog and decks are drawn from a QuestController object.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class CEditorQuest extends ACEditorBase<PaperCard, Deck> {
|
||||
private final QuestController questData;
|
||||
private final DeckController<Deck> controller;
|
||||
private final List<DeckSection> allSections = new ArrayList<DeckSection>();
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
|
||||
private Map<PaperCard, Integer> decksUsingMyCards;
|
||||
|
||||
private final Function<Entry<InventoryItem, Integer>, Comparable<?>> fnDeckCompare = new Function<Entry<InventoryItem, Integer>, Comparable<?>>() {
|
||||
@Override
|
||||
public Comparable<?> apply(final Entry<InventoryItem, Integer> from) {
|
||||
final Integer iValue = decksUsingMyCards.get(from.getKey());
|
||||
return iValue == null ? Integer.valueOf(0) : iValue;
|
||||
}
|
||||
};
|
||||
|
||||
private final Function<Entry<? extends InventoryItem, Integer>, Object> fnDeckGet = new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
final Integer iValue = decksUsingMyCards.get(from.getKey());
|
||||
return iValue == null ? "" : iValue.toString();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Child controller for quest deck editor UI.
|
||||
* <br><br>
|
||||
* Card catalog and decks are drawn from a QuestController object.
|
||||
*
|
||||
* @param questData0   {@link forge.quest.QuestController}
|
||||
*/
|
||||
public CEditorQuest(final QuestController questData0) {
|
||||
super(FScreen.DECK_EDITOR_QUEST);
|
||||
|
||||
allSections.add(DeckSection.Main);
|
||||
allSections.add(DeckSection.Sideboard);
|
||||
|
||||
this.questData = questData0;
|
||||
|
||||
final CardManager catalogManager = new CardManager(false);
|
||||
final CardManager deckManager = new CardManager(false);
|
||||
|
||||
catalogManager.setCaption("Quest Inventory");
|
||||
|
||||
catalogManager.setAlwaysNonUnique(true);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
|
||||
final Supplier<Deck> newCreator = new Supplier<Deck>() {
|
||||
@Override
|
||||
public Deck get() {
|
||||
return new Deck();
|
||||
}
|
||||
};
|
||||
|
||||
this.controller = new DeckController<Deck>(questData0.getMyDecks(), this, newCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds any card to the catalog and data pool.
|
||||
*
|
||||
* @param card {@link forge.item.PaperCard}
|
||||
*/
|
||||
public void addCheatCard(final PaperCard card, int qty) {
|
||||
this.getCatalogManager().addItem(card, qty);
|
||||
this.questData.getCards().getCardpool().add(card, qty);
|
||||
}
|
||||
|
||||
// fills number of decks using each card
|
||||
private Map<PaperCard, Integer> countDecksForEachCard() {
|
||||
final Map<PaperCard, Integer> result = new HashMap<PaperCard, Integer>();
|
||||
for (final Deck deck : this.questData.getMyDecks()) {
|
||||
for (final Entry<PaperCard, Integer> e : deck.getMain()) {
|
||||
final PaperCard card = e.getKey();
|
||||
final Integer amount = result.get(card);
|
||||
result.put(card, Integer.valueOf(amount == null ? 1 : 1 + amount.intValue()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
return CardLimit.Default;
|
||||
}
|
||||
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
CEditorConstructed.onAddItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
CEditorConstructed.onRemoveItems(this, items, toAlternate);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildAddContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
CEditorConstructed.buildAddContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#buildRemoveContextMenu()
|
||||
*/
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
CEditorConstructed.buildRemoveContextMenu(cmb, sectionMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#updateView()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
this.sectionMode = DeckSection.Main;
|
||||
|
||||
final Deck deck = this.controller.getModel();
|
||||
|
||||
final ItemPool<PaperCard> cardpool = new ItemPool<PaperCard>(PaperCard.class);
|
||||
cardpool.addAll(this.questData.getCards().getCardpool());
|
||||
// remove bottom cards that are in the deck from the card pool
|
||||
cardpool.removeAll(deck.getMain());
|
||||
// remove sideboard cards from the catalog
|
||||
cardpool.removeAll(deck.getOrCreate(DeckSection.Sideboard));
|
||||
// show cards, makes this user friendly
|
||||
this.getCatalogManager().setPool(cardpool);
|
||||
this.getDeckManager().setPool(deck.getMain());
|
||||
}
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<Deck> getDeckController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between the main deck and the sideboard editor.
|
||||
*/
|
||||
public void cycleEditorMode() {
|
||||
int curindex = allSections.indexOf(sectionMode);
|
||||
curindex = (curindex + 1) % allSections.size();
|
||||
sectionMode = allSections.get(curindex);
|
||||
|
||||
if (sectionMode == DeckSection.Sideboard) {
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(DeckSection.Sideboard));
|
||||
}
|
||||
else {
|
||||
this.getDeckManager().setPool(this.controller.getModel().getMain());
|
||||
}
|
||||
|
||||
this.controller.updateCaptions();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Override
|
||||
public void update() {
|
||||
this.decksUsingMyCards = this.countDecksForEachCard();
|
||||
|
||||
final Map<ColumnDef, ItemColumn> colOverridesCatalog = new HashMap<ColumnDef, ItemColumn>();
|
||||
final Map<ColumnDef, ItemColumn> colOverridesDeck = new HashMap<ColumnDef, ItemColumn>();
|
||||
|
||||
ItemManagerConfig.QUEST_EDITOR_POOL.addColOverride(colOverridesCatalog, ColumnDef.NEW, this.questData.getCards().getFnNewCompare(), this.questData.getCards().getFnNewGet());
|
||||
ItemManagerConfig.QUEST_DECK_EDITOR.addColOverride(colOverridesDeck, ColumnDef.NEW, this.questData.getCards().getFnNewCompare(), this.questData.getCards().getFnNewGet());
|
||||
ItemManagerConfig.QUEST_DECK_EDITOR.addColOverride(colOverridesDeck, ColumnDef.DECKS, this.fnDeckCompare, this.fnDeckGet);
|
||||
|
||||
this.getCatalogManager().setup(ItemManagerConfig.QUEST_EDITOR_POOL, colOverridesCatalog);
|
||||
this.getDeckManager().setup(ItemManagerConfig.QUEST_DECK_EDITOR, colOverridesDeck);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSave().setVisible(true);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnImport().setVisible(false);
|
||||
|
||||
this.getBtnCycleSection().setVisible(true);
|
||||
this.getBtnCycleSection().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
cycleEditorMode();
|
||||
} });
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
|
||||
if (this.controller.getModel() == null) {
|
||||
this.getDeckController().setModel(new Deck());
|
||||
}
|
||||
else {
|
||||
this.controller.refreshModel();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
if (SEditorIO.confirmSaveChanges(FScreen.DECK_EDITOR_QUEST, isClosing)) {
|
||||
Singletons.getModel().getQuest().save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
CSubmenuQuestDecks.SINGLETON_INSTANCE.update();
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param d0
|
||||
*/
|
||||
public void load(Deck deck) {
|
||||
controller.setModel(deck);
|
||||
}
|
||||
}
|
||||
@@ -1,625 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.Singletons;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.CardListViewer;
|
||||
import forge.gui.deckeditor.views.*;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.home.quest.CSubmenuQuestDecks;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.gui.toolbox.itemmanager.SpellShopManager;
|
||||
import forge.gui.toolbox.itemmanager.views.ColumnDef;
|
||||
import forge.gui.toolbox.itemmanager.views.ItemColumn;
|
||||
import forge.item.*;
|
||||
import forge.quest.QuestController;
|
||||
import forge.quest.io.ReadPriceList;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Child controller for quest card shop UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: CEditorQuestCardShop.java 15088 2012-04-07 11:34:05Z Max mtg $
|
||||
*/
|
||||
public final class CEditorQuestCardShop extends ACEditorBase<InventoryItem, DeckBase> {
|
||||
private final JLabel creditsLabel = new FLabel.Builder()
|
||||
.icon(FSkin.getIcon(FSkin.QuestIcons.ICO_COINSTACK))
|
||||
.fontSize(15).build();
|
||||
|
||||
// TODO: move these to the view where they belong
|
||||
private final JLabel sellPercentageLabel = new FLabel.Builder().text("0")
|
||||
.fontSize(11)
|
||||
.build();
|
||||
@SuppressWarnings("serial")
|
||||
private final JLabel fullCatalogToggle = new FLabel.Builder().text("See full catalog")
|
||||
.fontSize(14).hoverable(true).cmdClick(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
toggleFullCatalog();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
private double multiplier;
|
||||
private final QuestController questData;
|
||||
|
||||
private ItemPool<InventoryItem> cardsForSale;
|
||||
private final ItemPool<InventoryItem> fullCatalogCards =
|
||||
ItemPool.createFrom(Singletons.getMagicDb().getCommonCards().getAllCards(), InventoryItem.class);
|
||||
private boolean showingFullCatalog = false;
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
private DragCell probsParent = null;
|
||||
|
||||
// get pricelist:
|
||||
private final ReadPriceList r = new ReadPriceList();
|
||||
private final Map<String, Integer> mapPrices = this.r.getPriceList();
|
||||
private ItemPool<InventoryItem> decksUsingMyCards;
|
||||
|
||||
// remember changed gui elements
|
||||
private String CCTabLabel = new String();
|
||||
private String CCAddLabel = new String();
|
||||
private String CDTabLabel = new String();
|
||||
private String CDRemLabel = new String();
|
||||
private String prevRem4Label = null;
|
||||
private String prevRem4Tooltip = null;
|
||||
private Runnable prevRem4Cmd = null;
|
||||
|
||||
/**
|
||||
* Child controller for quest card shop UI.
|
||||
*
|
||||
* @param qd
|
||||
* a {@link forge.quest.data.QuestData} object.
|
||||
*/
|
||||
public CEditorQuestCardShop(final QuestController qd) {
|
||||
super(FScreen.QUEST_CARD_SHOP);
|
||||
|
||||
this.questData = qd;
|
||||
|
||||
final SpellShopManager catalogManager = new SpellShopManager(false);
|
||||
final SpellShopManager deckManager = new SpellShopManager(false);
|
||||
|
||||
catalogManager.setCaption("Spell Shop");
|
||||
deckManager.setCaption("Quest Inventory");
|
||||
|
||||
catalogManager.setAlwaysNonUnique(true);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
}
|
||||
|
||||
private void toggleFullCatalog() {
|
||||
showingFullCatalog = !showingFullCatalog;
|
||||
|
||||
if (showingFullCatalog) {
|
||||
this.getCatalogManager().setPool(fullCatalogCards, true);
|
||||
this.getBtnAdd().setEnabled(false);
|
||||
this.getBtnRemove().setEnabled(false);
|
||||
this.getBtnRemove4().setEnabled(false);
|
||||
fullCatalogToggle.setText("Return to spell shop");
|
||||
}
|
||||
else {
|
||||
this.getCatalogManager().setPool(cardsForSale);
|
||||
this.getBtnAdd().setEnabled(true);
|
||||
this.getBtnRemove().setEnabled(true);
|
||||
this.getBtnRemove4().setEnabled(true);
|
||||
fullCatalogToggle.setText("See full catalog");
|
||||
}
|
||||
}
|
||||
|
||||
// fills number of decks using each card
|
||||
private ItemPool<InventoryItem> countDecksForEachCard() {
|
||||
final ItemPool<InventoryItem> result = new ItemPool<InventoryItem>(InventoryItem.class);
|
||||
for (final Deck deck : this.questData.getMyDecks()) {
|
||||
CardPool main = deck.getMain();
|
||||
for (final Entry<PaperCard, Integer> e : main) {
|
||||
result.add(e.getKey());
|
||||
}
|
||||
if (deck.has(DeckSection.Sideboard)) {
|
||||
for (final Entry<PaperCard, Integer> e : deck.get(DeckSection.Sideboard)) {
|
||||
// only add card if we haven't already encountered it in main
|
||||
if (!main.contains(e.getKey())) {
|
||||
result.add(e.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Integer getCardValue(final InventoryItem card) {
|
||||
String ns = null;
|
||||
int value = 1337; // previously this was the returned default
|
||||
boolean foil = false;
|
||||
int foilMultiplier = 1;
|
||||
|
||||
if (card instanceof PaperCard) {
|
||||
ns = card.getName() + "|" + ((PaperCard) card).getEdition();
|
||||
foil = ((PaperCard) card).isFoil();
|
||||
} else {
|
||||
ns = card.getName();
|
||||
}
|
||||
|
||||
if (this.mapPrices.containsKey(ns)) {
|
||||
value = this.mapPrices.get(ns);
|
||||
} else if (card instanceof PaperCard) {
|
||||
switch (((IPaperCard) card).getRarity()) {
|
||||
case BasicLand:
|
||||
value = 4;
|
||||
break;
|
||||
case Common:
|
||||
value = 6;
|
||||
break;
|
||||
case Uncommon:
|
||||
value = 40;
|
||||
break;
|
||||
case Rare:
|
||||
value = 120;
|
||||
break;
|
||||
case MythicRare:
|
||||
value = 600;
|
||||
break;
|
||||
default:
|
||||
value = 15;
|
||||
break;
|
||||
}
|
||||
} else if (card instanceof BoosterPack) {
|
||||
value = 395;
|
||||
} else if (card instanceof TournamentPack) {
|
||||
value = 995;
|
||||
} else if (card instanceof FatPack) {
|
||||
value = 2365;
|
||||
} else if (card instanceof PreconDeck) {
|
||||
value = QuestController.getPreconDeals((PreconDeck) card).getCost();
|
||||
}
|
||||
|
||||
// TODO: make this changeable via a user-definable property?
|
||||
if (foil) {
|
||||
switch (((IPaperCard) card).getRarity()) {
|
||||
case BasicLand:
|
||||
foilMultiplier = 2;
|
||||
break;
|
||||
case Common:
|
||||
foilMultiplier = 2;
|
||||
break;
|
||||
case Uncommon:
|
||||
foilMultiplier = 2;
|
||||
break;
|
||||
case Rare:
|
||||
foilMultiplier = 3;
|
||||
break;
|
||||
case MythicRare:
|
||||
foilMultiplier = 3;
|
||||
break;
|
||||
default:
|
||||
foilMultiplier = 2;
|
||||
break;
|
||||
}
|
||||
value *= foilMultiplier;
|
||||
}
|
||||
|
||||
return Integer.valueOf(value);
|
||||
}
|
||||
|
||||
private final Function<Entry<InventoryItem, Integer>, Comparable<?>> fnPriceCompare = new Function<Entry<InventoryItem, Integer>, Comparable<?>>() {
|
||||
@Override
|
||||
public Comparable<?> apply(final Entry<InventoryItem, Integer> from) {
|
||||
return CEditorQuestCardShop.this.getCardValue(from.getKey());
|
||||
}
|
||||
};
|
||||
private final Function<Entry<? extends InventoryItem, Integer>, Object> fnPriceGet = new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
return CEditorQuestCardShop.this.getCardValue(from.getKey());
|
||||
}
|
||||
};
|
||||
private final Function<Entry<? extends InventoryItem, Integer>, Object> fnPriceSellGet = new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
return (int) (CEditorQuestCardShop.this.multiplier * CEditorQuestCardShop.this.getCardValue(from.getKey()));
|
||||
}
|
||||
};
|
||||
|
||||
private final Function<Entry<InventoryItem, Integer>, Comparable<?>> fnDeckCompare = new Function<Entry<InventoryItem, Integer>, Comparable<?>>() {
|
||||
@Override
|
||||
public Comparable<?> apply(final Entry<InventoryItem, Integer> from) {
|
||||
final Integer iValue = CEditorQuestCardShop.this.decksUsingMyCards.count(from.getKey());
|
||||
return iValue == null ? Integer.valueOf(0) : iValue;
|
||||
}
|
||||
};
|
||||
private final Function<Entry<? extends InventoryItem, Integer>, Object> fnDeckGet = new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
final Integer iValue = CEditorQuestCardShop.this.decksUsingMyCards.count(from.getKey());
|
||||
return iValue == null ? "" : iValue.toString();
|
||||
}
|
||||
};
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
return CardLimit.None;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<InventoryItem, Integer>> items, boolean toAlternate) {
|
||||
// disallow "buying" cards while showing the full catalog
|
||||
if (showingFullCatalog || toAlternate) {
|
||||
return;
|
||||
}
|
||||
|
||||
long totalCost = 0;
|
||||
ItemPool<InventoryItem> itemsToBuy = new ItemPool<InventoryItem>(InventoryItem.class);
|
||||
for (Entry<InventoryItem, Integer> itemEntry : items) {
|
||||
final InventoryItem item = itemEntry.getKey();
|
||||
if (item instanceof PaperCard || item instanceof SealedProduct || item instanceof PreconDeck) {
|
||||
final int qty = itemEntry.getValue();
|
||||
itemsToBuy.add(item, qty);
|
||||
totalCost += qty * this.getCardValue(item);
|
||||
}
|
||||
}
|
||||
if (itemsToBuy.isEmpty()) { return; }
|
||||
|
||||
List<InventoryItem> itemFlatList = itemsToBuy.toFlatList();
|
||||
String suffix = SItemManagerUtil.getItemDisplayString(itemFlatList, 1, true);
|
||||
String displayList = SItemManagerUtil.buildDisplayList(itemsToBuy);
|
||||
String title = "Buy " + suffix;
|
||||
|
||||
long creditsShort = totalCost - this.questData.getAssets().getCredits();
|
||||
if (creditsShort > 0) {
|
||||
FOptionPane.showMessageDialog("You need " + creditsShort + " more credits to purchase the following " + suffix.toLowerCase() + ".\n" + displayList, title);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FOptionPane.showConfirmDialog("Pay " + totalCost + " credits to purchase the following " +
|
||||
suffix.toLowerCase() + "?\n" + displayList, title, "Buy", "Cancel")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPool<InventoryItem> itemsToAdd = new ItemPool<InventoryItem>(InventoryItem.class);
|
||||
|
||||
for (Entry<InventoryItem, Integer> itemEntry : itemsToBuy) {
|
||||
final InventoryItem item = itemEntry.getKey();
|
||||
|
||||
final int qty = itemEntry.getValue();
|
||||
final int value = this.getCardValue(item);
|
||||
|
||||
if (item instanceof PaperCard) {
|
||||
this.questData.getCards().buyCard((PaperCard) item, qty, value);
|
||||
itemsToAdd.add(item, qty);
|
||||
}
|
||||
else if (item instanceof SealedProduct) {
|
||||
for (int i = 0; i < qty; i++) {
|
||||
SealedProduct booster = null;
|
||||
if (item instanceof BoosterPack) {
|
||||
booster = (BoosterPack) ((BoosterPack) item).clone();
|
||||
}
|
||||
else if (item instanceof TournamentPack) {
|
||||
booster = (TournamentPack) ((TournamentPack) item).clone();
|
||||
}
|
||||
else if (item instanceof FatPack) {
|
||||
booster = (FatPack) ((FatPack) item).clone();
|
||||
}
|
||||
this.questData.getCards().buyPack(booster, value);
|
||||
final List<PaperCard> newCards = booster.getCards();
|
||||
|
||||
itemsToAdd.addAllFlat(newCards);
|
||||
|
||||
final CardListViewer c = new CardListViewer(booster.getName(), "You have found the following cards inside:", newCards);
|
||||
c.setVisible(true);
|
||||
c.dispose();
|
||||
}
|
||||
}
|
||||
else if (item instanceof PreconDeck) {
|
||||
final PreconDeck deck = (PreconDeck) item;
|
||||
for (int i = 0; i < qty; i++) {
|
||||
this.questData.getCards().buyPreconDeck(deck, value);
|
||||
|
||||
itemsToAdd.addAll(deck.getDeck().getMain());
|
||||
}
|
||||
|
||||
boolean one = (qty == 1);
|
||||
FOptionPane.showMessageDialog(String.format(
|
||||
"%s '%s' %s added to your decklist.%n%n%s cards were also added to your pool.",
|
||||
one ? "Deck" : String.format("%d copies of deck", qty),
|
||||
deck.getName(), one ? "was" : "were", one ? "Its" : "Their"),
|
||||
"Thanks for purchasing!", FOptionPane.INFORMATION_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
this.getDeckManager().addItems(itemsToAdd);
|
||||
this.getCatalogManager().removeItems(itemsToBuy);
|
||||
|
||||
this.creditsLabel.setText("Credits: " + this.questData.getAssets().getCredits());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<InventoryItem, Integer>> items, boolean toAlternate) {
|
||||
if (showingFullCatalog || toAlternate) { return; }
|
||||
|
||||
long totalReceived = 0;
|
||||
ItemPool<InventoryItem> itemsToSell = new ItemPool<InventoryItem>(InventoryItem.class);
|
||||
for (Entry<InventoryItem, Integer> itemEntry : items) {
|
||||
final InventoryItem item = itemEntry.getKey();
|
||||
if (item instanceof PaperCard) {
|
||||
final int qty = itemEntry.getValue();
|
||||
itemsToSell.add(item, qty);
|
||||
totalReceived += qty * Math.min((int) (this.multiplier * this.getCardValue(item)), this.questData.getCards().getSellPriceLimit());
|
||||
}
|
||||
}
|
||||
if (itemsToSell.isEmpty()) { return; }
|
||||
|
||||
List<InventoryItem> itemFlatList = itemsToSell.toFlatList();
|
||||
String suffix = SItemManagerUtil.getItemDisplayString(itemFlatList, 1, true);
|
||||
String displayList = SItemManagerUtil.buildDisplayList(itemsToSell);
|
||||
String title = "Sell " + suffix;
|
||||
|
||||
if (!FOptionPane.showConfirmDialog("Sell the following " + suffix.toLowerCase() + " for " + totalReceived +
|
||||
" credit" + (totalReceived != 1 ? "s" : "") + "?\n" + displayList, title, "Sell", "Cancel")) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Entry<InventoryItem, Integer> itemEntry : itemsToSell) {
|
||||
final PaperCard card = (PaperCard) itemEntry.getKey();
|
||||
final int qty = itemEntry.getValue();
|
||||
final int price = Math.min((int) (this.multiplier * this.getCardValue(card)), this.questData.getCards().getSellPriceLimit());
|
||||
|
||||
this.questData.getCards().sellCard(card, qty, price);
|
||||
}
|
||||
|
||||
this.getCatalogManager().addItems(itemsToSell);
|
||||
this.getDeckManager().removeItems(itemsToSell);
|
||||
|
||||
this.creditsLabel.setText("Credits: " + this.questData.getAssets().getCredits());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
if (!showingFullCatalog) {
|
||||
cmb.addMoveItems("Buy", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
if (!showingFullCatalog) {
|
||||
cmb.addMoveItems("Sell", null);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeCards(List<Entry<InventoryItem, Integer>> cardsToRemove) {
|
||||
if (showingFullCatalog) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getCatalogManager().addItems(cardsToRemove);
|
||||
this.getDeckManager().removeItems(cardsToRemove);
|
||||
|
||||
for (Entry<InventoryItem, Integer> item : cardsToRemove) {
|
||||
if (!(item.getKey() instanceof PaperCard)) {
|
||||
continue;
|
||||
}
|
||||
PaperCard card = (PaperCard)item.getKey();
|
||||
final int price = Math.min((int) (this.multiplier * this.getCardValue(card)),
|
||||
this.questData.getCards().getSellPriceLimit());
|
||||
this.questData.getCards().sellCard(card, item.getValue(), price);
|
||||
}
|
||||
|
||||
this.creditsLabel.setText("Credits: " + this.questData.getAssets().getCredits());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#resetTables()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<DeckBase> getDeckController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Override
|
||||
public void update() {
|
||||
final Map<ColumnDef, ItemColumn> colOverridesCatalog = new HashMap<ColumnDef, ItemColumn>();
|
||||
final Map<ColumnDef, ItemColumn> colOverridesDeck = new HashMap<ColumnDef, ItemColumn>();
|
||||
|
||||
// Add spell shop-specific columns
|
||||
ItemManagerConfig.SPELL_SHOP.addColOverride(colOverridesCatalog, ColumnDef.PRICE, this.fnPriceCompare, this.fnPriceGet);
|
||||
ItemManagerConfig.SPELL_SHOP.addColOverride(colOverridesCatalog, ColumnDef.OWNED, questData.getCards().getFnOwnedCompare(), questData.getCards().getFnOwnedGet());
|
||||
ItemManagerConfig.QUEST_INVENTORY.addColOverride(colOverridesDeck, ColumnDef.PRICE, this.fnPriceCompare, this.fnPriceSellGet);
|
||||
ItemManagerConfig.QUEST_INVENTORY.addColOverride(colOverridesDeck, ColumnDef.NEW, this.questData.getCards().getFnNewCompare(), this.questData.getCards().getFnNewGet());
|
||||
ItemManagerConfig.QUEST_INVENTORY.addColOverride(colOverridesDeck, ColumnDef.DECKS, this.fnDeckCompare, this.fnDeckGet);
|
||||
|
||||
// Setup with current column set
|
||||
this.getCatalogManager().setup(ItemManagerConfig.SPELL_SHOP, colOverridesCatalog);
|
||||
this.getDeckManager().setup(ItemManagerConfig.QUEST_INVENTORY, colOverridesDeck);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
CCTabLabel = VCardCatalog.SINGLETON_INSTANCE.getTabLabel().getText();
|
||||
VCardCatalog.SINGLETON_INSTANCE.getTabLabel().setText("Cards for sale");
|
||||
|
||||
CCAddLabel = this.getBtnAdd().getText();
|
||||
this.getBtnAdd().setText("Buy Card");
|
||||
|
||||
CDTabLabel = VCurrentDeck.SINGLETON_INSTANCE.getTabLabel().getText();
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTabLabel().setText("Your Cards");
|
||||
|
||||
CDRemLabel = this.getBtnRemove().getText();
|
||||
this.getBtnRemove().setText("Sell Card");
|
||||
|
||||
VProbabilities.SINGLETON_INSTANCE.getTabLabel().setVisible(false);
|
||||
|
||||
prevRem4Label = this.getBtnRemove4().getText();
|
||||
prevRem4Tooltip = this.getBtnRemove4().getToolTipText();
|
||||
prevRem4Cmd = this.getBtnRemove4().getCommand();
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getPnlHeader().setVisible(false);
|
||||
|
||||
this.decksUsingMyCards = this.countDecksForEachCard();
|
||||
this.multiplier = this.questData.getCards().getSellMultiplier();
|
||||
this.cardsForSale = this.questData.getCards().getShopList();
|
||||
|
||||
final ItemPool<InventoryItem> ownedItems = new ItemPool<InventoryItem>(InventoryItem.class);
|
||||
ownedItems.addAll(this.questData.getCards().getCardpool().getView());
|
||||
|
||||
this.getCatalogManager().setPool(cardsForSale);
|
||||
this.getDeckManager().setPool(ownedItems);
|
||||
|
||||
this.getBtnRemove4().setText("Sell all extras");
|
||||
this.getBtnRemove4().setToolTipText("Sell unneeded extra copies of all cards");
|
||||
this.getBtnRemove4().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Entry<InventoryItem, Integer>> cardsToRemove = new LinkedList<Map.Entry<InventoryItem,Integer>>();
|
||||
for (Entry<InventoryItem, Integer> item : getDeckManager().getPool()) {
|
||||
PaperCard card = (PaperCard)item.getKey();
|
||||
int numToKeep = card.getRules().getType().isBasic() ? 50 : 4;
|
||||
if ("Relentless Rats".equals(card.getName())) {
|
||||
numToKeep = Integer.MAX_VALUE;
|
||||
}
|
||||
if (numToKeep < item.getValue()) {
|
||||
cardsToRemove.add(Pair.of(item.getKey(), item.getValue() - numToKeep));
|
||||
}
|
||||
}
|
||||
removeCards(cardsToRemove);
|
||||
}
|
||||
});
|
||||
|
||||
this.getDeckManager().getPnlButtons().add(creditsLabel, "gap 5px");
|
||||
this.creditsLabel.setText("Credits: " + this.questData.getAssets().getCredits());
|
||||
|
||||
final double multiPercent = this.multiplier * 100;
|
||||
final NumberFormat formatter = new DecimalFormat("#0.00");
|
||||
String maxSellingPrice = "";
|
||||
final int maxSellPrice = this.questData.getCards().getSellPriceLimit();
|
||||
|
||||
if (maxSellPrice < Integer.MAX_VALUE) {
|
||||
maxSellingPrice = String.format("Maximum selling price is %d credits.", maxSellPrice);
|
||||
}
|
||||
this.getCatalogManager().getPnlButtons().remove(this.getBtnAdd4());
|
||||
this.getCatalogManager().getPnlButtons().add(fullCatalogToggle, "w 25%, h 30!", 0);
|
||||
this.getCatalogManager().getPnlButtons().add(sellPercentageLabel);
|
||||
this.sellPercentageLabel.setText("<html>Selling cards at " + formatter.format(multiPercent)
|
||||
+ "% of their value.<br>" + maxSellingPrice + "</html>");
|
||||
|
||||
//TODO: Add filter for SItemManagerUtil.StatTypes.PACK
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
probsParent = removeTab(VProbabilities.SINGLETON_INSTANCE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
Singletons.getModel().getQuest().save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
if (showingFullCatalog) {
|
||||
toggleFullCatalog();
|
||||
}
|
||||
|
||||
CSubmenuQuestDecks.SINGLETON_INSTANCE.update();
|
||||
|
||||
// undo Card Shop Specifics
|
||||
this.getCatalogManager().getPnlButtons().remove(sellPercentageLabel);
|
||||
this.getCatalogManager().getPnlButtons().remove(fullCatalogToggle);
|
||||
this.getCatalogManager().getPnlButtons().add(this.getBtnAdd4());
|
||||
|
||||
this.getDeckManager().getPnlButtons().remove(creditsLabel);
|
||||
this.getBtnRemove4().setText(prevRem4Label);
|
||||
this.getBtnRemove4().setToolTipText(prevRem4Tooltip);
|
||||
this.getBtnRemove4().setCommand(prevRem4Cmd);
|
||||
|
||||
VCardCatalog.SINGLETON_INSTANCE.getTabLabel().setText(CCTabLabel);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTabLabel().setText(CDTabLabel);
|
||||
|
||||
this.getBtnAdd().setText(CCAddLabel);
|
||||
this.getBtnRemove().setText(CDRemLabel);
|
||||
|
||||
//TODO: Remove filter for SItemManagerUtil.StatTypes.PACK
|
||||
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (probsParent != null) {
|
||||
probsParent.addDoc(VProbabilities.SINGLETON_INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.gui.deckeditor.SEditorIO;
|
||||
import forge.gui.deckeditor.views.VAllDecks;
|
||||
import forge.gui.deckeditor.views.VDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.toolbox.itemmanager.CardManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerConfig;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.ItemPool;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: CEditorConstructed.java 18430 2012-11-27 22:42:36Z Hellfish $
|
||||
*/
|
||||
public final class CEditorVariant extends ACEditorBase<PaperCard, Deck> {
|
||||
private final DeckController<Deck> controller;
|
||||
private DragCell allDecksParent = null;
|
||||
private DragCell deckGenParent = null;
|
||||
private final Predicate<PaperCard> cardPoolCondition;
|
||||
|
||||
//=========== Constructor
|
||||
/**
|
||||
* Child controller for constructed deck editor UI.
|
||||
* This is the least restrictive mode;
|
||||
* all cards are available.
|
||||
*/
|
||||
public CEditorVariant(final IStorage<Deck> folder, final Predicate<PaperCard> poolCondition, final DeckSection deckSection0, final FScreen screen0) {
|
||||
super(screen0);
|
||||
|
||||
this.cardPoolCondition = poolCondition;
|
||||
this.sectionMode = deckSection0;
|
||||
|
||||
CardManager catalogManager = new CardManager(true);
|
||||
CardManager deckManager = new CardManager(true);
|
||||
|
||||
catalogManager.setCaption("Catalog");
|
||||
|
||||
this.setCatalogManager(catalogManager);
|
||||
this.setDeckManager(deckManager);
|
||||
|
||||
final Supplier<Deck> newCreator = new Supplier<Deck>() {
|
||||
@Override
|
||||
public Deck get() {
|
||||
return new Deck();
|
||||
}
|
||||
};
|
||||
this.controller = new DeckController<Deck>(folder, this, newCreator);
|
||||
}
|
||||
|
||||
//=========== Overridden from ACEditorBase
|
||||
|
||||
@Override
|
||||
protected CardLimit getCardLimit() {
|
||||
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
return CardLimit.Default;
|
||||
}
|
||||
return CardLimit.None; //if not enforcing deck legality, don't enforce default limit
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onAddItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onAddItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
if (toAlternate) { return; }
|
||||
|
||||
ItemPool<PaperCard> itemsToAdd = getAllowedAdditions(items);
|
||||
if (itemsToAdd.isEmpty()) { return; }
|
||||
|
||||
this.getDeckManager().addItems(itemsToAdd);
|
||||
this.getCatalogManager().selectItemEntrys(itemsToAdd); //just select all added cards in Catalog
|
||||
this.controller.notifyModelChanged();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#onRemoveItems()
|
||||
*/
|
||||
@Override
|
||||
protected void onRemoveItems(Iterable<Entry<PaperCard, Integer>> items, boolean toAlternate) {
|
||||
if (toAlternate) { return; }
|
||||
|
||||
this.getDeckManager().removeItems(items);
|
||||
this.getCatalogManager().selectItemEntrys(items); //just select all removed cards in Catalog
|
||||
this.controller.notifyModelChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildAddContextMenu(EditorContextMenuBuilder cmb) {
|
||||
cmb.addMoveItems("Add", "to deck");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildRemoveContextMenu(EditorContextMenuBuilder cmb) {
|
||||
cmb.addMoveItems("Remove", "from deck");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#updateView()
|
||||
*/
|
||||
@Override
|
||||
public void resetTables() {
|
||||
Iterable<PaperCard> allNT = Singletons.getMagicDb().getVariantCards().getAllCards();
|
||||
allNT = Iterables.filter(allNT, cardPoolCondition);
|
||||
|
||||
this.getCatalogManager().setPool(ItemPool.createFrom(allNT, PaperCard.class), true);
|
||||
this.getDeckManager().setPool(this.controller.getModel().getOrCreate(this.sectionMode));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see forge.gui.deckeditor.ACEditorBase#getController()
|
||||
*/
|
||||
@Override
|
||||
public DeckController<Deck> getDeckController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.ACEditorBase#show(forge.Command)
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.getCatalogManager().setup(ItemManagerConfig.CARD_CATALOG);
|
||||
this.getDeckManager().setup(ItemManagerConfig.DECK_EDITOR);
|
||||
|
||||
SItemManagerUtil.resetUI(this);
|
||||
|
||||
deckGenParent = removeTab(VDeckgen.SINGLETON_INSTANCE);
|
||||
allDecksParent = removeTab(VAllDecks.SINGLETON_INSTANCE);
|
||||
|
||||
this.controller.refreshModel();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#canSwitchAway()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSwitchAway(boolean isClosing) {
|
||||
return SEditorIO.confirmSaveChanges(getScreen(), isClosing);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.deckeditor.controllers.ACEditorBase#resetUIChanges()
|
||||
*/
|
||||
@Override
|
||||
public void resetUIChanges() {
|
||||
//Re-add tabs
|
||||
if (deckGenParent != null) {
|
||||
deckGenParent.addDoc(VDeckgen.SINGLETON_INSTANCE);
|
||||
}
|
||||
if (allDecksParent != null) {
|
||||
allDecksParent.addDoc(VAllDecks.SINGLETON_INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.views.VProbabilities;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.ItemPool;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Controls the "analysis" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CProbabilities implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("serial")
|
||||
public void initialize() {
|
||||
VProbabilities.SINGLETON_INSTANCE.getLblReshuffle().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
VProbabilities.SINGLETON_INSTANCE.rebuildLabels(analyze());
|
||||
}
|
||||
|
||||
//========== Other methods
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends InventoryItem, TModel extends DeckBase> List<String> analyze() {
|
||||
final ACEditorBase<T, TModel> ed = (ACEditorBase<T, TModel>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
if (ed == null) { return new ArrayList<String>(); }
|
||||
|
||||
final ItemPool<PaperCard> deck = ItemPool.createFrom(ed.getDeckManager().getPool(), PaperCard.class);
|
||||
|
||||
final List<String> cardProbabilities = new ArrayList<String>();
|
||||
|
||||
final List<PaperCard> shuffled = deck.toFlatList();
|
||||
Collections.shuffle(shuffled, MyRandom.getRandom());
|
||||
|
||||
// Log totals of each card for decrementing
|
||||
final Map<PaperCard, Integer> cardTotals = new HashMap<PaperCard, Integer>();
|
||||
for (final PaperCard c : shuffled) {
|
||||
if (cardTotals.containsKey(c)) { cardTotals.put(c, cardTotals.get(c) + 1); }
|
||||
else { cardTotals.put(c, 1); }
|
||||
}
|
||||
|
||||
// Run through shuffled deck and calculate probabilities.
|
||||
// Formulas is (remaining instances of this card / total cards remaining)
|
||||
final Iterator<PaperCard> itr = shuffled.iterator();
|
||||
PaperCard tmp;
|
||||
// int prob;
|
||||
while (itr.hasNext()) {
|
||||
tmp = itr.next();
|
||||
|
||||
// prob = SEditorUtil.calculatePercentage(
|
||||
// cardTotals.get(tmp), shuffled.size());
|
||||
|
||||
cardTotals.put(tmp, cardTotals.get(tmp) - 1);
|
||||
cardProbabilities.add(tmp.getName()); // + " (" + prob + "%)");
|
||||
itr.remove();
|
||||
}
|
||||
|
||||
return cardProbabilities;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import forge.UiCommand;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.MagicColor;
|
||||
import forge.deck.DeckBase;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.views.VStatistics;
|
||||
import forge.gui.framework.ICDoc;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Controls the "analysis" panel in the deck editor UI.
|
||||
*
|
||||
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum CStatistics implements ICDoc {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
analyze();
|
||||
}
|
||||
|
||||
private void setLabelValue(JLabel label, ItemPool<PaperCard> deck, Predicate<CardRules> predicate, int total) {
|
||||
int tmp = deck.countAll(Predicates.compose(predicate, PaperCard.FN_GET_RULES));
|
||||
label.setText(tmp + " (" + SItemManagerUtil.calculatePercentage(tmp, total) + "%)");
|
||||
}
|
||||
|
||||
//========== Other methods
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends InventoryItem, TModel extends DeckBase> void analyze() {
|
||||
final ACEditorBase<T, TModel> ed = (ACEditorBase<T, TModel>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
if (ed == null) { return; }
|
||||
|
||||
final ItemPool<PaperCard> deck = ItemPool.createFrom(ed.getDeckManager().getPool(), PaperCard.class);
|
||||
|
||||
int total = deck.countAll();
|
||||
|
||||
// Hack-ish: avoid /0 cases, but still populate labels :)
|
||||
if (total == 0) { total = 1; }
|
||||
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCreature(), deck, CardRulesPredicates.Presets.IS_CREATURE, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblLand(), deck, CardRulesPredicates.Presets.IS_LAND, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblEnchantment(), deck, CardRulesPredicates.Presets.IS_ENCHANTMENT, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblArtifact(), deck, CardRulesPredicates.Presets.IS_ARTIFACT, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblInstant(), deck, CardRulesPredicates.Presets.IS_INSTANT, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblSorcery(), deck, CardRulesPredicates.Presets.IS_SORCERY, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblPlaneswalker(), deck, CardRulesPredicates.Presets.IS_PLANESWALKER, total);
|
||||
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblMulti(), deck, CardRulesPredicates.Presets.IS_MULTICOLOR, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblColorless(), deck, CardRulesPredicates.Presets.IS_COLORLESS, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblBlack(), deck, CardRulesPredicates.isMonoColor(MagicColor.BLACK), total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblBlue(), deck, CardRulesPredicates.isMonoColor(MagicColor.BLUE), total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblGreen(), deck, CardRulesPredicates.isMonoColor(MagicColor.GREEN), total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblRed(), deck, CardRulesPredicates.isMonoColor(MagicColor.RED), total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblWhite(), deck, CardRulesPredicates.isMonoColor(MagicColor.WHITE), total);
|
||||
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC0(), deck, SItemManagerUtil.StatTypes.CMC_0.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC1(), deck, SItemManagerUtil.StatTypes.CMC_1.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC2(), deck, SItemManagerUtil.StatTypes.CMC_2.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC3(), deck, SItemManagerUtil.StatTypes.CMC_3.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC4(), deck, SItemManagerUtil.StatTypes.CMC_4.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC5(), deck, SItemManagerUtil.StatTypes.CMC_5.predicate, total);
|
||||
setLabelValue(VStatistics.SINGLETON_INSTANCE.getLblCMC6(), deck, SItemManagerUtil.StatTypes.CMC_6.predicate, total);
|
||||
|
||||
int tmc = 0;
|
||||
for (final Entry<PaperCard, Integer> e : deck) {
|
||||
tmc += e.getKey().getRules().getManaCost().getCMC() * e.getValue();
|
||||
}
|
||||
double amc = Math.round((double) tmc / (double) total * 100) / 100.0d;
|
||||
|
||||
VStatistics.SINGLETON_INSTANCE.getLblTotal().setText("TOTAL CARDS: " + deck.countAll());
|
||||
VStatistics.SINGLETON_INSTANCE.getLblTMC().setText("TOTAL MANA COST: " + tmc);
|
||||
VStatistics.SINGLETON_INSTANCE.getLblAMC().setText("AVERAGE MANA COST: " + amc);
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Nate
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.deckeditor.controllers;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import forge.deck.DeckBase;
|
||||
import forge.gui.deckeditor.menus.DeckFileMenu;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
*/
|
||||
public class DeckController<T extends DeckBase> {
|
||||
private T model;
|
||||
private boolean saved;
|
||||
private boolean modelInStorage;
|
||||
private final IStorage<T> rootFolder;
|
||||
private IStorage<T> currentFolder;
|
||||
private String modelPath;
|
||||
private final ACEditorBase<?, T> view;
|
||||
private final Supplier<T> newModelCreator;
|
||||
|
||||
/**
|
||||
* Instantiates a new deck controller.
|
||||
*
|
||||
* @param folder0 the folder0
|
||||
* @param view0 the view0
|
||||
* @param newModelCreator0 the new model creator0
|
||||
*/
|
||||
public DeckController(final IStorage<T> folder0, final ACEditorBase<?, T> view0, final Supplier<T> newModelCreator0) {
|
||||
this.rootFolder = folder0;
|
||||
this.currentFolder = rootFolder;
|
||||
this.view = view0;
|
||||
this.model = null;
|
||||
this.saved = true;
|
||||
this.modelInStorage = false;
|
||||
this.modelPath = "";
|
||||
this.newModelCreator = newModelCreator0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model.
|
||||
*
|
||||
* @return the document
|
||||
*/
|
||||
public T getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
public String getModelPath() {
|
||||
return this.modelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model.
|
||||
*
|
||||
*/
|
||||
public void setModel(final T document) {
|
||||
this.setModel(document, false);
|
||||
}
|
||||
public void setModel(final T document, final boolean isStored) {
|
||||
this.modelInStorage = isStored;
|
||||
this.model = document;
|
||||
this.view.resetTables();
|
||||
|
||||
CStatistics.SINGLETON_INSTANCE.update();
|
||||
CProbabilities.SINGLETON_INSTANCE.update();
|
||||
|
||||
if (isStored) {
|
||||
if (this.isModelInSyncWithFolder()) {
|
||||
this.setSaved(true);
|
||||
}
|
||||
else {
|
||||
this.notifyModelChanged();
|
||||
}
|
||||
}
|
||||
else { //TODO: Make this smarter
|
||||
this.currentFolder = this.rootFolder;
|
||||
this.modelPath = "";
|
||||
this.setSaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isModelInSyncWithFolder() {
|
||||
if (model.getName().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final T modelStored = this.currentFolder.get(this.model.getName());
|
||||
// checks presence in dictionary only.
|
||||
if (modelStored == this.model) {
|
||||
return true;
|
||||
}
|
||||
if (modelStored == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return modelStored.equals(this.model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the view.
|
||||
*
|
||||
* @return the view
|
||||
*/
|
||||
public ACEditorBase<?, T> getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify model changed.
|
||||
*/
|
||||
public void notifyModelChanged() {
|
||||
if (saved) {
|
||||
this.setSaved(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSaved(boolean val) {
|
||||
saved = val;
|
||||
updateCaptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload current model
|
||||
*/
|
||||
public void reload() {
|
||||
String name = this.getModelName();
|
||||
if (name.isEmpty()) {
|
||||
newModel();
|
||||
}
|
||||
else {
|
||||
load(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(final String path, final String name) {
|
||||
if (StringUtils.isBlank(path)) {
|
||||
currentFolder = rootFolder;
|
||||
}
|
||||
else {
|
||||
currentFolder = rootFolder.tryGetFolder(path);
|
||||
}
|
||||
modelPath = path;
|
||||
load(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void load(final String name) {
|
||||
T newModel = this.currentFolder.get(name);
|
||||
if (newModel != null) {
|
||||
this.setModel((T) newModel.copyTo(name), true);
|
||||
}
|
||||
else {
|
||||
this.setSaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void save() {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy to new instance before adding to current folder so further changes are auto-saved
|
||||
this.currentFolder.add((T) this.model.copyTo(this.model.getName()));
|
||||
this.modelInStorage = true;
|
||||
this.setSaved(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save as.
|
||||
*
|
||||
* @param name0 the name0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void saveAs(final String name0) {
|
||||
this.model = (T)this.model.copyTo(name0);
|
||||
this.modelInStorage = false;
|
||||
this.save();
|
||||
this.view.resetTables(); //ensure pool updated in CCurrentDeck
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is saved.
|
||||
*
|
||||
* @return true, if is saved
|
||||
*/
|
||||
public boolean isSaved() {
|
||||
return this.saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* File exists.
|
||||
*
|
||||
* @param deckName the deck name
|
||||
* @return true, if successful
|
||||
*/
|
||||
public boolean fileExists(final String deckName) {
|
||||
return this.currentFolder.contains(deckName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import deck.
|
||||
*
|
||||
* @param newDeck the new deck
|
||||
*/
|
||||
public void importDeck(final T newDeck) {
|
||||
this.setModel(newDeck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh current model or create new one if none
|
||||
*/
|
||||
public void refreshModel() {
|
||||
if (this.model == null) {
|
||||
newModel();
|
||||
}
|
||||
else {
|
||||
setModel(this.model, this.modelInStorage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* New model.
|
||||
*/
|
||||
public void newModel() {
|
||||
this.model = this.newModelCreator.get();
|
||||
this.setSaved(true);
|
||||
this.view.resetTables();
|
||||
}
|
||||
|
||||
public String getModelName() {
|
||||
return this.model != null ? this.model.getName() : "";
|
||||
}
|
||||
|
||||
public void updateCaptions() {
|
||||
String tabCaption = "Current Deck";
|
||||
String title = this.getModelName();
|
||||
String itemManagerCaption = title.isEmpty() ? "[Untitled]" : title;
|
||||
|
||||
if (!saved) {
|
||||
tabCaption = "*" + tabCaption;
|
||||
itemManagerCaption = "*" + itemManagerCaption;
|
||||
}
|
||||
itemManagerCaption += " - " + this.view.getSectionMode().name();
|
||||
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTabLabel().setText(tabCaption);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().setText(title);
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getItemManager().setCaption(itemManagerCaption);
|
||||
DeckFileMenu.updateSaveEnabled();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package forge.gui.deckeditor.menus;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Gets the menus associated with the Game screen.
|
||||
*
|
||||
*/
|
||||
public class CDeckEditorUIMenus {
|
||||
|
||||
private final boolean SHOW_ICONS = true;
|
||||
|
||||
public List<JMenu> getMenus() {
|
||||
List<JMenu> menus = new ArrayList<JMenu>();
|
||||
menus.add(DeckFileMenu.getMenu(SHOW_ICONS));
|
||||
return menus;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package forge.gui.deckeditor.menus;
|
||||
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.views.VCurrentDeck;
|
||||
import forge.gui.menus.MenuUtil;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinnedMenuItem;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
/**
|
||||
* Returns a JMenu containing options associated with current game.
|
||||
* <p>
|
||||
* Replicates options available in Dock tab.
|
||||
*/
|
||||
public final class DeckFileMenu {
|
||||
private DeckFileMenu() { }
|
||||
|
||||
private static boolean showIcons;
|
||||
|
||||
public static JMenu getMenu(boolean showMenuIcons) {
|
||||
showIcons = showMenuIcons;
|
||||
|
||||
JMenu menu = new JMenu("File");
|
||||
menu.setMnemonic(KeyEvent.VK_F);
|
||||
menu.add(getMenuItem_New());
|
||||
menu.add(getMenuItem_Open());
|
||||
menu.add(getMenuItem_Import());
|
||||
menu.add(new JSeparator());
|
||||
menu.add(getMenuItem_Save());
|
||||
menu.add(getMenuItem_SaveAs());
|
||||
menu.add(new JSeparator());
|
||||
menu.add(getMenuItem_Print());
|
||||
updateSaveEnabled();
|
||||
return menu;
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem menuItem_Save, menuItem_SaveAs;
|
||||
|
||||
public static void updateSaveEnabled() {
|
||||
if (menuItem_Save != null) {
|
||||
menuItem_Save.setEnabled(CDeckEditorUI.SINGLETON_INSTANCE.hasChanges());
|
||||
}
|
||||
if (menuItem_SaveAs != null) {
|
||||
menuItem_SaveAs.setEnabled(CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController() != null);
|
||||
}
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_New() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("New Deck");
|
||||
menuItem.setIcon(showIcons ? MenuUtil.getMenuIcon(FSkin.InterfaceIcons.ICO_NEW) : null);
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_N));
|
||||
menuItem.addActionListener(getNewAction());
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getNewAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnNew().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_Open() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("Open Deck");
|
||||
menuItem.setIcon(showIcons ? MenuUtil.getMenuIcon(FSkin.InterfaceIcons.ICO_OPEN) : null);
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_O));
|
||||
menuItem.addActionListener(getOpenAction());
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getOpenAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnOpen().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_Import() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("Import Deck");
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_I));
|
||||
menuItem.addActionListener(getImportAction());
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getImportAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnImport().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_Save() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("Save Deck");
|
||||
menuItem.setIcon(showIcons ? MenuUtil.getMenuIcon(FSkin.InterfaceIcons.ICO_SAVE) : null);
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_S));
|
||||
menuItem.addActionListener(getSaveAction());
|
||||
menuItem_Save = menuItem;
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getSaveAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSave().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_SaveAs() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("Save Deck As");
|
||||
menuItem.setIcon(showIcons ? MenuUtil.getMenuIcon(FSkin.InterfaceIcons.ICO_SAVEAS) : null);
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_E));
|
||||
menuItem.addActionListener(getSaveAsAction());
|
||||
menuItem_SaveAs = menuItem;
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getSaveAsAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnSaveAs().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static SkinnedMenuItem getMenuItem_Print() {
|
||||
SkinnedMenuItem menuItem = new SkinnedMenuItem("Print to HTML file");
|
||||
menuItem.setIcon(showIcons ? MenuUtil.getMenuIcon(FSkin.InterfaceIcons.ICO_PRINT) : null);
|
||||
menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_P));
|
||||
menuItem.addActionListener(getPrintAction());
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
private static ActionListener getPrintAction() {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VCurrentDeck.SINGLETON_INSTANCE.getBtnPrintProxies().getCommand().run();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/** Forge Card Game. */
|
||||
package forge.gui.deckeditor;
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.game.GameType;
|
||||
import forge.gui.deckeditor.controllers.CAllDecks;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.itemmanager.DeckManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerContainer;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Assembles Swing components of all deck viewer in deck editor.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*/
|
||||
public enum VAllDecks implements IVDoc<CAllDecks> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("All Decks");
|
||||
|
||||
private final DeckManager lstDecks = new DeckManager(GameType.Constructed);
|
||||
|
||||
//========== Constructor
|
||||
private VAllDecks() {
|
||||
lstDecks.setCaption("Decks");
|
||||
}
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getDocumentID()
|
||||
*/
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_ALLDECKS;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getTabLabel()
|
||||
*/
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getLayoutControl()
|
||||
*/
|
||||
@Override
|
||||
public CAllDecks getLayoutControl() {
|
||||
return CAllDecks.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell)
|
||||
*/
|
||||
@Override
|
||||
public void setParentCell(DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getParentCell()
|
||||
*/
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
CAllDecks.SINGLETON_INSTANCE.refresh(); //ensure decks refreshed in case any deleted or added since last loaded
|
||||
|
||||
JPanel parentBody = parentCell.getBody();
|
||||
parentBody.setLayout(new MigLayout("insets 5, gap 0, wrap, hidemode 3"));
|
||||
parentBody.add(new ItemManagerContainer(lstDecks), "push, grow");
|
||||
}
|
||||
|
||||
//========== Retrieval methods
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public DeckManager getLstDecks() {
|
||||
return lstDecks;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.gui.deckeditor.controllers.CCardCatalog;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerContainer;
|
||||
import forge.item.InventoryItem;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Assembles Swing components of card catalog in deck editor.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*
|
||||
*/
|
||||
public enum VCardCatalog implements IVDoc<CCardCatalog> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
public static final int SEARCH_MODE_INVERSE_INDEX = 1;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("Card Catalog");
|
||||
|
||||
private final ItemManagerContainer itemManagerContainer = new ItemManagerContainer();
|
||||
private ItemManager<? extends InventoryItem> itemManager;
|
||||
|
||||
//========== Constructor
|
||||
/** */
|
||||
private VCardCatalog() {
|
||||
}
|
||||
|
||||
//========== Overridden from IVDoc
|
||||
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_CATALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CCardCatalog getLayoutControl() {
|
||||
return CCardCatalog.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentCell(DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populate() {
|
||||
JPanel parentBody = parentCell.getBody();
|
||||
parentBody.setLayout(new MigLayout("insets 5, gap 0, wrap, hidemode 3"));
|
||||
parentBody.add(itemManagerContainer, "push, grow");
|
||||
}
|
||||
|
||||
public ItemManager<? extends InventoryItem> getItemManager() {
|
||||
return this.itemManager;
|
||||
}
|
||||
|
||||
public void setItemManager(final ItemManager<? extends InventoryItem> itemManager0) {
|
||||
if (this.itemManager == itemManager0) { return; }
|
||||
this.itemManager = itemManager0;
|
||||
itemManagerContainer.setItemManager(itemManager0);
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.gui.deckeditor.controllers.CCurrentDeck;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FTextField;
|
||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||
import forge.gui.toolbox.itemmanager.ItemManagerContainer;
|
||||
import forge.item.InventoryItem;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* Assembles Swing components of current deck being edited in deck editor.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*/
|
||||
public enum VCurrentDeck implements IVDoc<CCurrentDeck> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("Current Deck");
|
||||
|
||||
// Other fields
|
||||
|
||||
private final FLabel btnSave = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.tooltip("Save Deck (Ctrl+S)")
|
||||
.iconInBackground(true)
|
||||
.iconAlignX(SwingConstants.CENTER)
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_SAVE))
|
||||
.text(" ").hoverable(true).build();
|
||||
|
||||
private final FLabel btnSaveAs = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.tooltip("Save Deck As (Ctrl+E)")
|
||||
.iconInBackground(true)
|
||||
.iconAlignX(SwingConstants.CENTER)
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_SAVEAS))
|
||||
.text(" ").hoverable(true).build();
|
||||
|
||||
private final FLabel btnLoad = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.tooltip("Open Deck (Ctrl+O)")
|
||||
.iconInBackground(true)
|
||||
.iconAlignX(SwingConstants.CENTER)
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_OPEN))
|
||||
.text(" ").hoverable(true).build();
|
||||
|
||||
private final FLabel btnNew = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.tooltip("New Deck (Ctrl+N)")
|
||||
.iconInBackground(true)
|
||||
.iconAlignX(SwingConstants.CENTER)
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_NEW))
|
||||
.text(" ").hoverable(true).build();
|
||||
|
||||
private final FLabel btnPrintProxies = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.tooltip("Print to HTML file (Ctrl+P)")
|
||||
.iconInBackground(true)
|
||||
.iconAlignX(SwingConstants.CENTER)
|
||||
.icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_PRINT))
|
||||
.text(" ").hoverable(true).build();
|
||||
|
||||
private final FLabel btnImport = new FLabel.Builder()
|
||||
.fontSize(14)
|
||||
.text("Import")
|
||||
.tooltip("Attempt to import a deck from a non-Forge format (Ctrl+I)")
|
||||
.opaque(true).hoverable(true).build();
|
||||
|
||||
private final FTextField txfTitle = new FTextField.Builder().ghostText("[New Deck]").build();
|
||||
|
||||
private final JPanel pnlHeader = new JPanel();
|
||||
|
||||
private final FLabel lblTitle = new FLabel.Builder().text("Title").fontSize(14).build();
|
||||
|
||||
private final ItemManagerContainer itemManagerContainer = new ItemManagerContainer();
|
||||
private ItemManager<? extends InventoryItem> itemManager;
|
||||
|
||||
//========== Constructor
|
||||
|
||||
private VCurrentDeck() {
|
||||
// Header area
|
||||
pnlHeader.setOpaque(false);
|
||||
pnlHeader.setLayout(new MigLayout("insets 3, gapx 3, hidemode 3"));
|
||||
|
||||
pnlHeader.add(lblTitle, "h 26px!");
|
||||
pnlHeader.add(txfTitle, "pushx, growx");
|
||||
pnlHeader.add(btnSave, "w 26px!, h 26px!");
|
||||
pnlHeader.add(btnNew, "w 26px!, h 26px!");
|
||||
|
||||
pnlHeader.add(btnLoad, "w 26px!, h 26px!");
|
||||
pnlHeader.add(btnSaveAs, "w 26px!, h 26px!");
|
||||
pnlHeader.add(btnPrintProxies, "w 26px!, h 26px!");
|
||||
pnlHeader.add(btnImport, "w 61px!, h 26px!");
|
||||
}
|
||||
|
||||
//========== Overridden from IVDoc
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getDocumentID()
|
||||
*/
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_CURRENTDECK;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getTabLabel()
|
||||
*/
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getLayoutControl()
|
||||
*/
|
||||
@Override
|
||||
public CCurrentDeck getLayoutControl() {
|
||||
return CCurrentDeck.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell)
|
||||
*/
|
||||
@Override
|
||||
public void setParentCell(final DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getParentCell()
|
||||
*/
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
final JPanel parentBody = parentCell.getBody();
|
||||
parentBody.setLayout(new MigLayout("insets 5, gap 0 3, wrap, hidemode 3"));
|
||||
parentBody.add(pnlHeader, "pushx, growx");
|
||||
parentBody.add(itemManagerContainer, "push, grow");
|
||||
}
|
||||
|
||||
public ItemManager<? extends InventoryItem> getItemManager() {
|
||||
return this.itemManager;
|
||||
}
|
||||
|
||||
public void setItemManager(final ItemManager<? extends InventoryItem> itemManager0) {
|
||||
this.itemManager = itemManager0;
|
||||
itemManagerContainer.setItemManager(itemManager0);
|
||||
}
|
||||
|
||||
public FLabel getLblTitle() { return lblTitle; }
|
||||
|
||||
//========== Retrieval
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnSave() {
|
||||
return btnSave;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnSaveAs() {
|
||||
return btnSaveAs;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnPrintProxies() {
|
||||
return btnPrintProxies;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnOpen() {
|
||||
return btnLoad;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnNew() {
|
||||
return btnNew;
|
||||
}
|
||||
|
||||
/** @return {@link forge.gui.toolbar.FTextField} */
|
||||
public FTextField getTxfTitle() {
|
||||
return txfTitle;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public JPanel getPnlHeader() {
|
||||
return pnlHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public FLabel getBtnImport() {
|
||||
// TODO Auto-generated method stub
|
||||
return (FLabel) btnImport;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.gui.deckeditor.controllers.CDeckgen;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
/**
|
||||
* Assembles Swing components of deck editor analysis tab.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*/
|
||||
public enum VDeckgen implements IVDoc<CDeckgen> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("Deck Generation");
|
||||
|
||||
// Deckgen buttons
|
||||
private final FLabel btnRandCardpool = new FLabel.Builder()
|
||||
.tooltip("Generate random constructed cardpool in current deck area")
|
||||
.text("Random Cardpool").fontSize(14)
|
||||
.opaque(true).hoverable(true).build();
|
||||
|
||||
private final FLabel btnRandDeck2 = new FLabel.Builder()
|
||||
.tooltip("Generate 2 color constructed deck in current deck area")
|
||||
.text("Constructed (2 color)").fontSize(14)
|
||||
.opaque(true).hoverable(true).build();
|
||||
|
||||
private final FLabel btnRandDeck3 = new FLabel.Builder()
|
||||
.tooltip("Generate 3 color constructed deck in current deck area")
|
||||
.text("Constructed (3 color)").fontSize(14)
|
||||
.opaque(true).hoverable(true).build();
|
||||
|
||||
private final FLabel btnRandDeck5 = new FLabel.Builder()
|
||||
.tooltip("Generate 5 color constructed deck in current deck area")
|
||||
.text("Constructed (5 color)").fontSize(14)
|
||||
.opaque(true).hoverable(true).build();
|
||||
|
||||
//========== Constructor
|
||||
private VDeckgen() {
|
||||
}
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getDocumentID()
|
||||
*/
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_DECKGEN;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getTabLabel()
|
||||
*/
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getLayoutControl()
|
||||
*/
|
||||
@Override
|
||||
public CDeckgen getLayoutControl() {
|
||||
return CDeckgen.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell)
|
||||
*/
|
||||
@Override
|
||||
public void setParentCell(final DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getParentCell()
|
||||
*/
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
parentCell.getBody().setLayout(new MigLayout("insets 0, gap 0, wrap, ax center"));
|
||||
|
||||
final String constraints = "w 80%!, h 30px!, gap 0 0 10px 0";
|
||||
parentCell.getBody().add(btnRandCardpool, constraints);
|
||||
parentCell.getBody().add(btnRandDeck2, constraints);
|
||||
parentCell.getBody().add(btnRandDeck3, constraints);
|
||||
parentCell.getBody().add(btnRandDeck5, constraints);
|
||||
}
|
||||
|
||||
//========== Retrieval methods
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnRandCardpool() {
|
||||
return btnRandCardpool;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnRandDeck2() {
|
||||
return btnRandDeck2;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnRandDeck3() {
|
||||
return btnRandDeck3;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getBtnRandDeck5() {
|
||||
return btnRandDeck5;
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.deck.DeckBase;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.controllers.ACEditorBase;
|
||||
import forge.gui.deckeditor.controllers.CProbabilities;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FScrollPane;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Assembles Swing components of deck editor analysis tab.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*/
|
||||
public enum VProbabilities implements IVDoc<CProbabilities> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("Draw Order");
|
||||
|
||||
// Title labels
|
||||
private final FLabel lblReshuffle = new FLabel.Builder()
|
||||
.hoverable(true).text("CLICK HERE TO RE-SHUFFLE").tooltip("See a new sample shuffle")
|
||||
.fontSize(16).build();
|
||||
private final FLabel lblSampleHand = new FLabel.Builder().fontStyle(Font.BOLD)
|
||||
.fontSize(12).text("SAMPLE HAND").opaque(true).build();
|
||||
private final FLabel lblRemainingDraws = new FLabel.Builder().fontStyle(Font.BOLD)
|
||||
.fontSize(12).text("REMAINING DRAWS").opaque(true).build();
|
||||
// private final JLabel lblExplanation = new FLabel.Builder()
|
||||
// .fontSize(11).text("XX % = frequency that card will appear at that position").build();
|
||||
|
||||
// Layout containers
|
||||
private final JPanel pnlContent = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
|
||||
private final FScrollPane scroller = new FScrollPane(pnlContent, false);
|
||||
private final JPanel pnlHand = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
|
||||
private final JPanel pnlLibrary = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
|
||||
|
||||
//========== Constructor
|
||||
private VProbabilities() {
|
||||
pnlContent.setOpaque(false);
|
||||
pnlHand.setOpaque(false);
|
||||
pnlLibrary.setOpaque(false);
|
||||
scroller.getViewport().setBorder(null);
|
||||
|
||||
lblSampleHand.setBorder(new FSkin.MatteSkinBorder(1, 0, 1, 0, FSkin.getColor(FSkin.Colors.CLR_BORDERS)));
|
||||
lblSampleHand.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
|
||||
|
||||
lblRemainingDraws.setBorder(new FSkin.MatteSkinBorder(1, 0, 1, 0, FSkin.getColor(FSkin.Colors.CLR_BORDERS)));
|
||||
lblRemainingDraws.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
|
||||
|
||||
// Core layout
|
||||
pnlContent.add(lblReshuffle, "w 96%!, h 29px!, gap 2% 0 5px 5px");
|
||||
pnlContent.add(lblSampleHand, "w 96%!, h 25px!, gap 2% 0 0 0");
|
||||
// pnlContent.add(lblExplanation, "w 96%!, h 25px!, gap 2% 0 0 0");
|
||||
pnlContent.add(pnlHand, "w 96%!, gap 2% 0 0 5px");
|
||||
pnlContent.add(lblRemainingDraws, "w 96%!, h 25px!, gap 2% 0 0 0");
|
||||
pnlContent.add(pnlLibrary, "w 96%!, gap 2% 0 5px 0");
|
||||
}
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getDocumentID()
|
||||
*/
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_PROBABILITIES;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getTabLabel()
|
||||
*/
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getLayoutControl()
|
||||
*/
|
||||
@Override
|
||||
public CProbabilities getLayoutControl() {
|
||||
return CProbabilities.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell)
|
||||
*/
|
||||
@Override
|
||||
public void setParentCell(final DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getParentCell()
|
||||
*/
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
parentCell.getBody().setLayout(new MigLayout("insets 0, gap 0"));
|
||||
parentCell.getBody().add(scroller, "w 96%!, h 96%!, gap 2% 0 2% 0");
|
||||
}
|
||||
|
||||
//========== Retrieval methods
|
||||
/** @return {@link javax.swing.JLabel} */
|
||||
public FLabel getLblReshuffle() {
|
||||
return lblReshuffle;
|
||||
}
|
||||
|
||||
//========== Other methods
|
||||
/** @param shuffledVals   A map of card names and their positional probability. */
|
||||
public void rebuildLabels(final List<String> shuffledVals) {
|
||||
pnlHand.removeAll();
|
||||
pnlLibrary.removeAll();
|
||||
|
||||
JLabel lbl;
|
||||
final String constraints = "w 96%, h 25px!, gap 2% 0 0 0";
|
||||
|
||||
for (int i = 0; i < shuffledVals.size(); i++) {
|
||||
lbl = (i % 2 == 1 ? buildLabel(true) : buildLabel(false));
|
||||
lbl.setText(shuffledVals.get(i));
|
||||
|
||||
if (i < 7) { pnlHand.add(lbl, constraints); }
|
||||
else { pnlLibrary.add(lbl, constraints); }
|
||||
}
|
||||
|
||||
pnlHand.validate();
|
||||
pnlLibrary.validate();
|
||||
}
|
||||
|
||||
private <T extends InventoryItem, TModel extends DeckBase> JLabel buildLabel(final boolean zebra) {
|
||||
final FLabel lbl = new FLabel.Builder().text("--")
|
||||
.fontAlign(SwingConstants.CENTER).fontSize(13)
|
||||
.build();
|
||||
|
||||
lbl.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mouseEntered(final MouseEvent e) {
|
||||
final ACEditorBase<T, TModel> ed = (ACEditorBase<T, TModel>)
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController();
|
||||
|
||||
final List<PaperCard> cards = (List<PaperCard>) ed.getDeckManager().getPool().toFlatList();
|
||||
final String name1 = lbl.getText();
|
||||
String name2;
|
||||
|
||||
for (PaperCard c : cards) {
|
||||
name2 = c.getName();
|
||||
if (name2.length() > name1.length()) { continue; }
|
||||
|
||||
if (name2.equals(name1.substring(0, name2.length()))) {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.setCard(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (zebra) {
|
||||
lbl.setOpaque(true);
|
||||
lbl.setBackground(FSkin.getColor(FSkin.Colors.CLR_ZEBRA));
|
||||
}
|
||||
|
||||
return lbl;
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
package forge.gui.deckeditor.views;
|
||||
|
||||
import forge.gui.deckeditor.controllers.CStatistics;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.gui.toolbox.FScrollPane;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinImage;
|
||||
import forge.gui.toolbox.itemmanager.SItemManagerUtil;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Assembles Swing components of deck editor analysis tab.
|
||||
*
|
||||
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
|
||||
*/
|
||||
public enum VStatistics implements IVDoc<CStatistics> {
|
||||
/** */
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
// Fields used with interface IVDoc
|
||||
private DragCell parentCell;
|
||||
private final DragTab tab = new DragTab("Statistics");
|
||||
|
||||
// Global stats
|
||||
private FLabel lblTotal = new FLabel.Builder()
|
||||
.text("Total cards: 0").tooltip("TOTAL CARDS")
|
||||
.fontStyle(Font.BOLD).fontSize(11).fontStyle(Font.BOLD).build();
|
||||
private FLabel lblTMC = new FLabel.Builder()
|
||||
.text("Total mana cost: 0").tooltip("TOTAL MANA COST")
|
||||
.fontStyle(Font.BOLD).fontSize(11).fontStyle(Font.BOLD).build();
|
||||
private FLabel lblAMC = new FLabel.Builder()
|
||||
.text("Average mana cost: 0.00").tooltip("AVERAGE MANA COST")
|
||||
.fontStyle(Font.BOLD).fontSize(11).fontStyle(Font.BOLD).build();
|
||||
|
||||
// Total and color count labels
|
||||
private final JPanel pnlStats = new JPanel();
|
||||
private final FLabel lblMulti = buildLabel(SItemManagerUtil.StatTypes.MULTICOLOR, true);
|
||||
private final FLabel lblBlack = buildLabel(SItemManagerUtil.StatTypes.BLACK, false);
|
||||
private final FLabel lblBlue = buildLabel(SItemManagerUtil.StatTypes.BLUE, true);
|
||||
private final FLabel lblGreen = buildLabel(SItemManagerUtil.StatTypes.GREEN, false);
|
||||
private final FLabel lblRed = buildLabel(SItemManagerUtil.StatTypes.RED, true);
|
||||
private final FLabel lblWhite = buildLabel(SItemManagerUtil.StatTypes.WHITE, false);
|
||||
private final FLabel lblColorless = buildLabel(SItemManagerUtil.StatTypes.COLORLESS, true);
|
||||
|
||||
// Card type labels
|
||||
private final FLabel lblArtifact = buildLabel(SItemManagerUtil.StatTypes.ARTIFACT, true);
|
||||
private final FLabel lblCreature = buildLabel(SItemManagerUtil.StatTypes.CREATURE, false);
|
||||
private final FLabel lblEnchantment = buildLabel(SItemManagerUtil.StatTypes.ENCHANTMENT, true);
|
||||
private final FLabel lblInstant = buildLabel(SItemManagerUtil.StatTypes.INSTANT, false);
|
||||
private final FLabel lblLand = buildLabel(SItemManagerUtil.StatTypes.LAND, true);
|
||||
private final FLabel lblPlaneswalker = buildLabel(SItemManagerUtil.StatTypes.PLANESWALKER, false);
|
||||
private final FLabel lblSorcery = buildLabel(SItemManagerUtil.StatTypes.SORCERY, true);
|
||||
|
||||
// CMC labels
|
||||
private final FLabel lblCMC0 = buildLabel(SItemManagerUtil.StatTypes.CMC_0, true);
|
||||
private final FLabel lblCMC1 = buildLabel(SItemManagerUtil.StatTypes.CMC_1, false);
|
||||
private final FLabel lblCMC2 = buildLabel(SItemManagerUtil.StatTypes.CMC_2, true);
|
||||
private final FLabel lblCMC3 = buildLabel(SItemManagerUtil.StatTypes.CMC_3, false);
|
||||
private final FLabel lblCMC4 = buildLabel(SItemManagerUtil.StatTypes.CMC_4, true);
|
||||
private final FLabel lblCMC5 = buildLabel(SItemManagerUtil.StatTypes.CMC_5, false);
|
||||
private final FLabel lblCMC6 = buildLabel(SItemManagerUtil.StatTypes.CMC_6, true);
|
||||
|
||||
// Layout containers
|
||||
private final FScrollPane scroller = new FScrollPane(pnlStats, false);
|
||||
|
||||
//========== Constructor
|
||||
private VStatistics() {
|
||||
scroller.getViewport().setBorder(null);
|
||||
|
||||
// Color stats
|
||||
lblMulti.setToolTipText("Multicolor Card Count");
|
||||
lblBlack.setToolTipText("Black Card Count");
|
||||
lblBlue.setToolTipText("Blue Card Count");
|
||||
lblGreen.setToolTipText("Green Card Count");
|
||||
lblRed.setToolTipText("Red Card Count");
|
||||
lblWhite.setToolTipText("White Card Count");
|
||||
lblColorless.setToolTipText("Colorless Card Count");
|
||||
|
||||
// Type stats
|
||||
lblArtifact.setToolTipText("Artifact Card Count");
|
||||
lblCreature.setToolTipText("Creature Card Count");
|
||||
lblEnchantment.setToolTipText("Enchantment Card Count");
|
||||
lblInstant.setToolTipText("Instant Card Count");
|
||||
lblLand.setToolTipText("Land Card Count");
|
||||
lblPlaneswalker.setToolTipText("Planeswalker Card Count");
|
||||
lblSorcery.setToolTipText("Sorcery Card Count");
|
||||
|
||||
// CMC stats
|
||||
lblCMC0.setToolTipText("CMC 0 Card Count");
|
||||
lblCMC1.setToolTipText("CMC 1 Card Count");
|
||||
lblCMC2.setToolTipText("CMC 2 Card Count");
|
||||
lblCMC3.setToolTipText("CMC 3 Card Count");
|
||||
lblCMC4.setToolTipText("CMC 4 Card Count");
|
||||
lblCMC5.setToolTipText("CMC 5 Card Count");
|
||||
lblCMC6.setToolTipText("CMC 6+ Card Count");
|
||||
|
||||
// Stats container
|
||||
pnlStats.setOpaque(false);
|
||||
pnlStats.setLayout(new MigLayout("insets 0, gap 0, ax center, wrap 3"));
|
||||
|
||||
pnlStats.add(lblTotal, "w 96%!, h 20px!, span 3 1, gap 2% 0 0 0");
|
||||
pnlStats.add(lblTMC, "w 96%!, h 20px!, span 3 1, gap 2% 0 0 0");
|
||||
pnlStats.add(lblAMC, "w 96%!, h 20px!, span 3 1, gap 2% 0 0 0");
|
||||
|
||||
// Add labels to container
|
||||
final String constraints = "w 32%!, h 35px!";
|
||||
pnlStats.add(lblMulti, constraints);
|
||||
pnlStats.add(lblArtifact, constraints);
|
||||
pnlStats.add(lblCMC0, constraints);
|
||||
|
||||
pnlStats.add(lblBlack, constraints);
|
||||
pnlStats.add(lblCreature, constraints);
|
||||
pnlStats.add(lblCMC1, constraints);
|
||||
|
||||
pnlStats.add(lblBlue, constraints);
|
||||
pnlStats.add(lblEnchantment, constraints);
|
||||
pnlStats.add(lblCMC2, constraints);
|
||||
|
||||
pnlStats.add(lblGreen, constraints);
|
||||
pnlStats.add(lblInstant, constraints);
|
||||
pnlStats.add(lblCMC3, constraints);
|
||||
|
||||
pnlStats.add(lblRed, constraints);
|
||||
pnlStats.add(lblLand, constraints);
|
||||
pnlStats.add(lblCMC4, constraints);
|
||||
|
||||
pnlStats.add(lblWhite, constraints);
|
||||
pnlStats.add(lblPlaneswalker, constraints);
|
||||
pnlStats.add(lblCMC5, constraints);
|
||||
|
||||
pnlStats.add(lblColorless, constraints);
|
||||
pnlStats.add(lblSorcery, constraints);
|
||||
pnlStats.add(lblCMC6, constraints);
|
||||
}
|
||||
|
||||
//========== Overridden methods
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getDocumentID()
|
||||
*/
|
||||
@Override
|
||||
public EDocID getDocumentID() {
|
||||
return EDocID.EDITOR_STATISTICS;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getTabLabel()
|
||||
*/
|
||||
@Override
|
||||
public DragTab getTabLabel() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getLayoutControl()
|
||||
*/
|
||||
@Override
|
||||
public CStatistics getLayoutControl() {
|
||||
return CStatistics.SINGLETON_INSTANCE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell)
|
||||
*/
|
||||
@Override
|
||||
public void setParentCell(final DragCell cell0) {
|
||||
this.parentCell = cell0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#getParentCell()
|
||||
*/
|
||||
@Override
|
||||
public DragCell getParentCell() {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.IVDoc#populate()
|
||||
*/
|
||||
@Override
|
||||
public void populate() {
|
||||
parentCell.getBody().setLayout(new MigLayout("insets 0, gap 0"));
|
||||
parentCell.getBody().add(scroller, "w 96%!, h 96%!, gap 2% 0 2% 0");
|
||||
}
|
||||
|
||||
//========== Retrieval methods
|
||||
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblMulti() { return lblMulti; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblBlack() { return lblBlack; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblBlue() { return lblBlue; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblGreen() { return lblGreen; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblRed() { return lblRed; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblWhite() { return lblWhite; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblColorless() { return lblColorless; }
|
||||
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblArtifact() { return lblArtifact; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblEnchantment() { return lblEnchantment; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCreature() { return lblCreature; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblSorcery() { return lblSorcery; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblInstant() { return lblInstant; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblPlaneswalker() { return lblPlaneswalker; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblLand() { return lblLand; }
|
||||
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC0() { return lblCMC0; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC1() { return lblCMC1; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC2() { return lblCMC2; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC3() { return lblCMC3; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC4() { return lblCMC4; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC5() { return lblCMC5; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblCMC6() { return lblCMC6; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblTotal() { return lblTotal; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblTMC() { return lblTMC; }
|
||||
/** @return {@link forge.gui.toolbox.FLabel} */
|
||||
public FLabel getLblAMC() { return lblAMC; }
|
||||
|
||||
//========== Other methods
|
||||
|
||||
private FLabel buildLabel(SkinImage icon, boolean zebra) {
|
||||
final FLabel lbl = new FLabel.Builder().text("0")
|
||||
.icon(icon).iconScaleAuto(false)
|
||||
.fontSize(11).build();
|
||||
|
||||
if (zebra) {
|
||||
lbl.setOpaque(true);
|
||||
lbl.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
|
||||
}
|
||||
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private FLabel buildLabel(SItemManagerUtil.StatTypes statType, boolean zebra) {
|
||||
return buildLabel(statType.img, zebra);
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.download;
|
||||
|
||||
import forge.ImageCache;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardRules;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.NewConstants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GuiDownloadPicturesLQ extends GuiDownloader {
|
||||
public GuiDownloadPicturesLQ() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Map<String, String> getNeededImages() {
|
||||
Map<String, String> downloads = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
for (PaperCard c : Singletons.getMagicDb().getCommonCards().getAllCards()) {
|
||||
addDLObject(c, downloads, false);
|
||||
if (ImageCache.hasBackFacePicture(c))
|
||||
addDLObject(c, downloads, true);
|
||||
}
|
||||
|
||||
for (PaperCard c : Singletons.getMagicDb().getVariantCards().getAllCards()) {
|
||||
addDLObject(c, downloads, false);
|
||||
}
|
||||
|
||||
// Add missing tokens to the list of things to download.
|
||||
addMissingItems(downloads, NewConstants.IMAGE_LIST_TOKENS_FILE, NewConstants.CACHE_TOKEN_PICS_DIR);
|
||||
|
||||
return downloads;
|
||||
}
|
||||
|
||||
private void addDLObject(PaperCard c, Map<String, String> downloads, boolean backFace) {
|
||||
CardRules cardRules = c.getRules();
|
||||
String urls = cardRules.getPictureUrl(backFace);
|
||||
if (StringUtils.isEmpty(urls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String filename = ImageCache.getImageKey(c, backFace, false);
|
||||
File destFile = new File(NewConstants.CACHE_CARD_PICS_DIR, filename + ".jpg");
|
||||
if (destFile.exists())
|
||||
return;
|
||||
|
||||
filename = destFile.getAbsolutePath();
|
||||
|
||||
if (downloads.containsKey(filename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String urlToDownload;
|
||||
int urlIndex = 0;
|
||||
int allUrlsLen = 1;
|
||||
if (urls.indexOf("\\") < 0)
|
||||
urlToDownload = urls;
|
||||
else {
|
||||
String[] allUrls = urls.split("\\\\");
|
||||
allUrlsLen = allUrls.length;
|
||||
urlIndex = (c.getArtIndex()-1) % allUrlsLen;
|
||||
urlToDownload = allUrls[urlIndex];
|
||||
}
|
||||
|
||||
//System.out.println(c.getName() + "|" + c.getEdition() + " - " + c.getArtIndex() + " -> " + urlIndex + "/" + allUrlsLen + " === " + filename + " <<< " + urlToDownload);
|
||||
downloads.put(destFile.getAbsolutePath(), urlToDownload);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.download;
|
||||
|
||||
import forge.properties.NewConstants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GuiDownloadPrices extends GuiDownloader {
|
||||
public GuiDownloadPrices() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> getNeededImages() {
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
result.put(NewConstants.QUEST_CARD_PRICE_FILE, NewConstants.URL_PRICE_DOWNLOAD);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.download;
|
||||
|
||||
import forge.properties.NewConstants;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/** */
|
||||
@SuppressWarnings("serial")
|
||||
public class GuiDownloadQuestImages extends GuiDownloader {
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for GuiDownloadQuestImages.
|
||||
* </p>
|
||||
*/
|
||||
public GuiDownloadQuestImages() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getNeededCards.
|
||||
* </p>
|
||||
*
|
||||
* @return an array of {@link forge.gui.download.GuiDownloadSetPicturesLQ} objects.
|
||||
*/
|
||||
@Override
|
||||
protected final Map<String, String> getNeededImages() {
|
||||
// read all card names and urls
|
||||
final Map<String, String> urls = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_OPPONENT_ICONS_FILE, NewConstants.CACHE_ICON_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_PET_SHOP_ICONS_FILE, NewConstants.CACHE_ICON_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_BOOSTERS_FILE, NewConstants.CACHE_BOOSTER_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_FATPACKS_FILE, NewConstants.CACHE_FATPACK_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_PRECONS_FILE, NewConstants.CACHE_PRECON_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_TOURNAMENTPACKS_FILE, NewConstants.CACHE_TOURNAMENTPACK_PICS_DIR);
|
||||
addMissingItems(urls, NewConstants.IMAGE_LIST_QUEST_TOKENS_FILE, NewConstants.CACHE_TOKEN_PICS_DIR);
|
||||
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.download;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ImageCache;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardEdition;
|
||||
import forge.item.PaperCard;
|
||||
import forge.properties.NewConstants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GuiDownloadSetPicturesLQ extends GuiDownloader {
|
||||
public GuiDownloadSetPicturesLQ() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Map<String, String> getNeededImages() {
|
||||
Map<String, String> downloads = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
for (final PaperCard c : Iterables.concat(Singletons.getMagicDb().getCommonCards().getAllCards(), Singletons.getMagicDb().getVariantCards().getAllCards())) {
|
||||
final String setCode3 = c.getEdition();
|
||||
if (StringUtils.isBlank(setCode3) || CardEdition.UNKNOWN.getCode().equals(setCode3)) {
|
||||
// we don't want cards from unknown sets
|
||||
continue;
|
||||
}
|
||||
addDLObject(ImageCache.getDownloadUrl(c, false), ImageCache.getImageKey(c, false, true), downloads);
|
||||
|
||||
if (ImageCache.hasBackFacePicture(c)) {
|
||||
addDLObject(ImageCache.getDownloadUrl(c, true), ImageCache.getImageKey(c, true, true), downloads);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing tokens to the list of things to download.
|
||||
addMissingItems(downloads, NewConstants.IMAGE_LIST_TOKENS_FILE, NewConstants.CACHE_TOKEN_PICS_DIR);
|
||||
|
||||
return downloads;
|
||||
}
|
||||
|
||||
private void addDLObject(String urlPath, String filename, Map<String, String> downloads) {
|
||||
File destFile = new File(NewConstants.CACHE_CARD_PICS_DIR, filename + ".jpg");
|
||||
// System.out.println(filename);
|
||||
if (!destFile.exists()) {
|
||||
downloads.put(destFile.getAbsolutePath(), NewConstants.URL_PIC_DOWNLOAD + urlPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.gui.download;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import forge.UiCommand;
|
||||
import forge.ImageCache;
|
||||
import forge.error.BugReporter;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.gui.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 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 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");
|
||||
|
||||
// Proxy info
|
||||
private int type;
|
||||
|
||||
// Progress variables
|
||||
private Map<String, String> 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 GuiDownloader() {
|
||||
String radConstraints = "w 100%!, h 30px!, gap 2% 0 0 10px";
|
||||
JXButtonPanel grpPanel = new JXButtonPanel();
|
||||
grpPanel.add(radProxyNone, radConstraints);
|
||||
grpPanel.add(radProxyHTTP, radConstraints);
|
||||
grpPanel.add(radProxySocks, radConstraints);
|
||||
|
||||
radProxyNone.addChangeListener(new ProxyHandler(0));
|
||||
radProxyHTTP.addChangeListener(new ProxyHandler(1));
|
||||
radProxySocks.addChangeListener(new ProxyHandler(2));
|
||||
radProxyNone.setSelected(true);
|
||||
|
||||
btnClose.setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_TEXT)));
|
||||
btnStart.setFont(FSkin.getFont(18));
|
||||
btnStart.setVisible(false);
|
||||
|
||||
barProgress.reset();
|
||||
barProgress.setString("Scanning for existing items...");
|
||||
pnlDialog.setBackgroundTexture(FSkin.getIcon(FSkin.Backgrounds.BG_TEXTURE));
|
||||
|
||||
// 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(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");
|
||||
|
||||
final JPanel pnl = FOverlay.SINGLETON_INSTANCE.getPanel();
|
||||
pnl.removeAll();
|
||||
pnl.setLayout(new MigLayout("insets 0, gap 0, wrap, ax center, ay center"));
|
||||
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<Void, Void> thrGetImages = new SwingWorker<Void, Void>() {
|
||||
@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<String, String> 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
btnStart.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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<String, String> 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<String, String> getNeededImages();
|
||||
|
||||
protected static void addMissingItems(Map<String, String> list, String nameUrlFile, String dir) {
|
||||
for (Pair<String, String> 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 final int type;
|
||||
|
||||
public ProxyHandler(final int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/** Forge Card Game. */
|
||||
package forge.gui.download;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
import forge.UiCommand;
|
||||
|
||||
/**
|
||||
* An intentionally empty ICDoc to fill field slots unused
|
||||
* by the current layout of a match UI.
|
||||
*/
|
||||
public class CEmptyDoc implements ICDoc {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
*/
|
||||
@Override
|
||||
public UiCommand getCommandOnSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#update()
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
}
|
||||
@@ -1,475 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.Singletons;
|
||||
import forge.gui.toolbox.FPanel;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinImage;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.view.FView;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Top-level container in drag layout. A cell holds
|
||||
* tabs, a drag handle, and a tab overflow selector.
|
||||
* <br>A cell also has two borders, right and bottom,
|
||||
* for resizing.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class DragCell extends JPanel implements ILocalRepaint {
|
||||
// Layout creation worker vars
|
||||
private RectangleOfDouble roughSize;
|
||||
private int smoothX = 0;
|
||||
private int smoothY = 0;
|
||||
private int smoothW = 0;
|
||||
private int smoothH = 0;
|
||||
|
||||
// Core layout stuff
|
||||
private final JPanel pnlHead = new JPanel(new MigLayout("insets 0, gap 0, hidemode 3"));
|
||||
private final FPanel pnlBody = new FPanel();
|
||||
private final JPanel pnlBorderRight = new JPanel();
|
||||
private final JPanel pnlBorderBottom = new JPanel();
|
||||
private final int tabPaddingPx = 2;
|
||||
private final int margin = 2 * tabPaddingPx;
|
||||
|
||||
// Tab handling layout stuff
|
||||
private final List<IVDoc<? extends ICDoc>> allDocs = new ArrayList<IVDoc<? extends ICDoc>>();
|
||||
private final JLabel lblHandle = new DragHandle();
|
||||
private final JLabel lblOverflow = new JLabel();
|
||||
private IVDoc<? extends ICDoc> docSelected = null;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public DragCell() {
|
||||
super(new MigLayout("insets 0, gap 0, wrap 2"));
|
||||
|
||||
this.setOpaque(false);
|
||||
pnlHead.setOpaque(false);
|
||||
|
||||
pnlHead.setBackground(Color.DARK_GRAY);
|
||||
|
||||
lblOverflow.setForeground(Color.white);
|
||||
lblOverflow.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
lblOverflow.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
lblOverflow.setFont(new Font(Font.DIALOG, Font.PLAIN, 10));
|
||||
lblOverflow.setOpaque(true);
|
||||
lblOverflow.setBackground(Color.black);
|
||||
lblOverflow.setToolTipText("Other tabs");
|
||||
|
||||
pnlBorderRight.setOpaque(false);
|
||||
pnlBorderRight.addMouseListener(SResizingUtil.getResizeXListener());
|
||||
pnlBorderRight.addMouseMotionListener(SResizingUtil.getDragXListener());
|
||||
|
||||
pnlBorderBottom.setOpaque(false);
|
||||
pnlBorderBottom.addMouseListener(SResizingUtil.getResizeYListener());
|
||||
pnlBorderBottom.addMouseMotionListener(SResizingUtil.getDragYListener());
|
||||
|
||||
lblOverflow.addMouseListener(SOverflowUtil.getOverflowListener());
|
||||
|
||||
pnlHead.add(lblHandle, "pushx, growx, h 100%!, gap " + tabPaddingPx + "px " + tabPaddingPx + "px 0 0", -1);
|
||||
pnlHead.add(lblOverflow, "w 20px!, h 100%!, gap " + tabPaddingPx + "px " + tabPaddingPx + "px 0 0", -1);
|
||||
|
||||
pnlBody.setCornerDiameter(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the cell layout without affecting contents.
|
||||
* <p>
|
||||
* Primarily used to toggle visibility of tabs.
|
||||
*/
|
||||
public void doCellLayout(boolean showTabs) {
|
||||
this.removeAll();
|
||||
int borderT = SLayoutConstants.BORDER_T;
|
||||
int headH = ((showTabs || allDocs.size() > 1) ? SLayoutConstants.HEAD_H : 0);
|
||||
this.add(pnlHead,
|
||||
"w 100% - " + borderT + "px!" + ", " + "h " + headH + "px!");
|
||||
this.add(pnlBorderRight,
|
||||
"w " + borderT + "px!" + ", " + "h 100% - " + borderT + "px!, span 1 2");
|
||||
this.add(pnlBody,
|
||||
"w 100% - " + borderT + "px!" + ", " + "h 100% - " + (headH + borderT) + "px!");
|
||||
this.add(pnlBorderBottom,
|
||||
"w 100% - " + borderT + "px!" + ", " + "h " + borderT + "px!");
|
||||
if (this.isShowing()) {
|
||||
this.validate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines visibility of tabs on game screen.
|
||||
*/
|
||||
private boolean showGameTabs() {
|
||||
ForgePreferences prefs = Singletons.getModel().getPreferences();
|
||||
return !prefs.getPrefBoolean(FPref.UI_HIDE_GAME_TABS);
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public JPanel getHead() {
|
||||
return DragCell.this.pnlHead;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public JPanel getBody() {
|
||||
return DragCell.this.pnlBody;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public JPanel getBorderRight() {
|
||||
return DragCell.this.pnlBorderRight;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JPanel} */
|
||||
public JPanel getBorderBottom() {
|
||||
return DragCell.this.pnlBorderBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a defensive copy list of all documents in this cell.
|
||||
* @return {@link java.util.List}<{@link forge.gui.framework.IVDoc}>
|
||||
*/
|
||||
public List<IVDoc<? extends ICDoc>> getDocs() {
|
||||
return Lists.newArrayList(allDocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintSelf() {
|
||||
final Dimension d = DragCell.this.getSize();
|
||||
repaint(0, 0, d.width, d.height);
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public int getW() {
|
||||
return this.getWidth();
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public int getH() {
|
||||
return this.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen location of left edge of cell.
|
||||
* @return int
|
||||
*/
|
||||
public int getAbsX() {
|
||||
int i = 0;
|
||||
|
||||
try { i = (int) this.getLocationOnScreen().getX(); }
|
||||
catch (final Exception e) { }
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen location of right edge of cell.
|
||||
* @return int
|
||||
*/
|
||||
public int getAbsX2() {
|
||||
int i = 0;
|
||||
|
||||
try { i = this.getAbsX() + this.getW(); }
|
||||
catch (final Exception e) { }
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen location of upper edge of cell.
|
||||
* @return int
|
||||
*/
|
||||
public int getAbsY() {
|
||||
int i = 0;
|
||||
|
||||
try { i = (int) this.getLocationOnScreen().getY(); }
|
||||
catch (final Exception e) { }
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen location of lower edge of cell.
|
||||
* @return int
|
||||
*/
|
||||
public int getAbsY2() {
|
||||
int i = 0;
|
||||
|
||||
try { i = this.getAbsY() + this.getH(); }
|
||||
catch (final Exception e) { }
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically calculates rough bounds of this cell.
|
||||
* @param rectangleOfDouble
|
||||
*/
|
||||
public void updateRoughBounds() {
|
||||
final double contentW = FView.SINGLETON_INSTANCE.getPnlContent().getWidth();
|
||||
final double contentH = FView.SINGLETON_INSTANCE.getPnlContent().getHeight();
|
||||
|
||||
this.roughSize = new RectangleOfDouble(this.getX() / contentW, this.getY() / contentH,
|
||||
this.getW() / contentW, this.getH() / contentH);
|
||||
}
|
||||
|
||||
/** Explicitly sets percent bounds of this cell. Will be smoothed
|
||||
* later to avoid pixel rounding errors.
|
||||
* @param x0   double
|
||||
* @param y0   double
|
||||
* @param w0   double
|
||||
* @param h0   double
|
||||
*/
|
||||
public void setRoughBounds(RectangleOfDouble rectangleOfDouble) {
|
||||
this.roughSize = rectangleOfDouble;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @return
|
||||
*/
|
||||
public RectangleOfDouble getRoughBounds() {
|
||||
return roughSize;
|
||||
}
|
||||
|
||||
/** Sets bounds in superclass using smoothed values from this class. */
|
||||
public void setSmoothBounds() {
|
||||
super.setBounds(smoothX, smoothY, smoothW, smoothH);
|
||||
}
|
||||
|
||||
/** @param x0   int */
|
||||
public void setSmoothX(final int x0) {
|
||||
this.smoothX = x0;
|
||||
}
|
||||
|
||||
/** @param y0   int */
|
||||
public void setSmoothY(final int y0) {
|
||||
this.smoothY = y0;
|
||||
}
|
||||
|
||||
/** @param w0   int */
|
||||
public void setSmoothW(final int w0) {
|
||||
this.smoothW = w0;
|
||||
}
|
||||
|
||||
/** @param h0   int */
|
||||
public void setSmoothH(final int h0) {
|
||||
this.smoothH = h0;
|
||||
}
|
||||
|
||||
/** Adds a document to the tabs.
|
||||
* @param doc0   {@link forge.gui.framework.IVDoc} */
|
||||
public void addDoc(final IVDoc<? extends ICDoc> doc0) {
|
||||
if (doc0 instanceof VEmptyDoc) { return; }
|
||||
allDocs.add(doc0);
|
||||
doc0.setParentCell(this);
|
||||
pnlHead.add(doc0.getTabLabel(), "h 100%!, gap " + tabPaddingPx + "px " + tabPaddingPx + "px 0 0", allDocs.size() - 1);
|
||||
|
||||
// Ensure that a tab is selected
|
||||
setSelected(getSelected());
|
||||
|
||||
doCellLayout(showGameTabs());
|
||||
}
|
||||
|
||||
/** Removes a document from the layout and tabs.
|
||||
* @param doc0   {@link forge.gui.framework.IVDoc} */
|
||||
public void removeDoc(final IVDoc<? extends ICDoc> doc0) {
|
||||
boolean wasSelected = (docSelected == doc0);
|
||||
allDocs.remove(doc0);
|
||||
pnlHead.remove(doc0.getTabLabel());
|
||||
if (wasSelected) { //after removing selected doc, select most recent doc if possible
|
||||
setSelected(null);
|
||||
}
|
||||
|
||||
doCellLayout(showGameTabs());
|
||||
}
|
||||
|
||||
/** - Deselects previous selection, if there is one<br>
|
||||
* - Decrements the priorities of all other tabs<br>
|
||||
* - Sets selected as priority 1<br>
|
||||
*
|
||||
* <br><b>null</b> will reset
|
||||
* (deselect all tabs, and then select the first in the group).
|
||||
*
|
||||
* <br><br>Unless there are no tab docs in this cell, there
|
||||
* will always be a selection.
|
||||
*
|
||||
* @param doc0   {@link forge.gui.framework.IVDoc} tab document.
|
||||
*/
|
||||
public void setSelected(final IVDoc<? extends ICDoc> doc0) {
|
||||
if (null != doc0 && docSelected == doc0) {
|
||||
// already selected
|
||||
return;
|
||||
}
|
||||
|
||||
docSelected = null;
|
||||
pnlBody.removeAll();
|
||||
|
||||
// Priorities are used to "remember" tab selection history.
|
||||
for (final IVDoc<? extends ICDoc> doc : allDocs) {
|
||||
if (doc.equals(doc0)) {
|
||||
docSelected = doc0;
|
||||
doc.getTabLabel().priorityOne();
|
||||
doc.getTabLabel().setSelected(true);
|
||||
doc.populate();
|
||||
doc.getLayoutControl().update();
|
||||
}
|
||||
else {
|
||||
doc.getTabLabel().setSelected(false);
|
||||
doc.getTabLabel().priorityDecrease();
|
||||
}
|
||||
}
|
||||
|
||||
pnlBody.revalidate();
|
||||
pnlBody.repaint();
|
||||
|
||||
// Reached the end without a selection? Select the first in the group.
|
||||
if (docSelected == null && allDocs.size() > 0) { setSelected(allDocs.get(0)); }
|
||||
}
|
||||
|
||||
/** Returns currently selected document in this cell.
|
||||
* @return {@link forge.gui.framework.IVDoc} */
|
||||
public IVDoc<? extends ICDoc> getSelected() {
|
||||
return docSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable resize on the X axis for this cell.
|
||||
*
|
||||
* @param enable0   boolean
|
||||
*/
|
||||
public void toggleResizeX(final boolean enable0) {
|
||||
this.removeMouseListener(SResizingUtil.getResizeXListener());
|
||||
|
||||
if (enable0) {
|
||||
this.addMouseListener(SResizingUtil.getResizeXListener());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable resize on the Y axis for this cell.
|
||||
*
|
||||
* @param enable0   boolean
|
||||
*/
|
||||
public void toggleResizeY(final boolean enable0) {
|
||||
this.removeMouseListener(SResizingUtil.getResizeYListener());
|
||||
|
||||
if (enable0) {
|
||||
this.addMouseListener(SResizingUtil.getResizeYListener());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes visual display of head bar.
|
||||
*/
|
||||
public void refresh() {
|
||||
final int headW = pnlHead.getWidth();
|
||||
if (docSelected == null) { return; }
|
||||
if (headW <= 0) { return; }
|
||||
if (allDocs.isEmpty()) { return; }
|
||||
|
||||
// Order tabs by priority
|
||||
final List<DragTab> priority = new ArrayList<DragTab>();
|
||||
final DragTab selectedTab = docSelected.getTabLabel();
|
||||
DragTab nextTab = selectedTab;
|
||||
|
||||
while (nextTab != null) {
|
||||
priority.add(nextTab);
|
||||
nextTab = getNextTabInPriority(nextTab.getPriority());
|
||||
}
|
||||
|
||||
// Like Einstein's cosmological constant, the extra "8" here
|
||||
// makes the whole thing work, but the reason for its existence is unknown.
|
||||
// Part of it is 4px of padding from lblHandle...
|
||||
int tempW = lblOverflow.getWidth() + margin + 8;
|
||||
int docOverflowCounter = 0;
|
||||
|
||||
// Hide/show all other tabs.
|
||||
for (final DragTab tab : priority) {
|
||||
tempW += tab.getWidth() + margin;
|
||||
tab.setVisible(false);
|
||||
tab.setMaximumSize(null);
|
||||
|
||||
if (tab.equals(selectedTab) || tempW < headW) { tab.setVisible(true); }
|
||||
else { docOverflowCounter++; }
|
||||
}
|
||||
|
||||
// Resize selected tab if necessary.
|
||||
tempW = (docOverflowCounter == 0 ? headW - margin : headW - lblOverflow.getWidth() - margin - 10);
|
||||
selectedTab.setMaximumSize(new Dimension(tempW, 20));
|
||||
|
||||
// Update overflow label
|
||||
lblOverflow.setText("+" + docOverflowCounter);
|
||||
if (docOverflowCounter == 0) { lblOverflow.setVisible(false); }
|
||||
else { lblOverflow.setVisible(true); }
|
||||
}
|
||||
|
||||
private DragTab getNextTabInPriority(final int currentPriority0) {
|
||||
DragTab neo = null;
|
||||
DragTab temp;
|
||||
int lowest = Integer.MAX_VALUE;
|
||||
|
||||
for (final IVDoc<? extends ICDoc> d : allDocs) {
|
||||
temp = d.getTabLabel();
|
||||
|
||||
// This line prevents two tabs from having the same priority.
|
||||
if (neo != null && temp.getPriority() == lowest) {
|
||||
temp.priorityDecrease();
|
||||
}
|
||||
|
||||
if (d.equals(docSelected)) { continue; }
|
||||
if (temp.getPriority() > lowest) { continue; }
|
||||
if (temp.getPriority() <= currentPriority0) { continue; }
|
||||
|
||||
// If he's The One, he's made it through the tests.
|
||||
lowest = temp.getPriority();
|
||||
neo = temp;
|
||||
}
|
||||
|
||||
return neo;
|
||||
}
|
||||
|
||||
/** Paints dragging handle image the length of the label. */
|
||||
private class DragHandle extends JLabel {
|
||||
private final SkinImage img = FSkin.getImage(FSkin.LayoutImages.IMG_HANDLE);
|
||||
private boolean hovered = false;
|
||||
|
||||
public DragHandle() {
|
||||
this.addMouseListener(SRearrangingUtil.getRearrangeClickEvent());
|
||||
this.addMouseMotionListener(SRearrangingUtil.getRearrangeDragEvent());
|
||||
|
||||
this.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(final MouseEvent e) {
|
||||
hovered = true; repaintSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(final MouseEvent e) {
|
||||
hovered = false; repaintSelf();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(final Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (!hovered) { return; }
|
||||
|
||||
final Dimension imgSize = img.getSizeForPaint(g);
|
||||
final int imgW = imgSize.width;
|
||||
if (imgW < 1) { return; }
|
||||
final int imgH = imgSize.height;
|
||||
|
||||
for (int x = 0; x < getWidth(); x += imgW) {
|
||||
FSkin.drawImage(g, img, x, ((getHeight() - imgH) / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinnedLabel;
|
||||
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* The tab label object in drag layout.
|
||||
* No modification should be necessary to this object.
|
||||
* Simply call the constructor with a title string argument.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class DragTab extends SkinnedLabel implements ILocalRepaint {
|
||||
private boolean selected = false;
|
||||
private int priority = 10;
|
||||
|
||||
/**
|
||||
* The tab label object in drag layout.
|
||||
* No modification should be necessary to this object.
|
||||
* Simply call the constructor with a title string argument.
|
||||
*
|
||||
* @param title0   {java.lang.String}
|
||||
*/
|
||||
public DragTab(final String title0) {
|
||||
super(title0);
|
||||
setToolTipText(title0);
|
||||
setOpaque(false);
|
||||
setSelected(false);
|
||||
setBorder(new EmptyBorder(2, 5, 2, 5));
|
||||
this.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
|
||||
this.addMouseListener(SRearrangingUtil.getRearrangeClickEvent());
|
||||
this.addMouseMotionListener(SRearrangingUtil.getRearrangeDragEvent());
|
||||
}
|
||||
|
||||
/** @param isSelected0   boolean */
|
||||
public void setSelected(final boolean isSelected0) {
|
||||
selected = isSelected0;
|
||||
repaintSelf();
|
||||
}
|
||||
|
||||
/** Decreases display priority of this tab in relation to its siblings in an overflow case. */
|
||||
public void priorityDecrease() {
|
||||
priority++;
|
||||
}
|
||||
|
||||
/** Sets this tab as first to be displayed if siblings overflow. */
|
||||
public void priorityOne() {
|
||||
priority = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns display priority of this tab in relation to its siblings in an overflow case.
|
||||
* @return int
|
||||
*/
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
// There should be no need for this method.
|
||||
@SuppressWarnings("unused")
|
||||
private void setPriority() {
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintSelf() {
|
||||
final Dimension d = DragTab.this.getSize();
|
||||
repaint(0, 0, d.width, d.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(final Graphics g) {
|
||||
if (!selected) {
|
||||
FSkin.setGraphicsColor(g, FSkin.getColor(FSkin.Colors.CLR_INACTIVE));
|
||||
g.fillRoundRect(0, 0, getWidth() - 1, getHeight() * 2, 6, 6);
|
||||
FSkin.setGraphicsColor(g, FSkin.getColor(FSkin.Colors.CLR_BORDERS));
|
||||
g.drawRoundRect(0, 0, getWidth() - 1, getHeight() * 2, 6, 6);
|
||||
}
|
||||
else {
|
||||
FSkin.setGraphicsColor(g, FSkin.getColor(FSkin.Colors.CLR_ACTIVE));
|
||||
g.fillRoundRect(0, 0, getWidth() - 1, getHeight() * 2, 6, 6);
|
||||
FSkin.setGraphicsColor(g, FSkin.getColor(FSkin.Colors.CLR_BORDERS));
|
||||
g.drawRoundRect(0, 0, getWidth() - 1, getHeight() * 2, 6, 6);
|
||||
}
|
||||
|
||||
super.paintComponent(g);
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package forge.gui.framework;
|
||||
|
||||
import forge.gui.deckeditor.views.*;
|
||||
import forge.gui.home.gauntlet.VSubmenuGauntletBuild;
|
||||
import forge.gui.home.gauntlet.VSubmenuGauntletContests;
|
||||
import forge.gui.home.gauntlet.VSubmenuGauntletLoad;
|
||||
import forge.gui.home.gauntlet.VSubmenuGauntletQuick;
|
||||
import forge.gui.home.quest.*;
|
||||
import forge.gui.home.sanctioned.VSubmenuConstructed;
|
||||
import forge.gui.home.sanctioned.VSubmenuDraft;
|
||||
import forge.gui.home.sanctioned.VSubmenuSealed;
|
||||
import forge.gui.home.settings.VSubmenuAvatars;
|
||||
import forge.gui.home.settings.VSubmenuDownloaders;
|
||||
import forge.gui.home.settings.VSubmenuPreferences;
|
||||
import forge.gui.home.settings.VSubmenuReleaseNotes;
|
||||
import forge.gui.match.views.*;
|
||||
import forge.gui.workshop.views.VCardDesigner;
|
||||
import forge.gui.workshop.views.VCardScript;
|
||||
import forge.gui.workshop.views.VWorkshopCatalog;
|
||||
|
||||
/**
|
||||
* These are the identifiers for tabs found in the drag layout.
|
||||
* These IDs are used in the save XML and card layouts.
|
||||
*
|
||||
* <br><br><i>(E at beginning of class name denotes an enum.)</i>
|
||||
*/
|
||||
public enum EDocID { /** */
|
||||
CARD_PICTURE (VPicture.SINGLETON_INSTANCE), /** */
|
||||
CARD_DETAIL (VDetail.SINGLETON_INSTANCE), /** */
|
||||
CARD_ANTES (VAntes.SINGLETON_INSTANCE), /** */
|
||||
|
||||
EDITOR_ALLDECKS (VAllDecks.SINGLETON_INSTANCE), /** */
|
||||
EDITOR_STATISTICS (VStatistics.SINGLETON_INSTANCE), /** */
|
||||
EDITOR_PROBABILITIES (VProbabilities.SINGLETON_INSTANCE), /** */
|
||||
EDITOR_CATALOG (VCardCatalog.SINGLETON_INSTANCE), /** */
|
||||
EDITOR_CURRENTDECK (VCurrentDeck.SINGLETON_INSTANCE), /** */
|
||||
EDITOR_DECKGEN (VDeckgen.SINGLETON_INSTANCE), /** */
|
||||
|
||||
WORKSHOP_CATALOG (VWorkshopCatalog.SINGLETON_INSTANCE), /** */
|
||||
WORKSHOP_CARDDESIGNER (VCardDesigner.SINGLETON_INSTANCE), /** */
|
||||
WORKSHOP_CARDSCRIPT (VCardScript.SINGLETON_INSTANCE), /** */
|
||||
|
||||
HOME_QUESTCHALLENGES (VSubmenuChallenges.SINGLETON_INSTANCE), /** */
|
||||
HOME_QUESTDUELS (VSubmenuDuels.SINGLETON_INSTANCE), /** */
|
||||
HOME_QUESTDATA (VSubmenuQuestData.SINGLETON_INSTANCE), /** */
|
||||
HOME_QUESTDECKS (VSubmenuQuestDecks.SINGLETON_INSTANCE), /** */
|
||||
HOME_QUESTPREFS (VSubmenuQuestPrefs.SINGLETON_INSTANCE), /** */
|
||||
HOME_GAUNTLETBUILD (VSubmenuGauntletBuild.SINGLETON_INSTANCE), /** */
|
||||
HOME_GAUNTLETLOAD (VSubmenuGauntletLoad.SINGLETON_INSTANCE), /** */
|
||||
HOME_GAUNTLETQUICK (VSubmenuGauntletQuick.SINGLETON_INSTANCE), /** */
|
||||
HOME_GAUNTLETCONTESTS (VSubmenuGauntletContests.SINGLETON_INSTANCE), /** */
|
||||
HOME_PREFERENCES (VSubmenuPreferences.SINGLETON_INSTANCE), /** */
|
||||
HOME_AVATARS (VSubmenuAvatars.SINGLETON_INSTANCE), /** */
|
||||
HOME_UTILITIES (VSubmenuDownloaders.SINGLETON_INSTANCE), /** */
|
||||
HOME_CONSTRUCTED (VSubmenuConstructed.SINGLETON_INSTANCE), /** */
|
||||
HOME_DRAFT (VSubmenuDraft.SINGLETON_INSTANCE), /** */
|
||||
HOME_SEALED (VSubmenuSealed.SINGLETON_INSTANCE), /** */
|
||||
HOME_RELEASE_NOTES (VSubmenuReleaseNotes.SINGLETON_INSTANCE),
|
||||
|
||||
REPORT_MESSAGE (VPrompt.SINGLETON_INSTANCE), /** */
|
||||
REPORT_STACK (VStack.SINGLETON_INSTANCE), /** */
|
||||
REPORT_COMBAT (VCombat.SINGLETON_INSTANCE), /** */
|
||||
REPORT_LOG (VLog.SINGLETON_INSTANCE), /** */
|
||||
REPORT_PLAYERS (VPlayers.SINGLETON_INSTANCE), /** */
|
||||
|
||||
DEV_MODE (VDev.SINGLETON_INSTANCE), /** */
|
||||
BUTTON_DOCK (VDock.SINGLETON_INSTANCE), /** */
|
||||
|
||||
// Non-user battlefields (AI or teammate), use setDoc to register.
|
||||
FIELD_0 (null), /** */
|
||||
FIELD_1 (null), /** */
|
||||
FIELD_2 (null), /** */
|
||||
FIELD_3 (null), /** */
|
||||
FIELD_4 (null), /** */
|
||||
FIELD_5 (null), /** */
|
||||
FIELD_6 (null), /** */
|
||||
FIELD_7 (null), /** */
|
||||
|
||||
// Non-user hands (AI or teammate), use setDoc to register.
|
||||
HAND_0 (null), /** */
|
||||
HAND_1 (null), /** */
|
||||
HAND_2 (null), /** */
|
||||
HAND_3 (null), /** */
|
||||
HAND_4 (null), /** */
|
||||
HAND_5 (null), /** */
|
||||
HAND_6 (null), /** */
|
||||
HAND_7 (null), /** */
|
||||
|
||||
COMMAND_0 (null), /** */
|
||||
COMMAND_1 (null), /** */
|
||||
COMMAND_2 (null), /** */
|
||||
COMMAND_3 (null), /** */
|
||||
COMMAND_4 (null), /** */
|
||||
COMMAND_5 (null), /** */
|
||||
COMMAND_6 (null), /** */
|
||||
COMMAND_7 (null); /** */
|
||||
|
||||
public final static EDocID[] Commands = new EDocID[] {COMMAND_0, COMMAND_1, COMMAND_2, COMMAND_3, COMMAND_4, COMMAND_5, COMMAND_6, COMMAND_7};
|
||||
public final static EDocID[] Fields = new EDocID[] {FIELD_0, FIELD_1, FIELD_2, FIELD_3, FIELD_4, FIELD_5, FIELD_6, FIELD_7};
|
||||
public final static EDocID[] Hands = new EDocID[] {HAND_0, HAND_1, HAND_2, HAND_3, HAND_4, HAND_5, HAND_6, HAND_7};
|
||||
|
||||
// End enum declarations, start enum methods.
|
||||
private IVDoc<? extends ICDoc> vDoc;
|
||||
|
||||
/** @param doc0   {@link forge.gui.framework.IVDoc} */
|
||||
EDocID(final IVDoc<? extends ICDoc> doc0) {
|
||||
this.vDoc = doc0;
|
||||
}
|
||||
|
||||
/** @param doc0   {@link forge.gui.framework.IVDoc} */
|
||||
public void setDoc(final IVDoc<? extends ICDoc> doc0) {
|
||||
this.vDoc = doc0;
|
||||
}
|
||||
|
||||
/** @return {@link forge.gui.framework.IVDoc} */
|
||||
public IVDoc<? extends ICDoc> getDoc() {
|
||||
if (vDoc == null) { throw new NullPointerException("No document found for " + this.name() + "."); }
|
||||
return vDoc;
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.gui.bazaar.CBazaarUI;
|
||||
import forge.gui.bazaar.VBazaarUI;
|
||||
import forge.gui.deckeditor.CDeckEditorUI;
|
||||
import forge.gui.deckeditor.VDeckEditorUI;
|
||||
import forge.gui.home.CHomeUI;
|
||||
import forge.gui.home.VHomeUI;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.VMatchUI;
|
||||
import forge.gui.toolbox.FOptionPane;
|
||||
import forge.gui.toolbox.FSkin;
|
||||
import forge.gui.toolbox.FSkin.SkinImage;
|
||||
import forge.gui.workshop.CWorkshopUI;
|
||||
import forge.gui.workshop.VWorkshopUI;
|
||||
import forge.properties.FileLocation;
|
||||
import forge.properties.NewConstants;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Definitions for Forge screens
|
||||
*
|
||||
*/
|
||||
public enum FScreen {
|
||||
HOME_SCREEN(
|
||||
VHomeUI.SINGLETON_INSTANCE,
|
||||
CHomeUI.SINGLETON_INSTANCE,
|
||||
"Home",
|
||||
FSkin.getIcon(FSkin.InterfaceIcons.ICO_FAVICON),
|
||||
false,
|
||||
"Exit Forge",
|
||||
null),
|
||||
MATCH_SCREEN(
|
||||
VMatchUI.SINGLETON_INSTANCE,
|
||||
CMatchUI.SINGLETON_INSTANCE,
|
||||
"Game",
|
||||
FSkin.getIcon(FSkin.DockIcons.ICO_ALPHASTRIKE), //TODO: Create icon for match screen
|
||||
true,
|
||||
"Concede Game",
|
||||
NewConstants.MATCH_LAYOUT_FILE),
|
||||
WORKSHOP_SCREEN(
|
||||
VWorkshopUI.SINGLETON_INSTANCE,
|
||||
CWorkshopUI.SINGLETON_INSTANCE,
|
||||
"Workshop",
|
||||
FSkin.getIcon(FSkin.DockIcons.ICO_SETTINGS), //TODO: Create icon for workshop screen
|
||||
false,
|
||||
"Back to Home",
|
||||
NewConstants.WORKSHOP_LAYOUT_FILE),
|
||||
DECK_EDITOR_CONSTRUCTED(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
false,
|
||||
"Back to Home",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_ARCHENEMY(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Scheme Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_COMMANDER(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Commander Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_PLANECHASE(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Planar Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_VANGUARD(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Vanguard Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_DRAFT(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Draft Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_SEALED(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Sealed Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DECK_EDITOR_QUEST(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Quest Deck Editor",
|
||||
FSkin.getImage(FSkin.EditorImages.IMG_PACK),
|
||||
true,
|
||||
"Close Editor",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
QUEST_CARD_SHOP(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Spell Shop",
|
||||
FSkin.getIcon(FSkin.QuestIcons.ICO_BOOK),
|
||||
true,
|
||||
"Leave Shop",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
DRAFTING_PROCESS(
|
||||
VDeckEditorUI.SINGLETON_INSTANCE,
|
||||
CDeckEditorUI.SINGLETON_INSTANCE,
|
||||
"Draft",
|
||||
FSkin.getImage(FSkin.ZoneImages.IMG_HAND),
|
||||
true,
|
||||
"Leave Draft",
|
||||
NewConstants.EDITOR_LAYOUT_FILE),
|
||||
QUEST_BAZAAR(
|
||||
VBazaarUI.SINGLETON_INSTANCE,
|
||||
CBazaarUI.SINGLETON_INSTANCE,
|
||||
"Bazaar",
|
||||
FSkin.getIcon(FSkin.QuestIcons.ICO_BOTTLES),
|
||||
true,
|
||||
"Leave Bazaar",
|
||||
null);
|
||||
|
||||
private final IVTopLevelUI view;
|
||||
private final ICDoc controller;
|
||||
private final String tabCaption;
|
||||
private final SkinImage tabIcon;
|
||||
private final boolean allowTabClose;
|
||||
private final String closeButtonTooltip;
|
||||
private final FileLocation layoutFile;
|
||||
|
||||
private FScreen(IVTopLevelUI view0, ICDoc controller0, String tabCaption0, SkinImage tabIcon0, boolean allowTabClose0, String closeButtonTooltip0, FileLocation layoutFile0) {
|
||||
this.view = view0;
|
||||
this.controller = controller0;
|
||||
this.tabCaption = tabCaption0;
|
||||
this.tabIcon = tabIcon0;
|
||||
this.allowTabClose = allowTabClose0;
|
||||
this.closeButtonTooltip = closeButtonTooltip0;
|
||||
this.layoutFile = layoutFile0;
|
||||
}
|
||||
|
||||
public IVTopLevelUI getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public ICDoc getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public String getTabCaption() {
|
||||
return tabCaption;
|
||||
}
|
||||
|
||||
public SkinImage getTabIcon() {
|
||||
return tabIcon;
|
||||
}
|
||||
|
||||
public boolean allowTabClose() {
|
||||
return allowTabClose;
|
||||
}
|
||||
|
||||
public String getCloseButtonTooltip() {
|
||||
return closeButtonTooltip;
|
||||
}
|
||||
|
||||
public boolean onSwitching(FScreen toScreen) {
|
||||
return view.onSwitching(this, toScreen);
|
||||
}
|
||||
|
||||
public boolean onClosing() {
|
||||
return view.onClosing(this);
|
||||
}
|
||||
|
||||
public FileLocation getLayoutFile() {
|
||||
return layoutFile;
|
||||
}
|
||||
|
||||
public boolean deleteLayoutFile() {
|
||||
if (layoutFile == null) { return false; }
|
||||
|
||||
try {
|
||||
File file = new File(layoutFile.userPrefLoc);
|
||||
file.delete();
|
||||
return true;
|
||||
}
|
||||
catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
FOptionPane.showErrorDialog("Failed to delete layout file.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Singletons.getControl().setCurrentScreen(this);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
Singletons.getView().getNavigationBar().closeTab(this);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
import forge.UiCommand;
|
||||
|
||||
/**
|
||||
* Dictates methods required for any controller
|
||||
* of an {@link forge.gui.framework.IVDoc}.
|
||||
*
|
||||
* <br><br><i>(I at beginning of class name denotes an interface.)</i>
|
||||
* <br><i>(C at beginning of class name denotes a controller class.)</i>
|
||||
*/
|
||||
public interface ICDoc {
|
||||
/**
|
||||
* Fires when this controller's view tab is selected.
|
||||
* Since this method is fired when all tabs are first
|
||||
* initialized, be wary of NPEs created by referring to
|
||||
* non-existent components.
|
||||
*
|
||||
* @return {@link forge.UiCommand} */
|
||||
UiCommand getCommandOnSelect();
|
||||
|
||||
/**
|
||||
* This method is called once, after the view singleton has been fully realized
|
||||
* for the first time. It should execute operations which should only
|
||||
* be done once, but require non-null view components.
|
||||
* <br><br>
|
||||
* This method should only be called once, in FView, after singletons are populated.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Update whatever content is in the panel.
|
||||
*/
|
||||
void update();
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package forge.gui.framework;
|
||||
|
||||
/**
|
||||
* This interface provides a unifying type to all enums
|
||||
* used as tab identifiers in XML and card layouts.
|
||||
*
|
||||
* <br><br><i>(I at beginning of class name denotes an interface.)</i>
|
||||
*/
|
||||
public interface IDocIdList { }
|
||||
@@ -1,15 +0,0 @@
|
||||
package forge.gui.framework;
|
||||
|
||||
/**
|
||||
* This interface requires a repaintThis() method, which
|
||||
* boost performance by locally repaints a component,
|
||||
* rather than repainting the entire screen.
|
||||
*
|
||||
* <br><br><i>(I at beginning of class name denotes an interface.)</i>
|
||||
*/
|
||||
public interface ILocalRepaint {
|
||||
/** Boosts performance on repaints by locally repainting a component,
|
||||
* rather than repainting the entire screen.
|
||||
*/
|
||||
void repaintSelf();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user