Refactor out swing-related code from forge-gui into new forge-gui-desktop module

This commit is contained in:
drdev
2014-04-08 23:01:39 +00:00
parent 18dd421935
commit 9439bbbc57
535 changed files with 31219 additions and 19307 deletions

View File

@@ -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) {

View 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
});
}

View 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() {
}
}

View File

@@ -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() { }
}

View 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
}
}

View File

@@ -0,0 +1,5 @@
package forge.assets;
public interface ISkinImage {
}

View 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();
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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();
}
});
}
}

View File

@@ -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();
}
}

View File

@@ -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 {

View File

@@ -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 &emsp; 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, ' '));
}
}

View File

@@ -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", "");
}
}
}

View 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;
}
}

View File

@@ -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));
}

View 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");
}
}

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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() { }
}

View File

@@ -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);
}
}

View File

@@ -1,4 +1,4 @@
package forge.gui.events;
package forge.events;
public interface IUiEventVisitor<T> {
T visit(UiEventBlockerAssigned event);

View File

@@ -1,4 +1,4 @@
package forge.gui.events;
package forge.events;
public abstract class UiEvent {

View File

@@ -1,4 +1,4 @@
package forge.gui.events;
package forge.events;
import forge.game.GameEntity;
import forge.game.card.Card;

View File

@@ -1,4 +1,4 @@
package forge.gui.events;
package forge.events;
import forge.game.card.Card;

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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
* &emsp; 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
});
}
}

View File

@@ -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;
}
}

View File

@@ -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(&quot;A long label that will wrap automatically.&quot;);
* </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);
}
}
}

View File

@@ -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 &emsp; {@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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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 &emsp; {@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 &emsp; {@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
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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.");
}
}

View File

@@ -1,5 +0,0 @@
package forge.gui.deckchooser;
public interface IDecksComboBoxListener {
public void deckTypeSelected(DecksComboBoxEvent ev);
}

View File

@@ -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() { }
}

View File

@@ -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 "";
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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 &emsp; 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 &emsp; {@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 &emsp; {@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());
}
}
}

View File

@@ -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);
}
}

View File

@@ -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() {
}
}

View File

@@ -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";
}
};
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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() {
}
}

View File

@@ -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);
}
}

View File

@@ -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 &emsp; {@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);
}
}
}

View File

@@ -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 &emsp; {@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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
};
}
}

View File

@@ -1,3 +0,0 @@
/** Forge Card Game. */
package forge.gui.deckeditor;

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 &emsp; 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -1,3 +0,0 @@
/** Forge Card Game. */
package forge.gui.download;

View File

@@ -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() {
}
}

View File

@@ -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 &emsp; double
* @param y0 &emsp; double
* @param w0 &emsp; double
* @param h0 &emsp; 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 &emsp; int */
public void setSmoothX(final int x0) {
this.smoothX = x0;
}
/** @param y0 &emsp; int */
public void setSmoothY(final int y0) {
this.smoothY = y0;
}
/** @param w0 &emsp; int */
public void setSmoothW(final int w0) {
this.smoothW = w0;
}
/** @param h0 &emsp; int */
public void setSmoothH(final int h0) {
this.smoothH = h0;
}
/** Adds a document to the tabs.
* @param doc0 &emsp; {@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 &emsp; {@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 &emsp; {@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 &emsp; 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 &emsp; 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));
}
}
}
}

View File

@@ -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 &emsp; {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 &emsp; 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);
}
}

View File

@@ -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 &emsp; {@link forge.gui.framework.IVDoc} */
EDocID(final IVDoc<? extends ICDoc> doc0) {
this.vDoc = doc0;
}
/** @param doc0 &emsp; {@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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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 { }

View File

@@ -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