From 8b9727519512fea064613723eb8fbdf5c8f9f807 Mon Sep 17 00:00:00 2001 From: Sloth Date: Mon, 19 Aug 2013 11:13:17 +0000 Subject: [PATCH 01/11] - The AI will now trade blockers with attackers like Goblin Lackey more aggressively. --- res/cardsfolder/g/goblin_lackey.txt | 2 +- res/cardsfolder/s/suqata_assassin.txt | 2 +- res/cardsfolder/s/swamp_mosquito.txt | 2 +- res/cardsfolder/z/zealot_il_vec.txt | 2 +- .../java/forge/game/ai/AiBlockController.java | 38 ++++++++++++++++--- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/res/cardsfolder/g/goblin_lackey.txt b/res/cardsfolder/g/goblin_lackey.txt index c22cca0cc2f..7447dc8fb02 100644 --- a/res/cardsfolder/g/goblin_lackey.txt +++ b/res/cardsfolder/g/goblin_lackey.txt @@ -3,6 +3,6 @@ ManaCost:R Types:Creature Goblin PT:1/1 T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigChange | TriggerDescription$ Whenever CARDNAME deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield. -SVar:TrigChange:AB$ChangeZone | Cost$ 0 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Permanent.Goblin | ChangeNum$ 1 +SVar:TrigChange:AB$ ChangeZone | Cost$ 0 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Permanent.Goblin | ChangeNum$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/goblin_lackey.jpg Oracle:Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield. \ No newline at end of file diff --git a/res/cardsfolder/s/suqata_assassin.txt b/res/cardsfolder/s/suqata_assassin.txt index f16cfd102b1..cf2abaeb34c 100644 --- a/res/cardsfolder/s/suqata_assassin.txt +++ b/res/cardsfolder/s/suqata_assassin.txt @@ -4,6 +4,6 @@ Types:Creature Human Assassin PT:1/1 K:Fear T:Mode$ AttackerUnblocked | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPoison | TriggerDescription$ Whenever CARDNAME attacks and isn't blocked, defending player gets a poison counter. (A player with ten or more poison counters loses the game.) -SVar:TrigPoison:AB$Poison | Cost$ 0 | Defined$ DefendingPlayer | Num$ 1 +SVar:TrigPoison:AB$ Poison | Cost$ 0 | Defined$ DefendingPlayer | Num$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/suqata_assassin.jpg Oracle:Fear (This creature can't be blocked except by artifact creatures and/or black creatures.)\nWhenever Suq'Ata Assassin attacks and isn't blocked, defending player gets a poison counter. (A player with ten or more poison counters loses the game.) \ No newline at end of file diff --git a/res/cardsfolder/s/swamp_mosquito.txt b/res/cardsfolder/s/swamp_mosquito.txt index 293d6fb240b..b78ce089460 100644 --- a/res/cardsfolder/s/swamp_mosquito.txt +++ b/res/cardsfolder/s/swamp_mosquito.txt @@ -4,6 +4,6 @@ Types:Creature Insect PT:0/1 K:Flying T:Mode$ AttackerUnblocked | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPoison | TriggerDescription$ Whenever CARDNAME attacks and isn't blocked, defending player gets a poison counter. (A player with ten or more poison counters loses the game.) -SVar:TrigPoison:AB$Poison | Cost$ 0 | Defined$ DefendingPlayer | Num$ 1 +SVar:TrigPoison:AB$ Poison | Cost$ 0 | Defined$ DefendingPlayer | Num$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/swamp_mosquito.jpg Oracle:Flying\nWhenever Swamp Mosquito attacks and isn't blocked, defending player gets a poison counter. (A player with ten or more poison counters loses the game.) \ No newline at end of file diff --git a/res/cardsfolder/z/zealot_il_vec.txt b/res/cardsfolder/z/zealot_il_vec.txt index 12f2b44401f..4a5cbbfd6a2 100644 --- a/res/cardsfolder/z/zealot_il_vec.txt +++ b/res/cardsfolder/z/zealot_il_vec.txt @@ -4,7 +4,7 @@ Types:Creature Human Rebel PT:1/1 K:Shadow T:Mode$ AttackerUnblocked | ValidCard$ Card.Self | Execute$ TrigDamage | OptionalDecider$ You | TriggerDescription$ Whenever CARDNAME attacks and isn't blocked, you may have it deal 1 damage to target creature. If you do, prevent all combat damage CARDNAME would deal this turn. -SVar:TrigDamage:AB$DealDamage | Cost$ 0 | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 1 | SubAbility$ DBPump +SVar:TrigDamage:AB$ DealDamage | Cost$ 0 | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 1 | SubAbility$ DBPump SVar:DBPump:DB$Pump | KW$ HIDDEN Prevent all combat damage that would be dealt by CARDNAME. SVar:Picture:http://www.wizards.com/global/images/magic/general/zealot_il_vec.jpg Oracle:Shadow (This creature can block or be blocked by only creatures with shadow.)\nWhenever Zealot il-Vec attacks and isn't blocked, you may have it deal 1 damage to target creature. If you do, prevent all combat damage Zealot il-Vec would deal this turn. \ No newline at end of file diff --git a/src/main/java/forge/game/ai/AiBlockController.java b/src/main/java/forge/game/ai/AiBlockController.java index 1a071f93d06..beeef3fd36a 100644 --- a/src/main/java/forge/game/ai/AiBlockController.java +++ b/src/main/java/forge/game/ai/AiBlockController.java @@ -18,6 +18,7 @@ package forge.game.ai; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import com.google.common.base.Predicate; @@ -28,6 +29,9 @@ import forge.CardLists; import forge.CardPredicates; import forge.CounterType; import forge.GameEntity; +import forge.card.TriggerReplacementBase; +import forge.card.trigger.Trigger; +import forge.card.trigger.TriggerType; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.player.Player; @@ -197,7 +201,7 @@ public class AiBlockController { // 1.Blockers that can destroy the attacker but won't get // destroyed killingBlockers = getKillingBlockers(combat, attacker, safeBlockers); - if (killingBlockers.size() > 0) { + if (!killingBlockers.isEmpty()) { blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers); } else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) { blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers); @@ -215,11 +219,34 @@ public class AiBlockController { } } // 4.Blockers that can destroy the attacker and are worth less - if (blocker == null && killingBlockers.size() > 0) { + if (blocker == null && !killingBlockers.isEmpty()) { final Card worst = ComputerUtilCard.getWorstCreatureAI(killingBlockers); + int value = ComputerUtilCard.evaluateCreature(attacker); + + // check for triggers when unblocked + for (Trigger trigger : attacker.getTriggers()) { + final HashMap trigParams = trigger.getMapParams(); + TriggerType mode = trigger.getMode(); - if ((ComputerUtilCard.evaluateCreature(worst) + diff) < ComputerUtilCard - .evaluateCreature(attacker)) { + if (!trigger.requirementsCheck(attacker.getGame())) { + continue; + } + + if (mode == TriggerType.DamageDone) { + if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), attacker) + && attacker.getNetCombatDamage() > 0 + && (!trigParams.containsKey("ValidTarget") + || TriggerReplacementBase.matchesValid(combat.getDefenderByAttacker(attacker), trigParams.get("ValidTarget").split(","), attacker))) { + value += 50; + } + } else if (mode == TriggerType.AttackerUnblocked) { + if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), attacker)) { + value += 50; + } + } + } + + if ((ComputerUtilCard.evaluateCreature(worst) + diff) < value) { blocker = worst; } } @@ -378,14 +405,13 @@ public class AiBlockController { List possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true); killingBlockers = getKillingBlockers(combat, attacker, possibleBlockers); - if ((killingBlockers.size() > 0) && ComputerUtilCombat.lifeInDanger(ai, combat)) { + if (!killingBlockers.isEmpty() && ComputerUtilCombat.lifeInDanger(ai, combat)) { final Card blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers); combat.addBlocker(attacker, blocker); currentAttackers.remove(attacker); } } attackersLeft = (new ArrayList(currentAttackers)); - } // Chump Blocks (should only be made if life is in danger) From df6860294d7b6cfa70abc8d3cc063ef13cbb22a6 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Mon, 19 Aug 2013 20:48:21 +0000 Subject: [PATCH 02/11] =?UTF-8?q?Support=20for=20nested=20folders=20that?= =?UTF-8?q?=20store=20constructed=20decks=20-=20part=201=20of=20=E2=89=883?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 1 + .../java/forge/card/EditionCollection.java | 3 +- .../forge/deck/io/DeckGroupSerializer.java | 10 +++++- .../java/forge/deck/io/DeckSerializer.java | 13 ++++++- src/main/java/forge/util/IItemReader.java | 5 +++ .../forge/util/storage/StorageReaderBase.java | 27 +++++++++++++++ .../forge/util/storage/StorageReaderFile.java | 8 ++--- .../storage/StorageReaderFileSections.java | 7 ++-- .../util/storage/StorageReaderFolder.java | 34 +++++++++++-------- 9 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 src/main/java/forge/util/storage/StorageReaderBase.java diff --git a/.gitattributes b/.gitattributes index fda4b3fb075..756e9c3de50 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15193,6 +15193,7 @@ src/main/java/forge/util/package-info.java -text src/main/java/forge/util/storage/IStorage.java -text src/main/java/forge/util/storage/StorageBase.java -text src/main/java/forge/util/storage/StorageImmediatelySerialized.java svneol=native#text/plain +src/main/java/forge/util/storage/StorageReaderBase.java -text src/main/java/forge/util/storage/StorageReaderFile.java -text src/main/java/forge/util/storage/StorageReaderFileSections.java -text src/main/java/forge/util/storage/StorageReaderFolder.java -text diff --git a/src/main/java/forge/card/EditionCollection.java b/src/main/java/forge/card/EditionCollection.java index 62330f70615..d8023df8761 100644 --- a/src/main/java/forge/card/EditionCollection.java +++ b/src/main/java/forge/card/EditionCollection.java @@ -28,6 +28,7 @@ import com.google.common.collect.Lists; import forge.util.IItemReader; import forge.util.storage.StorageBase; +import forge.util.storage.StorageReaderBase; public final class EditionCollection extends StorageBase { @@ -107,7 +108,7 @@ public final class EditionCollection extends StorageBase { */ public IItemReader getBoosterGenerator() { // TODO Auto-generated method stub - return new IItemReader() { + return new StorageReaderBase(null) { @Override public Map readAll() { diff --git a/src/main/java/forge/deck/io/DeckGroupSerializer.java b/src/main/java/forge/deck/io/DeckGroupSerializer.java index 4c8229250e3..96a9a47a7d0 100644 --- a/src/main/java/forge/deck/io/DeckGroupSerializer.java +++ b/src/main/java/forge/deck/io/DeckGroupSerializer.java @@ -23,6 +23,8 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.ImmutableList; + import forge.deck.Deck; import forge.deck.DeckGroup; import forge.util.FileUtil; @@ -111,7 +113,7 @@ public class DeckGroupSerializer extends StorageReaderFolder implemen * @return the file */ public File makeFileFor(final DeckGroup decks) { - return new File(this.getDirectory(), decks.getBestFileName()); + return new File(this.directory, decks.getBestFileName()); } /* @@ -134,4 +136,10 @@ public class DeckGroupSerializer extends StorageReaderFolder implemen }; } + @Override + public Iterable getSubFolders() { + // Sealed decks are kept in separate folders, no further drilling possible + return ImmutableList.of(); + } + } diff --git a/src/main/java/forge/deck/io/DeckSerializer.java b/src/main/java/forge/deck/io/DeckSerializer.java index 292db1ae134..1d98751d261 100644 --- a/src/main/java/forge/deck/io/DeckSerializer.java +++ b/src/main/java/forge/deck/io/DeckSerializer.java @@ -40,6 +40,7 @@ import forge.properties.NewConstants; import forge.util.FileSection; import forge.util.FileSectionManual; import forge.util.FileUtil; +import forge.util.IItemReader; import forge.util.IItemSerializer; import forge.util.storage.StorageReaderFolder; import freemarker.template.Configuration; @@ -194,7 +195,7 @@ public class DeckSerializer extends StorageReaderFolder implements IItemSe } public File makeFileFor(final Deck deck) { - return new File(this.getDirectory(), deck.getBestFileName() + FILE_EXTENSION); + return new File(this.directory, deck.getBestFileName() + FILE_EXTENSION); } @Override @@ -245,4 +246,14 @@ public class DeckSerializer extends StorageReaderFolder implements IItemSe return null; } + + /* (non-Javadoc) + * @see forge.util.storage.StorageReaderBase#getReaderForFolder(java.io.File) + */ + @Override + public IItemReader getReaderForFolder(File subfolder) { + if ( !subfolder.getParentFile().equals(directory) ) + throw new UnsupportedOperationException("Only child folders of " + directory + " may be processed"); + return new DeckSerializer(subfolder, false); + } } diff --git a/src/main/java/forge/util/IItemReader.java b/src/main/java/forge/util/IItemReader.java index a084452cbb0..4f57bcea2df 100644 --- a/src/main/java/forge/util/IItemReader.java +++ b/src/main/java/forge/util/IItemReader.java @@ -17,6 +17,7 @@ */ package forge.util; +import java.io.File; import java.util.Map; /** @@ -41,4 +42,8 @@ public interface IItemReader { * @return the item key */ String getItemKey(T item); + + Iterable getSubFolders(); + + IItemReader getReaderForFolder(File subfolder); } diff --git a/src/main/java/forge/util/storage/StorageReaderBase.java b/src/main/java/forge/util/storage/StorageReaderBase.java new file mode 100644 index 00000000000..9d343ca3ed5 --- /dev/null +++ b/src/main/java/forge/util/storage/StorageReaderBase.java @@ -0,0 +1,27 @@ +package forge.util.storage; + +import java.io.File; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; + +import forge.util.IItemReader; + +public abstract class StorageReaderBase implements IItemReader { + + protected final Function keySelector; + public StorageReaderBase(final Function keySelector0) { + keySelector = keySelector0; + } + + @Override + public Iterable getSubFolders() { + // TODO Auto-generated method stub + return ImmutableList.of(); + } + + @Override + public IItemReader getReaderForFolder(File subfolder) { + throw new UnsupportedOperationException("This reader is not supposed to have nested folders"); + } +} \ No newline at end of file diff --git a/src/main/java/forge/util/storage/StorageReaderFile.java b/src/main/java/forge/util/storage/StorageReaderFile.java index 0cc53b4850b..2fd938a6166 100644 --- a/src/main/java/forge/util/storage/StorageReaderFile.java +++ b/src/main/java/forge/util/storage/StorageReaderFile.java @@ -28,7 +28,6 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.base.Function; import forge.util.FileUtil; -import forge.util.IItemReader; /** * This class treats every line of a given file as a source for a named object. @@ -36,10 +35,10 @@ import forge.util.IItemReader; * @param * the generic type */ -public abstract class StorageReaderFile implements IItemReader { +public abstract class StorageReaderFile extends StorageReaderBase { private final File file; - private final Function keySelector; + /** * Instantiates a new storage reader file. @@ -58,8 +57,8 @@ public abstract class StorageReaderFile implements IItemReader { * @param keySelector0 the key selector0 */ public StorageReaderFile(final File file0, final Function keySelector0) { + super(keySelector0); this.file = file0; - this.keySelector = keySelector0; } /* (non-Javadoc) @@ -121,5 +120,4 @@ public abstract class StorageReaderFile implements IItemReader { public String getItemKey(final T item) { return this.keySelector.apply(item); } - } diff --git a/src/main/java/forge/util/storage/StorageReaderFileSections.java b/src/main/java/forge/util/storage/StorageReaderFileSections.java index 93cc75b6dab..08881df2169 100644 --- a/src/main/java/forge/util/storage/StorageReaderFileSections.java +++ b/src/main/java/forge/util/storage/StorageReaderFileSections.java @@ -30,7 +30,6 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.base.Function; import forge.util.FileUtil; -import forge.util.IItemReader; /** * This class treats every line of a given file as a source for a named object. @@ -38,18 +37,18 @@ import forge.util.IItemReader; * @param * the generic type */ -public abstract class StorageReaderFileSections implements IItemReader { +public abstract class StorageReaderFileSections extends StorageReaderBase { private final File file; - private final Function keySelector; public StorageReaderFileSections(final String pathname, final Function keySelector0) { this(new File(pathname), keySelector0); } public StorageReaderFileSections(final File file0, final Function keySelector0) { + super(keySelector0); this.file = file0; - this.keySelector = keySelector0; + } /* (non-Javadoc) diff --git a/src/main/java/forge/util/storage/StorageReaderFolder.java b/src/main/java/forge/util/storage/StorageReaderFolder.java index fca958cb83b..54f1a314e96 100644 --- a/src/main/java/forge/util/storage/StorageReaderFolder.java +++ b/src/main/java/forge/util/storage/StorageReaderFolder.java @@ -18,9 +18,11 @@ package forge.util.storage; import java.io.File; +import java.io.FileFilter; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -34,7 +36,6 @@ import com.google.common.base.Function; import forge.deck.io.OldDeckFileFormatException; import forge.error.BugReporter; -import forge.util.IItemReader; /** * This class treats every file in the given folder as a source for a named @@ -43,19 +44,9 @@ import forge.util.IItemReader; * * @param the generic type */ -public abstract class StorageReaderFolder implements IItemReader { +public abstract class StorageReaderFolder extends StorageReaderBase { - private final File directory; - private final Function keySelector; - - /** - * Gets the directory. - * - * @return the directory - */ - protected final File getDirectory() { - return this.directory; - } + protected final File directory; /** * Instantiates a new storage reader folder. @@ -63,9 +54,9 @@ public abstract class StorageReaderFolder implements IItemReader { * @param deckDir0 the deck dir0 */ public StorageReaderFolder(final File deckDir0, Function keySelector0) { - + super(keySelector0); + this.directory = deckDir0; - keySelector = keySelector0; if (this.directory == null) { throw new IllegalArgumentException("No deck directory specified"); @@ -153,5 +144,18 @@ public abstract class StorageReaderFolder implements IItemReader { public String getItemKey(T item) { return keySelector.apply(item); } + + // methods handling nested folders are provided. It's up to consumer whether to use these or not. + @Override + public Iterable getSubFolders() { + File[] list = this.directory.listFiles(new FileFilter() { + + @Override + public boolean accept(File file) { + return file.isDirectory() && !file.isHidden(); + } + }); + return Arrays.asList(list); + } } From ada5fa50569439a2b9d1cb8924a80deca3565627 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Mon, 19 Aug 2013 22:40:10 +0000 Subject: [PATCH 03/11] Subfolders for constructed decks work in r/o mode (part 2 of ~3) --- .gitattributes | 1 + src/main/java/forge/deck/CardCollections.java | 2 +- src/main/java/forge/deck/Deck.java | 9 ++++- src/main/java/forge/deck/DeckBase.java | 4 ++- src/main/java/forge/deck/DeckGroup.java | 9 +++++ src/main/java/forge/deck/DeckgenUtil.java | 11 +++++- .../forge/game/limited/CustomLimited.java | 9 +++++ .../gui/toolbox/special/FDeckChooser.java | 18 +++++++--- .../storage/StorageImmediatelySerialized.java | 26 ++++++++++++++ .../util/storage/StorageNestedFolders.java | 36 +++++++++++++++++++ 10 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 src/main/java/forge/util/storage/StorageNestedFolders.java diff --git a/.gitattributes b/.gitattributes index 756e9c3de50..b43c8aa2ab9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15193,6 +15193,7 @@ src/main/java/forge/util/package-info.java -text src/main/java/forge/util/storage/IStorage.java -text src/main/java/forge/util/storage/StorageBase.java -text src/main/java/forge/util/storage/StorageImmediatelySerialized.java svneol=native#text/plain +src/main/java/forge/util/storage/StorageNestedFolders.java -text src/main/java/forge/util/storage/StorageReaderBase.java -text src/main/java/forge/util/storage/StorageReaderFile.java -text src/main/java/forge/util/storage/StorageReaderFileSections.java -text diff --git a/src/main/java/forge/deck/CardCollections.java b/src/main/java/forge/deck/CardCollections.java index 902580a7d77..50ac0cd3aee 100644 --- a/src/main/java/forge/deck/CardCollections.java +++ b/src/main/java/forge/deck/CardCollections.java @@ -49,7 +49,7 @@ public class CardCollections { public CardCollections() { StopWatch sw = new StopWatch(); sw.start(); - this.constructed = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true)); + this.constructed = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true), true); this.draft = new StorageImmediatelySerialized(new DeckGroupSerializer(new File(NewConstants.DECK_DRAFT_DIR))); this.sealed = new StorageImmediatelySerialized(new DeckGroupSerializer(new File(NewConstants.DECK_SEALED_DIR))); this.cube = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_CUBE_DIR))); diff --git a/src/main/java/forge/deck/Deck.java b/src/main/java/forge/deck/Deck.java index ea716322215..4ce33c9a83f 100644 --- a/src/main/java/forge/deck/Deck.java +++ b/src/main/java/forge/deck/Deck.java @@ -77,7 +77,14 @@ public class Deck extends DeckBase implements Iterable { +public abstract class DeckBase implements Serializable, Comparable, InventoryItem { private static final long serialVersionUID = -7538150536939660052L; // gameType is from Constant.GameType, like GameType.Regular diff --git a/src/main/java/forge/deck/DeckGroup.java b/src/main/java/forge/deck/DeckGroup.java index 13147718fb3..03b7abae2a2 100644 --- a/src/main/java/forge/deck/DeckGroup.java +++ b/src/main/java/forge/deck/DeckGroup.java @@ -135,4 +135,13 @@ public class DeckGroup extends DeckBase { } }; + /* (non-Javadoc) + * @see forge.item.InventoryItem#getItemType() + */ + @Override + public String getItemType() { + // TODO Auto-generated method stub + return "Set of limited mode decks"; + } + } diff --git a/src/main/java/forge/deck/DeckgenUtil.java b/src/main/java/forge/deck/DeckgenUtil.java index 5c505afc302..1e192ef7d34 100644 --- a/src/main/java/forge/deck/DeckgenUtil.java +++ b/src/main/java/forge/deck/DeckgenUtil.java @@ -105,7 +105,16 @@ public class DeckgenUtil { * @return {@link forge.deck.Deck} */ public static Deck getConstructedDeck(final String selection) { - return Singletons.getModel().getDecks().getConstructed().get(selection); + IStorage path = Singletons.getModel().getDecks().getConstructed(); + String name = selection; + int idxSlash = name.indexOf('/'); + while( idxSlash > 0 ) { + String sf = name.substring(0, idxSlash); + path = path.getFolders().get(sf); + name = name.substring(idxSlash+1); + idxSlash = name.indexOf('/'); + }; + return path.get(name); } public static Deck getPreconDeck(String selection) { diff --git a/src/main/java/forge/game/limited/CustomLimited.java b/src/main/java/forge/game/limited/CustomLimited.java index 10b6bf52e6d..e84368d76f9 100644 --- a/src/main/java/forge/game/limited/CustomLimited.java +++ b/src/main/java/forge/game/limited/CustomLimited.java @@ -82,6 +82,15 @@ public class CustomLimited extends DeckBase { return this.getName(); } + /* (non-Javadoc) + * @see forge.item.InventoryItem#getItemType() + */ + @Override + public String getItemType() { + // TODO Auto-generated method stub + return "Limited game setup"; + } + /** * Parses the. * diff --git a/src/main/java/forge/gui/toolbox/special/FDeckChooser.java b/src/main/java/forge/gui/toolbox/special/FDeckChooser.java index 2d149c74f2e..b8058274fff 100644 --- a/src/main/java/forge/gui/toolbox/special/FDeckChooser.java +++ b/src/main/java/forge/gui/toolbox/special/FDeckChooser.java @@ -29,7 +29,7 @@ import forge.gui.toolbox.FList; import forge.gui.toolbox.FRadioButton; import forge.gui.toolbox.FScrollPane; import forge.gui.toolbox.JXButtonPanel; -import forge.item.PreconDeck; +import forge.item.InventoryItem; import forge.quest.QuestController; import forge.quest.QuestEvent; import forge.quest.QuestEventChallenge; @@ -194,8 +194,7 @@ public class FDeckChooser extends JPanel { lst.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); final List customNames = new ArrayList(); - final IStorage allDecks = Singletons.getModel().getDecks().getConstructed(); - for (final Deck d : allDecks) { customNames.add(d.getName()); } + addDecksRecursive(Singletons.getModel().getDecks().getConstructed(), customNames, null); lst.setListData(customNames.toArray(ArrayUtils.EMPTY_STRING_ARRAY)); lst.setName(DeckgenUtil.DeckTypes.CUSTOM.toString()); @@ -210,14 +209,23 @@ public class FDeckChooser extends JPanel { lst.setSelectedIndex(0); } + private void addDecksRecursive(IStorage node, List customNames, String namePrefix ) { + String path = namePrefix == null ? "" : namePrefix + "/"; + for (final String fn : node.getFolders().getNames() ) + { + IStorage f = node.getFolders().get(fn); + addDecksRecursive(f, customNames, path + fn); + } + for (final T d : node) { customNames.add(path + d.getName()); } + } + /** Handles all control for "custom" radio button click. */ private void updatePrecons() { final JList lst = getLstDecks(); lst.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); final List customNames = new ArrayList(); - final IStorage allDecks = QuestController.getPrecons(); - for (final PreconDeck d : allDecks) { customNames.add(d.getName()); } + addDecksRecursive(QuestController.getPrecons(), customNames, null); lst.setListData(customNames.toArray(ArrayUtils.EMPTY_STRING_ARRAY)); lst.setName(DeckgenUtil.DeckTypes.PRECON.toString()); diff --git a/src/main/java/forge/util/storage/StorageImmediatelySerialized.java b/src/main/java/forge/util/storage/StorageImmediatelySerialized.java index bcc500f5381..2d984edc125 100644 --- a/src/main/java/forge/util/storage/StorageImmediatelySerialized.java +++ b/src/main/java/forge/util/storage/StorageImmediatelySerialized.java @@ -17,6 +17,9 @@ */ package forge.util.storage; +import com.google.common.base.Function; + +import forge.util.IItemReader; import forge.util.IItemSerializer; //reads and writeDeck Deck objects @@ -32,6 +35,15 @@ import forge.util.IItemSerializer; public class StorageImmediatelySerialized extends StorageBase { private final IItemSerializer serializer; + private final IStorage> subfolders; + + private final Function, IStorage> nestedFactory = new Function, IStorage>() { + @Override + public IStorage apply(IItemReader io) { + return new StorageImmediatelySerialized((IItemSerializer) io, true); + } + }; + /** *

* Constructor for DeckManager. @@ -40,8 +52,14 @@ public class StorageImmediatelySerialized extends StorageBase { * @param io the io */ public StorageImmediatelySerialized(final IItemSerializer io) { + this(io, false); + } + + + public StorageImmediatelySerialized(final IItemSerializer io, boolean withSubFolders) { super(io); this.serializer = io; + subfolders = withSubFolders ? new StorageNestedFolders(io, nestedFactory) : null; } /* @@ -65,4 +83,12 @@ public class StorageImmediatelySerialized extends StorageBase { public final void delete(final String deckName) { this.serializer.erase(this.map.remove(deckName)); } + + /* (non-Javadoc) + * @see forge.util.storage.StorageBase#getFolders() + */ + @Override + public IStorage> getFolders() { + return subfolders == null ? super.getFolders() : subfolders; + } } diff --git a/src/main/java/forge/util/storage/StorageNestedFolders.java b/src/main/java/forge/util/storage/StorageNestedFolders.java new file mode 100644 index 00000000000..ffdbdb4d858 --- /dev/null +++ b/src/main/java/forge/util/storage/StorageNestedFolders.java @@ -0,0 +1,36 @@ +package forge.util.storage; + +import java.io.File; +import java.util.HashMap; + +import org.apache.commons.lang.NotImplementedException; + +import com.google.common.base.Function; + +import forge.util.IItemReader; + + +public class StorageNestedFolders extends StorageBase> { + + public StorageNestedFolders(IItemReader io, Function, IStorage> factory) { + super(new HashMap>()); + for(File sf : io.getSubFolders() ) + { + map.put(sf.getName(), factory.apply(io.getReaderForFolder(sf))); + } + } + + // need code implementations for folder create/delete operations + + @Override + public void add(IStorage deck) { + // need folder name here! + throw new NotImplementedException(); + } + + @Override + public void delete(String deckName) { + throw new NotImplementedException(); + } + +} From eebbb45b2c82d65d2f64ac00827d7625aa5aec9b Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Mon, 19 Aug 2013 22:48:33 +0000 Subject: [PATCH 04/11] better display of subfolders --- src/main/java/forge/deck/DeckgenUtil.java | 4 ++-- src/main/java/forge/gui/toolbox/special/FDeckChooser.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/forge/deck/DeckgenUtil.java b/src/main/java/forge/deck/DeckgenUtil.java index 1e192ef7d34..48cc3841795 100644 --- a/src/main/java/forge/deck/DeckgenUtil.java +++ b/src/main/java/forge/deck/DeckgenUtil.java @@ -109,9 +109,9 @@ public class DeckgenUtil { String name = selection; int idxSlash = name.indexOf('/'); while( idxSlash > 0 ) { - String sf = name.substring(0, idxSlash); + String sf = name.substring(0, idxSlash).trim(); path = path.getFolders().get(sf); - name = name.substring(idxSlash+1); + name = name.substring(idxSlash+1).trim(); idxSlash = name.indexOf('/'); }; return path.get(name); diff --git a/src/main/java/forge/gui/toolbox/special/FDeckChooser.java b/src/main/java/forge/gui/toolbox/special/FDeckChooser.java index b8058274fff..0799e3adcd6 100644 --- a/src/main/java/forge/gui/toolbox/special/FDeckChooser.java +++ b/src/main/java/forge/gui/toolbox/special/FDeckChooser.java @@ -210,7 +210,7 @@ public class FDeckChooser extends JPanel { } private void addDecksRecursive(IStorage node, List customNames, String namePrefix ) { - String path = namePrefix == null ? "" : namePrefix + "/"; + String path = namePrefix == null ? "" : namePrefix + " / "; for (final String fn : node.getFolders().getNames() ) { IStorage f = node.getFolders().get(fn); From 1677e1eeb158f7d804fe772a4717d43e519c8421 Mon Sep 17 00:00:00 2001 From: drdev Date: Tue, 20 Aug 2013 00:07:39 +0000 Subject: [PATCH 05/11] Refactor so ItemManager is abstract and ItemFilter doesn't extend JPanel Start working on transitioning card color/type filters --- .gitattributes | 2 + .../controllers/CEditorConstructed.java | 6 +-- .../controllers/CEditorDraftingProcess.java | 6 +-- .../controllers/CEditorLimited.java | 6 +-- .../deckeditor/controllers/CEditorQuest.java | 6 +-- .../controllers/CEditorQuestCardShop.java | 6 +-- .../controllers/CEditorVariant.java | 6 +-- .../gui/toolbox/itemmanager/CardManager.java | 24 +++++++++ .../itemmanager/InventoryItemManager.java | 19 +++++++ .../gui/toolbox/itemmanager/ItemManager.java | 19 ++++--- .../itemmanager/filters/CardCMCFilter.java | 5 +- .../itemmanager/filters/CardColorFilter.java | 14 ++++-- .../itemmanager/filters/CardFormatFilter.java | 5 +- .../itemmanager/filters/CardPowerFilter.java | 5 +- .../filters/CardQuestWorldFilter.java | 5 +- .../itemmanager/filters/CardSetFilter.java | 5 +- .../filters/CardToughnessFilter.java | 5 +- .../itemmanager/filters/CardTypeFilter.java | 14 ++++-- .../itemmanager/filters/ItemFilter.java | 31 +++++++++--- .../itemmanager/filters/ListLabelFilter.java | 1 - .../itemmanager/filters/TextFieldFilter.java | 1 - .../filters/ToggleButtonsFilter.java | 50 +++++++++++++++++++ .../itemmanager/filters/ValueRangeFilter.java | 1 - 23 files changed, 188 insertions(+), 54 deletions(-) create mode 100644 src/main/java/forge/gui/toolbox/itemmanager/CardManager.java create mode 100644 src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java diff --git a/.gitattributes b/.gitattributes index b43c8aa2ab9..0fd710ad239 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15008,6 +15008,8 @@ src/main/java/forge/gui/toolbox/SaveOpenDialog.java -text src/main/java/forge/gui/toolbox/imaging/FImagePanel.java -text src/main/java/forge/gui/toolbox/imaging/FImageUtil.java -text src/main/java/forge/gui/toolbox/imaging/ImageUtil.java -text +src/main/java/forge/gui/toolbox/itemmanager/CardManager.java -text +src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java -text src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java -text src/main/java/forge/gui/toolbox/itemmanager/ItemManagerContainer.java -text src/main/java/forge/gui/toolbox/itemmanager/ItemManagerModel.java -text diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java index 0655747bfc1..d3d5c723c8a 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java @@ -35,7 +35,7 @@ import forge.gui.deckeditor.views.VCardCatalog; import forge.gui.deckeditor.views.VCurrentDeck; import forge.gui.framework.EDocID; import forge.gui.toolbox.FLabel; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.CardManager; import forge.gui.toolbox.itemmanager.SItemManagerIO; import forge.gui.toolbox.itemmanager.SItemManagerUtil; import forge.gui.toolbox.itemmanager.SItemManagerIO.EditorPreference; @@ -94,8 +94,8 @@ public final class CEditorConstructed extends ACEditorBase { boolean wantUnique = SItemManagerIO.getPref(EditorPreference.display_unique_only); - final ItemManager catalogManager = new ItemManager(PaperCard.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), wantUnique); - final ItemManager deckManager = new ItemManager(PaperCard.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), wantUnique); + final CardManager catalogManager = new CardManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), wantUnique); + final CardManager deckManager = new CardManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), wantUnique); VCardCatalog.SINGLETON_INSTANCE.setItemManager(catalogManager); VCurrentDeck.SINGLETON_INSTANCE.setItemManager(deckManager); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorDraftingProcess.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorDraftingProcess.java index ab2aa028905..5b89e430461 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorDraftingProcess.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorDraftingProcess.java @@ -37,7 +37,7 @@ import forge.gui.deckeditor.views.VCurrentDeck; import forge.gui.deckeditor.views.VDeckgen; import forge.gui.framework.DragCell; import forge.gui.home.sanctioned.CSubmenuDraft; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.CardManager; import forge.gui.toolbox.itemmanager.table.SColumnUtil; import forge.item.PaperCard; import forge.item.InventoryItem; @@ -64,8 +64,8 @@ public class CEditorDraftingProcess extends ACEditorBase { * Updates the deck editor UI as necessary draft selection mode. */ public CEditorDraftingProcess() { - final ItemManager catalogManager = new ItemManager(PaperCard.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); - final ItemManager deckManager = new ItemManager(PaperCard.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager catalogManager = new CardManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager deckManager = new CardManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); VCardCatalog.SINGLETON_INSTANCE.setItemManager(catalogManager); VCurrentDeck.SINGLETON_INSTANCE.setItemManager(deckManager); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorLimited.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorLimited.java index 824aed8da01..fb384b7495e 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorLimited.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorLimited.java @@ -30,7 +30,7 @@ import forge.gui.deckeditor.views.VDeckgen; import forge.gui.framework.DragCell; import forge.gui.home.sanctioned.CSubmenuDraft; import forge.gui.home.sanctioned.CSubmenuSealed; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.CardManager; import forge.gui.toolbox.itemmanager.SItemManagerUtil; import forge.gui.toolbox.itemmanager.table.SColumnUtil; import forge.item.PaperCard; @@ -59,8 +59,8 @@ public final class CEditorLimited extends ACEditorBase { * @param deckMap0   {@link forge.deck.DeckGroup}<{@link forge.util.storage.IStorage}> */ public CEditorLimited(final IStorage deckMap0) { - final ItemManager catalogManager = new ItemManager(PaperCard.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); - final ItemManager deckManager = new ItemManager(PaperCard.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager catalogManager = new CardManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager deckManager = new CardManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); VCardCatalog.SINGLETON_INSTANCE.setItemManager(catalogManager); VCurrentDeck.SINGLETON_INSTANCE.setItemManager(deckManager); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuest.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuest.java index 1caa013e288..c88848a706c 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuest.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuest.java @@ -38,7 +38,7 @@ import forge.gui.deckeditor.views.VDeckgen; import forge.gui.framework.DragCell; import forge.gui.home.quest.CSubmenuQuestDecks; import forge.gui.toolbox.FLabel; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.CardManager; import forge.gui.toolbox.itemmanager.SItemManagerUtil; import forge.gui.toolbox.itemmanager.table.TableColumnInfo; import forge.gui.toolbox.itemmanager.table.SColumnUtil; @@ -95,8 +95,8 @@ public final class CEditorQuest extends ACEditorBase { public CEditorQuest(final QuestController questData0) { this.questData = questData0; - final ItemManager catalogManager = new ItemManager(PaperCard.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); - final ItemManager deckManager = new ItemManager(PaperCard.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager catalogManager = new CardManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); + final CardManager deckManager = new CardManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); catalogManager.setAlwaysNonUnique(true); deckManager.setAlwaysNonUnique(true); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java index 6b503cf0312..12d5f302eb2 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java @@ -48,7 +48,7 @@ import forge.gui.framework.DragCell; import forge.gui.home.quest.CSubmenuQuestDecks; import forge.gui.toolbox.FLabel; import forge.gui.toolbox.FSkin; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.InventoryItemManager; import forge.gui.toolbox.itemmanager.SItemManagerUtil; import forge.gui.toolbox.itemmanager.table.TableColumnInfo; import forge.gui.toolbox.itemmanager.table.SColumnUtil; @@ -127,8 +127,8 @@ public final class CEditorQuestCardShop extends ACEditorBase catalogManager = new ItemManager(InventoryItem.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); - final ItemManager deckManager = new ItemManager(InventoryItem.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); + final InventoryItemManager catalogManager = new InventoryItemManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), false); + final InventoryItemManager deckManager = new InventoryItemManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), false); catalogManager.setAlwaysNonUnique(true); deckManager.setAlwaysNonUnique(true); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorVariant.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorVariant.java index 93059f9151a..53f45041308 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorVariant.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorVariant.java @@ -34,7 +34,7 @@ import forge.gui.deckeditor.views.VCurrentDeck; import forge.gui.deckeditor.views.VDeckgen; import forge.gui.framework.DragCell; import forge.gui.framework.EDocID; -import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.CardManager; import forge.gui.toolbox.itemmanager.SItemManagerUtil; import forge.gui.toolbox.itemmanager.table.TableColumnInfo; import forge.gui.toolbox.itemmanager.table.SColumnUtil; @@ -74,8 +74,8 @@ public final class CEditorVariant extends ACEditorBase { cardPoolCondition = poolCondition; exitToScreen = exitTo; - final ItemManager catalogManager = new ItemManager(PaperCard.class, VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), true); - final ItemManager deckManager = new ItemManager(PaperCard.class, VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), true); + final CardManager catalogManager = new CardManager(VCardCatalog.SINGLETON_INSTANCE.getStatLabels(), true); + final CardManager deckManager = new CardManager(VCurrentDeck.SINGLETON_INSTANCE.getStatLabels(), true); VCardCatalog.SINGLETON_INSTANCE.setItemManager(catalogManager); VCurrentDeck.SINGLETON_INSTANCE.setItemManager(deckManager); diff --git a/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java b/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java new file mode 100644 index 00000000000..85fd6bb5aaa --- /dev/null +++ b/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java @@ -0,0 +1,24 @@ +package forge.gui.toolbox.itemmanager; + +import java.util.Map; + +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; +import forge.gui.toolbox.itemmanager.filters.CardColorFilter; +import forge.gui.toolbox.itemmanager.filters.CardTypeFilter; +import forge.item.PaperCard; + +/** + * TODO: Write javadoc for this type. + * + */ +@SuppressWarnings("serial") +public final class CardManager extends ItemManager { + + public CardManager(Map statLabels0, boolean wantUnique0) { + super(PaperCard.class, statLabels0, wantUnique0); + + this.addFilter(new CardColorFilter(this)); + this.addFilter(new CardTypeFilter(this)); + } +} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java b/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java new file mode 100644 index 00000000000..90dd9b4cabc --- /dev/null +++ b/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java @@ -0,0 +1,19 @@ +package forge.gui.toolbox.itemmanager; + +import java.util.Map; + +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; +import forge.item.InventoryItem; + +/** + * TODO: Write javadoc for this type. + * + */ +@SuppressWarnings("serial") +public final class InventoryItemManager extends ItemManager { + + public InventoryItemManager(Map statLabels0, boolean wantUnique0) { + super(InventoryItem.class, statLabels0, wantUnique0); + } +} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java b/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java index 42fd0a148d6..ed2c727207c 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java @@ -51,8 +51,8 @@ import forge.util.Aggregates; * @param * the generic type */ -public final class ItemManager extends JPanel { - private static final long serialVersionUID = 3164349984277267922L; +@SuppressWarnings("serial") +public abstract class ItemManager extends JPanel { private ItemPool pool; private final ItemManagerModel model; private Predicate filterPredicate = null; @@ -74,7 +74,7 @@ public final class ItemManager extends JPanel { * @param statLabels0 stat labels for this item manager * @param wantUnique0 whether this table should display only one item with the same name */ - public ItemManager(final Class genericType0, Map statLabels0, final boolean wantUnique0) { + protected ItemManager(final Class genericType0, Map statLabels0, final boolean wantUnique0) { this.genericType = genericType0; this.statLabels = statLabels0; this.wantUnique = wantUnique0; @@ -105,9 +105,12 @@ public final class ItemManager extends JPanel { int height = this.getHeight(); //position toolbar components //TODO: Uncomment - /*int toolbarHeight = FTextField.HEIGHT + 3; - this.txtSearch.setBounds(x, y, width / 2, FTextField.HEIGHT); - y += toolbarHeight;*/ + /*this.txtSearch.setBounds(x, y, width / 2, FTextField.HEIGHT); + y += FTextField.HEIGHT + 3; + for (ItemFilter filter : this.filters.values()) { + filter.getPanel().setBounds(x, y, width, ItemFilter.PANEL_HEIGHT); + y += ItemFilter.PANEL_HEIGHT + 3; + }*/ //position current item view this.tableScroller.setBounds(x, y, width, height - y); @@ -339,13 +342,13 @@ public final class ItemManager extends JPanel { public void addFilter(ItemFilter filter) { this.filters.put(filter.getType(), filter); - this.add(filter); + this.add(filter.getPanel()); this.revalidate(); } public void removeFilter(ItemFilter filter) { this.filters.remove(filter.getType()); - this.remove(filter); + this.remove(filter.getPanel()); this.revalidate(); } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java index 5bb7f9efcd9..53c938ecbbf 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardCMCFilter extends ValueRangeFilter { public CardCMCFilter(ItemManager itemManager0) { @@ -20,7 +21,7 @@ public class CardCMCFilter extends ValueRangeFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java index d774846c44c..f803dc50474 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java @@ -1,13 +1,15 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; import forge.item.PaperCard; /** * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardColorFilter extends ToggleButtonsFilter { public CardColorFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,8 +21,14 @@ public class CardColorFilter extends ToggleButtonsFilter { } @Override - protected void addComponents() { - + protected void buildPanel(JPanel panel) { + addToggleButton(panel, StatTypes.WHITE); + addToggleButton(panel, StatTypes.BLUE); + addToggleButton(panel, StatTypes.BLACK); + addToggleButton(panel, StatTypes.RED); + addToggleButton(panel, StatTypes.GREEN); + addToggleButton(panel, StatTypes.COLORLESS); + addToggleButton(panel, StatTypes.MULTICOLOR); } @Override diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java index ced3cb035c8..c5036d05b35 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardFormatFilter extends ListLabelFilter { public CardFormatFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,7 +20,7 @@ public class CardFormatFilter extends ListLabelFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java index 5e1d6734a06..bdce7d69865 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardPowerFilter extends ValueRangeFilter { public CardPowerFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,7 +20,7 @@ public class CardPowerFilter extends ValueRangeFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java index 440712fc696..e37421d805b 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardQuestWorldFilter extends ListLabelFilter { public CardQuestWorldFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,7 +20,7 @@ public class CardQuestWorldFilter extends ListLabelFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java index a63303b49e0..4eda5c28f54 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardSetFilter extends ListLabelFilter { public CardSetFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,7 +20,7 @@ public class CardSetFilter extends ListLabelFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java index 1847f707b53..2380d4e3188 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java @@ -1,5 +1,7 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -7,7 +9,6 @@ import forge.item.PaperCard; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardToughnessFilter extends ValueRangeFilter { public CardToughnessFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,7 +20,7 @@ public class CardToughnessFilter extends ValueRangeFilter { } @Override - protected void addComponents() { + protected void buildPanel(JPanel panel) { } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java index 717c7d3a340..a5515553e29 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java @@ -1,13 +1,15 @@ package forge.gui.toolbox.itemmanager.filters; +import javax.swing.JPanel; + import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; import forge.item.PaperCard; /** * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public class CardTypeFilter extends ToggleButtonsFilter { public CardTypeFilter(ItemManager itemManager0) { super(itemManager0); @@ -19,8 +21,14 @@ public class CardTypeFilter extends ToggleButtonsFilter { } @Override - protected void addComponents() { - + protected void buildPanel(JPanel panel) { + addToggleButton(panel, StatTypes.LAND); + addToggleButton(panel, StatTypes.ARTIFACT); + addToggleButton(panel, StatTypes.CREATURE); + addToggleButton(panel, StatTypes.ENCHANTMENT); + addToggleButton(panel, StatTypes.PLANESWALKER); + addToggleButton(panel, StatTypes.INSTANT); + addToggleButton(panel, StatTypes.SORCERY); } @Override diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java index 17db43ec326..a45fab8e87f 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java @@ -11,9 +11,11 @@ import forge.item.InventoryItem; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") -public abstract class ItemFilter extends JPanel { +public abstract class ItemFilter { private final ItemManager itemManager; + private JPanel panel; + + public static int PANEL_HEIGHT = 30; public enum FilterTypes { CardCMC, @@ -28,16 +30,31 @@ public abstract class ItemFilter extends JPanel { protected ItemFilter(ItemManager itemManager0) { this.itemManager = itemManager0; - this.setOpaque(false); - this.addComponents(); - this.add(new FLabel.Builder().text("X").fontSize(10).hoverable(true) - .tooltip("Remove filter").cmdClick(new Command() { + } + + @SuppressWarnings("serial") + public JPanel getPanel() { + if (panel == null) { + panel = new JPanel(); + panel.setOpaque(false); + + this.buildPanel(panel); + + //add button to remove filter + panel.add(new FLabel.Builder() + .text("X") + .fontSize(10) + .hoverable(true) + .tooltip("Remove filter") + .cmdClick(new Command() { @Override public void run() { itemManager.removeFilter(ItemFilter.this); ItemFilter.this.onRemoved(); } }).build(), "top"); + } + return panel; } protected void applyChange() { @@ -45,6 +62,6 @@ public abstract class ItemFilter extends JPanel { } public abstract FilterTypes getType(); - protected abstract void addComponents(); + protected abstract void buildPanel(JPanel panel); protected abstract void onRemoved(); } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java index fa43ccb5f19..4d487fbcdfe 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java @@ -7,7 +7,6 @@ import forge.item.InventoryItem; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public abstract class ListLabelFilter extends ItemFilter { protected ListLabelFilter(ItemManager itemManager0) { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java index 4d7611eadac..0d7c9f43c93 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java @@ -7,7 +7,6 @@ import forge.item.InventoryItem; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public abstract class TextFieldFilter extends ItemFilter { protected TextFieldFilter(ItemManager itemManager0) { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java index 2f8f0c47571..e154b37b57b 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java @@ -1,6 +1,15 @@ package forge.gui.toolbox.itemmanager.filters; +import java.awt.Dimension; +import java.util.ArrayList; + +import javax.swing.ImageIcon; +import javax.swing.JPanel; + +import forge.Command; +import forge.gui.toolbox.FLabel; import forge.gui.toolbox.itemmanager.ItemManager; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; import forge.item.InventoryItem; /** @@ -9,8 +18,49 @@ import forge.item.InventoryItem; */ @SuppressWarnings("serial") public abstract class ToggleButtonsFilter extends ItemFilter { + private static final Dimension BUTTON_SIZE = new Dimension(60, 24); + + private final ArrayList buttons = new ArrayList(); protected ToggleButtonsFilter(ItemManager itemManager0) { super(itemManager0); } + + protected void addToggleButton(JPanel panel, StatTypes s) { + addToggleButton(panel, s.toLabelString(), s.img); + } + + protected void addToggleButton(JPanel panel, String filterName, ImageIcon icon) { + final FLabel button = new FLabel.Builder() + .icon(icon).iconScaleAuto(false) + .fontSize(11) + .tooltip(filterName + " (click to toggle the filter, right-click to show only " + filterName.toLowerCase() + ")") + .hoverable().selectable(true).selected(true) + .build(); + + button.setPreferredSize(BUTTON_SIZE); + button.setMinimumSize(BUTTON_SIZE); + + button.setCommand(new Command() { + @Override + public void run() { + applyChange(); + } + }); + + //hook so right-clicking a button toggles itself on and toggles off all other buttons + button.setRightClickCommand(new Command() { + @Override + public void run() { + for(FLabel btn : buttons) { + btn.setSelected(false); + } + button.setSelected(true); + applyChange(); + } + }); + + this.buttons.add(button); + panel.add(button); + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java index 646727d939d..a5c5733f327 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java @@ -7,7 +7,6 @@ import forge.item.InventoryItem; * TODO: Write javadoc for this type. * */ -@SuppressWarnings("serial") public abstract class ValueRangeFilter extends ItemFilter { protected ValueRangeFilter(ItemManager itemManager0) { From f745ba9f5d28b95b4800bd3fc5a08baf32109dd4 Mon Sep 17 00:00:00 2001 From: Sol Date: Tue, 20 Aug 2013 02:56:20 +0000 Subject: [PATCH 06/11] - Updating Svar --- res/cardsfolder/s/smelt_ward_gatekeepers.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/res/cardsfolder/s/smelt_ward_gatekeepers.txt b/res/cardsfolder/s/smelt_ward_gatekeepers.txt index 92b2986ee8c..45ebb13bbe9 100644 --- a/res/cardsfolder/s/smelt_ward_gatekeepers.txt +++ b/res/cardsfolder/s/smelt_ward_gatekeepers.txt @@ -5,5 +5,6 @@ PT:2/4 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | IsPresent$ Gate.YouCtrl | PresentCompare$ GE2 | Execute$ TrigGainControl | TriggerDescription$ When CARDNAME enters the battlefield, if you control two or more Gates, gain control of target creature an opponent controls until end of turn. Untap that creature. That creature gains haste until end of turn. SVar:TrigGainControl:AB$ GainControl | Cost$ 0 | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Untap$ True | LoseControl$ EOT | SubAbility$ DBPump SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Haste +SVar:PlayMain1:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/smelt_ward_gatekeepers.jpg Oracle:When Smelt-Ward Gatekeepers enters the battlefield, if you control two or more Gates, gain control of target creature an opponent controls until end of turn. Untap that creature. It gains haste until end of turn. \ No newline at end of file From 34618e957cba9fcdf771933dd8a25618f0af571d Mon Sep 17 00:00:00 2001 From: spr Date: Tue, 20 Aug 2013 05:26:24 +0000 Subject: [PATCH 07/11] - Forge forced to use Java cross platform "Metal" look and feel which should only really affect Mac users (in a good way hopefully!). More details, see http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=11126#p125845. --- .gitattributes | 1 + src/main/java/forge/control/FControl.java | 31 ++---- .../forge/properties/ForgeLookAndFeel.java | 98 +++++++++++++++++++ 3 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 src/main/java/forge/properties/ForgeLookAndFeel.java diff --git a/.gitattributes b/.gitattributes index 0fd710ad239..6a71c67f353 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15101,6 +15101,7 @@ src/main/java/forge/net/protocol/toserver/IPacketSrv.java -text src/main/java/forge/net/protocol/toserver/IncorrectPacketSrv.java -text src/main/java/forge/net/protocol/toserver/package-info.java -text src/main/java/forge/package-info.java svneol=native#text/plain +src/main/java/forge/properties/ForgeLookAndFeel.java -text src/main/java/forge/properties/ForgePreferences.java svneol=native#text/plain src/main/java/forge/properties/NewConstants.java svneol=native#text/plain src/main/java/forge/properties/Preferences.java svneol=native#text/plain diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index d0fc52ced9d..3a345e83ee8 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -29,7 +29,6 @@ import java.util.List; import javax.swing.ImageIcon; import javax.swing.JLayeredPane; import javax.swing.SwingUtilities; -import javax.swing.UIManager; import javax.swing.WindowConstants; import forge.Card; @@ -63,6 +62,7 @@ import forge.gui.match.nonsingleton.VField; import forge.gui.match.views.VAntes; import forge.gui.toolbox.FSkin; import forge.net.FServer; +import forge.properties.ForgeLookAndFeel; import forge.properties.ForgePreferences.FPref; import forge.properties.NewConstants; import forge.quest.QuestController; @@ -166,13 +166,11 @@ public enum FControl { public void initialize() { // Preloads skin components (using progress bar). FSkin.loadFull(); - - //This must be done here or at least between the skin being loaded and any FTabbedPanes being created. - //Why,Swing? Why is this not a property of JTabbbedPane? - UIManager.put("TabbedPane.selected", FSkin.getColor(FSkin.Colors.CLR_ACTIVE)); - UIManager.put("TabbedPane.contentOpaque", FSkin.getColor(FSkin.Colors.CLR_THEME)); - UIManager.put("TabbedPane.unselectedBackground", FSkin.getColor(FSkin.Colors.CLR_THEME2)); - setComboBoxLookAndFeel(); + + // This must be done here or at least between the skin being loaded + // and any GUI controls being created. + FSkin.setProgessBarMessage("Setting look and feel..."); + setForgeLookAndFeel(); this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); @@ -208,20 +206,9 @@ public enum FControl { public void run() { Singletons.getView().initialize(); } }); } - /** - * @see UIManager Defaults - */ - private void setComboBoxLookAndFeel() { - if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_THEMED_COMBOBOX)) { - UIManager.put("ComboBox.background", FSkin.getColor(FSkin.Colors.CLR_THEME2)); - UIManager.put("ComboBox.foreground", FSkin.getColor(FSkin.Colors.CLR_TEXT)); - UIManager.put("ComboBox.selectionBackground", FSkin.getColor(FSkin.Colors.CLR_ACTIVE)); - UIManager.put("ComboBox.selectionForeground", FSkin.getColor(FSkin.Colors.CLR_TEXT)); - UIManager.put("ComboBox.disabledBackground", FSkin.getColor(FSkin.Colors.CLR_THEME2)); - UIManager.put("ComboBox.disabledForeground", FSkin.getColor(FSkin.Colors.CLR_THEME2).darker()); - UIManager.put("Button.select", FSkin.getColor(FSkin.Colors.CLR_ACTIVE)); - UIManager.put("ComboBox.font", FSkin.getFont(UIManager.getFont("ComboBox.font").getSize())); - } + private void setForgeLookAndFeel() { + ForgeLookAndFeel laf = new ForgeLookAndFeel(); + laf.setForgeLookAndFeel(Singletons.getView().getFrame()); } /** diff --git a/src/main/java/forge/properties/ForgeLookAndFeel.java b/src/main/java/forge/properties/ForgeLookAndFeel.java new file mode 100644 index 00000000000..b24b315d15e --- /dev/null +++ b/src/main/java/forge/properties/ForgeLookAndFeel.java @@ -0,0 +1,98 @@ +package forge.properties; + +import java.awt.Color; +import java.awt.Font; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import forge.Singletons; +import forge.gui.toolbox.FSkin; +import forge.properties.ForgePreferences.FPref; + +/** + * Sets the look and feel of the GUI based on the selected Forge theme. + * + * @see UIManager Defaults + */ +public final class ForgeLookAndFeel { + + private Color FORE_COLOR = FSkin.getColor(FSkin.Colors.CLR_TEXT); + private Color BACK_COLOR = FSkin.getColor(FSkin.Colors.CLR_THEME2); + private Color HIGHLIGHT_COLOR = BACK_COLOR.brighter(); + + /** + * Sets the look and feel of the GUI based on the selected Forge theme. + */ + public void setForgeLookAndFeel(JFrame appFrame) { + if (isUIManagerEnabled()) { + if (setMetalLookAndFeel(appFrame)) { + setComboBoxLookAndFeel(); + setTabbedPaneLookAndFeel(); + setButtonLookAndFeel(); + } + } + } + + /** + * Sets the standard "Java L&F" (also called "Metal") that looks the same on all platforms. + *

+ * If not explicitly set then the Mac uses its native L&F which does + * not support various settings (eg. combobox background color). + */ + private boolean setMetalLookAndFeel(JFrame appFrame) { + boolean isMetalLafSet = false; + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + SwingUtilities.updateComponentTreeUI(appFrame); + isMetalLafSet = true; + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { + // Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log. + e.printStackTrace(); + } + return isMetalLafSet; + } + + private void setTabbedPaneLookAndFeel() { + UIManager.put("TabbedPane.selected", HIGHLIGHT_COLOR); + UIManager.put("TabbedPane.contentOpaque", FSkin.getColor(FSkin.Colors.CLR_THEME)); + UIManager.put("TabbedPane.unselectedBackground", BACK_COLOR); + } + + /** + * Sets the look and feel for a non-editable JComboBox. + */ + private void setComboBoxLookAndFeel() { + UIManager.put("ComboBox.background", BACK_COLOR); + UIManager.put("ComboBox.foreground", FORE_COLOR); + UIManager.put("ComboBox.selectionBackground", HIGHLIGHT_COLOR); + UIManager.put("ComboBox.selectionForeground", FORE_COLOR); + UIManager.put("ComboBox.disabledBackground", BACK_COLOR); + UIManager.put("ComboBox.disabledForeground", BACK_COLOR.darker()); + UIManager.put("ComboBox.font", getDefaultFont("ComboBox.font")); + UIManager.put("Button.select", HIGHLIGHT_COLOR); + } + + private void setButtonLookAndFeel() { + UIManager.put("Button.foreground", FORE_COLOR); + UIManager.put("Button.background", BACK_COLOR); + UIManager.put("Button.select", HIGHLIGHT_COLOR); + UIManager.put("Button.focus", FORE_COLOR.darker()); + UIManager.put("Button.rollover", false); + } + + /** + * Determines whether theme styles should be applied to GUI. + *

+ * TODO: Currently is using UI_THEMED_COMBOBOX setting but will + * eventually want to rename for clarity. + */ + private boolean isUIManagerEnabled() { + return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_THEMED_COMBOBOX); + } + + private Font getDefaultFont(String component) { + return FSkin.getFont(UIManager.getFont(component).getSize()); + } + +} From f8f03a57ba4b9b70345cb15dc3023ac2029e6f8d Mon Sep 17 00:00:00 2001 From: swordshine Date: Wed, 21 Aug 2013 00:42:32 +0000 Subject: [PATCH 08/11] - More cards ready for multiplayer --- res/cardsfolder/a/archangel_of_strife.txt | 24 ++++++------------- res/cardsfolder/e/exhume.txt | 4 ++-- res/cardsfolder/m/mana_breach.txt | 6 ++--- res/cardsfolder/m/memory_jar.txt | 11 ++++----- res/cardsfolder/o/overburden.txt | 6 ++--- .../card/ability/effects/EffectEffect.java | 5 ---- 6 files changed, 18 insertions(+), 38 deletions(-) diff --git a/res/cardsfolder/a/archangel_of_strife.txt b/res/cardsfolder/a/archangel_of_strife.txt index af633f7a245..a6f0de1728e 100644 --- a/res/cardsfolder/a/archangel_of_strife.txt +++ b/res/cardsfolder/a/archangel_of_strife.txt @@ -3,23 +3,13 @@ ManaCost:5 W W Types:Creature Angel PT:6/6 K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Tolstoy | Static$ True | TriggerDescription$ As CARDNAME enters the battlefield, each player chooses war or peace. Creatures controlled by players who chose war get +3/+0. Creatures controlled by players who chose peace get +0/+3. -SVar:Tolstoy:AB$ GenericChoice | Cost$ 0 | Defined$ You | Choices$ WarChoice,PeaceChoice | SubAbility$ OppChoice -SVar:OppChoice:DB$ GenericChoice | Cost$ 0 | Defined$ Opponent | Choices$ Attacking,Defensive -SVar:WarChoice:DB$ Effect | Name$ Archangel War Effect | ChoiceDescription$ War | Duration$ UntilHostLeavesPlay | RememberEffect$ True -SVar:PeaceChoice:DB$ Effect | Name$ Archangel Peace Effect | ChoiceDescription$ Peace | Duration$ UntilHostLeavesPlay | RememberEffect$ True -SVar:Attacking:DB$ Effect | Name$ Archangel War Effect | ChoiceDescription$ War | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay | RememberEffect$ True -SVar:Defensive:DB$ Effect | Name$ Archangel Peace Effect | ChoiceDescription$ Peace | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay | RememberEffect$ True -S:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddPower$ 3 | CheckSVar$ WarYou | SVarCompare$ GE1 | References$ WarYou -S:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddToughness$ 3 | CheckSVar$ PeaceYou | SVarCompare$ GE1 | References$ PeaceYou -S:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Creature.YouDontCtrl | AddPower$ 3 | CheckSVar$ WarOpp | SVarCompare$ GE1 | References$ WarOpp -S:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Creature.YouDontCtrl | AddToughness$ 3 | CheckSVar$ PeaceOpp | SVarCompare$ GE1 | References$ PeaceOpp -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Defined$ Self | Execute$ DBCleanup | Static$ True -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:WarYou:Count$ValidCommand Card.namedArchangel War Effect+YouCtrl+IsRemembered -SVar:PeaceYou:Count$ValidCommand Card.namedArchangel Peace Effect+YouCtrl+IsRemembered -SVar:WarOpp:Count$ValidCommand Card.namedArchangel War Effect+YouDontCtrl+IsRemembered -SVar:PeaceOpp:Count$ValidCommand Card.namedArchangel Peace Effect+YouDontCtrl+IsRemembered +K:ETBReplacement:Other:ChooseEach +SVar:ChooseEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoice | SpellDescription$ As CARDNAME enters the battlefield, each player chooses war or peace. Creatures controlled by players who chose war get +3/+0. Creatures controlled by players who chose peace get +0/+3. +SVar:DBChoice:DB$ GenericChoice | Cost$ 0 | Defined$ Player.IsRemembered | Choices$ WarChoice,PeaceChoice +SVar:WarChoice:DB$ Effect | Name$ Archangel War Effect | StaticAbilities$ WarPump | ChoiceDescription$ War | EffectOwner$ Player.IsRemembered | Duration$ UntilHostLeavesPlay +SVar:PeaceChoice:DB$ Effect | Name$ Archangel Peace Effect | StaticAbilities$ PeacePump | ChoiceDescription$ Peace | EffectOwner$ Player.IsRemembered | Duration$ UntilHostLeavesPlay +SVar:WarPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddPower$ 3 | Description$ Creatures you control get +3/+0. +SVar:PeacePump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddToughness$ 3 | Description$ Creatures you control get +0/+3. SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/archangel_of_strife.jpg Oracle:Flying\nAs Archangel of Strife enters the battlefield, each player chooses war or peace.\nCreatures controlled by players who chose war get +3/+0.\nCreatures controlled by players who chose peace get +0/+3. \ No newline at end of file diff --git a/res/cardsfolder/e/exhume.txt b/res/cardsfolder/e/exhume.txt index 0ed9681a24c..8ec64cab832 100644 --- a/res/cardsfolder/e/exhume.txt +++ b/res/cardsfolder/e/exhume.txt @@ -1,7 +1,7 @@ Name:Exhume ManaCost:1 B Types:Sorcery -A:SP$ ChangeZone | Cost$ 1 B | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouCtrl | ChangeNum$ 1 | Hidden$ True | SubAbility$ DBChangeZoneOpp | SpellDescription$ Each player puts a creature card from his or her graveyard onto the battlefield. -SVar:DBChangeZoneOpp:DB$ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.YouDontCtrl | DefinedPlayer$ Opponent | ChangeNum$ 1 | Hidden$ True +A:SP$ RepeatEach | Cost$ 1 B | RepeatSubAbility$ DBChangeZone | RepeatPlayers$ Player | SpellDescription$ Each player puts a creature card from his or her graveyard onto the battlefield. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.RememberedPlayerCtrl | DefinedPlayer$ Player.IsRemembered | Chooser$ Player.IsRemembered | ChangeNum$ 1 | Hidden$ True SVar:Picture:http://www.wizards.com/global/images/magic/general/exhume.jpg Oracle:Each player puts a creature card from his or her graveyard onto the battlefield. \ No newline at end of file diff --git a/res/cardsfolder/m/mana_breach.txt b/res/cardsfolder/m/mana_breach.txt index 007e2f81840..f3ff236e42d 100644 --- a/res/cardsfolder/m/mana_breach.txt +++ b/res/cardsfolder/m/mana_breach.txt @@ -1,10 +1,8 @@ Name:Mana Breach ManaCost:2 U Types:Enchantment -T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigBounceYou | TriggerDescription$ Whenever a player casts a spell, that player returns a land he or she controls to its owner's hand. -T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigBounceOpp | Secondary$ True | TriggerDescription$ Whenever a player casts a spell, that player returns a land he or she controls to its owner's hand. -SVar:TrigBounceYou:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeNum$ 1 | ChangeType$ Land.YouCtrl | Mandatory$ True | DefinedPlayer$ You | Hidden$ True -SVar:TrigBounceOpp:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeNum$ 1 | ChangeType$ Land.YouDontCtrl | Mandatory$ True | DefinedPlayer$ Opponent | Hidden$ True +T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ Player | TriggerZones$ Battlefield | Execute$ TrigBounce | TriggerDescription$ Whenever a player casts a spell, that player returns a land he or she controls to its owner's hand. +SVar:TrigBounce:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeNum$ 1 | ChangeType$ Land | Mandatory$ True | DefinedPlayer$ TriggeredActivator | Chooser$ TriggeredActivator | Hidden$ True SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/mana_breach.jpg Oracle:Whenever a player casts a spell, that player returns a land he or she controls to its owner's hand. \ No newline at end of file diff --git a/res/cardsfolder/m/memory_jar.txt b/res/cardsfolder/m/memory_jar.txt index 4f82d751ac6..691e04f52d2 100644 --- a/res/cardsfolder/m/memory_jar.txt +++ b/res/cardsfolder/m/memory_jar.txt @@ -1,12 +1,11 @@ Name:Memory Jar ManaCost:5 Types:Artifact -A:AB$ ChangeZoneAll | Cost$ T Sac<1/CARDNAME> | ChangeType$ Card | Origin$ Hand | Destination$ Exile | ExileFaceDown$ True | RememberChanged$ True | SubAbility$ YouDraw | SpellDescription$ Each player exiles all cards from his or her hand face down and draws seven cards. At the beginning of the next end step, each player discards his or her hand and returns to his or her hand each card he or she exiled this way. -SVar:YouDraw:DB$ Draw | Defined$ You | NumCards$ 7 | SubAbility$ OppDraw -SVar:OppDraw:DB$ Draw | Defined$ Opponent | NumCards$ 7 | SubAbility$ DelayedReturn -SVar:DelayedReturn:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ YouDiscard | TriggerDescription$ Both players discard their hands. Return exiled cards to their owners' hands. -SVar:YouDiscard: AB$ Discard | Cost$ 0 | Defined$ You | Mode$ Hand | SubAbility$ OppDiscard -SVar:OppDiscard: DB$ Discard | Defined$ Opponent | Mode$ Hand | SubAbility$ ReturnAll +A:AB$ ChangeZoneAll | Cost$ T Sac<1/CARDNAME> | ChangeType$ Card | Origin$ Hand | Destination$ Exile | ExileFaceDown$ True | RememberChanged$ True | SubAbility$ DrawEach | SpellDescription$ Each player exiles all cards from his or her hand face down and draws seven cards. At the beginning of the next end step, each player discards his or her hand and returns to his or her hand each card he or she exiled this way. +SVar:DrawEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBDraw | SubAbility$ DelayedReturn +SVar:DBDraw:DB$ Draw | Defined$ Player.IsRemembered | NumCards$ 7 +SVar:DelayedReturn:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ DiscardEach | TriggerDescription$ Both players discard their hands. Return exiled cards to their owners' hands. +SVar:DiscardEach:AB$ Discard | Cost$ 0 | Defined$ Each | Mode$ Hand | SubAbility$ ReturnAll SVar:ReturnAll:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:RemAIDeck:True diff --git a/res/cardsfolder/o/overburden.txt b/res/cardsfolder/o/overburden.txt index caa8082799c..82cc3c97064 100644 --- a/res/cardsfolder/o/overburden.txt +++ b/res/cardsfolder/o/overburden.txt @@ -1,10 +1,8 @@ Name:Overburden ManaCost:1 U Types:Enchantment -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigBounceYou | TriggerDescription$ Whenever a player puts a nontoken creature onto the battlefield, that player returns a land he or she controls to its owner's hand. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken+YouDontCtrl | TriggerZones$ Battlefield | Execute$ TrigBounceOpp | Secondary$ True | TriggerDescription$ Whenever a player puts a nontoken creature onto the battlefield, that player returns a land he or she controls to its owner's hand. -SVar:TrigBounceYou:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeType$ Land.YouCtrl | ChangeNum$ 1 | Mandatory$ True | DefinedPlayer$ You | Hidden$ True -SVar:TrigBounceOpp:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeType$ Land.YouDontCtrl | ChangeNum$ 1 | Mandatory$ True | DefinedPlayer$ Opponent | Hidden$ True +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken | TriggerZones$ Battlefield | Execute$ TrigBounce | TriggerDescription$ Whenever a player puts a nontoken creature onto the battlefield, that player returns a land he or she controls to its owner's hand. +SVar:TrigBounce:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | ChangeType$ Land | ChangeNum$ 1 | Mandatory$ True | DefinedPlayer$ TriggeredCardController | Chooser$ TriggeredCardController | Hidden$ True SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/overburden.jpg Oracle:Whenever a player puts a nontoken creature onto the battlefield, that player returns a land he or she controls to its owner's hand. \ No newline at end of file diff --git a/src/main/java/forge/card/ability/effects/EffectEffect.java b/src/main/java/forge/card/ability/effects/EffectEffect.java index 6fb58c2db95..5cbce705377 100644 --- a/src/main/java/forge/card/ability/effects/EffectEffect.java +++ b/src/main/java/forge/card/ability/effects/EffectEffect.java @@ -188,11 +188,6 @@ public class EffectEffect extends SpellAbilityEffect { eff.setChosenColor(hostCard.getChosenColor()); } - // Remember created effect - if (sa.hasParam("RememberEffect")) { - game.getCardState(hostCard).addRemembered(eff); - } - // Duration final String duration = sa.getParam("Duration"); if ((duration == null) || !duration.equals("Permanent")) { From 5ee58d727a1c99b198387b7e034ad55ac081f6ca Mon Sep 17 00:00:00 2001 From: spr Date: Wed, 21 Aug 2013 01:13:26 +0000 Subject: [PATCH 09/11] - New forge menu bar (part 1 of 2). Includes common Forge and Help menus. --- .gitattributes | 5 + src/main/java/forge/control/FControl.java | 13 +- .../forge/gui/framework/SResizingUtil.java | 813 +++++++++--------- src/main/java/forge/gui/menubar/FMenuBar.java | 51 ++ .../java/forge/gui/menubar/IMenuProvider.java | 19 + src/main/java/forge/gui/menubar/MenuUtil.java | 61 ++ src/main/java/forge/gui/menus/ForgeMenu.java | 70 ++ src/main/java/forge/gui/menus/HelpMenu.java | 124 +++ .../forge/gui/toolbox/imaging/ImageUtil.java | 9 + .../forge/properties/ForgeLookAndFeel.java | 68 ++ .../forge/properties/ForgePreferences.java | 1 + 11 files changed, 827 insertions(+), 407 deletions(-) create mode 100644 src/main/java/forge/gui/menubar/FMenuBar.java create mode 100644 src/main/java/forge/gui/menubar/IMenuProvider.java create mode 100644 src/main/java/forge/gui/menubar/MenuUtil.java create mode 100644 src/main/java/forge/gui/menus/ForgeMenu.java create mode 100644 src/main/java/forge/gui/menus/HelpMenu.java diff --git a/.gitattributes b/.gitattributes index 6a71c67f353..d9cee3024c1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14986,6 +14986,11 @@ src/main/java/forge/gui/match/views/VPicture.java -text src/main/java/forge/gui/match/views/VPlayers.java -text src/main/java/forge/gui/match/views/VStack.java -text src/main/java/forge/gui/match/views/package-info.java svneol=native#text/plain +src/main/java/forge/gui/menubar/FMenuBar.java -text +src/main/java/forge/gui/menubar/IMenuProvider.java -text +src/main/java/forge/gui/menubar/MenuUtil.java -text +src/main/java/forge/gui/menus/ForgeMenu.java -text +src/main/java/forge/gui/menus/HelpMenu.java -text src/main/java/forge/gui/package-info.java svneol=native#text/plain src/main/java/forge/gui/toolbox/CardFaceSymbols.java svneol=native#text/plain src/main/java/forge/gui/toolbox/FAbsolutePositioner.java -text diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index 3a345e83ee8..efce9c8f6a6 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -60,6 +60,8 @@ import forge.gui.match.controllers.CMessage; import forge.gui.match.controllers.CStack; import forge.gui.match.nonsingleton.VField; import forge.gui.match.views.VAntes; +import forge.gui.menubar.FMenuBar; +import forge.gui.menubar.MenuUtil; import forge.gui.toolbox.FSkin; import forge.net.FServer; import forge.properties.ForgeLookAndFeel; @@ -81,7 +83,8 @@ import forge.view.FView; */ public enum FControl { instance; - + + private FMenuBar menuBar; private List shortcuts; private JLayeredPane display; private Screens state = Screens.UNKNOWN; @@ -171,6 +174,8 @@ public enum FControl { // and any GUI controls being created. FSkin.setProgessBarMessage("Setting look and feel..."); setForgeLookAndFeel(); + + createMenuBar(); this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); @@ -210,6 +215,12 @@ public enum FControl { ForgeLookAndFeel laf = new ForgeLookAndFeel(); laf.setForgeLookAndFeel(Singletons.getView().getFrame()); } + + private void createMenuBar() { + if (MenuUtil.isMenuBarVisible()) { + this.menuBar = new FMenuBar(Singletons.getView().getFrame()); + } + } /** * Switches between display states in top level JFrame. diff --git a/src/main/java/forge/gui/framework/SResizingUtil.java b/src/main/java/forge/gui/framework/SResizingUtil.java index 7bdc95cbcc3..65837a0dd4f 100644 --- a/src/main/java/forge/gui/framework/SResizingUtil.java +++ b/src/main/java/forge/gui/framework/SResizingUtil.java @@ -1,406 +1,407 @@ -package forge.gui.framework; - -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Rectangle; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionAdapter; -import java.awt.event.MouseMotionListener; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.swing.JPanel; - -import forge.gui.FNetOverlay; -import forge.gui.toolbox.FAbsolutePositioner; -import forge.gui.toolbox.FOverlay; -import forge.view.FView; - -/** - * Package-private utilities for resizing drag behavior using - * the draggable panels registered in FView. - * - *

(S at beginning of class name denotes a static factory.) - */ -public final class SResizingUtil { - private static final List LEFT_PANELS = new ArrayList(); - private static final List RIGHT_PANELS = new ArrayList(); - private static final List TOP_PANELS = new ArrayList(); - private static final List BOTTOM_PANELS = new ArrayList(); - - private static int dX; - private static int evtX; - private static int dY; - private static int evtY; - - /** Minimum cell width. */ - public static final int W_MIN = 100; - /** Minimum cell height. */ - public static final int H_MIN = 75; - - private static final MouseListener MAD_RESIZE_X = new MouseAdapter() { - @Override - public void mouseEntered(final MouseEvent e) { - FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - } - - @Override - public void mouseExited(final MouseEvent e) { - FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - @Override - public void mousePressed(final MouseEvent e) { - SResizingUtil.startResizeX(e); - } - - @Override - public void mouseReleased(final MouseEvent e) { - SResizingUtil.endResize(); - } - }; - - private static final MouseListener MAD_RESIZE_Y = new MouseAdapter() { - @Override - public void mouseEntered(final MouseEvent e) { - FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); - } - - @Override - public void mouseExited(final MouseEvent e) { - FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - @Override - public void mousePressed(final MouseEvent e) { - SResizingUtil.startResizeY(e); - } - - @Override - public void mouseReleased(final MouseEvent e) { - SResizingUtil.endResize(); - } - }; - - private static final MouseMotionListener MMA_DRAG_X = new MouseMotionAdapter() { - @Override - public void mouseDragged(final MouseEvent e) { - SResizingUtil.resizeX(e); - } - }; - - private static final MouseMotionListener MMA_DRAG_Y = new MouseMotionAdapter() { - @Override - public void mouseDragged(final MouseEvent e) { - SResizingUtil.resizeY(e); - } - }; - - private static final ComponentListener CAD_RESIZE = new ComponentAdapter() { - @Override - public void componentResized(final ComponentEvent e) { - resizeWindow(); - SRearrangingUtil.updateBorders(); - } - }; - - public static void resizeWindow() { - final List cells = FView.SINGLETON_INSTANCE.getDragCells(); - final JPanel pnlContent = FView.SINGLETON_INSTANCE.getPnlContent(); - final JPanel pnlInsets = FView.SINGLETON_INSTANCE.getPnlInsets(); - - Rectangle mainBounds = FView.SINGLETON_INSTANCE.getFrame().getContentPane().getBounds(); - FAbsolutePositioner.SINGLETON_INSTANCE.containerResized(mainBounds); - FOverlay.SINGLETON_INSTANCE.getPanel().setBounds(mainBounds); - FNetOverlay.SINGLETON_INSTANCE.containerResized(mainBounds); - - pnlInsets.setBounds(mainBounds); - pnlInsets.validate(); - - final int w = pnlContent.getWidth(); - final int h = pnlContent.getHeight(); - - double roughVal = 0; - int smoothVal = 0; - - Set existingComponents = new HashSet(); - for (Component c : pnlContent.getComponents()) { - existingComponents.add(c); - } - - // This is the core of the pixel-perfect layout. To avoid ±1 px errors on borders - // from rounding individual panels, the intermediate values (exactly accurate, in %) - // for width and height are rounded based on comparison to other panels in the - // layout. This is to avoid errors such as: - // 10% = 9.8px -> 10px -> x 3 = 30px - // 30% = 29.4px -> 29px (!) - for (final DragCell cellA : cells) { - RectangleOfDouble cellSizeA = cellA.getRoughBounds(); - roughVal = cellSizeA.getX() * w + cellSizeA.getW() * w; - - smoothVal = (int) Math.round(roughVal); - for (final DragCell cellB : cells) { - RectangleOfDouble cellSizeB = cellB.getRoughBounds(); - if ((cellSizeB.getX() * w + cellSizeB.getW() * w) == roughVal) { - cellB.setSmoothW(smoothVal - (int) Math.round(cellSizeB.getX() * w)); - } - } - cellA.setSmoothW(smoothVal - (int) Math.round(cellSizeA.getX() * w)); - - roughVal = cellSizeA.getY() * h + cellSizeA.getH() * h; - smoothVal = (int) Math.round(roughVal); - for (final DragCell cellB : cells) { - RectangleOfDouble cellSizeB = cellB.getRoughBounds(); - if (cellSizeB.getY() * h + cellSizeB.getH() * h == roughVal) { - cellB.setSmoothH(smoothVal - (int) Math.round(cellSizeB.getY() * h)); - } - } - cellA.setSmoothH(smoothVal - (int) Math.round(cellSizeA.getY() * h)); - - // X and Y coordinate can be rounded as usual. - cellA.setSmoothX((int) Math.round(cellSizeA.getX() * w)); - cellA.setSmoothY((int) Math.round(cellSizeA.getY() * h)); - - // only add component if not already in container; otherwise the keyboard focus - // jumps around to the most recenly added component - if (!existingComponents.contains(cellA)) { - pnlContent.add(cellA); - } - } - - // Lock in final bounds and build heads. - for (final DragCell t : cells) { - t.setSmoothBounds(); - t.validate(); - t.refresh(); - } - - cells.clear(); - } - - /** @param e   {@link java.awt.event.MouseEvent} */ - public static void resizeX(final MouseEvent e) { - dX = (int) e.getLocationOnScreen().getX() - evtX; - evtX = (int) e.getLocationOnScreen().getX(); - boolean leftLock = false; - boolean rightLock = false; - - for (final DragCell t : LEFT_PANELS) { - if ((t.getW() + dX) < W_MIN) { leftLock = true; break; } - } - - for (final DragCell t : RIGHT_PANELS) { - if ((t.getW() - dX) < W_MIN) { rightLock = true; break; } - } - - if (dX < 0 && leftLock) { return; } - if (dX > 0 && rightLock) { return; } - - for (final DragCell t : LEFT_PANELS) { - t.setBounds(t.getX(), t.getY(), t.getW() + dX, t.getH()); - t.refresh(); - } - - for (final DragCell t : RIGHT_PANELS) { - t.setBounds(t.getX() + dX, t.getY(), t.getW() - dX, t.getH()); - t.refresh(); - } - } - - /** @param e   {@link java.awt.event.MouseEvent} */ - public static void resizeY(final MouseEvent e) { - dY = (int) e.getLocationOnScreen().getY() - evtY; - evtY = (int) e.getLocationOnScreen().getY(); - boolean topLock = false; - boolean bottomLock = false; - - for (final DragCell t : TOP_PANELS) { - if ((t.getH() + dY) < H_MIN) { topLock = true; break; } - } - - for (final DragCell t : BOTTOM_PANELS) { - if ((t.getH() - dY) < H_MIN) { bottomLock = true; break; } - } - - if (dY < 0 && topLock) { return; } - if (dY > 0 && bottomLock) { return; } - - for (final DragCell t : TOP_PANELS) { - t.setBounds(t.getX(), t.getY(), t.getW(), t.getH() + dY); - t.revalidate(); - t.repaintSelf(); - } - - for (final DragCell t : BOTTOM_PANELS) { - t.setBounds(t.getX(), t.getY() + dY, t.getW(), t.getH() - dY); - t.revalidate(); - t.repaintSelf(); - } - } - - /** @param e   {@link java.awt.event.MouseEvent} */ - public static void startResizeX(final MouseEvent e) { - evtX = (int) e.getLocationOnScreen().getX(); - LEFT_PANELS.clear(); - RIGHT_PANELS.clear(); - - final DragCell src = (DragCell) ((JPanel) e.getSource()).getParent(); - final int srcX2 = src.getAbsX2(); - - int limTop = -1; - int limBottom = Integer.MAX_VALUE; - int tempX = -1; - int tempX2 = -1; - int tempY = -1; - int tempY2 = -1; - - // Add all panels who share a left or right edge with the - // same coordinate as the right edge of the source panel. - for (final DragCell t : FView.SINGLETON_INSTANCE.getDragCells()) { - tempX = t.getAbsX(); - tempX2 = t.getAbsX2(); - - if (srcX2 == tempX) { RIGHT_PANELS.add(t); } - else if (srcX2 == tempX2) { LEFT_PANELS.add(t); } - } - - // Set limits at panels which are level at intersections. - for (final DragCell pnlL : LEFT_PANELS) { - if (pnlL.equals(src)) { continue; } - tempY = pnlL.getAbsY(); - tempY2 = pnlL.getAbsY2(); - - for (final DragCell pnlR : RIGHT_PANELS) { - // Upper edges match? Set a bottom limit. - if (tempY >= src.getAbsY2() && tempY == pnlR.getAbsY() && tempY < limBottom) { - limBottom = tempY; - } - // Lower edges match? Set an upper limit. - else if (tempY2 <= src.getAbsY() && tempY2 == pnlR.getAbsY2() && tempY2 > limTop) { - limTop = tempY2; - } - } - } - - // Remove non-contiguous panels from left side using limits. - final Iterator itrLeft = LEFT_PANELS.iterator(); - while (itrLeft.hasNext()) { - final DragCell t = itrLeft.next(); - - if (t.getAbsY() >= limBottom || t.getAbsY2() <= limTop) { - itrLeft.remove(); - } - } - - // Remove non-contiguous panels from right side using limits. - final Iterator itrRight = RIGHT_PANELS.iterator(); - while (itrRight.hasNext()) { - final DragCell t = itrRight.next(); - - if (t.getAbsY() >= limBottom || t.getAbsY2() <= limTop) { - itrRight.remove(); - } - } - } - - /** @param e   {@link java.awt.event.MouseEvent} */ - public static void startResizeY(final MouseEvent e) { - evtY = (int) e.getLocationOnScreen().getY(); - TOP_PANELS.clear(); - BOTTOM_PANELS.clear(); - - final DragCell src = (DragCell) ((JPanel) e.getSource()).getParent(); - final int srcY2 = src.getAbsY2(); - - int limLeft = -1; - int limRight = Integer.MAX_VALUE; - int tempX = -1; - int tempX2 = -1; - int tempY = -1; - int tempY2 = -1; - - // Add all panels who share a top or bottom edge with the - // same coordinate as the bottom edge of the source panel. - for (final DragCell t : FView.SINGLETON_INSTANCE.getDragCells()) { - tempY = t.getAbsY(); - tempY2 = t.getAbsY2(); - - if (srcY2 == tempY) { BOTTOM_PANELS.add(t); } - else if (srcY2 == tempY2) { TOP_PANELS.add(t); } - } - - // Set limits at panels which are level at intersections. - for (final DragCell pnlT : TOP_PANELS) { - if (pnlT.equals(src)) { continue; } - tempX = pnlT.getAbsX(); - tempX2 = pnlT.getAbsX2(); - - for (final DragCell pnlB : BOTTOM_PANELS) { - // Right edges match? Set a right limit. - if (tempX >= src.getAbsX2() && tempX == pnlB.getAbsX() && tempX < limRight) { - limRight = tempX; - } - // Left edges match? Set an left limit. - else if (tempX2 <= src.getAbsX() && tempX2 == pnlB.getAbsX2() && tempX2 > limLeft) { - limLeft = tempX2; - } - } - } - - // Remove non-contiguous panels from left side using limits. - final Iterator itrTop = TOP_PANELS.iterator(); - while (itrTop.hasNext()) { - final DragCell t = itrTop.next(); - if (t.getAbsX() >= limRight || t.getAbsX2() <= limLeft) { - itrTop.remove(); - } - } - - // Remove non-contiguous panels from right side using limits. - final Iterator itrBottom = BOTTOM_PANELS.iterator(); - while (itrBottom.hasNext()) { - final DragCell t = itrBottom.next(); - if (t.getAbsX() >= limRight || t.getAbsX2() <= limLeft) { - itrBottom.remove(); - } - } - } - - /** */ - public static void endResize() { - SLayoutIO.saveLayout(null); - } - - /** @return {@link java.awt.event.MouseListener} */ - public static MouseListener getResizeXListener() { - return MAD_RESIZE_X; - } - - /** @return {@link java.awt.event.MouseListener} */ - public static MouseListener getResizeYListener() { - return MAD_RESIZE_Y; - } - - /** @return {@link java.awt.event.MouseMotionListener} */ - public static MouseMotionListener getDragXListener() { - return MMA_DRAG_X; - } - - /** @return {@link java.awt.event.MouseMotionListener} */ - public static MouseMotionListener getDragYListener() { - return MMA_DRAG_Y; - } - - /** @return {@link java.awt.event.ComponentListener} */ - public static ComponentListener getWindowResizeListener() { - return CAD_RESIZE; - } -} +package forge.gui.framework; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.swing.JPanel; + +import forge.gui.FNetOverlay; +import forge.gui.toolbox.FAbsolutePositioner; +import forge.gui.toolbox.FOverlay; +import forge.view.FView; + +/** + * Package-private utilities for resizing drag behavior using + * the draggable panels registered in FView. + * + *

(S at beginning of class name denotes a static factory.) + */ +public final class SResizingUtil { + private static final List LEFT_PANELS = new ArrayList(); + private static final List RIGHT_PANELS = new ArrayList(); + private static final List TOP_PANELS = new ArrayList(); + private static final List BOTTOM_PANELS = new ArrayList(); + + private static int dX; + private static int evtX; + private static int dY; + private static int evtY; + + /** Minimum cell width. */ + public static final int W_MIN = 100; + /** Minimum cell height. */ + public static final int H_MIN = 75; + + private static final MouseListener MAD_RESIZE_X = new MouseAdapter() { + @Override + public void mouseEntered(final MouseEvent e) { + FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + } + + @Override + public void mouseExited(final MouseEvent e) { + FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + @Override + public void mousePressed(final MouseEvent e) { + SResizingUtil.startResizeX(e); + } + + @Override + public void mouseReleased(final MouseEvent e) { + SResizingUtil.endResize(); + } + }; + + private static final MouseListener MAD_RESIZE_Y = new MouseAdapter() { + @Override + public void mouseEntered(final MouseEvent e) { + FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); + } + + @Override + public void mouseExited(final MouseEvent e) { + FView.SINGLETON_INSTANCE.getLpnDocument().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + @Override + public void mousePressed(final MouseEvent e) { + SResizingUtil.startResizeY(e); + } + + @Override + public void mouseReleased(final MouseEvent e) { + SResizingUtil.endResize(); + } + }; + + private static final MouseMotionListener MMA_DRAG_X = new MouseMotionAdapter() { + @Override + public void mouseDragged(final MouseEvent e) { + SResizingUtil.resizeX(e); + } + }; + + private static final MouseMotionListener MMA_DRAG_Y = new MouseMotionAdapter() { + @Override + public void mouseDragged(final MouseEvent e) { + SResizingUtil.resizeY(e); + } + }; + + private static final ComponentListener CAD_RESIZE = new ComponentAdapter() { + @Override + public void componentResized(final ComponentEvent e) { + resizeWindow(); + SRearrangingUtil.updateBorders(); + } + }; + + public static void resizeWindow() { + final List cells = FView.SINGLETON_INSTANCE.getDragCells(); + final JPanel pnlContent = FView.SINGLETON_INSTANCE.getPnlContent(); + final JPanel pnlInsets = FView.SINGLETON_INSTANCE.getPnlInsets(); + + Rectangle mainBounds = FView.SINGLETON_INSTANCE.getFrame().getContentPane().getBounds(); + mainBounds.y = 0; // Play nicely with MenuBar if visible or not. + FAbsolutePositioner.SINGLETON_INSTANCE.containerResized(mainBounds); + FOverlay.SINGLETON_INSTANCE.getPanel().setBounds(mainBounds); + FNetOverlay.SINGLETON_INSTANCE.containerResized(mainBounds); + + pnlInsets.setBounds(mainBounds); + pnlInsets.validate(); + + final int w = pnlContent.getWidth(); + final int h = pnlContent.getHeight(); + + double roughVal = 0; + int smoothVal = 0; + + Set existingComponents = new HashSet(); + for (Component c : pnlContent.getComponents()) { + existingComponents.add(c); + } + + // This is the core of the pixel-perfect layout. To avoid ±1 px errors on borders + // from rounding individual panels, the intermediate values (exactly accurate, in %) + // for width and height are rounded based on comparison to other panels in the + // layout. This is to avoid errors such as: + // 10% = 9.8px -> 10px -> x 3 = 30px + // 30% = 29.4px -> 29px (!) + for (final DragCell cellA : cells) { + RectangleOfDouble cellSizeA = cellA.getRoughBounds(); + roughVal = cellSizeA.getX() * w + cellSizeA.getW() * w; + + smoothVal = (int) Math.round(roughVal); + for (final DragCell cellB : cells) { + RectangleOfDouble cellSizeB = cellB.getRoughBounds(); + if ((cellSizeB.getX() * w + cellSizeB.getW() * w) == roughVal) { + cellB.setSmoothW(smoothVal - (int) Math.round(cellSizeB.getX() * w)); + } + } + cellA.setSmoothW(smoothVal - (int) Math.round(cellSizeA.getX() * w)); + + roughVal = cellSizeA.getY() * h + cellSizeA.getH() * h; + smoothVal = (int) Math.round(roughVal); + for (final DragCell cellB : cells) { + RectangleOfDouble cellSizeB = cellB.getRoughBounds(); + if (cellSizeB.getY() * h + cellSizeB.getH() * h == roughVal) { + cellB.setSmoothH(smoothVal - (int) Math.round(cellSizeB.getY() * h)); + } + } + cellA.setSmoothH(smoothVal - (int) Math.round(cellSizeA.getY() * h)); + + // X and Y coordinate can be rounded as usual. + cellA.setSmoothX((int) Math.round(cellSizeA.getX() * w)); + cellA.setSmoothY((int) Math.round(cellSizeA.getY() * h)); + + // only add component if not already in container; otherwise the keyboard focus + // jumps around to the most recenly added component + if (!existingComponents.contains(cellA)) { + pnlContent.add(cellA); + } + } + + // Lock in final bounds and build heads. + for (final DragCell t : cells) { + t.setSmoothBounds(); + t.validate(); + t.refresh(); + } + + cells.clear(); + } + + /** @param e   {@link java.awt.event.MouseEvent} */ + public static void resizeX(final MouseEvent e) { + dX = (int) e.getLocationOnScreen().getX() - evtX; + evtX = (int) e.getLocationOnScreen().getX(); + boolean leftLock = false; + boolean rightLock = false; + + for (final DragCell t : LEFT_PANELS) { + if ((t.getW() + dX) < W_MIN) { leftLock = true; break; } + } + + for (final DragCell t : RIGHT_PANELS) { + if ((t.getW() - dX) < W_MIN) { rightLock = true; break; } + } + + if (dX < 0 && leftLock) { return; } + if (dX > 0 && rightLock) { return; } + + for (final DragCell t : LEFT_PANELS) { + t.setBounds(t.getX(), t.getY(), t.getW() + dX, t.getH()); + t.refresh(); + } + + for (final DragCell t : RIGHT_PANELS) { + t.setBounds(t.getX() + dX, t.getY(), t.getW() - dX, t.getH()); + t.refresh(); + } + } + + /** @param e   {@link java.awt.event.MouseEvent} */ + public static void resizeY(final MouseEvent e) { + dY = (int) e.getLocationOnScreen().getY() - evtY; + evtY = (int) e.getLocationOnScreen().getY(); + boolean topLock = false; + boolean bottomLock = false; + + for (final DragCell t : TOP_PANELS) { + if ((t.getH() + dY) < H_MIN) { topLock = true; break; } + } + + for (final DragCell t : BOTTOM_PANELS) { + if ((t.getH() - dY) < H_MIN) { bottomLock = true; break; } + } + + if (dY < 0 && topLock) { return; } + if (dY > 0 && bottomLock) { return; } + + for (final DragCell t : TOP_PANELS) { + t.setBounds(t.getX(), t.getY(), t.getW(), t.getH() + dY); + t.revalidate(); + t.repaintSelf(); + } + + for (final DragCell t : BOTTOM_PANELS) { + t.setBounds(t.getX(), t.getY() + dY, t.getW(), t.getH() - dY); + t.revalidate(); + t.repaintSelf(); + } + } + + /** @param e   {@link java.awt.event.MouseEvent} */ + public static void startResizeX(final MouseEvent e) { + evtX = (int) e.getLocationOnScreen().getX(); + LEFT_PANELS.clear(); + RIGHT_PANELS.clear(); + + final DragCell src = (DragCell) ((JPanel) e.getSource()).getParent(); + final int srcX2 = src.getAbsX2(); + + int limTop = -1; + int limBottom = Integer.MAX_VALUE; + int tempX = -1; + int tempX2 = -1; + int tempY = -1; + int tempY2 = -1; + + // Add all panels who share a left or right edge with the + // same coordinate as the right edge of the source panel. + for (final DragCell t : FView.SINGLETON_INSTANCE.getDragCells()) { + tempX = t.getAbsX(); + tempX2 = t.getAbsX2(); + + if (srcX2 == tempX) { RIGHT_PANELS.add(t); } + else if (srcX2 == tempX2) { LEFT_PANELS.add(t); } + } + + // Set limits at panels which are level at intersections. + for (final DragCell pnlL : LEFT_PANELS) { + if (pnlL.equals(src)) { continue; } + tempY = pnlL.getAbsY(); + tempY2 = pnlL.getAbsY2(); + + for (final DragCell pnlR : RIGHT_PANELS) { + // Upper edges match? Set a bottom limit. + if (tempY >= src.getAbsY2() && tempY == pnlR.getAbsY() && tempY < limBottom) { + limBottom = tempY; + } + // Lower edges match? Set an upper limit. + else if (tempY2 <= src.getAbsY() && tempY2 == pnlR.getAbsY2() && tempY2 > limTop) { + limTop = tempY2; + } + } + } + + // Remove non-contiguous panels from left side using limits. + final Iterator itrLeft = LEFT_PANELS.iterator(); + while (itrLeft.hasNext()) { + final DragCell t = itrLeft.next(); + + if (t.getAbsY() >= limBottom || t.getAbsY2() <= limTop) { + itrLeft.remove(); + } + } + + // Remove non-contiguous panels from right side using limits. + final Iterator itrRight = RIGHT_PANELS.iterator(); + while (itrRight.hasNext()) { + final DragCell t = itrRight.next(); + + if (t.getAbsY() >= limBottom || t.getAbsY2() <= limTop) { + itrRight.remove(); + } + } + } + + /** @param e   {@link java.awt.event.MouseEvent} */ + public static void startResizeY(final MouseEvent e) { + evtY = (int) e.getLocationOnScreen().getY(); + TOP_PANELS.clear(); + BOTTOM_PANELS.clear(); + + final DragCell src = (DragCell) ((JPanel) e.getSource()).getParent(); + final int srcY2 = src.getAbsY2(); + + int limLeft = -1; + int limRight = Integer.MAX_VALUE; + int tempX = -1; + int tempX2 = -1; + int tempY = -1; + int tempY2 = -1; + + // Add all panels who share a top or bottom edge with the + // same coordinate as the bottom edge of the source panel. + for (final DragCell t : FView.SINGLETON_INSTANCE.getDragCells()) { + tempY = t.getAbsY(); + tempY2 = t.getAbsY2(); + + if (srcY2 == tempY) { BOTTOM_PANELS.add(t); } + else if (srcY2 == tempY2) { TOP_PANELS.add(t); } + } + + // Set limits at panels which are level at intersections. + for (final DragCell pnlT : TOP_PANELS) { + if (pnlT.equals(src)) { continue; } + tempX = pnlT.getAbsX(); + tempX2 = pnlT.getAbsX2(); + + for (final DragCell pnlB : BOTTOM_PANELS) { + // Right edges match? Set a right limit. + if (tempX >= src.getAbsX2() && tempX == pnlB.getAbsX() && tempX < limRight) { + limRight = tempX; + } + // Left edges match? Set an left limit. + else if (tempX2 <= src.getAbsX() && tempX2 == pnlB.getAbsX2() && tempX2 > limLeft) { + limLeft = tempX2; + } + } + } + + // Remove non-contiguous panels from left side using limits. + final Iterator itrTop = TOP_PANELS.iterator(); + while (itrTop.hasNext()) { + final DragCell t = itrTop.next(); + if (t.getAbsX() >= limRight || t.getAbsX2() <= limLeft) { + itrTop.remove(); + } + } + + // Remove non-contiguous panels from right side using limits. + final Iterator itrBottom = BOTTOM_PANELS.iterator(); + while (itrBottom.hasNext()) { + final DragCell t = itrBottom.next(); + if (t.getAbsX() >= limRight || t.getAbsX2() <= limLeft) { + itrBottom.remove(); + } + } + } + + /** */ + public static void endResize() { + SLayoutIO.saveLayout(null); + } + + /** @return {@link java.awt.event.MouseListener} */ + public static MouseListener getResizeXListener() { + return MAD_RESIZE_X; + } + + /** @return {@link java.awt.event.MouseListener} */ + public static MouseListener getResizeYListener() { + return MAD_RESIZE_Y; + } + + /** @return {@link java.awt.event.MouseMotionListener} */ + public static MouseMotionListener getDragXListener() { + return MMA_DRAG_X; + } + + /** @return {@link java.awt.event.MouseMotionListener} */ + public static MouseMotionListener getDragYListener() { + return MMA_DRAG_Y; + } + + /** @return {@link java.awt.event.ComponentListener} */ + public static ComponentListener getWindowResizeListener() { + return CAD_RESIZE; + } +} diff --git a/src/main/java/forge/gui/menubar/FMenuBar.java b/src/main/java/forge/gui/menubar/FMenuBar.java new file mode 100644 index 00000000000..6032fc4d881 --- /dev/null +++ b/src/main/java/forge/gui/menubar/FMenuBar.java @@ -0,0 +1,51 @@ +package forge.gui.menubar; + +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import forge.gui.menus.ForgeMenu; +import forge.gui.menus.HelpMenu; + +@SuppressWarnings("serial") +public class FMenuBar extends JMenuBar { + + public FMenuBar(JFrame f) { + f.setJMenuBar(this); + setPreferredSize(new Dimension(f.getWidth(), 26)); + setupMenuBar(null); + } + + public void setupMenuBar(IMenuProvider provider) { + removeAll(); + add(ForgeMenu.getMenu()); + addProviderMenus(provider); + add(HelpMenu.getMenu()); + repaint(); + } + + private void addProviderMenus(IMenuProvider provider) { + if (provider != null && provider.getMenus() != null) { + for (JMenu m : provider.getMenus()) { + m.setBorderPainted(false); + add(m); + } + } + } + + /** + * Enables or disables the MenuBar. + *

+ * Note: Disabling a component does not disable its children. + */ + public void setMenuBarEnabled(boolean isEnabled) { + setEnabled(isEnabled); + for (Component c : getComponents()) { + c.setEnabled(isEnabled); + } + } + +} diff --git a/src/main/java/forge/gui/menubar/IMenuProvider.java b/src/main/java/forge/gui/menubar/IMenuProvider.java new file mode 100644 index 00000000000..061aacf03c3 --- /dev/null +++ b/src/main/java/forge/gui/menubar/IMenuProvider.java @@ -0,0 +1,19 @@ +package forge.gui.menubar; + +import java.util.List; + +import javax.swing.JMenu; + +/** + * By implementing this interface a class can add menus to the menu bar + * by calling the {@code MenuBarManager.SetupMenuBar()} method. + * + */ +public interface IMenuProvider { + + /** + * Returns a list of JMenu objects for display in MenuBar. + */ + List getMenus(); + +} diff --git a/src/main/java/forge/gui/menubar/MenuUtil.java b/src/main/java/forge/gui/menubar/MenuUtil.java new file mode 100644 index 00000000000..35912930709 --- /dev/null +++ b/src/main/java/forge/gui/menubar/MenuUtil.java @@ -0,0 +1,61 @@ +package forge.gui.menubar; + +import java.awt.Toolkit; +import java.io.IOException; + +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import forge.Singletons; +import forge.gui.toolbox.FSkin; +import forge.gui.toolbox.FSkin.SkinProp; +import forge.gui.toolbox.imaging.ImageUtil; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; + +public final class MenuUtil { + private MenuUtil() { } + + private static ForgePreferences prefs = Singletons.getModel().getPreferences(); + + // Get appropriate OS standard accelerator key for menu shortcuts. + private static final int DEFAULT_MenuShortcutKeyMask = + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + public static boolean isMenuBarVisible() { + return !prefs.getPrefBoolean(FPref.UI_HIDE_MENUBAR); + } + + public static void openUrlInBrowser(String url) { + try { + java.awt.Desktop.getDesktop().browse(java.net.URI.create(url)); + } catch (IOException e) { + // Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log. + e.printStackTrace(); + } + } + + public static ImageIcon getMenuIcon(SkinProp ico) { + return ImageUtil.getMenuIcon(FSkin.getIcon(ico)); + } + + public static KeyStroke getAcceleratorKey(int key) { + return KeyStroke.getKeyStroke(key, DEFAULT_MenuShortcutKeyMask); + } + + public static boolean getUserConfirmation(String prompt, String dialogTitle) { + Object[] options = {"Yes", "No"}; + int reply = JOptionPane.showOptionDialog( + null, + prompt, + dialogTitle, + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[1]); + return (reply == JOptionPane.YES_OPTION); + } + +} diff --git a/src/main/java/forge/gui/menus/ForgeMenu.java b/src/main/java/forge/gui/menus/ForgeMenu.java new file mode 100644 index 00000000000..50019ff512c --- /dev/null +++ b/src/main/java/forge/gui/menus/ForgeMenu.java @@ -0,0 +1,70 @@ +package forge.gui.menus; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import forge.Singletons; +import forge.control.RestartUtil; +import forge.control.FControl.Screens; +import forge.gui.menubar.MenuUtil; + +public final class ForgeMenu { + private ForgeMenu() { } + + public static JMenu getMenu() { + JMenu menu = new JMenu("Forge"); + menu.setMnemonic(KeyEvent.VK_F); + menu.add(getMenuItem_Restart()); + menu.add(getMenuItem_Exit()); + return menu; + } + + private static JMenuItem getMenuItem_Exit() { + JMenuItem menuItem = new JMenuItem("Exit"); + menuItem.addActionListener(getExitAction()); + return menuItem; + } + + private static ActionListener getExitAction() { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (!isHomeScreenActive()) { + String userPrompt = "Please confirm you want to close Forge.\n\n"; + if (!MenuUtil.getUserConfirmation(userPrompt, "Exit Forge")) { + return; + } + } + System.exit(0); + } + }; + } + + private static JMenuItem getMenuItem_Restart() { + JMenuItem menuItem = new JMenuItem("Restart"); + menuItem.addActionListener(getRestartAction()); + return menuItem; + } + + private static ActionListener getRestartAction() { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (!isHomeScreenActive()) { + String userPrompt = "Please confirm you want to restart Forge.\n\n"; + if (!MenuUtil.getUserConfirmation(userPrompt, "Restart Forge")) { + return; + } + } + RestartUtil.restartApplication(null); + } + }; + } + + private static boolean isHomeScreenActive() { + return Singletons.getControl().getState() == Screens.HOME_SCREEN; + } + +} diff --git a/src/main/java/forge/gui/menus/HelpMenu.java b/src/main/java/forge/gui/menus/HelpMenu.java new file mode 100644 index 00000000000..7a3264cd298 --- /dev/null +++ b/src/main/java/forge/gui/menus/HelpMenu.java @@ -0,0 +1,124 @@ +package forge.gui.menus; + +import java.awt.Desktop; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.IOException; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import forge.gui.menubar.MenuUtil; +import forge.util.FileUtil; + +public final class HelpMenu { + private HelpMenu() { } + + public static JMenu getMenu() { + JMenu menu = new JMenu("Help"); + menu.setMnemonic(KeyEvent.VK_H); + menu.add(getMenu_GettingStarted()); + menu.add(getMenu_Articles()); + menu.add(getMenu_Troubleshooting()); + menu.addSeparator(); + menu.add(getMenuItem_ReleaseNotes()); + menu.add(getMenuItem_License()); + return menu; + } + + private static JMenu getMenu_Troubleshooting() { + JMenu mnu = new JMenu("Troubleshooting"); + mnu.add(getMenuItem_UrlLink("How to Provide a Useful Bug Report", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=9621")); + mnu.addSeparator(); + mnu.add(getMenuItem_ReadMeFile()); + return mnu; + } + + private static JMenu getMenu_Articles() { + JMenu mnu = new JMenu("Articles"); + mnu.add(getMenuItem_UrlLink("HOW-TO: Customize your Sealed Deck games with fantasy blocks", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8164")); + mnu.add(getMenuItem_UrlLink("Quest Mode: Guide to Formats, Worlds, and everything", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=9258")); + return mnu; + } + + private static JMenu getMenu_GettingStarted() { + JMenu mnu = new JMenu("Getting Started"); + mnu.add(getMenuItem_UrlLink("Forge Wiki", "http://www.slightlymagic.net/wiki/Forge")); + mnu.add(getMenuItem_UrlLink("What is Forge?", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=468")); + return mnu; + } + + private static JMenuItem getMenuItem_ReadMeFile() { + JMenuItem menuItem = new JMenuItem("README.txt"); + menuItem.addActionListener(getOpenFileAction(getFile("README.txt"))); + return menuItem; + } + + private static JMenuItem getMenuItem_License() { + JMenuItem menuItem = new JMenuItem("Forge License"); + menuItem.addActionListener(getOpenFileAction(getFile("LICENSE.txt"))); + return menuItem; + } + + private static JMenuItem getMenuItem_ReleaseNotes() { + JMenuItem menuItem = new JMenuItem("Release Notes"); + menuItem.addActionListener(getOpenFileAction(getFile("CHANGES.txt"))); + return menuItem; + } + + private static ActionListener getOpenFileAction(final File file) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + openFile(file); + } catch (IOException e1) { + // Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log. + e1.printStackTrace(); + } + } + }; + } + + protected static File getFile(String filename) { + // !! Linux is case-sensitive so file name and extension need to match exactly !! + File file = null; + String filePath = FileUtil.pathCombine(System.getProperty("user.dir"), filename); + if (FileUtil.doesFileExist(filePath)) { + file = new File(filePath); + } + return file; + } + + /** + * @see http://stackoverflow.com/questions/6273221/open-a-text-file-in-the-default-text-editor-via-java + */ + private static void openFile(File file) throws IOException { + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + String cmd = "rundll32 url.dll,FileProtocolHandler " + file.getCanonicalPath(); + Runtime.getRuntime().exec(cmd); + } + else { + Desktop.getDesktop().open(file); + } + } + + + private static JMenuItem getMenuItem_UrlLink(String caption, String url) { + JMenuItem menuItem = new JMenuItem(caption); + menuItem.addActionListener(getLaunchUrlAction(url)); + return menuItem; + } + + private static ActionListener getLaunchUrlAction(final String url) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MenuUtil.openUrlInBrowser(url); + } + }; + } + +} diff --git a/src/main/java/forge/gui/toolbox/imaging/ImageUtil.java b/src/main/java/forge/gui/toolbox/imaging/ImageUtil.java index 5a5de1f8cd4..a943521e6fc 100644 --- a/src/main/java/forge/gui/toolbox/imaging/ImageUtil.java +++ b/src/main/java/forge/gui/toolbox/imaging/ImageUtil.java @@ -19,6 +19,9 @@ package forge.gui.toolbox.imaging; import java.awt.Dimension; +import java.awt.Image; + +import javax.swing.ImageIcon; /** * Useful general imaging routines. @@ -29,6 +32,12 @@ import java.awt.Dimension; public final class ImageUtil { private ImageUtil() {} + public static ImageIcon getMenuIcon(ImageIcon sourceIcon) { + Image img = sourceIcon.getImage(); + Image newimg = img.getScaledInstance(16, 16, java.awt.Image.SCALE_SMOOTH); + return new ImageIcon(newimg); + } + /** * Gets the nearest rotation for a requested rotation. *

diff --git a/src/main/java/forge/properties/ForgeLookAndFeel.java b/src/main/java/forge/properties/ForgeLookAndFeel.java index b24b315d15e..9d2e51ea9e3 100644 --- a/src/main/java/forge/properties/ForgeLookAndFeel.java +++ b/src/main/java/forge/properties/ForgeLookAndFeel.java @@ -2,10 +2,15 @@ package forge.properties; import java.awt.Color; import java.awt.Font; +import java.util.ArrayList; + +import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.border.Border; + import forge.Singletons; import forge.gui.toolbox.FSkin; import forge.properties.ForgePreferences.FPref; @@ -20,6 +25,8 @@ public final class ForgeLookAndFeel { private Color FORE_COLOR = FSkin.getColor(FSkin.Colors.CLR_TEXT); private Color BACK_COLOR = FSkin.getColor(FSkin.Colors.CLR_THEME2); private Color HIGHLIGHT_COLOR = BACK_COLOR.brighter(); + private Border LINE_BORDER = BorderFactory.createLineBorder(FORE_COLOR.darker(), 1); + private Border EMPTY_BORDER = BorderFactory.createEmptyBorder(2,2,2,2); /** * Sets the look and feel of the GUI based on the selected Forge theme. @@ -27,6 +34,7 @@ public final class ForgeLookAndFeel { public void setForgeLookAndFeel(JFrame appFrame) { if (isUIManagerEnabled()) { if (setMetalLookAndFeel(appFrame)) { + setMenusLookAndFeel(); setComboBoxLookAndFeel(); setTabbedPaneLookAndFeel(); setButtonLookAndFeel(); @@ -52,6 +60,56 @@ public final class ForgeLookAndFeel { } return isMetalLafSet; } + + /** + * Sets the look and feel for a JMenuBar, JMenu, JMenuItem & variations. + */ + private void setMenusLookAndFeel() { + // JMenuBar + Color clrTheme = FSkin.getColor(FSkin.Colors.CLR_THEME); + Color backgroundColor = FSkin.stepColor(clrTheme, 0); + Color menuBarEdgeColor = FSkin.stepColor(clrTheme, -80); + UIManager.put("MenuBar.foreground", FORE_COLOR); + UIManager.put("MenuBar.gradient", getColorGradients(backgroundColor.darker(), backgroundColor)); + UIManager.put("MenuBar.border", BorderFactory.createMatteBorder(0, 0, 1, 0, menuBarEdgeColor)); + // JMenu + UIManager.put("Menu.foreground", FORE_COLOR); + UIManager.put("Menu.background", BACK_COLOR); + UIManager.put("Menu.borderPainted", false); + UIManager.put("Menu.selectionBackground", HIGHLIGHT_COLOR); + UIManager.put("Menu.selectionForeground", FORE_COLOR); + UIManager.put("Menu.border", EMPTY_BORDER); + UIManager.put("Menu.opaque", false); + // JPopupMenu + UIManager.put("PopupMenu.border", LINE_BORDER); + UIManager.put("PopupMenu.background", BACK_COLOR); + UIManager.put("PopupMenu.foreground", FORE_COLOR); + // JMenuItem + UIManager.put("MenuItem.foreground", FORE_COLOR); + UIManager.put("MenuItem.background", BACK_COLOR); + UIManager.put("MenuItem.border", EMPTY_BORDER); + UIManager.put("MenuItem.selectionBackground", HIGHLIGHT_COLOR); + UIManager.put("MenuItem.selectionForeground", FORE_COLOR); + UIManager.put("MenuItem.acceleratorForeground", FORE_COLOR.darker()); + UIManager.put("MenuItem.opaque", true); + // JSeparator (needs to be opaque!). + UIManager.put("Separator.foreground", FORE_COLOR.darker()); + UIManager.put("Separator.background", BACK_COLOR); + // JRadioButtonMenuItem + UIManager.put("RadioButtonMenuItem.foreground", FORE_COLOR); + UIManager.put("RadioButtonMenuItem.background", BACK_COLOR); + UIManager.put("RadioButtonMenuItem.selectionBackground", HIGHLIGHT_COLOR); + UIManager.put("RadioButtonMenuItem.selectionForeground", FORE_COLOR); + UIManager.put("RadioButtonMenuItem.border", EMPTY_BORDER); + UIManager.put("RadioButtonMenuItem.acceleratorForeground", FORE_COLOR.darker()); + // JCheckboxMenuItem + UIManager.put("CheckBoxMenuItem.foreground", FORE_COLOR); + UIManager.put("CheckBoxMenuItem.background", BACK_COLOR); + UIManager.put("CheckBoxMenuItem.selectionBackground", HIGHLIGHT_COLOR); + UIManager.put("CheckBoxMenuItem.selectionForeground", FORE_COLOR); + UIManager.put("CheckBoxMenuItem.border", EMPTY_BORDER); + UIManager.put("CheckBoxMenuItem.acceleratorForeground", FORE_COLOR.darker()); + } private void setTabbedPaneLookAndFeel() { UIManager.put("TabbedPane.selected", HIGHLIGHT_COLOR); @@ -95,4 +153,14 @@ public final class ForgeLookAndFeel { return FSkin.getFont(UIManager.getFont(component).getSize()); } + private ArrayList getColorGradients(Color bottom, Color top) { + ArrayList gradients = new ArrayList<>(); + gradients.add(0.0); + gradients.add(0.0); + gradients.add(top); + gradients.add(bottom); + gradients.add(bottom); + return gradients; + } + } diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index a76344cf22b..1d579414b91 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -61,6 +61,7 @@ public class ForgePreferences extends PreferencesStore { UI_CLONE_MODE_SOURCE ("false"), /** */ UI_MATCH_IMAGE_VISIBLE ("true"), UI_THEMED_COMBOBOX ("true"), + UI_HIDE_MENUBAR ("false"), // Dev setting only - cannot be set from GUI. UI_FOR_TOUCHSCREN("false"), From 4b30a7eca1383e6cd3a8c6c0c2ebd712346470c8 Mon Sep 17 00:00:00 2001 From: drdev Date: Wed, 21 Aug 2013 02:25:13 +0000 Subject: [PATCH 10/11] Make progress on API for new ItemManager filters Create LayoutHelper and TypeUtils classes --- .gitattributes | 5 +- .../java/forge/gui/toolbox/LayoutHelper.java | 116 ++++++++++++++++++ .../gui/toolbox/itemmanager/CardManager.java | 91 +++++++++++++- .../itemmanager/InventoryItemManager.java | 13 ++ .../gui/toolbox/itemmanager/ItemManager.java | 106 ++++++++++++---- .../itemmanager/filters/CardCMCFilter.java | 5 - .../itemmanager/filters/CardColorFilter.java | 5 - .../itemmanager/filters/CardFormatFilter.java | 38 +++++- .../itemmanager/filters/CardPowerFilter.java | 5 - .../filters/CardQuestWorldFilter.java | 42 ++++++- .../itemmanager/filters/CardSearchFilter.java | 26 ++++ .../itemmanager/filters/CardSetFilter.java | 42 ++++++- .../filters/CardToughnessFilter.java | 5 - .../itemmanager/filters/CardTypeFilter.java | 5 - .../itemmanager/filters/ItemFilter.java | 41 ++++--- .../itemmanager/filters/ListLabelFilter.java | 23 ++++ .../itemmanager/filters/TextFieldFilter.java | 15 --- .../itemmanager/filters/TextSearchFilter.java | 36 ++++++ .../filters/ToggleButtonsFilter.java | 11 ++ .../itemmanager/filters/ValueRangeFilter.java | 11 ++ src/main/java/forge/util/TypeUtil.java | 21 ++++ 21 files changed, 565 insertions(+), 97 deletions(-) create mode 100644 src/main/java/forge/gui/toolbox/LayoutHelper.java create mode 100644 src/main/java/forge/gui/toolbox/itemmanager/filters/CardSearchFilter.java delete mode 100644 src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java create mode 100644 src/main/java/forge/gui/toolbox/itemmanager/filters/TextSearchFilter.java create mode 100644 src/main/java/forge/util/TypeUtil.java diff --git a/.gitattributes b/.gitattributes index d9cee3024c1..1f2d22c0997 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15009,6 +15009,7 @@ src/main/java/forge/gui/toolbox/FSkin.java -text src/main/java/forge/gui/toolbox/FTabbedPane.java -text src/main/java/forge/gui/toolbox/FTextArea.java -text src/main/java/forge/gui/toolbox/FTextField.java -text +src/main/java/forge/gui/toolbox/LayoutHelper.java -text src/main/java/forge/gui/toolbox/SaveOpenDialog.java -text src/main/java/forge/gui/toolbox/imaging/FImagePanel.java -text src/main/java/forge/gui/toolbox/imaging/FImageUtil.java -text @@ -15026,12 +15027,13 @@ src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java -text +src/main/java/forge/gui/toolbox/itemmanager/filters/CardSearchFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java -text -src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java -text +src/main/java/forge/gui/toolbox/itemmanager/filters/TextSearchFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java -text src/main/java/forge/gui/toolbox/itemmanager/package-info.java -text @@ -15187,6 +15189,7 @@ src/main/java/forge/util/MyRandom.java svneol=native#text/plain src/main/java/forge/util/PredicateString.java -text src/main/java/forge/util/ReflectionUtil.java -text src/main/java/forge/util/TextUtil.java -text +src/main/java/forge/util/TypeUtil.java -text src/main/java/forge/util/XmlUtil.java -text src/main/java/forge/util/maps/CollectionSuppliers.java -text src/main/java/forge/util/maps/EnumMapOfLists.java -text diff --git a/src/main/java/forge/gui/toolbox/LayoutHelper.java b/src/main/java/forge/gui/toolbox/LayoutHelper.java new file mode 100644 index 00000000000..9d91ccbae85 --- /dev/null +++ b/src/main/java/forge/gui/toolbox/LayoutHelper.java @@ -0,0 +1,116 @@ +package forge.gui.toolbox; + +import javax.swing.JComponent; + +/** + * Helper class for doing custom layout + * + */ +public final class LayoutHelper { + private final int parentWidth, parentHeight; + private int x, y, lineBottom; + + public LayoutHelper(JComponent parent) { + parentWidth = parent.getWidth(); + parentHeight = parent.getHeight(); + } + + /** + * Layout component to fill remaining space of parent + * @param comp + */ + public void fill(final JComponent comp) { + if (x >= parentWidth) { + newLine(); + } + include(comp, parentWidth - x, parentHeight - y); + } + + /** + * Layout component to fill remaining space of current line + * @param comp + * @param height + */ + public void fillLine(final JComponent comp, int height) { + if (x >= parentWidth) { + newLine(); + } + include(comp, parentWidth - x, height); + } + + /** + * Include component in layout with a percentage width and fixed height + * @param comp + * @param widthPercent + * @param height + */ + public void include(final JComponent comp, float widthPercent, int height) { + include(comp, Math.round(parentWidth * widthPercent), height); + } + + /** + * Include component in layout with a fixed width and percentage height + * @param comp + * @param width + * @param heightPercent + */ + public void include(final JComponent comp, int width, float heightPercent) { + include(comp, width, Math.round(parentHeight * heightPercent)); + } + + /** + * Include component in layout with a percentage width and height + * @param comp + * @param widthPercent + * @param heightPercent + */ + public void include(final JComponent comp, float widthPercent, float heightPercent) { + include(comp, Math.round(parentWidth * widthPercent), Math.round(parentHeight * heightPercent)); + } + + /** + * Include component in layout with a fixed width and height + * @param comp + * @param width + * @param height + */ + public void include(final JComponent comp, int width, int height) { + if (width <= 0 || height <= 0) { return; } + + if (x + width > parentWidth) { + newLine(); + if (width > parentWidth) { + width = parentWidth; + } + } + if (y + height > parentHeight) { + y = parentHeight - height; + if (y >= parentHeight) { return; } + } + comp.setBounds(x, y, width, height); + x += width + 3; + if (y + height > lineBottom) { + lineBottom = y + height; + } + } + + /** + * Offset current layout helper position + * @param dx + * @param dy + */ + public void offset(int dx, int dy) { + x += dx; + y += dy; + } + + /** + * Start new line of layout + */ + public void newLine() { + if (lineBottom == y) { return; } + x = 0; + y = lineBottom + 3; + lineBottom = y; + } +} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java b/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java index 85fd6bb5aaa..88a713ee071 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/CardManager.java @@ -1,15 +1,31 @@ package forge.gui.toolbox.itemmanager; +import java.util.List; import java.util.Map; +import javax.swing.JMenu; +import javax.swing.JPopupMenu; +import forge.Singletons; +import forge.game.GameFormat; +import forge.gui.GuiUtils; +import forge.gui.home.quest.DialogChooseSets; import forge.gui.toolbox.FLabel; import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; +import forge.gui.toolbox.itemmanager.filters.CardCMCFilter; import forge.gui.toolbox.itemmanager.filters.CardColorFilter; +import forge.gui.toolbox.itemmanager.filters.CardFormatFilter; +import forge.gui.toolbox.itemmanager.filters.CardPowerFilter; +import forge.gui.toolbox.itemmanager.filters.CardQuestWorldFilter; +import forge.gui.toolbox.itemmanager.filters.CardSearchFilter; +import forge.gui.toolbox.itemmanager.filters.CardSetFilter; +import forge.gui.toolbox.itemmanager.filters.CardToughnessFilter; import forge.gui.toolbox.itemmanager.filters.CardTypeFilter; +import forge.gui.toolbox.itemmanager.filters.ItemFilter; import forge.item.PaperCard; +import forge.quest.QuestWorld; /** - * TODO: Write javadoc for this type. + * ItemManager for cards * */ @SuppressWarnings("serial") @@ -21,4 +37,77 @@ public final class CardManager extends ItemManager { this.addFilter(new CardColorFilter(this)); this.addFilter(new CardTypeFilter(this)); } + + @Override + protected ItemFilter createSearchFilter(String text) { + return new CardSearchFilter(this, text); + } + + @Override + protected void buildFilterMenu(JPopupMenu menu) { + JMenu fmt = new JMenu("Format"); + for (final GameFormat f : Singletons.getModel().getFormats()) { + GuiUtils.addMenuItem(fmt, f.getName(), null, new Runnable() { + @Override + public void run() { + addFilter(new CardFormatFilter(CardManager.this, f)); + } + }, CardFormatFilter.canAddFormat(f, getFilter(CardFormatFilter.class))); + } + menu.add(fmt); + + GuiUtils.addMenuItem(menu, "Sets...", null, new Runnable() { + @Override + public void run() { + CardSetFilter existingFilter = getFilter(CardSetFilter.class); + if (existingFilter != null) { + existingFilter.edit(); + } + else { + final DialogChooseSets dialog = new DialogChooseSets(null, null, true); + dialog.setOkCallback(new Runnable() { + @Override + public void run() { + List sets = dialog.getSelectedSets(); + if (!sets.isEmpty()) { + addFilter(new CardSetFilter(CardManager.this, sets)); + } + } + }); + } + } + }); + + JMenu range = new JMenu("Value range"); + GuiUtils.addMenuItem(range, "CMC", null, new Runnable() { + @Override + public void run() { + addFilter(new CardCMCFilter(CardManager.this)); + } + }, getFilter(CardCMCFilter.class) == null); + GuiUtils.addMenuItem(range, "Power", null, new Runnable() { + @Override + public void run() { + addFilter(new CardPowerFilter(CardManager.this)); + } + }, getFilter(CardPowerFilter.class) == null); + GuiUtils.addMenuItem(range, "Toughness", null, new Runnable() { + @Override + public void run() { + addFilter(new CardToughnessFilter(CardManager.this)); + } + }, getFilter(CardToughnessFilter.class) == null); + menu.add(range); + + JMenu world = new JMenu("Quest world"); + for (final QuestWorld w : Singletons.getModel().getWorlds()) { + GuiUtils.addMenuItem(world, w.getName(), null, new Runnable() { + @Override + public void run() { + addFilter(new CardQuestWorldFilter(CardManager.this, w)); + } + }, CardQuestWorldFilter.canAddQuestWorld(w, getFilter(CardQuestWorldFilter.class))); + } + menu.add(world); + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java b/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java index 90dd9b4cabc..5709dd6b6d0 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/InventoryItemManager.java @@ -2,8 +2,11 @@ package forge.gui.toolbox.itemmanager; import java.util.Map; +import javax.swing.JPopupMenu; + import forge.gui.toolbox.FLabel; import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; +import forge.gui.toolbox.itemmanager.filters.ItemFilter; import forge.item.InventoryItem; /** @@ -16,4 +19,14 @@ public final class InventoryItemManager extends ItemManager { public InventoryItemManager(Map statLabels0, boolean wantUnique0) { super(InventoryItem.class, statLabels0, wantUnique0); } + + @Override + protected ItemFilter createSearchFilter(String text) { + return null; + } + + @Override + protected void buildFilterMenu(JPopupMenu menu) { + + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java b/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java index ed2c727207c..98ce5ca536b 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java @@ -17,6 +17,9 @@ */ package forge.gui.toolbox.itemmanager; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -24,8 +27,9 @@ import java.util.Map; import java.util.Map.Entry; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; -import javax.swing.JTextField; +import javax.swing.KeyStroke; import com.google.common.base.Predicate; @@ -33,8 +37,11 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; +import forge.Command; +import forge.gui.GuiUtils; import forge.gui.toolbox.FLabel; import forge.gui.toolbox.FTextField; +import forge.gui.toolbox.LayoutHelper; import forge.gui.toolbox.ToolTipListener; import forge.gui.toolbox.itemmanager.filters.ItemFilter; import forge.gui.toolbox.itemmanager.table.ItemTable; @@ -43,6 +50,7 @@ import forge.item.InventoryItem; import forge.item.ItemPool; import forge.item.ItemPoolView; import forge.util.Aggregates; +import forge.util.TypeUtil; /** @@ -56,16 +64,21 @@ public abstract class ItemManager extends JPanel { private ItemPool pool; private final ItemManagerModel model; private Predicate filterPredicate = null; - private final Map> filters = - new HashMap>(); + private final Map>, List>> filters = + new HashMap>, List>>(); + private final List> orderedFilters = new ArrayList>(); private boolean wantUnique = false; private boolean alwaysNonUnique = false; private final Class genericType; private final Map statLabels; + private final FLabel btnAddFilter = new FLabel.ButtonBuilder() + .text("Add") + .tooltip("Click to add filters to the list") + .reactOnMouseDown().build(); + private final FTextField txtSearch = new FTextField.Builder().ghostText("Search").build(); private final ItemTable table; private final JScrollPane tableScroller; - private final JTextField txtSearch = new FTextField.Builder().ghostText("Search").build(); /** * ItemManager Constructor. @@ -92,28 +105,45 @@ public abstract class ItemManager extends JPanel { //build display this.setOpaque(false); this.setLayout(null); + this.add(this.btnAddFilter); this.add(this.txtSearch); this.add(this.tableScroller); + + //setup command for btnAddFilter + final Command addFilterCommand = new Command() { + @Override + public void run() { + JPopupMenu menu = new JPopupMenu("FilterMenu"); + GuiUtils.addMenuItem(menu, "Current text search", + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + new Runnable() { + @Override + public void run() { + ItemFilter searchFilter = createSearchFilter(txtSearch.getText()); + if (searchFilter != null) { + addFilter(searchFilter); + } + } + }, !txtSearch.isEmpty()); + buildFilterMenu(menu); + menu.show(btnAddFilter, 0, btnAddFilter.getHeight()); + } + }; + this.btnAddFilter.setCommand(addFilterCommand); + this.btnAddFilter.setRightClickCommand(addFilterCommand); //show menu on right-click too } @Override public void doLayout() { - int x = 0; - int y = 0; - int width = this.getWidth(); - int height = this.getHeight(); - - //position toolbar components //TODO: Uncomment - /*this.txtSearch.setBounds(x, y, width / 2, FTextField.HEIGHT); - y += FTextField.HEIGHT + 3; - for (ItemFilter filter : this.filters.values()) { - filter.getPanel().setBounds(x, y, width, ItemFilter.PANEL_HEIGHT); - y += ItemFilter.PANEL_HEIGHT + 3; + LayoutHelper helper = new LayoutHelper(this); + /*helper.include(this.btnAddFilter, 30, FTextField.HEIGHT); + helper.include(this.txtSearch, 0.5f, FTextField.HEIGHT); + helper.newLine(); + for (ItemFilter filter : this.orderedFilters) { + helper.fillLine(filter.getPanel(), ItemFilter.PANEL_HEIGHT); }*/ - - //position current item view - this.tableScroller.setBounds(x, y, width, height - y); + helper.fill(this.tableScroller); } /** @@ -340,16 +370,48 @@ public abstract class ItemManager extends JPanel { return this.statLabels.get(s); } + protected abstract ItemFilter createSearchFilter(String text); + protected abstract void buildFilterMenu(JPopupMenu menu); + + protected > F getFilter(Class filterClass) { + return TypeUtil.safeCast(this.filters.get(filterClass), filterClass); + } + + @SuppressWarnings("unchecked") public void addFilter(ItemFilter filter) { - this.filters.put(filter.getType(), filter); + final Class> filterClass = (Class>) filter.getClass(); + List> classFilters = this.filters.get(filterClass); + if (classFilters == null) { + classFilters = new ArrayList>(); + this.filters.put(filterClass, classFilters); + } + if (classFilters.size() > 0) { + //if filter with the same class already exists, try to merge if allowed + //NOTE: can always use first filter for these checks since if + //merge is supported, only one will ever exist + ItemFilter existingFilter = classFilters.get(0); + if (existingFilter.merge(filter)) { + //if new filter merged with existing filter, just update layout + this.revalidate(); + return; + } + } + classFilters.add(filter); this.add(filter.getPanel()); this.revalidate(); } + @SuppressWarnings("unchecked") public void removeFilter(ItemFilter filter) { - this.filters.remove(filter.getType()); - this.remove(filter.getPanel()); - this.revalidate(); + final Class> filterClass = (Class>) filter.getClass(); + final List> classFilters = this.filters.get(filterClass); + if (classFilters != null && classFilters.remove(filter)) { + if (classFilters.size() == 0) { + this.filters.remove(filterClass); + } + this.remove(filter.getPanel()); + this.revalidate(); + } } public void buildFilterPredicate() { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java index 53c938ecbbf..5d174ffd896 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardCMCFilter.java @@ -15,11 +15,6 @@ public class CardCMCFilter extends ValueRangeFilter { super(itemManager0); } - @Override - public FilterTypes getType() { - return FilterTypes.CardCMC; - } - @Override protected void buildPanel(JPanel panel) { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java index f803dc50474..deb5c15662b 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardColorFilter.java @@ -15,11 +15,6 @@ public class CardColorFilter extends ToggleButtonsFilter { super(itemManager0); } - @Override - public FilterTypes getType() { - return FilterTypes.CardColor; - } - @Override protected void buildPanel(JPanel panel) { addToggleButton(panel, StatTypes.WHITE); diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java index c5036d05b35..45211c76367 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardFormatFilter.java @@ -1,7 +1,11 @@ package forge.gui.toolbox.itemmanager.filters; +import java.util.HashSet; +import java.util.Set; + import javax.swing.JPanel; +import forge.game.GameFormat; import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -10,13 +14,28 @@ import forge.item.PaperCard; * */ public class CardFormatFilter extends ListLabelFilter { - public CardFormatFilter(ItemManager itemManager0) { - super(itemManager0); - } + private final Set formats = new HashSet(); + public CardFormatFilter(ItemManager itemManager0, GameFormat format0) { + super(itemManager0); + this.formats.add(format0); + } + + public static boolean canAddFormat(GameFormat format, ItemFilter existingFilter) { + return existingFilter == null || !((CardFormatFilter)existingFilter).formats.contains(format); + } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ @Override - public FilterTypes getType() { - return FilterTypes.CardFormat; + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + CardFormatFilter cardFormatFilter = (CardFormatFilter)filter; + this.formats.addAll(cardFormatFilter.formats); + return true; } @Override @@ -28,4 +47,13 @@ public class CardFormatFilter extends ListLabelFilter { protected void onRemoved() { } + + @Override + protected Iterable getList() { + Set strings = new HashSet(); + for (GameFormat f : this.formats) { + strings.add(f.getName()); + } + return strings; + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java index bdce7d69865..8e194d3cc2f 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardPowerFilter.java @@ -14,11 +14,6 @@ public class CardPowerFilter extends ValueRangeFilter { super(itemManager0); } - @Override - public FilterTypes getType() { - return FilterTypes.CardPower; - } - @Override protected void buildPanel(JPanel panel) { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java index e37421d805b..8737cb95ef8 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardQuestWorldFilter.java @@ -1,22 +1,45 @@ package forge.gui.toolbox.itemmanager.filters; +import java.util.HashSet; +import java.util.Set; + import javax.swing.JPanel; +import forge.Singletons; import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; +import forge.quest.QuestWorld; /** * TODO: Write javadoc for this type. * */ public class CardQuestWorldFilter extends ListLabelFilter { - public CardQuestWorldFilter(ItemManager itemManager0) { - super(itemManager0); - } + private final Set questWorlds = new HashSet(); + public CardQuestWorldFilter(ItemManager itemManager0, QuestWorld questWorld0) { + super(itemManager0); + this.questWorlds.add(questWorld0); + } + + public static boolean canAddQuestWorld(QuestWorld questWorld, ItemFilter existingFilter) { + if (questWorld.getFormat() == null && Singletons.getModel().getQuest().getMainFormat() == null) { + return false; //must have format + } + return existingFilter == null || !((CardQuestWorldFilter)existingFilter).questWorlds.contains(questWorld); + } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ @Override - public FilterTypes getType() { - return FilterTypes.CardQuestWorld; + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + CardQuestWorldFilter cardQuestWorldFilter = (CardQuestWorldFilter)filter; + this.questWorlds.addAll(cardQuestWorldFilter.questWorlds); + return true; } @Override @@ -28,4 +51,13 @@ public class CardQuestWorldFilter extends ListLabelFilter { protected void onRemoved() { } + + @Override + protected Iterable getList() { + Set strings = new HashSet(); + for (QuestWorld w : this.questWorlds) { + strings.add(w.getName()); + } + return strings; + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSearchFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSearchFilter.java new file mode 100644 index 00000000000..12ad0cdba1a --- /dev/null +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSearchFilter.java @@ -0,0 +1,26 @@ +package forge.gui.toolbox.itemmanager.filters; + +import javax.swing.JPanel; + +import forge.gui.toolbox.itemmanager.ItemManager; +import forge.item.PaperCard; + +/** + * TODO: Write javadoc for this type. + * + */ +public class CardSearchFilter extends TextSearchFilter { + public CardSearchFilter(ItemManager itemManager0, String text0) { + super(itemManager0, text0); + } + + @Override + protected void buildPanel(JPanel panel) { + + } + + @Override + protected void onRemoved() { + + } +} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java index 4eda5c28f54..2dadfd4b01b 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardSetFilter.java @@ -1,7 +1,12 @@ package forge.gui.toolbox.itemmanager.filters; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + import javax.swing.JPanel; +import forge.gui.home.quest.DialogChooseSets; import forge.gui.toolbox.itemmanager.ItemManager; import forge.item.PaperCard; @@ -10,13 +15,35 @@ import forge.item.PaperCard; * */ public class CardSetFilter extends ListLabelFilter { - public CardSetFilter(ItemManager itemManager0) { - super(itemManager0); - } + private final Set sets = new HashSet(); + public CardSetFilter(ItemManager itemManager0, Collection sets) { + super(itemManager0); + this.sets.addAll(sets); + } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ @Override - public FilterTypes getType() { - return FilterTypes.CardSet; + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + CardSetFilter cardSetFilter = (CardSetFilter)filter; + this.sets.addAll(cardSetFilter.sets); + return true; + } + + public void edit() { + final DialogChooseSets dialog = new DialogChooseSets(this.sets, null, true); + dialog.setOkCallback(new Runnable() { + @Override + public void run() { + sets.clear(); + sets.addAll(dialog.getSelectedSets()); + } + }); } @Override @@ -28,4 +55,9 @@ public class CardSetFilter extends ListLabelFilter { protected void onRemoved() { } + + @Override + protected Iterable getList() { + return this.sets; + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java index 2380d4e3188..a47dc0d07f4 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardToughnessFilter.java @@ -14,11 +14,6 @@ public class CardToughnessFilter extends ValueRangeFilter { super(itemManager0); } - @Override - public FilterTypes getType() { - return FilterTypes.CardToughness; - } - @Override protected void buildPanel(JPanel panel) { diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java index a5515553e29..7d949f90f42 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/CardTypeFilter.java @@ -15,11 +15,6 @@ public class CardTypeFilter extends ToggleButtonsFilter { super(itemManager0); } - @Override - public FilterTypes getType() { - return FilterTypes.CardType; - } - @Override protected void buildPanel(JPanel panel) { addToggleButton(panel, StatTypes.LAND); diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java index a45fab8e87f..d4c74fa095c 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ItemFilter.java @@ -13,35 +13,33 @@ import forge.item.InventoryItem; */ public abstract class ItemFilter { private final ItemManager itemManager; + private int number; private JPanel panel; public static int PANEL_HEIGHT = 30; - public enum FilterTypes { - CardCMC, - CardColor, - CardFormat, - CardPower, - CardQuestWorld, - CardSet, - CardToughness, - CardType - } - protected ItemFilter(ItemManager itemManager0) { this.itemManager = itemManager0; } + public int getNumber() { + return this.number; + } + + public void setNumber(int number0) { + this.number = number0; + } + @SuppressWarnings("serial") public JPanel getPanel() { - if (panel == null) { - panel = new JPanel(); - panel.setOpaque(false); + if (this.panel == null) { + this.panel = new JPanel(); + this.panel.setOpaque(false); this.buildPanel(panel); //add button to remove filter - panel.add(new FLabel.Builder() + this.panel.add(new FLabel.Builder() .text("X") .fontSize(10) .hoverable(true) @@ -54,14 +52,21 @@ public abstract class ItemFilter { } }).build(), "top"); } - return panel; + return this.panel; } protected void applyChange() { - itemManager.buildFilterPredicate(); + this.itemManager.buildFilterPredicate(); } - public abstract FilterTypes getType(); + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ + @SuppressWarnings("rawtypes") + public abstract boolean merge(ItemFilter filter); + protected abstract void buildPanel(JPanel panel); protected abstract void onRemoved(); } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java index 4d487fbcdfe..3c4e671a6e0 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ListLabelFilter.java @@ -12,4 +12,27 @@ public abstract class ListLabelFilter extends ItemFilte protected ListLabelFilter(ItemManager itemManager0) { super(itemManager0); } + + protected abstract Iterable getList(); + + public void buildPanel() { + StringBuilder label = new StringBuilder(); + boolean truncated = false; + for (String str : getList()) { + // don't let the full label get too long + if (label.length() < 32) { + label.append(" ").append(str).append(";"); + } else { + truncated = true; + break; + } + } + + // chop off last semicolons + label.delete(label.length() - 1, label.length()); + + if (truncated) { + label.append("..."); + } + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java deleted file mode 100644 index 0d7c9f43c93..00000000000 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/TextFieldFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package forge.gui.toolbox.itemmanager.filters; - -import forge.gui.toolbox.itemmanager.ItemManager; -import forge.item.InventoryItem; - -/** - * TODO: Write javadoc for this type. - * - */ -public abstract class TextFieldFilter extends ItemFilter { - - protected TextFieldFilter(ItemManager itemManager0) { - super(itemManager0); - } -} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/TextSearchFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/TextSearchFilter.java new file mode 100644 index 00000000000..45b0fb2364c --- /dev/null +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/TextSearchFilter.java @@ -0,0 +1,36 @@ +package forge.gui.toolbox.itemmanager.filters; + +import javax.swing.JPanel; + +import forge.gui.toolbox.FTextField; +import forge.gui.toolbox.itemmanager.ItemManager; +import forge.item.InventoryItem; + +/** + * TODO: Write javadoc for this type. + * + */ +public abstract class TextSearchFilter extends ItemFilter { + private String text; + + protected TextSearchFilter(ItemManager itemManager0, String text0) { + super(itemManager0); + this.text = text0; + } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ + @Override + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + return false; + } + + @Override + protected void buildPanel(JPanel panel) { + panel.add(new FTextField.Builder().text(this.text).build()); + } +} diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java index e154b37b57b..5ca5adec5fb 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ToggleButtonsFilter.java @@ -63,4 +63,15 @@ public abstract class ToggleButtonsFilter extends ItemF this.buttons.add(button); panel.add(button); } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ + @Override + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + return true; + } } diff --git a/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java b/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java index a5c5733f327..12e3e90e64a 100644 --- a/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java +++ b/src/main/java/forge/gui/toolbox/itemmanager/filters/ValueRangeFilter.java @@ -12,4 +12,15 @@ public abstract class ValueRangeFilter extends ItemFilt protected ValueRangeFilter(ItemManager itemManager0) { super(itemManager0); } + + /** + * Merge the given filter with this filter if possible + * @param filter + * @return true if filter merged in or to suppress adding a new filter, false to allow adding new filter + */ + @Override + @SuppressWarnings("rawtypes") + public boolean merge(ItemFilter filter) { + return true; + } } diff --git a/src/main/java/forge/util/TypeUtil.java b/src/main/java/forge/util/TypeUtil.java new file mode 100644 index 00000000000..ba92f20105f --- /dev/null +++ b/src/main/java/forge/util/TypeUtil.java @@ -0,0 +1,21 @@ +package forge.util; + +/** + * TODO: Write javadoc for this type. + * + */ +public class TypeUtil { + + /** + * Cast object to a given type if possible, returning null if not possible + * @param obj + * @param type + */ + @SuppressWarnings("unchecked") + public static T safeCast(Object obj, Class type) { + if (type.isInstance(obj)) { + return (T) obj; + } + return null; + } +} From 8f80f4d439250a298e59f0f0132f3ea7c0be9d4a Mon Sep 17 00:00:00 2001 From: swordshine Date: Wed, 21 Aug 2013 05:05:55 +0000 Subject: [PATCH 11/11] - Added Illuminated Folio --- .gitattributes | 1 + res/cardsfolder/i/illuminated_folio.txt | 7 +++ src/main/java/forge/CardPredicates.java | 9 +++ .../java/forge/card/cost/CostDiscard.java | 52 ++++++++++------- src/main/java/forge/card/cost/CostReveal.java | 57 ++++++++++++++++++- .../java/forge/card/cost/CostTapType.java | 49 +++++++++++----- 6 files changed, 140 insertions(+), 35 deletions(-) create mode 100644 res/cardsfolder/i/illuminated_folio.txt diff --git a/.gitattributes b/.gitattributes index 1f2d22c0997..bd1a9432c69 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5370,6 +5370,7 @@ res/cardsfolder/i/ikiral_outrider.txt svneol=native#text/plain res/cardsfolder/i/ill_gotten_gains.txt svneol=native#text/plain res/cardsfolder/i/illness_in_the_ranks.txt -text res/cardsfolder/i/illuminate.txt -text +res/cardsfolder/i/illuminated_folio.txt -text res/cardsfolder/i/illuminated_wings.txt svneol=native#text/plain res/cardsfolder/i/illumination.txt svneol=native#text/plain res/cardsfolder/i/illusion_reality.txt -text diff --git a/res/cardsfolder/i/illuminated_folio.txt b/res/cardsfolder/i/illuminated_folio.txt new file mode 100644 index 00000000000..bea6e9f86b9 --- /dev/null +++ b/res/cardsfolder/i/illuminated_folio.txt @@ -0,0 +1,7 @@ +Name:Illuminated Folio +ManaCost:5 +Types:Artifact +A:AB$ Draw | Cost$ 1 T Reveal<2/SameColor> | NumCards$ 1 | SpellDescription$ Draw a card. +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/illuminated_folio.jpg +Oracle:{1}, {T}, Reveal two cards from your hand that share a color: Draw a card. diff --git a/src/main/java/forge/CardPredicates.java b/src/main/java/forge/CardPredicates.java index b1d46323443..f296be02d88 100644 --- a/src/main/java/forge/CardPredicates.java +++ b/src/main/java/forge/CardPredicates.java @@ -109,6 +109,15 @@ public final class CardPredicates { }; } + public static final Predicate sharesColorWith(final Card color) { + return new Predicate() { + @Override + public boolean apply(Card c) { + return c.sharesColorWith(color); + } + }; + } + public static final Predicate possibleBlockers(final Card attacker) { return new Predicate() { @Override diff --git a/src/main/java/forge/card/cost/CostDiscard.java b/src/main/java/forge/card/cost/CostDiscard.java index 7ab49abe04f..07797188b95 100644 --- a/src/main/java/forge/card/cost/CostDiscard.java +++ b/src/main/java/forge/card/cost/CostDiscard.java @@ -192,30 +192,42 @@ public class CostDiscard extends CostPartWithList { } return executePayment(ability, Aggregates.random(handList, c)); - + } + if (discardType.contains("+WithSameName")) { + String type = discardType.replace("+WithSameName", ""); + handList = CardLists.getValidCards(handList, type.split(";"), activator, ability.getSourceCard()); + final List landList2 = handList; + handList = CardLists.filter(handList, new Predicate() { + @Override + public boolean apply(final Card c) { + for (Card card : landList2) { + if (!card.equals(c) && card.getName().equals(c.getName())) { + return true; + } + } + return false; + } + }); + if (c == 0) return true; + List discarded = new ArrayList(); + while (c > 0) { + InputSelectCards inp = new InputSelectCardsFromList(1, 1, handList); + inp.setMessage("Select one of the cards with the same name to discard. Already chosen: " + discarded); + inp.setCancelAllowed(true); + Singletons.getControl().getInputQueue().setInputAndWait(inp); + if (inp.hasCancelled()) + return false; + final Card first = inp.getSelected().get(0); + discarded.add(first); + handList = CardLists.filter(handList, CardPredicates.nameEquals(first.getName())); + handList.remove(first); + c--; + } + return executePayment(ability, discarded); } else { String type = new String(discardType); - boolean sameName = false; - if (type.contains("+WithSameName")) { - sameName = true; - type = type.replace("+WithSameName", ""); - } final String[] validType = type.split(";"); handList = CardLists.getValidCards(handList, validType, activator, ability.getSourceCard()); - final List landList2 = handList; - if (sameName) { - handList = CardLists.filter(handList, new Predicate() { - @Override - public boolean apply(final Card c) { - for (Card card : landList2) { - if (!card.equals(c) && card.getName().equals(c.getName())) { - return true; - } - } - return false; - } - }); - } if (c == null) { final String sVar = ability.getSVar(amount); diff --git a/src/main/java/forge/card/cost/CostReveal.java b/src/main/java/forge/card/cost/CostReveal.java index c12ca771d6d..5d999d720e2 100644 --- a/src/main/java/forge/card/cost/CostReveal.java +++ b/src/main/java/forge/card/cost/CostReveal.java @@ -20,10 +20,12 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; +import com.google.common.base.Predicate; import com.google.common.collect.Lists; import forge.Card; import forge.CardLists; +import forge.CardPredicates; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; @@ -40,7 +42,7 @@ import forge.gui.input.InputSelectCardsFromList; */ public class CostReveal extends CostPartWithList { // Reveal - + /** * Instantiates a new cost reveal. * @@ -83,6 +85,22 @@ public class CostReveal extends CostPartWithList { } } else if (this.getType().equals("Hand")) { return true; + } else if (this.getType().equals("SameColor")) { + if (amount == null) { + return false; + } else { + for (final Card card : handList) { + if (CardLists.filter(handList, new Predicate() { + @Override + public boolean apply(final Card c) { + return c.sharesColorWith(card); + } + }).size() >= amount) { + return true; + } + } + return false; + } } else { if (ability.isSpell()) { handList.remove(source); // can't pay for itself @@ -117,6 +135,10 @@ public class CostReveal extends CostPartWithList { if (this.getType().equals("Hand")) return new PaymentDecision(new ArrayList(ai.getCardsIn(ZoneType.Hand))); + if (this.getType().equals("SameColor")) { + return null; + } + hand = CardLists.getValidCards(hand, type.split(";"), ai, source); Integer c = this.convertAmount(); if (c == null) { @@ -152,6 +174,37 @@ public class CostReveal extends CostPartWithList { for(Card c : activator.getCardsIn(ZoneType.Hand)) executePayment(ability, c); return true; + } else if (this.getType().equals("SameColor")) { + Integer num = this.convertAmount(); + List handList = activator.getCardsIn(ZoneType.Hand); + final List handList2 = handList; + handList = CardLists.filter(handList, new Predicate() { + @Override + public boolean apply(final Card c) { + for (Card card : handList2) { + if (!card.equals(c) && card.sharesColorWith(c)) { + return true; + } + } + return false; + } + }); + if (num == 0) return true; + List revealed = new ArrayList(); + while (num > 0) { + InputSelectCards inp = new InputSelectCardsFromList(1, 1, handList); + inp.setMessage("Select one of cards to reveal. Already chosen:" + revealed); + inp.setCancelAllowed(true); + Singletons.getControl().getInputQueue().setInputAndWait(inp); + if (inp.hasCancelled()) + return false; + final Card first = inp.getSelected().get(0); + revealed.add(first); + handList = CardLists.filter(handList, CardPredicates.sharesColorWith(first)); + handList.remove(first); + num--; + } + return executePayment(ability, revealed); } else { Integer num = this.convertAmount(); @@ -193,6 +246,8 @@ public class CostReveal extends CostPartWithList { sb.append(this.getType()); } else if (this.getType().equals("Hand")) { return ("Reveal you hand"); + } else if (this.getType().equals("SameColor")) { + return ("Reveal " + i + " cards from your hand that share a color"); } else { final StringBuilder desc = new StringBuilder(); diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java index 9de27ad8df5..24bbafb49de 100644 --- a/src/main/java/forge/card/cost/CostTapType.java +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -161,6 +161,10 @@ public class CostTapType extends CostPartWithList { public final boolean payHuman(final SpellAbility ability, final Game game) { List typeList = new ArrayList(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield)); String type = this.getType(); + final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); + Integer c = this.convertAmount(); + boolean sameType = false; if (type.contains("sharesCreatureTypeWith")) { sameType = true; @@ -168,6 +172,16 @@ public class CostTapType extends CostPartWithList { } typeList = CardLists.getValidCards(typeList, type.split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); typeList = CardLists.filter(typeList, Presets.UNTAPPED); + if (c == null) { + final String sVar = ability.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = Cost.chooseXValue(source, ability, typeList.size()); + } else { + c = AbilityUtils.calculateAmount(source, amount, ability); + } + } + if (sameType) { final List List2 = typeList; typeList = CardLists.filter(typeList, new Predicate() { @@ -181,21 +195,28 @@ public class CostTapType extends CostPartWithList { return false; } }); - } - - final String amount = this.getAmount(); - final Card source = ability.getSourceCard(); - Integer c = this.convertAmount(); - if (c == null) { - final String sVar = ability.getSVar(amount); - // Generalize this - if (sVar.equals("XChoice")) { - c = Cost.chooseXValue(source, ability, typeList.size()); - } else { - c = AbilityUtils.calculateAmount(source, amount, ability); + if (c == 0) return true; + List tapped = new ArrayList(); + while (c > 0) { + InputSelectCards inp = new InputSelectCardsFromList(1, 1, typeList); + inp.setMessage("Select one of the cards to tap. Already chosen: " + tapped); + inp.setCancelAllowed(true); + Singletons.getControl().getInputQueue().setInputAndWait(inp); + if (inp.hasCancelled()) + return false; + final Card first = inp.getSelected().get(0); + tapped.add(first); + typeList = CardLists.filter(typeList, new Predicate() { + @Override + public boolean apply(final Card c) { + return c.sharesCreatureTypeWith(first); + } + }); + typeList.remove(first); + c--; } - } - + return executePayment(ability, tapped); + } InputSelectCards inp = new InputSelectCardsFromList(c, c, typeList); inp.setMessage("Select a " + getDescriptiveType() + " to tap (%d left)");