/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.client.deckeditor;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import mage.cards.Card;
import mage.cards.Sets;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.importer.DeckImporter;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.cards.BigCard;
import mage.client.cards.ICardGrid;
import mage.client.constants.Constants.DeckEditorMode;
import mage.client.deck.generator.DeckGenerator;
import mage.client.deck.generator.DeckGenerator.DeckGeneratorException;
import mage.client.dialog.AddLandDialog;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.Event;
import mage.client.util.Listener;
import mage.client.util.audio.AudioManager;
import mage.components.CardInfoPane;
import mage.game.GameException;
import mage.remote.Session;
import mage.view.CardView;
import mage.view.SimpleCardView;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
*/
public class DeckEditorPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(DeckEditorPanel.class);
private final JFileChooser fcSelectDeck;
private final JFileChooser fcImportDeck;
private Deck deck = new Deck();
private final Map<UUID, Card> temporaryCards = new HashMap<>(); // Cards dragged out of one part of the view into another
private boolean isShowCardInfo = false;
private UUID tableId;
private DeckEditorMode mode;
private int timeout;
private Timer countdown;
private UpdateDeckTask updateDeckTask;
private int timeToSubmit = -1;
/**
* Creates new form DeckEditorPanel
*/
public DeckEditorPanel() {
initComponents();
fcSelectDeck = new JFileChooser();
fcSelectDeck.setAcceptAllFileFilterUsed(false);
fcSelectDeck.addChoosableFileFilter(new DeckFilter());
fcImportDeck = new JFileChooser();
fcImportDeck.setAcceptAllFileFilterUsed(false);
fcImportDeck.addChoosableFileFilter(new ImportFilter());
deckArea.setOpaque(false);
jPanel1.setOpaque(false);
jSplitPane1.setOpaque(false);
restoreDividerLocationsAndDeckAreaSettings();
countdown = new Timer(1000,
e -> {
if (--timeout > 0) {
setTimeout(timeout);
} else {
if (updateDeckTask != null) {
updateDeckTask.cancel(true);
}
setTimeout(0);
countdown.stop();
removeDeckEditor();
}
});
// Set up tracking to save the deck editor settings when the deck editor is hidden.
addHierarchyListener((HierarchyEvent e) -> {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
if (!isShowing()) {
saveDividerLocationsAndDeckAreaSettings();
}
}
});
}
/**
* Free resources so GC can remove unused objects from memory
*/
public void cleanUp() {
saveDividerLocationsAndDeckAreaSettings();
if (updateDeckTask != null) {
updateDeckTask.cancel(true);
}
if (countdown != null) {
if (countdown.isRunning()) {
countdown.stop();
}
for (ActionListener al : countdown.getActionListeners()) {
countdown.removeActionListener(al);
}
}
this.cardSelector.cleanUp();
this.deckArea.cleanUp();
this.remove(bigCard);
this.bigCard = null;
}
private void saveDividerLocationsAndDeckAreaSettings() {
PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, Integer.toString(jSplitPane1.getDividerLocation()));
PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, this.deckArea.saveSettings().toString());
}
private void restoreDividerLocationsAndDeckAreaSettings() {
// Load horizontal split position setting
String dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, "");
if (!dividerLocation.isEmpty()) {
jSplitPane1.setDividerLocation(Integer.parseInt(dividerLocation));
}
// Load deck area settings
this.deckArea.loadSettings(
DeckArea.Settings.parse(
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, "")));
}
public void changeGUISize() {
this.cardSelector.changeGUISize();
this.deckArea.changeGUISize();
}
public void showDeckEditor(DeckEditorMode mode, Deck deck, UUID tableId, int time) {
if (deck != null) {
this.deck = deck;
}
this.tableId = tableId;
this.mode = mode;
this.btnAddLand.setVisible(false);
switch (mode) {
case LIMITED_BUILDING:
this.btnAddLand.setVisible(true);
this.txtTimeRemaining.setVisible(true);
// Fall through to sideboarding
case SIDEBOARDING:
this.btnSubmit.setVisible(true);
this.btnSubmitTimer.setVisible(true);
if (mode == DeckEditorMode.SIDEBOARDING) {
this.deckArea.setOrientation(/*limitedBuildingOrientation = */false);
} else /*(if (mode == LIMITED_BUILDING)*/ {
this.deckArea.setOrientation(/*limitedBuildingOrientation = */true);
}
this.cardSelector.setVisible(false);
this.btnExit.setVisible(false);
this.btnImport.setVisible(false);
this.btnGenDeck.setVisible(false);
if (!SessionHandler.isTestMode()) {
this.btnLoad.setVisible(false);
}
this.deckArea.showSideboard(true);
countdown.stop();
this.timeout = time;
setTimeout(timeout);
if (timeout != 0) {
countdown.start();
if (updateDeckTask == null || updateDeckTask.isDone()) {
updateDeckTask = new UpdateDeckTask(SessionHandler.getSession(), tableId, deck);
updateDeckTask.execute();
}
}
break;
case FREE_BUILDING:
this.deckArea.setOrientation(/*limitedBuildingOrientation = */false);
this.btnSubmit.setVisible(false);
this.btnSubmitTimer.setVisible(false);
this.btnAddLand.setVisible(true);
this.cardSelector.setVisible(true);
this.cardSelector.loadCards(this.bigCard);
//this.cardTableSelector.loadCards(this.bigCard);
this.btnExit.setVisible(true);
this.btnImport.setVisible(true);
this.btnGenDeck.setVisible(true);
if (!SessionHandler.isTestMode()) {
this.btnLoad.setVisible(true);
}
this.deckArea.showSideboard(true);
this.txtTimeRemaining.setVisible(false);
break;
case VIEW_LIMITED_DECK:
this.btnExit.setVisible(true);
this.btnSave.setVisible(true);
this.btnAddLand.setVisible(false);
this.btnGenDeck.setVisible(false);
this.btnImport.setVisible(false);
this.btnLoad.setVisible(false);
this.btnNew.setVisible(false);
this.btnSubmit.setVisible(false);
this.btnSubmitTimer.setVisible(false);
this.cardSelector.loadCards(this.bigCard);
this.cardSelector.setVisible(false);
this.deckArea.setOrientation(/*limitedBuildingOrientation = */true);
this.deckArea.showSideboard(true);
this.lblDeckName.setVisible(false);
this.txtDeckName.setVisible(false);
this.txtTimeRemaining.setVisible(false);
break;
}
init();
this.deckArea.setDeckEditorMode(mode);
}
private Card retrieveTemporaryCard(SimpleCardView cardView) {
Card card = temporaryCards.get(cardView.getId());
if (card == null) {
// Need to make a new card
Logger.getLogger(DeckEditorPanel.class).info("Retrieve " + cardView.getCardNumber() + " Failed");
card = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber()).getCard();
} else {
// Only need a temporary card once
temporaryCards.remove(cardView.getId());
}
return card;
}
private void storeTemporaryCard(Card card) {
temporaryCards.put(card.getId(), card);
}
private void init() {
//this.cardSelector.setVisible(true);
this.jPanel1.setVisible(true);
for (ICardGrid component : this.cardSelector.getCardGridComponents()) {
component.clearCardEventListeners();
component.addCardEventListener(
(Listener<Event>) event -> {
switch (event.getEventName()) {
case "double-click":
moveSelectorCardToDeck(event);
break;
case "alt-double-click":
if (mode == DeckEditorMode.FREE_BUILDING) {
moveSelectorCardToSideboard(event);
} else {
// because in match mode selector is used as sideboard the card goes to deck also for shift click
moveSelectorCardToDeck(event);
}
break;
case "remove-main":
DeckEditorPanel.this.deckArea.getDeckList().removeSelection();
break;
case "remove-sideboard":
DeckEditorPanel.this.deckArea.getSideboardList().removeSelection();
break;
}
refreshDeck();
});
}
this.deckArea.clearDeckEventListeners();
this.deckArea.addDeckEventListener(
(Listener<Event>) event -> {
if (mode == DeckEditorMode.FREE_BUILDING) {
switch (event.getEventName()) {
case "double-click": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) {
deck.getCards().remove(card);
break;
}
}
hidePopup();
refreshDeck();
break;
}
case "alt-double-click": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) {
deck.getCards().remove(card);
deck.getSideboard().add(card);
break;
}
}
hidePopup();
refreshDeck();
break;
}
case "set-number": {
setCardNumberToCardsList(event, deck.getCards());
break;
}
case "remove-specific-card": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) {
deck.getCards().remove(card);
storeTemporaryCard(card);
break;
}
}
break;
}
case "add-specific-card": {
SimpleCardView cardView = (CardView) event.getSource();
deck.getCards().add(retrieveTemporaryCard(cardView));
break;
}
}
} else {
// constructing phase or sideboarding during match -> card goes always to sideboard
switch (event.getEventName()) {
case "double-click":
case "alt-double-click": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) {
deck.getCards().remove(card);
deck.getSideboard().add(card);
cardSelector.loadSideboard(new ArrayList<>(deck.getSideboard()), this.bigCard);
break;
}
}
hidePopup();
refreshDeck();
break;
}
case "remove-specific-card": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) {
deck.getCards().remove(card);
storeTemporaryCard(card);
break;
}
}
break;
}
case "add-specific-card": {
SimpleCardView cardView = (CardView) event.getSource();
deck.getCards().add(retrieveTemporaryCard(cardView));
break;
}
}
}
});
this.deckArea.clearSideboardEventListeners();
this.deckArea.addSideboardEventListener(
(Listener<Event>) event -> {
if (mode == DeckEditorMode.FREE_BUILDING) {
// normal edit mode
switch (event.getEventName()) {
case "double-click":
// remove card from sideboard (don't add it to deck)
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) {
deck.getSideboard().remove(card);
break;
}
}
hidePopup();
refreshDeck();
break;
case "alt-double-click":
// remove card from sideboard
cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) {
deck.getSideboard().remove(card);
deck.getCards().add(card);
break;
}
}
hidePopup();
refreshDeck();
break;
case "set-number": {
setCardNumberToCardsList(event, deck.getSideboard());
break;
}
case "remove-specific-card": {
cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) {
deck.getSideboard().remove(card);
storeTemporaryCard(card);
break;
}
}
break;
}
case "add-specific-card": {
cardView = (CardView) event.getSource();
deck.getSideboard().add(retrieveTemporaryCard(cardView));
break;
}
}
} else {
// construct phase or sideboarding during match
switch (event.getEventName()) {
case "remove-specific-card": {
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) {
deck.getSideboard().remove(card);
storeTemporaryCard(card);
break;
}
}
break;
}
case "add-specific-card": {
SimpleCardView cardView = (CardView) event.getSource();
deck.getSideboard().add(retrieveTemporaryCard(cardView));
break;
}
case "double-click":
case "alt-double-click":
SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) {
deck.getSideboard().remove(card);
deck.getCards().add(card);
break;
}
}
hidePopup();
refreshDeck();
break;
}
}
});
refreshDeck();
this.setVisible(true);
this.repaint();
}
private void setCardNumberToCardsList(Event event, Set<Card> cards) {
CardView cardView = (CardView) event.getSource();
int numberToSet = event.getNumber();
int cardsFound = 0;
List<Card> toDelete = new ArrayList<>();
for (Card card : cards) {
if (card.getName().equals(cardView.getName())
&& Objects.equals(card.getCardNumber(), cardView.getCardNumber())
&& card.getExpansionSetCode().equals(cardView.getExpansionSetCode())) {
cardsFound++;
if (cardsFound > numberToSet) {
toDelete.add(card);
}
}
}
if (toDelete.isEmpty()) {
// add cards
CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
for (int i = cardsFound; i < numberToSet; i++) {
cards.add(cardInfo.getMockCard());
}
} else {
// remove cards
for (Card card : toDelete) {
cards.remove(card);
}
}
hidePopup();
refreshDeck();
}
private void moveSelectorCardToDeck(Event event) {
SimpleCardView cardView = (SimpleCardView) event.getSource();
CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
Card card = null;
if (mode == DeckEditorMode.SIDEBOARDING || mode == DeckEditorMode.LIMITED_BUILDING) {
for (Object o : deck.getSideboard()) {
card = (Card) o;
if (card.getId().equals(cardView.getId())) {
break;
}
}
} else {
card = cardInfo != null ? cardInfo.getMockCard() : null;
}
if (card != null) {
deck.getCards().add(card);
if (mode == DeckEditorMode.SIDEBOARDING || mode == DeckEditorMode.LIMITED_BUILDING) {
deck.getSideboard().remove(card);
cardSelector.removeCard(card.getId());
cardSelector.setCardCount(deck.getSideboard().size());
cardSelector.refresh();
}
if (cardInfoPane instanceof CardInfoPane) {
((CardInfoPane) cardInfoPane).setCard(new CardView(card), null);
}
hidePopup();
}
}
private void moveSelectorCardToSideboard(Event event) {
SimpleCardView cardView = (SimpleCardView) event.getSource();
CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
Card card = cardInfo != null ? cardInfo.getMockCard() : null;
if (card != null) {
deck.getSideboard().add(card);
}
if (cardInfoPane instanceof CardInfoPane) {
((CardInfoPane) cardInfoPane).setCard(new CardView(card), null);
}
hidePopup();
}
private void hidePopup() {
Plugins.instance.getActionCallback().mouseExited(null, null);
}
public void removeDeckEditor() {
hidePopup();
this.cleanUp();
Component c = this.getParent();
while (c != null && !(c instanceof DeckEditorPane)) {
c = c.getParent();
}
if (c != null) {
((DeckEditorPane) c).removeFrame();
}
}
public DeckEditorMode getDeckEditorMode() {
return mode;
}
private BigCard getBigCard() {
return this.bigCard;
}
private void refreshDeck() {
refreshDeck(false);
}
private void refreshDeck(boolean useLayout) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
this.txtDeckName.setText(deck.getName());
deckArea.loadDeck(deck, useLayout, bigCard);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
private void setTimeout(int s) {
int minute = s / 60;
int second = s - (minute * 60);
String text;
if (minute < 10) {
text = '0' + Integer.toString(minute) + ':';
} else {
text = Integer.toString(minute) + ':';
}
if (second < 10) {
text = text + '0' + Integer.toString(second);
} else {
text = text + Integer.toString(second);
}
this.txtTimeRemaining.setText(text);
if (s == 60) {
AudioManager.playOnCountdown1();
}
if (timeToSubmit > 0) {
timeToSubmit--;
btnSubmitTimer.setText("Submit (" + timeToSubmit + ')');
btnSubmitTimer.setToolTipText("Submit your deck in " + timeToSubmit + " seconds!");
}
}
private void initComponents() {
jSplitPane1 = new javax.swing.JSplitPane();
cardSelector = new mage.client.deckeditor.CardSelector();
deckArea = new mage.client.deckeditor.DeckArea();
jPanel1 = new javax.swing.JPanel();
bigCard = new mage.client.cards.BigCard();
txtDeckName = new javax.swing.JTextField();
lblDeckName = new javax.swing.JLabel();
btnSave = new javax.swing.JButton();
btnLoad = new javax.swing.JButton();
btnNew = new javax.swing.JButton();
btnExit = new javax.swing.JButton();
btnImport = new javax.swing.JButton();
btnSubmit = new javax.swing.JButton();
btnSubmitTimer = new javax.swing.JButton();
btnAddLand = new javax.swing.JButton();
btnGenDeck = new javax.swing.JButton();
txtTimeRemaining = new javax.swing.JTextField();
jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
jSplitPane1.setResizeWeight(0.5);
jSplitPane1.setTopComponent(cardSelector);
jSplitPane1.setBottomComponent(deckArea);
bigCard.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
cardInfoPane = Plugins.instance.getCardInfoPane();
if (cardInfoPane != null && System.getProperty("testCardInfo") != null) {
cardInfoPane.setPreferredSize(new Dimension(170, 150));
cardInfoPane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(200, 0, 0)));
isShowCardInfo = true;
} else {
cardInfoPane = new JLabel();
cardInfoPane.setVisible(false);
}
lblDeckName.setLabelFor(txtDeckName);
lblDeckName.setText("Deck Name:");
btnSave.setText("Save");
btnSave.addActionListener(evt -> btnSaveActionPerformed(evt));
btnLoad.setText("Load");
btnLoad.addActionListener(evt -> btnLoadActionPerformed(evt));
btnNew.setText("New");
btnNew.addActionListener(evt -> btnNewActionPerformed(evt));
btnExit.setText("Exit");
btnExit.addActionListener(evt -> btnExitActionPerformed(evt));
btnImport.setText("Import");
btnImport.setName("btnImport"); // NOI18N
btnImport.addActionListener(evt -> {
Object[] options = {"File", "Clipboard", "Append from Clipboard"};
int n = JOptionPane.showOptionDialog(MageFrame.getDesktop(),
"Where would you like to import from?",
"Deck import",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]
);
logger.info(n);
switch (n) {
case 0:
btnImportActionPerformed(evt);
break;
case 1:
btnImportFromClipboardActionPerformed(evt);
case 2:
btnImportFromClipboardActionWAppendPerformed(evt);
}
});
btnSubmit.setText("Submit");
btnSubmitTimer.setToolTipText("Submit your deck now!");
btnSubmit.setName("btnSubmit"); // NOI18N
btnSubmit.addActionListener(evt -> btnSubmitActionPerformed(evt));
btnSubmitTimer.setText("Submit (60s)");
btnSubmitTimer.setToolTipText("Submit your deck in one minute!");
btnSubmitTimer.setName("btnSubmitTimer");
btnSubmitTimer.addActionListener(evt -> btnSubmitTimerActionPerformed(evt));
btnAddLand.setText("Add Land");
btnAddLand.setName("btnAddLand"); // NOI18N
btnAddLand.addActionListener(evt -> btnAddLandActionPerformed(evt));
btnGenDeck.setText("Generate");
btnGenDeck.setName("btnGenDeck");
btnGenDeck.addActionListener(evt -> btnGenDeckActionPerformed(evt));
txtTimeRemaining.setEditable(false);
txtTimeRemaining.setForeground(java.awt.Color.red);
txtTimeRemaining.setHorizontalAlignment(javax.swing.JTextField.CENTER);
txtTimeRemaining.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
/*.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLayeredPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE))*/
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(lblDeckName)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtDeckName, javax.swing.GroupLayout.DEFAULT_SIZE, 189, Short.MAX_VALUE))
.addComponent(cardInfoPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(btnSave)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnLoad)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnNew)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnExit))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(btnImport)
.addContainerGap()
.addComponent(btnGenDeck)
.addContainerGap()
.addComponent(btnAddLand)
.addContainerGap()
.addComponent(btnSubmit)
.addContainerGap()
.addComponent(btnSubmitTimer))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(txtTimeRemaining))
)
.addContainerGap()));
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtDeckName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblDeckName))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnSave)
.addComponent(btnLoad)
.addComponent(btnNew)
.addComponent(btnExit))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnImport)
.addComponent(btnGenDeck)
.addComponent(btnAddLand)
.addComponent(btnSubmit)
.addComponent(btnSubmitTimer))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtTimeRemaining))
//.addComponent(jLayeredPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, isShowCardInfo ? 30 : 159, Short.MAX_VALUE)
.addComponent(cardInfoPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 104, Short.MAX_VALUE)
.addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 604, Short.MAX_VALUE)));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jSplitPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 615, Short.MAX_VALUE));
}
/**
* @param evt ActionEvent
*/
private void btnImportFromClipboardActionPerformed(ActionEvent evt) {
final DeckImportFromClipboardDialog dialog = new DeckImportFromClipboardDialog();
dialog.pack();
dialog.setVisible(true);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
try {
deck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true);
refreshDeck();
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
}
}
});
}
/**
* @param evt ActionEvent
*/
private void btnImportFromClipboardActionWAppendPerformed(ActionEvent evt) {
final DeckImportFromClipboardDialog dialog = new DeckImportFromClipboardDialog();
dialog.pack();
dialog.setVisible(true);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
Deck deckToAppend = null;
try {
deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true);
if (deckToAppend != null) {
deck = Deck.append(deckToAppend, deck);
refreshDeck();
}
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
}
}
});
}
private void btnLoadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLoadActionPerformed
//fcSelectDeck.setCurrentDirectory(new File());
String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
if (!lastFolder.isEmpty()) {
fcSelectDeck.setCurrentDirectory(new File(lastFolder));
}
int ret = fcSelectDeck.showOpenDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcSelectDeck.getSelectedFile();
{
/**
* Work around a JFileChooser bug on Windows 7-10 with JRT 7+ In
* the case where the user selects the exact same file as was
* previously selected without touching anything else in the
* dialog, getSelectedFile() will erroneously return null due to
* some combination of our settings.
*
* We manually sub in the last selected file in this case.
*/
if (file == null) {
if (!lastFolder.isEmpty()) {
file = new File(lastFolder);
}
}
}
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
deck = Deck.load(DeckImporterUtil.importDeck(file.getPath()), true, true);
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck(true);
try {
if (file != null) {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
}
} catch (IOException ex) {
}
}
fcSelectDeck.setSelectedFile(null);
}//GEN-LAST:event_btnLoadActionPerformed
private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveActionPerformed
String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
if (!lastFolder.isEmpty()) {
fcSelectDeck.setCurrentDirectory(new File(lastFolder));
}
deck.setName(this.txtDeckName.getText());
int ret = fcSelectDeck.showSaveDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcSelectDeck.getSelectedFile();
{
/**
* Work around a JFileChooser bug on Windows 7-10 with JRT 7+ In
* the case where the user selects the exact same file as was
* previously selected without touching anything else in the
* dialog, getSelectedFile() will erroneously return null due to
* some combination of our settings.
*
* We manually sub in the last selected file in this case.
*/
if (file == null) {
if (!lastFolder.isEmpty()) {
file = new File(lastFolder);
}
}
}
try {
String fileName = file.getPath();
if (!fileName.endsWith(".dck")) {
fileName += ".dck";
}
setCursor(new Cursor(Cursor.WAIT_CURSOR));
DeckCardLists cardLists = deck.getDeckCardLists();
cardLists.setCardLayout(deckArea.getCardLayout());
cardLists.setSideboardLayout(deckArea.getSideboardLayout());
Sets.saveDeck(fileName, cardLists);
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
try {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}//GEN-LAST:event_btnSaveActionPerformed
private void btnNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewActionPerformed
if (mode == DeckEditorMode.SIDEBOARDING || mode == DeckEditorMode.LIMITED_BUILDING) {
for (Card card : deck.getCards()) {
deck.getSideboard().add(card);
}
deck.getCards().clear();
cardSelector.loadSideboard(new ArrayList<>(deck.getSideboard()), this.bigCard);
} else {
deck = new Deck();
}
refreshDeck();
}//GEN-LAST:event_btnNewActionPerformed
private void btnExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExitActionPerformed
removeDeckEditor();
}//GEN-LAST:event_btnExitActionPerformed
private void btnImportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnImportActionPerformed
String lastFolder = MageFrame.getPreferences().get("lastImportFolder", "");
if (!lastFolder.isEmpty()) {
fcImportDeck.setCurrentDirectory(new File(lastFolder));
}
int ret = fcImportDeck.showOpenDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcImportDeck.getSelectedFile();
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
DeckImporter importer = DeckImporterUtil.getDeckImporter(file.getPath());
if (importer != null) {
deck = Deck.load(importer.importDeck(file.getPath()));
String errors = importer.getErrors();
if (!errors.isEmpty()) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), errors, "Error importing deck", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Unknown deck format", "Error importing deck", JOptionPane.ERROR_MESSAGE);
}
} catch (Exception ex) {
logger.fatal(ex);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
try {
if (file != null) {
MageFrame.getPreferences().put("lastImportFolder", file.getCanonicalPath());
}
} catch (IOException ex) {
}
}
fcImportDeck.setSelectedFile(null);
}//GEN-LAST:event_btnImportActionPerformed
private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitActionPerformed
if (updateDeckTask != null) {
updateDeckTask.cancel(true);
}
if (SessionHandler.submitDeck(tableId, deck.getDeckCardLists())) {
removeDeckEditor();
}
}//GEN-LAST:event_btnSubmitActionPerformed
private void btnSubmitTimerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitTimerActionPerformed
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
timeToSubmit = 60;
this.btnSubmitTimer.setEnabled(false);
ScheduledFuture scheduledFuture = scheduledExecutorService.schedule((Callable) () -> {
if (updateDeckTask != null) {
updateDeckTask.cancel(true);
}
if (SessionHandler.submitDeck(tableId, deck.getDeckCardLists())) {
removeDeckEditor();
}
return null;
}, 60, TimeUnit.SECONDS);
}//GEN-LAST:event_btnSubmitTimerActionPerformed
private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed
AddLandDialog addLand = new AddLandDialog();
addLand.showDialog(deck, mode);
refreshDeck();
}//GEN-LAST:event_btnAddLandActionPerformed
private void btnGenDeckActionPerformed(ActionEvent evt) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
String path = DeckGenerator.generateDeck();
deck = Deck.load(DeckImporterUtil.importDeck(path), true, true);
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading generated deck", JOptionPane.ERROR_MESSAGE);
} catch (DeckGeneratorException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Generator error", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private mage.client.cards.BigCard bigCard;
private javax.swing.JButton btnExit;
private javax.swing.JButton btnImport;
private javax.swing.JButton btnLoad;
private javax.swing.JButton btnNew;
private javax.swing.JButton btnSave;
private mage.client.deckeditor.CardSelector cardSelector;
private mage.client.deckeditor.DeckArea deckArea;
private javax.swing.JPanel jPanel1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JLabel lblDeckName;
private javax.swing.JTextField txtDeckName;
private javax.swing.JButton btnSubmit;
private javax.swing.JButton btnSubmitTimer;
private javax.swing.JButton btnAddLand;
private javax.swing.JButton btnGenDeck;
private JComponent cardInfoPane;
private javax.swing.JTextField txtTimeRemaining;
// End of variables declaration//GEN-END:variables
}
class DeckFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i + 1).toLowerCase();
}
return (ext == null) ? false : ext.equals("dck");
}
@Override
public String getDescription() {
return "Deck Files";
}
}
class ImportFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i + 1).toLowerCase();
}
if (ext != null) {
if (ext.toLowerCase().equals("dec") || ext.toLowerCase().equals("mwdeck") || ext.toLowerCase().equals("txt") || ext.toLowerCase().equals("dek")) {
return true;
}
}
return false;
}
@Override
public String getDescription() {
return "*.dec | *.mwDeck | *.txt | *.dek";
}
}
class UpdateDeckTask extends SwingWorker<Void, Void> {
private static final Logger logger = Logger.getLogger(UpdateDeckTask.class);
private final Session session;
private final UUID tableId;
private final Deck deck;
UpdateDeckTask(Session session, UUID tableId, Deck deck) {
this.session = session;
this.tableId = tableId;
this.deck = deck;
}
@Override
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
SessionHandler.updateDeck(tableId, deck.getDeckCardLists());
TimeUnit.SECONDS.sleep(5);
}
return null;
}
@Override
protected void done() {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
logger.fatal("Update Matches Task error", ex);
} catch (CancellationException ex) {
}
}
}