diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
index 54ce90f5586..dcc8f7e0ce6 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
@@ -97,6 +97,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardSelectorScrollPane.setOpaque(false);
cardSelectorScrollPane.getViewport().setOpaque(false);
+
+ taggerScrollPane.setOpaque(false);
+ taggerScrollPane.getViewport().setOpaque(false);
+
cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values()));
cbSortBy.setSelectedItem(sortSetting.getSortBy());
jTextFieldSearch.addActionListener(searchAction);
@@ -106,11 +110,15 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
tbColor.setOpaque(true); // false = transparent
tbTypes.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
tbTypes.setOpaque(true); // false = transparent
+ taggerScrollPane.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
+ taggerScrollPane.setOpaque(true);
cardSelectorBottomPanel.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
cardSelectorBottomPanel.setOpaque(true); // false = transparent
}
private void initListViewComponents() {
+ taggerTable = new JTable();
+
mainTable = new JTable();
mainModel = new TableModel();
@@ -136,6 +144,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
// mainTable.setToolTipText(cardSelectorScrollPane.getToolTipText());
cardSelectorScrollPane.setViewportView(mainTable);
+
+ taggerScrollPane.setViewportView(taggerTable);
+
+ taggerTable.setOpaque(false);
+
mainTable.setOpaque(false);
cbSortBy.setEnabled(false);
chkPiles.setEnabled(false);
@@ -630,6 +643,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
jButtonClean = new javax.swing.JButton();
cardCountLabel = new javax.swing.JLabel();
cardCount = new javax.swing.JLabel();
+
+ taggerScrollPane = new javax.swing.JScrollPane();
+ tablePanel = new javax.swing.JSplitPane(JSplitPane.HORIZONTAL_SPLIT, cardSelectorScrollPane, taggerScrollPane);
tbColor.setFloatable(false);
tbColor.setRollover(true);
@@ -1105,6 +1121,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardSelectorBottomPanel.setOpaque(false);
cardSelectorBottomPanel.setPreferredSize(new java.awt.Dimension(897, 40));
+ tablePanel.setOpaque(false);
+ tablePanel.setPreferredSize(new java.awt.Dimension(600, 40));
jButtonAddToMain.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/deck_in.png"))); // NOI18N
jButtonAddToMain.setToolTipText("Add selected cards to deck.
\nAlternative: Double click the card in card selector to move a card to the deck.");
@@ -1248,8 +1266,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardCountLabel.setToolTipText("Number of cards currently shown.");
cardCount.setText("0");
-
+
javax.swing.GroupLayout cardSelectorBottomPanelLayout = new javax.swing.GroupLayout(cardSelectorBottomPanel);
+
cardSelectorBottomPanel.setLayout(cardSelectorBottomPanelLayout);
cardSelectorBottomPanelLayout.setHorizontalGroup(
cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -1319,9 +1338,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tbColor, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(tbTypes, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
- .addComponent(cardSelectorScrollPane)
+ .addComponent(tablePanel)
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
);
+
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
@@ -1329,7 +1349,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
.addGap(0, 0, 0)
.addComponent(tbTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
- .addComponent(cardSelectorScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
+ .addComponent(tablePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE))
);
@@ -1690,6 +1710,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private TableModel mainModel;
private JTable mainTable;
+ private JTable taggerTable;
private ICardGrid currentView;
private final CheckBoxList listCodeSelected;
@@ -1751,6 +1772,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private javax.swing.JToolBar tbTypes;
private javax.swing.JToggleButton tbUncommon;
private javax.swing.JToggleButton tbWhite;
+
+ private javax.swing.JScrollPane taggerScrollPane;
+ private javax.swing.JSplitPane tablePanel;
+
// End of variables declaration//GEN-END:variables
private final mage.client.cards.CardGrid cardGrid; // grid for piles view mode (example: selected cards in drafting)
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/TaggerModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TaggerModel.java
new file mode 100644
index 00000000000..04b87f91111
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TaggerModel.java
@@ -0,0 +1,344 @@
+package mage.client.deckeditor.table;
+
+import mage.client.MageFrame;
+import mage.client.cards.BigCard;
+import mage.client.cards.CardEventSource;
+import mage.client.cards.ICardGrid;
+import mage.client.deckeditor.SortSetting;
+import mage.client.plugins.impl.Plugins;
+import mage.client.util.ClientDefaultSettings;
+import mage.client.util.ClientEventType;
+import mage.client.util.Event;
+import mage.client.util.Listener;
+import mage.client.util.gui.GuiDisplayUtil;
+import mage.constants.EnlargeMode;
+import mage.cards.RateCard;
+import mage.view.CardView;
+import mage.view.CardsView;
+import org.apache.log4j.Logger;
+import org.jdesktop.swingx.JXPanel;
+import org.mage.card.arcane.ManaSymbols;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableColumnModel;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * Table Model for card list.
+ *
+ * @author nantuko
+ */
+public class TaggerModel extends AbstractTableModel {
+
+ private static final long serialVersionUID = -528008802935423048L;
+
+ private static final Logger log = Logger.getLogger(TableModel.class);
+
+ protected final CardEventSource cardEventSource = new CardEventSource();
+ protected BigCard bigCard;
+ protected UUID gameId;
+ private final Map cards = new LinkedHashMap<>();
+ private final Map cardsNoCopies = new LinkedHashMap<>();
+ private final List view = new ArrayList<>();
+ private Dimension cardDimension;
+
+ private boolean displayNoCopies = false;
+ private UpdateCountsCallback updateCountsCallback;
+
+ private final String[] column = {"Qty", "Name", "Cost", "Color", "Type", "Stats", "Rarity", "Set", "Card number", "Draft Rating", "Color Identity"};
+ public final int COLUMN_INDEX_COST = 2;
+ public final int COLUMN_INDEX_RATING = 9;
+ public final int COLUMN_INDEX_COLOR_IDENTITY = 10;
+
+ private SortSetting sortSetting;
+ private int recentSortedColumn;
+ private boolean recentAscending;
+
+ private boolean numberEditable;
+
+ public TaggerModel() {
+ this.numberEditable = false;
+ }
+
+ public void clear() {
+ this.clearCardEventListeners();
+ this.clearCards();
+ this.view.clear();
+ }
+
+
+ public void clearCards() {
+ view.clear();
+ cards.clear();
+ }
+
+ @Override
+ public int getRowCount() {
+ return view.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return column.length;
+ }
+
+ @Override
+ public String getColumnName(int n) {
+ return column[n];
+ }
+
+ @Override
+ public Object getValueAt(int row, int column) {
+ return getColumn(view.get(row), column);
+ }
+
+ private Object getColumn(Object obj, int column) {
+ CardView c = (CardView) obj;
+ switch (column) {
+ case 0:
+ if (displayNoCopies) {
+ String key = c.getName() + c.getExpansionSetCode() + c.getCardNumber();
+ Integer count = cardsNoCopies.get(key);
+ return count != null ? count : "";
+ }
+ return "";
+ case 1:
+ return c.getDisplayFullName(); // show full name in deck editor table, e.g. adventure with spell name
+ case 2:
+ // new svg images version
+ return ManaSymbols.getClearManaCost(c.getManaCostStr());
+ /*
+ // old html images version
+ String manaCost = "";
+ for (String m : c.getManaCost()) {
+ manaCost += m;
+ }
+ String castingCost = UI.getDisplayManaCost(manaCost);
+ castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, ManaSymbols.Type.TABLE);
+ return "" + castingCost + "";
+ return castingCost;
+ */
+ case 3:
+ return c.getColorText();
+ case 4:
+ return c.getTypeText();
+ case 5:
+ return c.isCreature() ? c.getPower() + '/'
+ + c.getToughness() : "-";
+ case 6:
+ return c.getRarity() == null ? "" : c.getRarity().toString();
+ case 7:
+ return c.getExpansionSetCode();
+ case 8:
+ return c.getCardNumber();
+ case 9:
+ return RateCard.rateCard(c, Collections.emptyList());
+ case 10:
+ return ManaSymbols.getClearManaCost(c.getOriginalColorIdentity());
+ default:
+ return "error";
+ }
+ }
+
+ private void addCard(CardView card, BigCard bigCard, UUID gameId) {
+ if (cardDimension == null) {
+ cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(),
+ ClientDefaultSettings.dimensions.getFrameHeight());
+ }
+ cards.put(card.getId(), card);
+
+ if (displayNoCopies) {
+ String key = card.getName() + card.getExpansionSetCode() + card.getCardNumber();
+ Integer count = 1;
+ if (cardsNoCopies.containsKey(key)) {
+ count = cardsNoCopies.get(key) + 1;
+ } else {
+ view.add(card);
+ }
+ cardsNoCopies.put(key, count);
+ } else {
+ view.add(card);
+ }
+ }
+
+ @Override
+ public void drawCards(SortSetting sortSetting) {
+ fireTableDataChanged();
+ }
+
+ public void removeCard(UUID cardId) {
+ cards.remove(cardId);
+ view.removeIf(cardView -> cardView.getId().equals(cardId));
+ }
+
+ @Override
+ public void addCardEventListener(Listener listener) {
+ cardEventSource.addListener(listener);
+ }
+
+ @Override
+ public void clearCardEventListeners() {
+ cardEventSource.clearListeners();
+ }
+
+ public void setNumber(int index, int number) {
+ CardView card = view.get(index);
+ cardEventSource.fireEvent(card, ClientEventType.SET_NUMBER, number);
+ }
+
+ public void doubleClick(int index, MouseEvent e, boolean forceFakeAltDown) {
+ CardView card = view.get(index);
+ cardEventSource.fireEvent(card, ClientEventType.CARD_DOUBLE_CLICK, e, forceFakeAltDown);
+ }
+
+ public void removeFromMainEvent(int index) {
+ cardEventSource.fireEvent(ClientEventType.DECK_REMOVE_SELECTION_MAIN);
+ }
+
+ public void removeFromSideEvent(int index) {
+ cardEventSource.fireEvent(ClientEventType.DECK_REMOVE_SELECTION_SIDEBOARD);
+ }
+
+ public void addListeners(final JTable table) {
+ // updates card detail, listens to any key strokes
+
+ table.addKeyListener(new KeyListener() {
+ @Override
+ public void keyPressed(KeyEvent ev) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent ev) {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent ev) {
+ int row = table.getSelectedRow();
+ if (row != -1) {
+ showImage(row);
+ }
+ }
+ });
+
+ // updates card detail, listens to any mouse clicks
+ table.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (!SwingUtilities.isLeftMouseButton(e)) {
+ return;
+ }
+ int row = table.getSelectedRow();
+ if (row != -1) {
+ showImage(row);
+ }
+ }
+ });
+
+ // sorts
+ MouseListener mouse = new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (!SwingUtilities.isLeftMouseButton(e)) {
+ return;
+ }
+ TableColumnModel columnModel = table.getColumnModel();
+ int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+ int column = table.convertColumnIndexToModel(viewColumn);
+
+ if (column != -1) {
+ // sort ascending
+ boolean asc = true;
+ if (recentSortedColumn == column) {
+ asc = !recentAscending;
+ }
+ sortSetting.setSortIndex(column);
+ sortSetting.setAscending(asc);
+ sort(column, asc);
+ fireTableDataChanged();
+ }
+ }
+ };
+ table.getTableHeader().addMouseListener(mouse);
+ }
+
+ private void showImage(int row) {
+ CardView card = view.get(row);
+ if (!card.getId().equals(bigCard.getCardId())) {
+ if (!MageFrame.isLite()) {
+ Image image = Plugins.instance.getOriginalImage(card);
+ if (image instanceof BufferedImage) {
+ // XXX: scaled to fit width
+ bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, new ArrayList<>(), false);
+ } else {
+ drawCardText(card);
+ }
+ } else {
+ drawCardText(card);
+ }
+ }
+ }
+
+ private void drawCardText(CardView card) {
+ JXPanel panel = GuiDisplayUtil.getDescription(card, bigCard.getWidth(), bigCard.getHeight());
+ panel.setVisible(true);
+ bigCard.hideTextComponent();
+ bigCard.addJXPanel(card.getId(), panel);
+ }
+
+ public List getCardsView() {
+ return view;
+ }
+
+ public boolean sort(int column, boolean ascending) {
+ // used by addCard() to resort the cards
+ recentSortedColumn = column;
+ recentAscending = ascending;
+
+ MageCardComparator sorter = new MageCardComparator(column, ascending);
+ view.sort(sorter);
+
+ fireTableDataChanged();
+
+ return true;
+ }
+
+ public int getRecentSortedColumn() {
+ return recentSortedColumn;
+ }
+
+ public boolean isRecentAscending() {
+ return recentAscending;
+ }
+
+ public void setDisplayNoCopies(boolean value) {
+ this.displayNoCopies = value;
+ }
+
+ public void setUpdateCountsCallback(UpdateCountsCallback callback) {
+ this.updateCountsCallback = callback;
+ }
+
+ public void setNumberEditable(boolean numberEditable) {
+ this.numberEditable = numberEditable;
+ }
+
+ @Override
+ public int cardsSize() {
+ return cards.size();
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int col) {
+ if (numberEditable && col == 0) {
+ return true;
+ }
+ return super.isCellEditable(row, col);
+ }
+
+}
diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
index 0cb2e6e4242..2068250f33a 100644
--- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
@@ -893,7 +893,7 @@ public class TablesPanel extends javax.swing.JPanel {
formatFilterList.add(RowFilter.regexFilter("^Oathbreaker", TablesTableModel.COLUMN_DECK_TYPE));
}
if (btnFormatLimited.isSelected()) {
- formatFilterList.add(RowFilter.regexFilter("^Limited", TablesTableModel.COLUMN_DECK_TYPE));
+ formatFilterList.add(RowFilter.regexFilter("^(?:(?:Unl)|L)imited", TablesTableModel.COLUMN_DECK_TYPE));
}
if (btnFormatOther.isSelected()) {
formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Constructed - Freeform Unlimited|^Australian Highlander|^European Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE));
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java
index b07e82734eb..e33faeceaf2 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java
@@ -32,7 +32,7 @@ public class ScryfallApiCard {
transient public String imageLarge = "";
// potentially interesting fields, can be used in other places
- //public UUID oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
+ public String oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
//public Integer edhrec_rank; // TODO: use it to rating cards for AI and draft bots
//public Object legalities; // TODO: add verify check for bans list
//public Boolean full_art; // TODO: add verify check for full art usage in sets
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
index e104610ca62..00ff1b9fd30 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
@@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import mage.MageException;
+import mage.cards.repository.CardRepository;
import mage.client.remote.XmageURLConnection;
import mage.client.util.CardLanguage;
import mage.util.JsonUtil;
@@ -485,6 +486,9 @@ public class ScryfallImageSource implements CardImageSource {
continue;
}
+ // I LOVE POTENTIAL SQL INJECTION!!!!!
+ CardRepository.instance.execSQL("UPDATE card SET oracleId = '"+ card.oracle_id +"' WHERE setCode = '"+ card.set +"' AND cardNumber = '"+ card.collector_number +"'");
+
// keep only usefully languages
// memory optimization: fewer items, from 470 MB to 96 MB
diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java
index e9b8a2aab24..622df93e12a 100644
--- a/Mage/src/main/java/mage/cards/repository/CardInfo.java
+++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java
@@ -40,6 +40,8 @@ public class CardInfo {
protected String setCode;
@DatabaseField(indexName = "setCode_cardNumber_index")
protected String cardNumber;
+ @DatabaseField(indexName = "oracleId_index", canBeNull = true)
+ protected String oracleId;
/**
* Fast access to numerical card number (number without prefix/postfix: 123b -> 123)
*/
diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java
index 9f6e39d6a75..d4742bbaef6 100644
--- a/Mage/src/main/java/mage/cards/repository/CardRepository.java
+++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java
@@ -38,7 +38,7 @@ public enum CardRepository {
// TODO: delete db version from cards and expansions due un-used (cause dbs re-created on each update now)
private static final String VERSION_ENTITY_NAME = "card";
- private static final long CARD_DB_VERSION = 54; // raise this if db structure was changed
+ private static final long CARD_DB_VERSION = 55; // raise this if db structure was changed
private static final long CARD_CONTENT_VERSION = 241; // raise this if new cards were added to the server
private Dao cardsDao;