diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index c5de22b0bfa..837034678da 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -1,5 +1,6 @@ package mage.client; +import mage.MageException; import mage.cards.action.ActionCallback; import mage.cards.decks.Deck; import mage.cards.repository.CardRepository; @@ -53,6 +54,7 @@ import net.java.truevfs.access.TConfig; import net.java.truevfs.kernel.spec.FsAccessOption; import org.apache.log4j.Logger; import org.mage.card.arcane.ManaSymbols; +import org.mage.card.arcane.SvgUtils; import org.mage.plugins.card.images.DownloadPicturesService; import org.mage.plugins.card.info.CardInfoPaneImpl; import org.mage.plugins.card.utils.CardImageUtils; @@ -81,6 +83,7 @@ import java.util.prefs.Preferences; public class MageFrame extends javax.swing.JFrame implements MageClient { private static final String TITLE_NAME = "XMage"; + private static final Logger logger = Logger.getLogger(MageFrame.class); private static final Logger LOGGER = Logger.getLogger(MageFrame.class); private static final String LITE_MODE_ARG = "-lite"; @@ -186,7 +189,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { /** * Creates new form MageFrame */ - public MageFrame() { + public MageFrame() throws MageException { setWindowTitle(); EDTExceptionHandler.registerExceptionHandler(); @@ -248,8 +251,12 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { if (RateCard.PRELOAD_CARD_RATINGS_ON_STARTUP) { RateCard.bootstrapCardsAndRatings(); } + SvgUtils.checkSvgSupport(); ManaSymbols.loadImages(); Plugins.instance.loadPlugins(); + if (!Plugins.instance.isCardPluginLoaded()) { + throw new MageException("can't load card plugin"); + } initComponents(); @@ -1232,7 +1239,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } else { SwingUtilities.invokeLater(() -> userRequestDialog.showDialog(userRequestMessage)); } - } public void showErrorDialog(final String title, final String message) { @@ -1241,7 +1247,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } else { SwingUtilities.invokeLater(() -> errorDialog.showDialog(title, message)); } - } public void showCollectionViewer() { @@ -1329,7 +1334,12 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { splash.update(); } } - instance = new MageFrame(); + try { + instance = new MageFrame(); + } catch (Throwable e) { + logger.fatal("Critical error on start up, app will be closed: " + e.getMessage(), e); + System.exit(1); + } // debug menu instance.separatorDebug.setVisible(debugMode); @@ -1348,7 +1358,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { instance.currentConnection.setPassword(startPassword); } instance.setVisible(true); - }); } diff --git a/Mage.Client/src/main/java/mage/client/MagePane.java b/Mage.Client/src/main/java/mage/client/MagePane.java index 819ee17bf6b..aa5a6f48c76 100644 --- a/Mage.Client/src/main/java/mage/client/MagePane.java +++ b/Mage.Client/src/main/java/mage/client/MagePane.java @@ -1,10 +1,3 @@ - - - /* - * MagePane.java - * - * Created on 15-Dec-2009, 9:34:25 PM - */ package mage.client; import java.awt.*; diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.java b/Mage.Client/src/main/java/mage/client/cards/BigCard.java index b6d395327be..11b092957b1 100644 --- a/Mage.Client/src/main/java/mage/client/cards/BigCard.java +++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.java @@ -1,11 +1,3 @@ - - -/* - * BigCard.java - * - * Created on Jan 18, 2010, 3:21:33 PM - */ - package mage.client.cards; import java.awt.Dimension; diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.form b/Mage.Client/src/main/java/mage/client/cards/Card.form deleted file mode 100644 index 9bfc6954424..00000000000 --- a/Mage.Client/src/main/java/mage/client/cards/Card.form +++ /dev/null @@ -1,70 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java deleted file mode 100644 index 05443a4d1ea..00000000000 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ /dev/null @@ -1,544 +0,0 @@ -package mage.client.cards; - -import mage.cards.CardDimensions; -import mage.cards.MagePermanent; -import mage.cards.Sets; -import mage.cards.TextPopup; -import mage.cards.action.ActionCallback; -import mage.client.MageFrame; -import mage.client.game.PlayAreaPanel; -import mage.client.util.ClientDefaultSettings; -import mage.client.util.DefaultActionCallback; -import mage.client.util.ImageHelper; -import mage.client.util.gui.ArrowBuilder; -import mage.constants.CardType; -import mage.constants.EnlargeMode; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.view.*; -import org.apache.log4j.Logger; - -import javax.swing.*; -import javax.swing.text.*; -import java.awt.*; -import java.awt.event.*; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import static mage.client.constants.Constants.*; - -/** - * @author BetaSteward_at_googlemail.com - */ -@SuppressWarnings("serial") -public class Card extends MagePermanent implements MouseMotionListener, MouseListener, FocusListener, ComponentListener { - - protected Point p; - protected final CardDimensions dimension; - - protected final UUID gameId; - protected final BigCard bigCard; - protected CardView card; - protected Popup tooltipPopup; - protected boolean tooltipShowing; - - protected final TextPopup tooltipText = new TextPopup(); - protected BufferedImage background; - protected final BufferedImage image = new BufferedImage(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT, BufferedImage.TYPE_INT_RGB); - protected final BufferedImage small; - protected String backgroundName; - - // if this is set, it's opened if the user right clicks on the card panel - private JPopupMenu popupMenu; - - /** - * Creates new form Card - * - * @param card - * @param bigCard - * @param dimension - * @param gameId - */ - public Card(CardView card, BigCard bigCard, CardDimensions dimension, UUID gameId) { - this.dimension = dimension; - initComponents(); - - this.gameId = gameId; - this.card = card; - this.bigCard = bigCard; - small = new BufferedImage(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight(), BufferedImage.TYPE_INT_RGB); - backgroundName = getBackgroundName(); - background = ImageHelper.getBackground(card, backgroundName); - - StyledDocument doc = text.getStyledDocument(); - Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE); - Style regular = doc.addStyle("regular", def); - StyleConstants.setFontFamily(def, "arial"); - Style s = doc.addStyle("small", regular); - StyleConstants.setFontSize(s, 9); - - //addMouseListener(this); - //text.addMouseListener(this); - //addFocusListener(this); - //addMouseMotionListener(this); - //text.addMouseMotionListener(this); - //addComponentListener(this); - } - - public UUID getCardId() { - return card.getId(); - } - - @Override - public void update(PermanentView permanent) { - this.update((CardView) permanent); - } - - @Override - public void update(CardView card) { - this.card = card; - Graphics2D gImage = image.createGraphics(); - Graphics2D gSmall = small.createGraphics(); - String cardType = getType(card); - String testBackgroundName = getBackgroundName(); - if (!testBackgroundName.equals(backgroundName)) { - backgroundName = testBackgroundName; - background = ImageHelper.getBackground(card, backgroundName); - } - - tooltipText.setText(getText(cardType)); - - gImage.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gImage.setColor(Color.BLACK); - gImage.drawImage(background, 0, 0, this); - - if (!card.getManaCost().isEmpty()) { - ImageHelper.drawCosts(card.getManaCost(), gImage, FRAME_MAX_WIDTH - SYMBOL_MAX_XOFFSET, SYMBOL_MAX_YOFFSET, this); - } - - gSmall.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gSmall.setColor(Color.BLACK); - gSmall.drawImage(ImageHelper.scaleImage(image, ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()), 0, 0, this); - - gImage.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE)); - gImage.drawString(card.getName() + "TEST", CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET); - if (card.isCreature()) { - gImage.drawString(card.getPower() + '/' + card.getToughness(), POWBOX_TEXT_MAX_LEFT, POWBOX_TEXT_MAX_TOP); - } else if (card.isPlanesWalker()) { - gImage.drawString(card.getLoyalty(), POWBOX_TEXT_MAX_LEFT, POWBOX_TEXT_MAX_TOP); - } - - if (!card.getCardTypes().isEmpty()) { - gImage.drawString(cardType, CONTENT_MAX_XOFFSET, TYPE_MAX_YOFFSET); - } - - gImage.dispose(); - - gSmall.setFont(new Font("Arial", Font.PLAIN, ClientDefaultSettings.dimensions.getNameFontSize())); - gSmall.drawString(card.getName() + "TEST2", ClientDefaultSettings.dimensions.getContentXOffset(), ClientDefaultSettings.dimensions.getNameYOffset()); - if (card.isCreature()) { - gSmall.drawString(card.getPower() + "/-/" + card.getToughness(), ClientDefaultSettings.dimensions.getPowBoxTextLeft(), ClientDefaultSettings.dimensions.getPowBoxTextTop()); - } else if (card.isPlanesWalker()) { - gSmall.drawString(card.getLoyalty(), ClientDefaultSettings.dimensions.getPowBoxTextLeft(), ClientDefaultSettings.dimensions.getPowBoxTextTop()); - } - - if (!card.getCardTypes().isEmpty()) { - gSmall.drawString(cardType, ClientDefaultSettings.dimensions.getContentXOffset(), ClientDefaultSettings.dimensions.getTypeYOffset()); - } - drawText(); - - gSmall.dispose(); - } - - @Override - public void updateArtImage() { - - } - - protected String getText(String cardType) { - StringBuilder sb = new StringBuilder(); - if (card instanceof StackAbilityView || card instanceof AbilityView) { - for (String rule : getRules()) { - sb.append('\n').append(rule); - } - } else { - sb.append(card.getName()); - if (!card.getManaCost().isEmpty()) { - sb.append('\n').append(card.getManaCost()); - } - sb.append('\n').append(cardType); - if (card.getColor().hasColor()) { - sb.append('\n').append(card.getColor().toString()); - } - if (card.isCreature()) { - sb.append('\n').append(card.getPower()).append('/').append(card.getToughness()); - } else if (card.isPlanesWalker()) { - sb.append('\n').append(card.getLoyalty()); - } - for (String rule : getRules()) { - sb.append('\n').append(rule); - } - if (card.getExpansionSetCode() != null && !card.getExpansionSetCode().isEmpty()) { - sb.append('\n').append(card.getCardNumber()).append(" - "); - sb.append(Sets.getInstance().get(card.getExpansionSetCode()).getName()).append(" - "); - sb.append(card.getRarity() == null ? "none" : card.getRarity().toString()); - } - } -// sb.append("\n").append(card.getId()); - return sb.toString(); - } - - private String getBackgroundName() { - if (card instanceof StackAbilityView || card instanceof AbilityView) { - return "effect"; - } - StringBuilder sb = new StringBuilder(); - if (card.isLand()) { - sb.append("land").append(card.getSuperTypes()).append(card.getSubTypes()); - } else if (card.getCardTypes() != null && (card.isCreature() || card.isPlanesWalker())) { - sb.append("creature"); - } - sb.append(card.getColor()).append(card.getRarity()).append(card.getExpansionSetCode()); - return sb.toString(); - } - - protected void drawText() { - text.setText(""); - StyledDocument doc = text.getStyledDocument(); - - try { - for (String rule : getRules()) { - doc.insertString(doc.getLength(), rule + '\n', doc.getStyle("small")); - } - } catch (BadLocationException e) { - } - - text.setCaretPosition(0); - } - - protected List getRules() { - if (card.getCounters() != null) { - List rules = new ArrayList<>(card.getRules()); - for (CounterView counter : card.getCounters()) { - rules.add(counter.getCount() + " x " + counter.getName()); - } - return rules; - } else { - return card.getRules(); - } - } - - protected String getType(CardView card) { - StringBuilder sbType = new StringBuilder(); - - for (SuperType superType : card.getSuperTypes()) { - sbType.append(superType).append(' '); - } - - for (CardType cardType : card.getCardTypes()) { - sbType.append(cardType.toString()).append(' '); - } - - if (!card.getSubTypes().isEmpty()) { - sbType.append("- "); - for (SubType subType : card.getSubTypes()) { - sbType.append(subType).append(' '); - } - } - - return sbType.toString(); - } - - protected void drawDetailed(Graphics2D g) { - // Get the size of the card - int width = getWidth(); - int height = getHeight(); - - g.setColor(Color.black); - g.drawRoundRect(0, 0, width, height, 4, 4); - g.setColor(Color.white); - g.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE)); - g.drawString(card.getName(), 0, 0); - Logger.getLogger(Card.class).info("Drawing"); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - text = new javax.swing.JTextPane(); - - setMinimumSize(getPreferredSize()); - setOpaque(false); - setPreferredSize(new Dimension(dimension.getFrameWidth(), dimension.getFrameHeight())); - setLayout(null); - - jScrollPane1.setBorder(null); - jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - jScrollPane1.setFocusable(false); - jScrollPane1.setOpaque(false); - - text.setBorder(null); - text.setEditable(false); - text.setFont(new java.awt.Font("Arial", 0, 9)); - text.setFocusable(false); - text.setOpaque(false); - jScrollPane1.setViewportView(text); - - add(jScrollPane1); - jScrollPane1.setBounds(20, 110, 130, 100); - jScrollPane1.setBounds(new Rectangle(dimension.getContentXOffset(), dimension.getTextYOffset(), dimension.getTextWidth(), dimension.getTextHeight())); - }// //GEN-END:initComponents - - @Override - public void paintComponent(Graphics graphics) { - drawDetailed((Graphics2D) graphics); - /* - Graphics2D g2 = (Graphics2D) graphics; - g2.drawImage(small, 0, 0, this); - - //Add a border, red if card currently has focus - if (isFocusOwner()) { - g2.setColor(Color.RED); - } else { - g2.setColor(Color.BLACK); - } - g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1); - */ - } - - @Override - public void mouseDragged(MouseEvent arg0) { - } - - @Override - public void mouseMoved(MouseEvent arg0) { - // this.bigCard.showTextComponent(); - this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules(), false); - } - - @Override - public void mouseClicked(MouseEvent e) { - } - - @Override - public void mousePressed(MouseEvent e) { - requestFocusInWindow(); - DefaultActionCallback.instance.mouseClicked(gameId, card); - } - - @Override - public void mouseReleased(MouseEvent arg0) { - } - - @Override - public void mouseEntered(MouseEvent arg0) { - if (!tooltipShowing) { - if (tooltipPopup != null) { - tooltipPopup.hide(); - } - PopupFactory factory = PopupFactory.getSharedInstance(); - tooltipPopup = factory.getPopup(this, tooltipText, (int) this.getLocationOnScreen().getX() + ClientDefaultSettings.dimensions.getFrameWidth(), (int) this.getLocationOnScreen().getY() + 40); - tooltipPopup.show(); - //hack to get tooltipPopup to resize to fit text - tooltipPopup.hide(); - tooltipPopup = factory.getPopup(this, tooltipText, (int) this.getLocationOnScreen().getX() + ClientDefaultSettings.dimensions.getFrameWidth(), (int) this.getLocationOnScreen().getY() + 40); - tooltipPopup.show(); - tooltipShowing = true; - - // Draw Arrows for targets - List targets = card.getTargets(); - if (targets != null) { - for (UUID uuid : targets) { - PlayAreaPanel playAreaPanel = MageFrame.getGamePlayers(gameId).get(uuid); - if (playAreaPanel != null) { - Point target = playAreaPanel.getLocationOnScreen(); - Point me = this.getLocationOnScreen(); - ArrowBuilder.getBuilder().addArrow(gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red, ArrowBuilder.Type.TARGET); - } else { - for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point target = permanent.getLocationOnScreen(); - Point me = this.getLocationOnScreen(); - ArrowBuilder.getBuilder().addArrow(gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.red, ArrowBuilder.Type.TARGET); - } - } - } - } - } - } - } - - @Override - public void mouseExited(MouseEvent arg0) { - if (getMousePosition(true) != null) { - return; - } - if (tooltipPopup != null) { - tooltipPopup.hide(); - tooltipShowing = false; - ArrowBuilder.getBuilder().removeArrowsByType(gameId, ArrowBuilder.Type.TARGET); - ArrowBuilder.getBuilder().removeArrowsByType(gameId, ArrowBuilder.Type.PAIRED); - ArrowBuilder.getBuilder().removeArrowsByType(gameId, ArrowBuilder.Type.BANDED); - ArrowBuilder.getBuilder().removeArrowsByType(gameId, ArrowBuilder.Type.SOURCE); - ArrowBuilder.getBuilder().removeArrowsByType(gameId, ArrowBuilder.Type.ENCHANT_PLAYERS); - } - } - - @Override - public void focusGained(FocusEvent arg0) { - this.repaint(); - } - - @Override - public void focusLost(FocusEvent arg0) { - if (tooltipPopup != null) { - tooltipPopup.hide(); - } - this.repaint(); - } - - protected JScrollPane getText() { - return jScrollPane1; - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane jScrollPane1; - protected javax.swing.JTextPane text; - // End of variables declaration//GEN-END:variables - - @Override - public void componentResized(ComponentEvent e) { - } - - @Override - public void componentMoved(ComponentEvent e) { - } - - @Override - public void componentShown(ComponentEvent e) { - } - - @Override - public void componentHidden(ComponentEvent e) { - if (tooltipPopup != null) { - tooltipPopup.hide(); - } - } - - @Override - public List getLinks() { - return null; - } - - @Override - public boolean isTapped() { - return false; - } - - @Override - public boolean isFlipped() { - return false; - } - - @Override - public void onBeginAnimation() { - } - - @Override - public void onEndAnimation() { - } - - @Override - public void setAlpha(float transparency) { - } - - @Override - public CardView getOriginal() { - return card; - } - - @Override - public void setCardBounds(int x, int y, int width, int height) { - // do nothing - } - - @Override - public Image getImage() { - return image; - } - - @Override - public void setZone(String zone) { - //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public String getZone() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public void updateCallback(ActionCallback callback, UUID gameId) { - //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public PermanentView getOriginalPermanent() { - return null; - } - - @Override - public float getAlpha() { - return 0; - } - - @Override - public void toggleTransformed() { - } - - @Override - public boolean isTransformed() { - return false; - } - - @Override - public void showCardTitle() { - } - - @Override - public void setSelected(boolean isSelected) { - } - - @Override - public void setCardAreaRef(JPanel cardArea) { - } - - @Override - public void setChoosable(boolean isChoosable) { - } - - @Override - public void setCardCaptionTopOffset(int yOffsetPercent) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public JPopupMenu getPopupMenu() { - return popupMenu; - } - - @Override - public void setPopupMenu(JPopupMenu popupMenu) { - this.popupMenu = popupMenu; - } -} diff --git a/Mage.Client/src/main/java/mage/client/cards/CardArea.java b/Mage.Client/src/main/java/mage/client/cards/CardArea.java index 565f5137276..203a397995f 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardArea.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardArea.java @@ -1,26 +1,35 @@ package mage.client.cards; +import mage.abilities.icon.CardIconOrder; +import mage.abilities.icon.CardIconPosition; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; -import mage.client.util.ClientEventType; import mage.client.util.Event; import mage.client.util.GUISizeHelper; import mage.client.util.Listener; -import mage.view.AbilityView; -import mage.view.CardView; -import mage.view.CardsView; -import mage.view.SimpleCardView; -import org.mage.card.arcane.CardPanel; +import mage.util.DebugUtil; +import mage.view.*; +import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.util.List; import java.util.UUID; -public class CardArea extends JPanel implements MouseListener { +/** + * Panel with cards list, can show cards in two modes: + * - cards one by one in the line; + * - stacked and multicolumns (if many cards); + * + * Uses in some dialogs like pile choose and cards choosing + */ +public class CardArea extends JPanel implements CardEventProducer { + + private static final Logger logger = Logger.getLogger(CardArea.class); protected final CardEventSource cardEventSource = new CardEventSource(); @@ -38,7 +47,9 @@ public class CardArea extends JPanel implements MouseListener { private Dimension customCardSize = null; // custom card size for tests private boolean customNeedFullPermanentRender = false; // disable permanent render mode, see CardArea for more info private int customXOffsetBetweenCardsOrColumns = 0; - private MouseListener customMouseListener = null; + private CardIconPosition customCardIconPosition = null; + private CardIconOrder customCardIconOrder = null; + private int customCardIconsMaxVisibleCount = 0; /** * Create the panel. @@ -51,12 +62,26 @@ public class CardArea extends JPanel implements MouseListener { setGUISize(); cardArea = new JLayeredPane(); scrollPane.setViewportView(cardArea); + if (DebugUtil.GUI_GAME_DIALOGS_DRAW_CARDS_AREA_BORDER) { + this.setBorder(BorderFactory.createLineBorder(Color.yellow)); + } + + // ENABLE non card popup menu + // if you want process popup menu from card then use special event + cardArea.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { + Plugins.instance.getActionCallback().popupMenuPanel(e, CardArea.this); + } + } + }); } public void cleanUp() { for (Component comp : cardArea.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); cardArea.remove(comp); } } @@ -65,7 +90,7 @@ public class CardArea extends JPanel implements MouseListener { public void changeGUISize() { setGUISize(); for (Component component : cardArea.getComponents()) { - if (component instanceof CardPanel) { + if (component instanceof MageCard) { component.setBounds(0, 0, cardDimension.width, cardDimension.height); } } @@ -93,6 +118,8 @@ public class CardArea extends JPanel implements MouseListener { newSize.width += 20; newSize.height += 20; this.setPreferredSize(newSize); + scrollPane.getHorizontalScrollBar().setUnitIncrement(GUISizeHelper.getCardsScrollbarUnitInc(cardDimension.width)); + scrollPane.getVerticalScrollBar().setUnitIncrement(GUISizeHelper.getCardsScrollbarUnitInc(cardDimension.height)); } public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { @@ -107,7 +134,6 @@ public class CardArea extends JPanel implements MouseListener { } redraw(); - fixDialogSize(); } @@ -157,10 +183,16 @@ public class CardArea extends JPanel implements MouseListener { tmp.setAbility(card); // cross-reference, required for ability picker card = tmp; } - MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, + + CardIconRenderSettings customIconsRender = new CardIconRenderSettings() + .withDebugMode(true) + .withCustomPosition(customCardIconPosition) + .withCustomOrder(customCardIconOrder) + .withCustomMaxVisibleCount(customCardIconsMaxVisibleCount) + .withCustomIconSizePercent(30); + MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, customIconsRender, cardDimension, gameId, true, true, customRenderMode != -1 ? customRenderMode : PreferencesDialog.getRenderMode(), customNeedFullPermanentRender); - cardPanel.setBounds(rectangle); - cardPanel.addMouseListener(customMouseListener != null ? customMouseListener : this); + cardPanel.setCardContainerRef(this); cardPanel.update(card); cardPanel.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); cardArea.add(cardPanel); @@ -238,59 +270,6 @@ public class CardArea extends JPanel implements MouseListener { cardEventSource.clearListeners(); } - @Override - public void mouseClicked(MouseEvent e) { - } - - @Override - public void mousePressed(MouseEvent e) { - if (e.getClickCount() >= 1 && !e.isConsumed()) { - Object obj = e.getSource(); - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks - e.consume(); - if (obj instanceof Card) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } else if (obj instanceof MageCard) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } - } - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } - } - - @Override - public void mouseReleased(MouseEvent e) { - if (!e.isConsumed()) { - Object obj = e.getSource(); - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } else { - cardEventSource.fireEvent(ClientEventType.ACTION_CONSUMED); - } - } - - private void checkMenu(MouseEvent Me, SimpleCardView card) { - if (Me.isPopupTrigger()) { - Me.consume(); - cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU); - } - } - public void setCustomRenderMode(int customRenderMode) { this.customRenderMode = customRenderMode; } @@ -307,16 +286,20 @@ public class CardArea extends JPanel implements MouseListener { this.customXOffsetBetweenCardsOrColumns = customXOffsetBetweenCardsOrColumns; } - public void setCustomMouseListener(MouseListener customMouseListener) { - this.customMouseListener = customMouseListener; + public void setCustomCardIconsPanelPosition(CardIconPosition panelPosition) { + this.customCardIconPosition = panelPosition; + } + + public void setCustomCardIconsPanelOrder(CardIconOrder panelOrder) { + this.customCardIconOrder = panelOrder; + } + + public void setCustomCardIconsMaxVisibleCount(int maxVisibleCount) { + this.customCardIconsMaxVisibleCount = maxVisibleCount; } @Override - public void mouseEntered(MouseEvent e) { + public CardEventSource getCardEventSource() { + return this.cardEventSource; } - - @Override - public void mouseExited(MouseEvent e) { - } - } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java b/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java index eab2e9ae548..66be3e2108b 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java @@ -1,40 +1,53 @@ package mage.client.cards; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.MagePane; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; +import mage.client.util.ClientDefaultSettings; +import mage.util.DebugUtil; import mage.view.CardView; +import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; +import java.awt.event.*; import java.util.ArrayList; /** - * Created by StravantUser on 2016-09-22. + * Popup panel for dragging cards drawing + * + * @author StravantUser, JayDi85 */ -public class CardDraggerGlassPane implements MouseListener, MouseMotionListener { +public class CardDraggerGlassPane { + + private static final Logger logger = Logger.getLogger(CardDraggerGlassPane.class); + private final DragCardSource source; - private Component dragComponent; - private JRootPane currentRoot; - private JComponent glassPane; + private DragCardTarget currentTarget; + + private MageCard draggingCard; // original card that starting the dragging (full dragging cards keeps in currentCards) private ArrayList currentCards; - private MageCard dragView; - private DragCardTarget currentDragTarget; + private JComponent draggingGlassPane; + private MageCard draggingDrawView; // fake card for drawing on glass pane + + // processing drag events (moving and the end) + private MouseListener draggingMouseListener; + private MouseMotionListener draggingMouseMotionListener; + private boolean isDragging; + private Dimension cardDimension; // This should not be strictly needed, but for some reason I can't figure out getDeepestComponentAt and // getComponentAt do not seem to work correctly for our setup if called on the root MageFrame. - private MagePane currentEventRootMagePane; + private MagePane eventRootPane; // example: deck editor pane public CardDraggerGlassPane(DragCardSource source) { this.source = source; } - public void beginDrag(Component c, MouseEvent e) { + public void handleDragStart(MageCard card, MouseEvent cardEvent) { // Start drag if (isDragging) { return; @@ -42,145 +55,153 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener isDragging = true; // Record what we are dragging on - dragComponent = c; - currentRoot = SwingUtilities.getRootPane(c); + draggingCard = card; - // Pane - glassPane = (JComponent) currentRoot.getGlassPane(); - glassPane.setLayout(null); - glassPane.setOpaque(false); - glassPane.setVisible(true); + // Pane for dragging drawing (fake card) + JRootPane currentRoot = SwingUtilities.getRootPane(draggingCard); + draggingGlassPane = (JComponent) currentRoot.getGlassPane(); + draggingGlassPane.setLayout(null); + draggingGlassPane.setOpaque(false); + draggingGlassPane.setVisible(true); + if (DebugUtil.GUI_DECK_EDITOR_DRAW_DRAGGING_PANE_BORDER) { + draggingGlassPane.setBorder(BorderFactory.createLineBorder(Color.MAGENTA)); + } // Get root mage pane to handle drag targeting in - Component rootMagePane = c; + Component rootMagePane = draggingCard; while (rootMagePane != null && !(rootMagePane instanceof MagePane)) { rootMagePane = rootMagePane.getParent(); } if (rootMagePane == null) { throw new RuntimeException("CardDraggerGlassPane::beginDrag not in a MagePane?"); } else { - currentEventRootMagePane = (MagePane) rootMagePane; + eventRootPane = (MagePane) rootMagePane; } - // Hook up events - c.addMouseListener(this); - c.addMouseMotionListener(this); + // ENABLE drag moving and drag ending processing + if (this.draggingMouseMotionListener == null) { + this.draggingMouseMotionListener = new MouseMotionAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + handleDragging(e); + } + }; + } + if (this.draggingMouseListener == null) { + this.draggingMouseListener = new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + handleDragEnd(e); + } + }; + } + draggingCard.addMouseMotionListener(this.draggingMouseMotionListener); + draggingCard.addMouseListener(this.draggingMouseListener); // Event to local space - e = SwingUtilities.convertMouseEvent(c, e, glassPane); + MouseEvent glassEvent = SwingUtilities.convertMouseEvent(draggingCard.getMainPanel(), cardEvent, draggingGlassPane); // Get the cards to drag currentCards = new ArrayList<>(source.dragCardList()); - // Make a view for the first one and add it to us - dragView = Plugins.instance.getMageCard(currentCards.get(0), null, new Dimension(100, 140), null, true, false, PreferencesDialog.getRenderMode(), true); - for (MouseListener l : dragView.getMouseListeners()) { - dragView.removeMouseListener(l); + // make a fake card to drawing (card's top left corner under the cursor) + Rectangle rectangle = new Rectangle(glassEvent.getX(), glassEvent.getY(), getCardDimension().width, getCardDimension().height); + draggingDrawView = Plugins.instance.getMageCard(currentCards.get(0), null, new CardIconRenderSettings(), getCardDimension(), null, true, false, PreferencesDialog.getRenderMode(), true); + draggingDrawView.setCardContainerRef(null); // no feedback events + draggingDrawView.update(currentCards.get(0)); + draggingDrawView.setCardBounds(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + + // disable mouse events from fake card + for (MouseListener l : draggingDrawView.getMouseListeners()) { + draggingDrawView.removeMouseListener(l); } - for (MouseMotionListener l : dragView.getMouseMotionListeners()) { - dragView.removeMouseMotionListener(l); + for (MouseMotionListener l : draggingDrawView.getMouseMotionListeners()) { + draggingDrawView.removeMouseMotionListener(l); } - dragView.setLocation(e.getX(), e.getY()); - glassPane.add(dragView); + draggingGlassPane.add(draggingDrawView); // Notify the sounce source.dragCardBegin(); // Update the target - currentDragTarget = null; - updateCurrentTarget(SwingUtilities.convertMouseEvent(glassPane, e, currentEventRootMagePane), false); + currentTarget = null; + MouseEvent rootEvent = SwingUtilities.convertMouseEvent(draggingGlassPane, glassEvent, eventRootPane); + updateCurrentTarget(rootEvent, false); } - // e is relative to currentRoot - private void updateCurrentTarget(MouseEvent e, boolean isEnding) { - Component mouseOver = SwingUtilities.getDeepestComponentAt(currentEventRootMagePane, e.getX(), e.getY()); + private void handleDragging(MouseEvent e) { + // redraw fake card near the cursor + MouseEvent glassEvent = SwingUtilities.convertMouseEvent(draggingCard.getMainPanel(), e, draggingGlassPane); + draggingDrawView.setCardLocation(glassEvent.getX(), glassEvent.getY()); + draggingDrawView.repaint(); + + // Convert the event into root coords and update target + MouseEvent rootEvent = SwingUtilities.convertMouseEvent(draggingCard.getMainPanel(), e, eventRootPane); + updateCurrentTarget(rootEvent, false); + } + + private void handleDragEnd(MouseEvent e) { + // No longer dragging + isDragging = false; + + // Remove custom listeners + draggingCard.removeMouseListener(this.draggingMouseListener); + draggingCard.removeMouseMotionListener(this.draggingMouseMotionListener); + + // Convert the event into root coords + MouseEvent rootEvent = SwingUtilities.convertMouseEvent(draggingCard.getMainPanel(), e, eventRootPane); + + // Remove the drag card + draggingGlassPane.remove(draggingDrawView); + draggingGlassPane.repaint(); + + // Let the drag source know + source.dragCardEnd(currentTarget); + + // Update the target, and do the drop + updateCurrentTarget(rootEvent, true); + } + + private void updateCurrentTarget(MouseEvent rootEvent, boolean isEnding) { + // event related to eventRootPane + Component mouseOver = SwingUtilities.getDeepestComponentAt(eventRootPane, rootEvent.getX(), rootEvent.getY()); while (mouseOver != null) { if (mouseOver instanceof DragCardTarget) { DragCardTarget target = (DragCardTarget) mouseOver; - MouseEvent targetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, mouseOver); - if (target != currentDragTarget) { - if (currentDragTarget != null) { - MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, (Component) currentDragTarget); - currentDragTarget.dragCardExit(oldTargetEvent); + MouseEvent targetEvent = SwingUtilities.convertMouseEvent(eventRootPane, rootEvent, mouseOver); + if (target != currentTarget) { + if (currentTarget != null) { + MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(eventRootPane, rootEvent, (Component) currentTarget); + currentTarget.dragCardExit(oldTargetEvent); } - currentDragTarget = target; - currentDragTarget.dragCardEnter(targetEvent); + currentTarget = target; + currentTarget.dragCardEnter(targetEvent); } if (isEnding) { - currentDragTarget.dragCardExit(targetEvent); - currentDragTarget.dragCardDrop(targetEvent, source, currentCards); + currentTarget.dragCardExit(targetEvent); + currentTarget.dragCardDrop(targetEvent, source, currentCards); } else { - currentDragTarget.dragCardMove(targetEvent); + currentTarget.dragCardMove(targetEvent); } return; } mouseOver = mouseOver.getParent(); } - if (currentDragTarget != null) { - MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, (Component) currentDragTarget); - currentDragTarget.dragCardExit(oldTargetEvent); + if (currentTarget != null) { + MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(eventRootPane, rootEvent, (Component) currentTarget); + currentTarget.dragCardExit(oldTargetEvent); } - currentDragTarget = null; + currentTarget = null; + } + + protected Dimension getCardDimension() { + if (cardDimension == null) { + cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); + } + return cardDimension; } public boolean isDragging() { return isDragging; } - - /** - * Mouse released -> we are done the drag - */ - @Override - public void mouseReleased(MouseEvent e) { - // No longer dragging - isDragging = false; - - // Remove listeners - dragComponent.removeMouseListener(this); - dragComponent.removeMouseMotionListener(this); - - // Convert the event into root coords - e = SwingUtilities.convertMouseEvent(dragComponent, e, currentEventRootMagePane); - - // Remove the drag card - glassPane.remove(dragView); - glassPane.repaint(); - - // Let the drag source know - source.dragCardEnd(currentDragTarget); - - // Update the target, and do the drop - updateCurrentTarget(e, true); - } - - @Override - public void mouseDragged(MouseEvent e) { - // Update the view - MouseEvent glassE = SwingUtilities.convertMouseEvent(dragComponent, e, glassPane); - dragView.setLocation(glassE.getX(), glassE.getY()); - dragView.repaint(); - // Convert the event into root coords and update target - e = SwingUtilities.convertMouseEvent(dragComponent, e, currentEventRootMagePane); - updateCurrentTarget(e, false); - } - - @Override - public void mouseClicked(MouseEvent e) { - } - - @Override - public void mousePressed(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - } } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardEventProducer.java b/Mage.Client/src/main/java/mage/client/cards/CardEventProducer.java new file mode 100644 index 00000000000..02b10f006ed --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/cards/CardEventProducer.java @@ -0,0 +1,12 @@ +package mage.client.cards; + +/** + * Add it to components that will be generates card's events (example: panel with cards list) + * + * @author JayDi85 + */ +public interface CardEventProducer { + + CardEventSource getCardEventSource(); + +} diff --git a/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java b/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java index 4634d3ca6fc..29debb01369 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java @@ -1,16 +1,15 @@ - package mage.client.cards; -import mage.client.util.*; import mage.client.util.Event; +import mage.client.util.*; import mage.view.SimpleCardView; import java.awt.*; +import java.awt.event.MouseEvent; import java.io.Serializable; /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class CardEventSource implements EventSource, Serializable { @@ -22,25 +21,41 @@ public class CardEventSource implements EventSource, Serializable { dispatcher.addListener(listener); } - public void fireEvent(SimpleCardView card, ClientEventType eventType, int number){ - dispatcher.fireEvent(new Event(card, eventType, number)); - } - - public void fireEvent(ClientEventType eventType){ - dispatcher.fireEvent(new Event(null, eventType)); - } - - public void fireEvent(SimpleCardView card, ClientEventType eventType){ - dispatcher.fireEvent(new Event(card, eventType)); - } - - public void fireEvent(SimpleCardView card, Component component, int x, int y, ClientEventType message) { - dispatcher.fireEvent(new Event(card, message, x, y, component)); - } - @Override public void clearListeners() { dispatcher.clearListeners(); } + public void fireEvent(SimpleCardView card, ClientEventType eventType, int number) { + fireEvent(new Event(card, eventType, number)); + } + + public void fireEvent(ClientEventType eventType) { + fireEvent(new Event(null, eventType)); + } + + public void fireEvent(SimpleCardView card, ClientEventType eventType) { + fireEvent(new Event(card, eventType)); + } + + public void fireEvent(SimpleCardView card, Component component, int x, int y, ClientEventType message) { + fireEvent(new Event(card, message, 0, x, y, component)); + } + + public void fireEvent(SimpleCardView card, ClientEventType eventType, MouseEvent e, boolean forceFakeAltDown) { + Event event; + if (e != null) { + // normal mouse event + event = new Event(card, eventType, 0, e.getX(), e.getY(), e.getComponent(), e, forceFakeAltDown); + } else { + // fake mouse event + event = new Event(card, eventType, 0, 0, 0, null, null, forceFakeAltDown); + } + fireEvent(event); + } + + public void fireEvent(Event clientEvent) { + dispatcher.fireEvent(clientEvent); + } + } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java index 0c6fd69d8af..d237f83a716 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java @@ -1,43 +1,40 @@ - - - /* - * CardGrid.java - * - * Created on 30-Mar-2010, 9:25:40 PM - */ package mage.client.cards; + import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.deckeditor.SortSetting; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; - import mage.client.util.ClientEventType; import mage.client.util.Event; import mage.client.util.GUISizeHelper; import mage.client.util.Listener; + import mage.client.util.comparators.*; import mage.constants.Rarity; - import mage.utils.CardColorUtil; import mage.view.CardView; import mage.view.CardsView; - import org.mage.card.arcane.CardPanel; + import javax.swing.*; import java.awt.*; - import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.List; import java.util.*; import java.util.Map.Entry; + import java.util.stream.Collectors; /** - * @author BetaSteward_at_googlemail.com + * Deck editor: grid mode for selected cards list (NOT a drafting, only for free edition and sideboarding) + * TODO: combine source code with CardsList.java + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ - public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ICardGrid { + public class CardGrid extends javax.swing.JLayeredPane implements CardEventProducer, /* MouseListener,*/ ICardGrid { protected final CardEventSource cardEventSource = new CardEventSource(); protected BigCard bigCard; protected UUID gameId; private final Map cards = new HashMap<>(); private Dimension cardDimension; + private final List countLabels = new ArrayList<>(); /** * Max amount of cards in card grid for which card images will be drawn. @@ -102,101 +99,94 @@ } private void addCard(CardView card, BigCard bigCard, UUID gameId, boolean drawImage) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, drawImage, true, PreferencesDialog.getRenderMode(), true); - cards.put(card.getId(), cardImg); - cardImg.addMouseListener(this); - add(cardImg); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), cardDimension, gameId, drawImage, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(this); cardImg.update(card); + // card position calculated on parent call by drawCards + //cardImg.setCardBounds(rectangle.x, rectangle.y, rectangle.width, rectangle.height); cards.put(card.getId(), cardImg); + this.add(cardImg, (Integer) 10); // count label must be on layer 0 for background drawing } @Override public void drawCards(SortSetting sortSetting) { + this.countLabels.forEach(this::remove); + this.countLabels.clear(); + int maxWidth = this.getParent().getWidth(); - int cardVerticalOffset = GUISizeHelper.editorCardOffsetSize; + int vertOffsetPerCardInStack = GUISizeHelper.editorCardVertOffsetInStack; int numColumns = maxWidth / cardDimension.width; int curColumn = 0; int curRow = 0; if (!cards.isEmpty()) { Rectangle rectangle = new Rectangle(cardDimension.width, cardDimension.height); - List sortedCards = new ArrayList<>(cards.values()); + + List sortedCards = cards.values().stream().map(MageCard::getOriginal).collect(Collectors.toList()); + CardViewComparator comparator; switch (sortSetting.getSortBy()) { case NAME: - sortedCards.sort(new CardNameComparator()); + comparator = new CardViewNameComparator(); break; case CARD_TYPE: - sortedCards.sort(new CardTypeComparator()); + comparator = new CardViewCardTypeComparator(); break; case RARITY: - sortedCards.sort(new CardRarityComparator()); + comparator = new CardViewRarityComparator(); break; case COLOR: - sortedCards.sort(new CardColorComparator()); + comparator = new CardViewColorComparator(); break; case COLOR_IDENTITY: - sortedCards.sort(new CardColorDetailedIdentity()); + comparator = new CardViewColorIdentityComparator(); break; case CASTING_COST: - sortedCards.sort(new CardCostComparator()); + comparator = new CardViewCostComparator(); break; - + case UNSORTED: + comparator = new CardViewNoneComparator(); + break; + case EDH_POWER_LEVEL: + comparator = new CardViewEDHPowerLevelComparator(); + break; + default: + throw new IllegalArgumentException("Error, unknown sort settings in deck editor: " + sortSetting.getSortBy()); } + + sortedCards.sort(new CardViewNameComparator()); + sortedCards.sort(comparator); + MageCard lastCard = null; - for (MageCard cardImg : sortedCards) { + JLabel lastCountLabel = null; + for (CardView sortedCard : sortedCards) { + MageCard currentCard = this.cards.get(sortedCard.getId()); if (sortSetting.isPilesToggle()) { if (lastCard == null) { - lastCard = cardImg; + lastCard = currentCard; + // new new count label + lastCountLabel = addNewCountLabel(curColumn); } - switch (sortSetting.getSortBy()) { - case NAME: - if (!cardImg.getOriginal().getName().equals(lastCard.getOriginal().getName())) { - curColumn++; - curRow = 0; - } - break; - case CARD_TYPE: - if (!cardImg.getOriginal().getCardTypes().equals(lastCard.getOriginal().getCardTypes())) { - curColumn++; - curRow = 0; - } - break; - case RARITY: - if (cardImg.getOriginal().getRarity() != lastCard.getOriginal().getRarity()) { - curColumn++; - curRow = 0; - } - break; - case COLOR: - if (cardImg.getOriginal().getColor().compareTo(lastCard.getOriginal().getColor()) != 0) { - curColumn++; - curRow = 0; - } - break; - case COLOR_IDENTITY: - if (CardColorUtil.getColorIdentitySortValue(cardImg.getOriginal().getManaCost(), cardImg.getOriginal().getColor(), cardImg.getOriginal().getRules()) - != CardColorUtil.getColorIdentitySortValue(lastCard.getOriginal().getManaCost(), lastCard.getOriginal().getColor(), lastCard.getOriginal().getRules())) { - curColumn++; - curRow = 0; - } - break; - case CASTING_COST: - if (cardImg.getOriginal().getConvertedManaCost() != lastCard.getOriginal().getConvertedManaCost()) { - curColumn++; - curRow = 0; - } - break; + + // create new column on different card sorting + if (comparator.compare(currentCard.getOriginal(), lastCard.getOriginal()) != 0) { + curColumn++; + curRow = 0; + // add new count label + lastCountLabel = addNewCountLabel(curColumn); } - rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); - cardImg.setBounds(rectangle); - cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - moveToFront(cardImg); + + // update last count label stats + String description = comparator.getCategoryName(currentCard.getOriginal()); + DragCardGrid.updateCountLabel(lastCountLabel, curRow + 1, description); + + rectangle.setLocation(curColumn * cardDimension.width, curRow * vertOffsetPerCardInStack + DragCardGrid.COUNT_LABEL_HEIGHT); + currentCard.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); + moveToFront(currentCard); curRow++; - lastCard = cardImg; + lastCard = currentCard; } else { - rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); - cardImg.setBounds(rectangle); - cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - moveToFront(cardImg); + rectangle.setLocation(curColumn * cardDimension.width, curRow * vertOffsetPerCardInStack); + currentCard.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); + moveToFront(currentCard); curColumn++; if (curColumn == numColumns) { curColumn = 0; @@ -210,12 +200,20 @@ repaint(); } + private JLabel addNewCountLabel(int columnNumber) { + JLabel label = DragCardGrid.createCountLabel(null); + this.countLabels.add(label); + this.add(label, (Integer) 0); // draw on background + label.setLocation(columnNumber * cardDimension.width, 5); + label.setSize(cardDimension.width, DragCardGrid.COUNT_LABEL_HEIGHT); + label.setVisible(true); + return label; + } + private void clearCards() { // remove possible mouse listeners, preventing gc for (MageCard mageCard : cards.values()) { - if (mageCard instanceof CardPanel) { - ((CardPanel) mageCard).cleanUp(); - } + mageCard.cleanUp(); } this.cards.clear(); removeAllCardImg(); @@ -223,7 +221,7 @@ private void removeAllCardImg() { for (Component comp : getComponents()) { - if (comp instanceof Card || comp instanceof MageCard) { + if (comp instanceof MageCard) { remove(comp); } } @@ -231,15 +229,9 @@ private void removeCardImg(UUID cardId) { for (Component comp : getComponents()) { - if (comp instanceof Card) { - if (((Card) comp).getCardId().equals(cardId)) { - remove(comp); - comp = null; - } - } else if (comp instanceof MageCard) { + if (comp instanceof MageCard) { if (((MageCard) comp).getOriginal().getId().equals(cardId)) { remove(comp); - comp = null; } } } @@ -283,41 +275,11 @@ // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables - @Override - public void mouseClicked(MouseEvent e) { - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks - e.consume(); - Object obj = e.getSource(); - if (obj instanceof Card) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } else if (obj instanceof MageCard) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } - } - } + @Override - public void mousePressed(MouseEvent e) { - } - - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { + public CardEventSource getCardEventSource() { + return this.cardEventSource; } private void resizeArea() { @@ -350,7 +312,14 @@ } } - class CardNameComparator implements Comparator { + /** + * Workaround to use CardViewComparator with card panels + */ + interface CardPanelComparator extends Comparator { + String getCategoryName(MageCard sample); + } + + class CardPanelNameComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { @@ -359,7 +328,7 @@ } - class CardRarityComparator implements Comparator { + class CardPanelRarityComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { @@ -380,7 +349,7 @@ } - class CardCostComparator implements Comparator { + class CardPanelCostComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { @@ -394,7 +363,7 @@ } - class CardColorComparator implements Comparator { + class CardPanelColorComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { @@ -408,22 +377,20 @@ } - class CardColorDetailedIdentity implements Comparator { + class CardPanelColorIdentityComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { - int val = CardColorUtil.getColorIdentitySortValue(o1.getOriginal().getManaCost(), o1.getOriginal().getColor(), o1.getOriginal().getRules()) - - CardColorUtil.getColorIdentitySortValue(o2.getOriginal().getManaCost(), o2.getOriginal().getColor(), o2.getOriginal().getRules()); + int val = CardViewColorIdentityComparator.calcHash(o1.getOriginal()) - CardViewColorIdentityComparator.calcHash(o2.getOriginal()); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); } else { return val; } } - } - class CardTypeComparator implements Comparator { + class CardPanelTypeComparator implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { diff --git a/Mage.Client/src/main/java/mage/client/cards/CardIconsPanel.java b/Mage.Client/src/main/java/mage/client/cards/CardIconsPanel.java new file mode 100644 index 00000000000..0c339f06c8e --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/cards/CardIconsPanel.java @@ -0,0 +1,371 @@ +package mage.client.cards; + +import mage.abilities.icon.*; +import mage.abilities.icon.system.CombinedCountIcon; +import mage.client.components.StretchIcon; +import mage.client.dialog.PreferencesDialog; +import mage.client.util.GUISizeHelper; +import mage.util.DebugUtil; +import org.mage.card.arcane.CardRendererUtils; +import org.mage.card.arcane.ManaSymbols; +import org.mage.card.arcane.SvgUtils; +import org.mage.plugins.card.images.ImageCache; +import org.mage.plugins.card.utils.impl.ImageManagerImpl; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.*; +import java.util.stream.Collectors; + +/** + * GUI panel to drawning icons (one of the card's side) + * + * @author JayDi85 + */ +public class CardIconsPanel extends JPanel { + + private static final CardIconPosition DEFAULT_POSITION = CardIconPosition.LEFT; + private static final CardIconOrder DEFAULT_ORDER = CardIconOrder.START; + private static final int DEFAULT_MAX_VISIBLE_COUNT = 5; + private static final int DEFAULT_ICON_SIZE_PERCENT = 30; + + private static final int MINIMUM_ICON_SIZE = 32; // TODO: not working? + private static final int KEEP_ICON_IN_CARD_INSIDE_PERCENT = 70; // example: 66% - 2/3 keep inside and 1/3 keep outside + private static final int MAXIMUM_CARD_WIDTH_FOR_ICONS_SMALL_MODE = 100; // enable icons small mode for too small cards (combine ability icons to one); + + private final CardIconPosition position; + private final CardIconOrder order; + private final int iconSizePercent; // icons size, related to card's width + private final List icons; + private final int cellsMaxCount; // split card side to cells, can be 1, 3, 5, 7 (x left + 1x center + x right) + private final int cellsVisibleCount; // if it contains too much elements then replace it by combined element (example: cells x7, visible x3) + private final int cellsOffset; // how many cells must be offset from the start and the end. Example: 0 - nothing, 1 - 1x from left and 1x from right + private int iconsGap = 3; // gap between icons in the cells (aplies from left and right sides) + private int halfSize = 0; // offset for icons from the card's border + private Font font = null; + + // auto-calced for small mode, see calcSizes + private int calcedCellsMaxCount = 1; + private int calcedCellsVisibleCount = 1; + private int calcedSizeSizePercent = 30; + private int calcedCellsOffset = 1; + private CardIconPosition calcedPosition; + private CardIconOrder calcedOrder; + + public CardIconsPanel(CardIconRenderSettings render) { + this(render.getCustomPosition() != null ? render.getCustomPosition() : DEFAULT_POSITION, + render.getCustomOrder() != null ? render.getCustomOrder() : DEFAULT_ORDER, + render.getCustomMaxVisibleCount() > 0 ? render.getCustomMaxVisibleCount() : DEFAULT_MAX_VISIBLE_COUNT, + render.getCustomIconSizePercent() > 0 ? render.getCustomIconSizePercent() : DEFAULT_ICON_SIZE_PERCENT + ); + } + + public CardIconsPanel(CardIconPosition position, CardIconOrder order, int cellsVisibleCount, int iconSizePercent) { + this(position, order, cellsVisibleCount, iconSizePercent, new ArrayList<>(), new Rectangle(100, 100)); + } + + public CardIconsPanel(CardIconPosition position, CardIconOrder order, int cellsVisibleCount, int iconSizePercent, List icons, Rectangle startingCardSize) { + super(null); + this.position = position != null ? position : DEFAULT_POSITION; + this.iconSizePercent = iconSizePercent; + this.icons = icons; + + // corners have only one icon with center order + if (this.position.getMaxIconsAmount() == 1) { + this.order = CardIconOrder.START; + this.cellsOffset = 0; + this.cellsMaxCount = 1; + } else { + this.order = order != null ? order : DEFAULT_ORDER; + this.cellsOffset = 1; + this.cellsMaxCount = 7; + } + int maxIcons = Math.max(1, Math.min(this.cellsMaxCount, cellsVisibleCount)); // must be in [1..cells]; + this.cellsVisibleCount = Math.min(maxIcons, this.position.getMaxIconsAmount()); + + this.setVisible(false); + this.setOpaque(false); + if (DebugUtil.GUI_CARD_ICONS_DRAW_PANEL_BORDER) { + this.setBorder(BorderFactory.createLineBorder(Color.red)); + } + + this.updateSizes(startingCardSize); + } + + public void updateSizes(Rectangle cardSize) { + this.calcSizes(cardSize); + + // panel uses GridLayout with gaps, grid is static size, so the sizes structure: + // [gap + icon + gap + icon + ... gap] + + // corner icons must be same sizes as max possible on left/right (for a more beautiful look) + int panelFullSize = this.halfSize * 2 + cardSize.height; + int panelIconSize = (panelFullSize - (7 + 1) * this.iconsGap) / 7; + int cornerHalfSize = Math.min(panelIconSize, this.halfSize * 2) / 2; // real icons can be limited by height or width + + // move panel to the inner (for a more beautiful look) + // 2/3 keep inside and 1/3 keep outside + // panels already centered by halfSize, so use "- this.halfSize" + int panelOffset = Math.round(this.halfSize * 2 * KEEP_ICON_IN_CARD_INSIDE_PERCENT / 100f) - this.halfSize; + + Rectangle panelRect; + Point panelTranslate; + switch (this.calcedPosition) { + case TOP: + panelRect = new Rectangle(cardSize.x - this.halfSize, cardSize.y - this.halfSize, cardSize.width + this.halfSize * 2, this.halfSize * 2); + panelTranslate = new Point(0, panelOffset); + this.setLayout(new GridLayout(1, this.calcedCellsMaxCount, iconsGap, 0)); + break; + case LEFT: + panelRect = new Rectangle(cardSize.x - this.halfSize, cardSize.y - this.halfSize, this.halfSize * 2, cardSize.height + this.halfSize * 2); + panelTranslate = new Point(panelOffset, 0); + this.setLayout(new GridLayout(this.calcedCellsMaxCount, 1, 0, iconsGap)); + break; + case RIGHT: + panelRect = new Rectangle(cardSize.x + cardSize.width - this.halfSize, cardSize.y - this.halfSize, this.halfSize * 2, cardSize.height + this.halfSize * 2); + panelTranslate = new Point(-panelOffset, 0); + this.setLayout(new GridLayout(this.calcedCellsMaxCount, 1, 0, iconsGap)); + break; + case BOTTOM: + panelRect = new Rectangle(cardSize.x - this.halfSize, cardSize.y + cardSize.height - this.halfSize, cardSize.width + this.halfSize * 2, this.halfSize * 2); + panelTranslate = new Point(0, -panelOffset); + this.setLayout(new GridLayout(1, this.calcedCellsMaxCount, iconsGap, 0)); + break; + case CORNER_TOP_LEFT: + panelRect = new Rectangle(cardSize.x - cornerHalfSize, cardSize.y - cornerHalfSize, cornerHalfSize * 2, cornerHalfSize * 2); + panelTranslate = new Point(panelOffset, panelOffset); + this.setLayout(new GridLayout(1, 1, 0, 0)); + break; + case CORNER_TOP_RIGHT: + panelRect = new Rectangle(cardSize.x + cardSize.width - cornerHalfSize, cardSize.y - cornerHalfSize, cornerHalfSize * 2, cornerHalfSize * 2); + panelTranslate = new Point(-panelOffset, panelOffset); + this.setLayout(new GridLayout(1, 1, 0, 0)); + break; + case CORNER_BOTTOM_LEFT: + panelRect = new Rectangle(cardSize.x - cornerHalfSize, cardSize.y + cardSize.height - cornerHalfSize, cornerHalfSize * 2, cornerHalfSize * 2); + panelTranslate = new Point(panelOffset, -panelOffset); + this.setLayout(new GridLayout(1, 1, 0, 0)); + break; + case CORNER_BOTTOM_RIGHT: + panelRect = new Rectangle(cardSize.x + cardSize.width - cornerHalfSize, cardSize.y + cardSize.height - cornerHalfSize, cornerHalfSize * 2, cornerHalfSize * 2); + panelTranslate = new Point(-panelOffset, -panelOffset); + this.setLayout(new GridLayout(1, 1, 0, 0)); + break; + default: + throw new IllegalArgumentException("Card icons do not support position " + this.calcedPosition); + } + panelRect.translate(panelTranslate.x, panelTranslate.y); + this.setBounds(panelRect); + + // reload icons for new size + this.updateIcons(); + } + + public void updateIcons() { + updateIcons(null); + } + + public void updateIcons(List newIcons) { + this.removeAll(); + if (newIcons != null) { + this.icons.clear(); + this.icons.addAll(newIcons); + } + + // auto-hide panel on empty icons + if (this.icons.isEmpty()) { + this.setVisible(false); + return; + } else { + this.setVisible(true); + } + + int actualMaxVisibleCount = Math.min(this.calcedCellsVisibleCount, this.calcedCellsMaxCount - this.calcedCellsOffset * 2); // preserve offset cells + + List visibleComponents = new ArrayList<>(); + List combinedComponents = new ArrayList<>(); + List orderedComponents = new ArrayList<>(); + + // structure: + // * icons panel - control the icons size and offsets in card; + // * grid panel - control the icons order and position (put empty panel for empty space) + // * grid's cell - control one icon + // * label - stretched icon image that occupy all cell's space + Map cardLinks = new HashMap<>(); + this.icons.stream() + .sorted(CardIconComparator.instance) + .forEach(icon -> { + Component iconComponent = createIconComponent(icon); + if (iconComponent != null) { + visibleComponents.add(iconComponent); + cardLinks.put(iconComponent, icon); + } + }); + + // OPTIMIZE visible components (if card contains too much icons then combine it in one "...") + if (visibleComponents.size() > actualMaxVisibleCount) { + while (visibleComponents.size() > actualMaxVisibleCount - 1) { + // combined must contains minimum 2 elements + combinedComponents.add(visibleComponents.remove(visibleComponents.size() - 1)); + } + String combinedHint = combinedComponents + .stream() + .map(cardLinks::get) + .filter(Objects::nonNull) + .sorted(CardIconComparator.instance) + .map(CardIcon::getCombinedInfo) + .collect(Collectors.joining("
")); + CardIcon combinedIcon = new CombinedCountIcon(combinedComponents.size(), combinedHint); + Component combinedComponent = createIconComponent(combinedIcon); + if (combinedComponent != null) { + visibleComponents.add(combinedComponent); + } + } + + // add offsets to the start of the list + if (this.calcedOrder == CardIconOrder.START || this.calcedOrder == CardIconOrder.END) { + for (int i = 0; i < this.calcedCellsOffset; i++) { + JPanel panel = new JPanel(null); + panel.setOpaque(false); + visibleComponents.add(0, panel); + } + } + + // fill components list to max (grid can't put elements to cells, so must fill all) + while (visibleComponents.size() < this.calcedCellsMaxCount) { + JPanel panel = new JPanel(null); + panel.setOpaque(false); + visibleComponents.add(panel); + } + + // ORDER visible components + // icons sort order example with CENTER order: + // 1: [1] + // 3: [2 1 3] + // 5: [4 2 1 3 5] + // 7: [6 4 2 1 3 5 7] + // + // icons sort order example with START order (END order is same but reversed): + // 1: [1] + // 3: [1 2 3] + // 5: [1 2 3 4 5] + // 7: [1 2 3 4 5 6 7] + if (this.calcedOrder == CardIconOrder.CENTER) { + // CENTER + if (this.calcedCellsMaxCount == 1) { + Arrays.asList(1).forEach(i -> orderedComponents.add(visibleComponents.get(i - 1))); + } else if (this.calcedCellsMaxCount == 3) { + Arrays.asList(2, 1, 3).forEach(i -> orderedComponents.add(visibleComponents.get(i - 1))); + } else if (this.calcedCellsMaxCount == 5) { + Arrays.asList(4, 2, 1, 3, 5).forEach(i -> orderedComponents.add(visibleComponents.get(i - 1))); + } else if (this.calcedCellsMaxCount == 7) { + Arrays.asList(6, 4, 2, 1, 3, 5, 7).forEach(i -> orderedComponents.add(visibleComponents.get(i - 1))); + } else { + throw new IllegalArgumentException("Card icons do not support max size as " + this.calcedCellsMaxCount); + } + } else if (this.calcedOrder == CardIconOrder.START) { + // START + orderedComponents.addAll(visibleComponents); + } else if (this.calcedOrder == CardIconOrder.END) { + // END + orderedComponents.addAll(visibleComponents); + Collections.reverse(orderedComponents); + } else { + throw new IllegalArgumentException("Card icons do not support order type " + this.calcedOrder); + } + + // ADD real components to the grid + orderedComponents.forEach(this::add); + } + + private Component createIconComponent(CardIcon icon) { + if (!SvgUtils.haveSvgSupport()) { + return null; + } + + // direct call + //BufferedImage iconImage = ImageManagerImpl.instance.getCardIcon(icon.getIconType().getResourceName(), this.halfSize * 2); + + // cached call + BufferedImage iconImageCached = ImageCache.getCardIconImage(icon.getIconType().getResourceName(), this.halfSize * 2); + + if (iconImageCached != null && this.font != null) { + BufferedImage iconImageWithText = ImageManagerImpl.deepCopy(iconImageCached); // must copy cached value before modify + + // text + JLabel label = new JLabel(); + label.setToolTipText("" + ManaSymbols.replaceSymbolsWithHTML(icon.getHint(), ManaSymbols.Type.CARD_ICON_HINT)); + if (!icon.getText().isEmpty()) { + Graphics2D g2d = iconImageWithText.createGraphics(); + g2d.setColor(PreferencesDialog.getCurrentTheme().getCardIconsTextColor()); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Rectangle rect = CardRendererUtils.reduceRect(new Rectangle(0, 0, iconImageWithText.getWidth(), iconImageWithText.getHeight()), 0.8f); + CardRendererUtils.drawCenteredText(g2d, icon.getText(), rect, this.font, true); + g2d.dispose(); + } + + // the stretch icon can occupy all space (full grid's cell) + StretchIcon s = new StretchIcon(iconImageWithText, true); + label.setIcon(s); + label.setIconTextGap(0); + if (DebugUtil.GUI_CARD_ICONS_DRAW_ICON_BORDER) { + label.setBorder(BorderFactory.createLineBorder(Color.green)); + } + return label; + } + return null; + } + + private void calcSizes(Rectangle cardSize) { + // small mode takes 20% of card's sizes diff + boolean smallMode = false; + int maxW = GUISizeHelper.battlefieldCardMaxDimension.width; + int minW = GUISizeHelper.battlefieldCardMinDimension.width; + if (minW > maxW) { + // on wrong settings + maxW = GUISizeHelper.battlefieldCardMinDimension.width; + minW = GUISizeHelper.battlefieldCardMaxDimension.width; + } + // cardSize.width < 120 - disable small mode on too big cards + if (cardSize.width < MAXIMUM_CARD_WIDTH_FOR_ICONS_SMALL_MODE && (cardSize.width < minW + (maxW - minW) * 0.2f)) { + smallMode = true; + } + + // auto-sizeable icons (smaller for small card, normal for big) + this.calcedSizeSizePercent = this.iconSizePercent; + if (smallMode) { + this.calcedSizeSizePercent = Math.round(this.calcedSizeSizePercent * 1.5f); + } + + // auto-amount for icons (less for small, normal for big) + this.calcedCellsMaxCount = this.cellsMaxCount; + this.calcedCellsVisibleCount = this.cellsVisibleCount; + this.calcedCellsOffset = this.cellsOffset; + this.calcedPosition = this.position; + this.calcedOrder = this.order; + if (smallMode) { + this.calcedCellsMaxCount = Math.min(5, this.calcedCellsMaxCount); + this.calcedCellsVisibleCount = Math.min(1, this.calcedCellsVisibleCount); + this.calcedCellsOffset = Math.min(1, this.calcedCellsOffset); + // change order of multi-icons and ignore corners (make icons it centered) + if (this.calcedPosition.getMaxIconsAmount() > 1) { + this.calcedOrder = CardIconOrder.CENTER; + } + } + + // REAL SIZES + + // auto-sizeable gaps (use test render form to find best values) + this.iconsGap = Math.floorDiv(cardSize.width, 100) * 2; + + // icons intersect the card like mtg arena + this.halfSize = Math.max(MINIMUM_ICON_SIZE / 2, Math.round(cardSize.width / 100.0f * this.calcedSizeSizePercent / 2.0f)); + this.font = new Font("Arial", Font.PLAIN + Font.BOLD, Math.round(this.halfSize * 1.5f)); + } + + public int getHalfSize() { + return halfSize; + } +} diff --git a/Mage.Client/src/main/java/mage/client/cards/CardIconsPanelFactory.java b/Mage.Client/src/main/java/mage/client/cards/CardIconsPanelFactory.java new file mode 100644 index 00000000000..5733acaec82 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/cards/CardIconsPanelFactory.java @@ -0,0 +1,35 @@ +package mage.client.cards; + +import mage.abilities.icon.CardIconOrder; +import mage.abilities.icon.CardIconPosition; +import mage.abilities.icon.CardIconRenderSettings; + +/** + * GUI panel to drawning icons (one of the card's side) + * + * @author JayDi85 + */ +public class CardIconsPanelFactory { + + public static CardIconsPanel createAbilitiesPanel() { + return new CardIconsPanel( + CardIconPosition.LEFT, + CardIconOrder.CENTER, + 5, + 30 + ); + } + + public static CardIconsPanel createPlayablePanel() { + return new CardIconsPanel( + CardIconPosition.CORNER_BOTTOM_LEFT, + CardIconOrder.CENTER, + 1, + 30 + ); + } + + public static CardIconsPanel createDebugPanel(CardIconRenderSettings render) { + return new CardIconsPanel(render); + } +} diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index a7a561c0f74..d79ed7c1e9d 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -1,35 +1,39 @@ package mage.client.cards; + import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.dialog.PreferencesDialog; + import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; import mage.client.util.CardsViewUtil; import mage.client.util.ClientDefaultSettings; import mage.client.util.GUISizeHelper; + import mage.constants.Zone; + import mage.util.DebugUtil; import mage.view.*; import org.apache.log4j.Logger; - import org.mage.card.arcane.CardPanel; + import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import java.awt.*; import java.util.*; - import java.util.Map.Entry; /** - * @author BetaSteward_at_googlemail.com + * Panel for stack and hand zones + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class Cards extends javax.swing.JPanel { - private static final Logger LOGGER = Logger.getLogger(Cards.class); + private static final Logger logger = Logger.getLogger(Cards.class); private static final Border EMPTY_BORDER = new EmptyBorder(0, 0, 0, 0); private final Map cards = new LinkedHashMap<>(); private boolean dontDisplayTapped = false; - private static final int GAP_X = 5; // needed for marking cards with coloured fram (e.g. on hand) - private String zone; - - private int minOffsetY = 0; + private Zone zone; + private int lastLoadedCardsCount = 0; + private final JScrollPane parentScrollPane; /** * Defines whether component should be visible whenever there is no objects @@ -43,23 +47,37 @@ * Creates new form Cards */ public Cards() { - this(false); + this(false, null); } - public Cards(boolean skipAddingScrollPane) { + /** + * + * @param skipAddingScrollPane use parent scrolls instead own + * @param parentScrollPane + */ + public Cards(boolean skipAddingScrollPane, JScrollPane parentScrollPane) { initComponents(skipAddingScrollPane); setOpaque(false); - //cardArea.setOpaque(false); setBackgroundColor(new Color(0, 0, 0, 100)); + + // scrollpane can be own or from parent + this.parentScrollPane = parentScrollPane; if (!skipAddingScrollPane) { jScrollPane1.setOpaque(false); jScrollPane1.getViewport().setOpaque(false); jScrollPane1.setBorder(EMPTY_BORDER); } + if (Plugins.instance.isCardPluginLoaded()) { cardArea.setLayout(null); } cardArea.setBorder(EMPTY_BORDER); + + if (DebugUtil.GUI_GAME_DRAW_HAND_AND_STACK_BORDER) { + setBorder(BorderFactory.createLineBorder(Color.green)); + cardArea.setBorder(BorderFactory.createLineBorder(Color.yellow)); + } + setGUISize(); } @@ -81,6 +99,7 @@ if (jScrollPane1 != null) { jScrollPane1.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); jScrollPane1.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + jScrollPane1.getHorizontalScrollBar().setUnitIncrement(GUISizeHelper.getCardsScrollbarUnitInc(getCardDimension().width)); } } @@ -115,22 +134,30 @@ public boolean loadCards(CardsView cardsView, BigCard bigCard, UUID gameId, boolean revertOrder) { boolean changed = false; + // auto-move scrollbars to the end of the list + boolean moveScrollbar; + if (zone == Zone.HAND) { + // hand moves on new cards only + moveScrollbar = lastLoadedCardsCount != 0 && cardsView.size() > lastLoadedCardsCount; + } else { + // stack moves on any changes (e.g. show current stack object) + moveScrollbar = cardsView.size() != lastLoadedCardsCount; + } + this.lastLoadedCardsCount = cardsView.size(); + // remove objects no longer to display changed = removeOutdatedCards(cardsView); // Workaround for bug leaving display of objects on the stack (issue #213 https://github.com/magefree/mage/issues/213) if (cardsView.isEmpty() && countCards() > 0) { // problem happens with transformable cards - LOGGER.fatal("Card object on the cards panel was not removed"); + logger.fatal("Card object on the cards panel was not removed"); for (Component comp : cardArea.getComponents()) { - if (comp instanceof Card) { - Card card = (Card) comp; - LOGGER.fatal("Card name:" + card.getName() + " type:" + card.getType(null)); - } else if (comp instanceof MageCard) { + if (comp instanceof MageCard) { MageCard mageCard = (MageCard) comp; - LOGGER.fatal("MageCard name:" + mageCard.getName() + " toolTiptext:" + mageCard.getToolTipText()); + logger.fatal("MageCard name:" + mageCard.getName() + " toolTiptext:" + mageCard.getToolTipText()); } else { - LOGGER.fatal("Unknown object:" + comp.getName() + " className:" + comp.getClass().getName()); + logger.fatal("Unknown object:" + comp.getName() + " className:" + comp.getClass().getName()); } cardArea.remove(comp); } @@ -158,8 +185,7 @@ CardView tmp = ((StackAbilityView) card).getSourceCard(); tmp.overrideRules(card.getRules()); tmp.setChoosable(card.isChoosable()); - tmp.setPlayable(card.isPlayable()); - tmp.setPlayableAmount(card.getPlayableAmount()); + tmp.setPlayableStats(card.getPlayableStats().copy()); tmp.setSelected(card.isSelected()); tmp.setIsAbility(true); tmp.overrideTargets(card.getTargets()); @@ -188,11 +214,28 @@ this.revalidate(); this.repaint(); + // auto-scroll (must use it at the end) + if (changed && moveScrollbar) { + SwingUtilities.invokeLater(() -> { + if (jScrollPane1 != null) { + jScrollPane1.getHorizontalScrollBar().setValue(jScrollPane1.getHorizontalScrollBar().getMaximum()); + } + if (parentScrollPane != null) { + parentScrollPane.getHorizontalScrollBar().setValue(parentScrollPane.getHorizontalScrollBar().getMaximum()); + } + }); + } + return changed; } public void sizeCards(Dimension cardDimension) { - cardArea.setPreferredSize(new Dimension((int) ((cards.size()) * (cardDimension.getWidth() + GAP_X)) + 20, (int) (cardDimension.getHeight()) + 20)); + cardArea.setPreferredSize(new Dimension( + (int) ((cards.size()) * (cardDimension.getWidth() + MageActionCallback.getHandOrStackBetweenGapX(zone))) + + MageActionCallback.getHandOrStackMargins(zone).getWidth(), + (int) (cardDimension.getHeight()) + + MageActionCallback.getHandOrStackMargins(zone).getHeight() + )); cardArea.revalidate(); cardArea.repaint(); } @@ -211,7 +254,7 @@ public void setCardDimension(Dimension dimension) { this.cardDimension = dimension; for (Component component : cardArea.getComponents()) { - if (component instanceof CardPanel) { + if (component instanceof MageCard) { component.setBounds(0, 0, dimension.width, dimension.height); } } @@ -219,49 +262,52 @@ } private void addCard(CardView card, BigCard bigCard, UUID gameId) { - MageCard mageCard = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode(), true); + MageCard mageCard = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode(), true); + mageCard.setCardContainerRef(cardArea); + mageCard.update(card); if (zone != null) { mageCard.setZone(zone); } cards.put(card.getId(), mageCard); cardArea.add(mageCard); definePosition(mageCard); - mageCard.setCardAreaRef(cardArea); } - private void definePosition(MageCard card) { - int dx = 0; - for (Component comp : cardArea.getComponents()) { - if (!comp.equals(card)) { - dx = Math.max(dx, (int) comp.getLocation().getX()); + private void definePosition(MageCard newCard) { + int dx = MageActionCallback.getHandOrStackMargins(zone).getLeft(); // starting position + + // search last card's position + for (Component currentComp : cardArea.getComponents()) { + if (!currentComp.equals(newCard) && currentComp instanceof MageCard) { + MageCard currentCard = (MageCard) currentComp; + dx = Math.max(dx, currentCard.getCardLocation().getCardX()); } } - dx += ((CardPanel) card).getCardWidth() + GAP_X; - card.setLocation(dx, (int) card.getLocation().getY()); + + // Y position sets here one time only (all sorting and drag manipulations works with X) + // add card to the end + dx += newCard.getCardLocation().getCardWidth() + MageActionCallback.getHandOrStackBetweenGapX(newCard.getZone()); + newCard.setCardLocation(dx, MageActionCallback.getHandOrStackMargins(newCard.getZone()).getTop()); } private boolean removeOutdatedCards(CardsView cardsView) { boolean changed = false; + // links to components + cards.keySet().removeIf(id -> !cardsView.containsKey(id)); + // components for (Component comp : cardArea.getComponents()) { - UUID cardId = null; - if (comp instanceof Card) { - cardId = ((Card) comp).getCardId(); - } else if (comp instanceof MageCard) { - cardId = ((MageCard) comp).getOriginal().getId(); + if (comp instanceof MageCard) { + if (!cards.containsValue(comp)) { + cardArea.remove(comp); + changed = true; + } } else { - LOGGER.error("Unknown card conponent in cards panel to remove: " + comp); - } - if (cardId == null || !cardsView.containsKey(cardId)) { - cardArea.remove(comp); - changed = true; + logger.error("Unknown card conponent in cards panel to remove: " + comp); } } - // links - cards.keySet().removeIf(id -> !cardsView.containsKey(id)); - return changed; } @@ -298,10 +344,6 @@ private javax.swing.JScrollPane jScrollPane1; // End of variables declaration//GEN-END:variables - public void setDontDisplayTapped(boolean dontDisplayTapped) { - this.dontDisplayTapped = dontDisplayTapped; - } - public void setHScrollSpeed(int unitIncrement) { if (jScrollPane1 != null) { jScrollPane1.getHorizontalScrollBar().setUnitIncrement(unitIncrement); @@ -315,28 +357,29 @@ } private void layoutCards() { - java.util.List cardsToLayout = new ArrayList<>(); // get all the card panels + java.util.List cardsToLayout = new ArrayList<>(); for (Component component : cardArea.getComponents()) { - if (component instanceof CardPanel) { - cardsToLayout.add((CardPanel) component); + if (component instanceof MageCard) { + cardsToLayout.add((MageCard) component); } } + + // WARNING, must be same sort code as MageActionCallback->sortLayout (if not then hand cards will be messed after drag) + // sort the cards - cardsToLayout.sort(Comparator.comparingInt(cp -> cp.getLocation().x)); - // relocate the cards - int dx = 0; - for (Component component : cardsToLayout) { - component.setLocation(dx, Math.max(component.getLocation().y, minOffsetY)); - dx += ((CardPanel) component).getCardWidth() + GAP_X; + cardsToLayout.sort(Comparator.comparingInt(cp -> cp.getCardLocation().getCardX())); + + // relocate the cards (support only horizontal style: hand and stack panels) + // TODO: add shrinking of cards list for too big amount (cards will be overlapped, use MageActionCallback.HAND_CARDS_BETWEEN_GAP_X to control it) + int dx = MageActionCallback.getHandOrStackBetweenGapX(zone); // starting position + for (MageCard component : cardsToLayout) { + component.setCardLocation(dx, component.getCardLocation().getCardY()); + dx += component.getCardLocation().getCardWidth() + MageActionCallback.getHandOrStackBetweenGapX(zone); } } - public void setZone(String zone) { + public void setZone(Zone zone) { this.zone = zone; } - - public void setMinOffsetY(int minOffsetY) { - this.minOffsetY = minOffsetY; - } } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 3e09b9a23f5..16996a40b46 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -1,12 +1,6 @@ - - - /* - * CardsList.java - * - * Created on Dec 18, 2009, 10:40:12 AM - */ package mage.client.cards; + import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.constants.Constants.DeckEditorMode; import mage.client.constants.Constants.SortBy; @@ -17,11 +11,10 @@ import mage.client.plugins.impl.Plugins; import mage.client.util.Event; import mage.client.util.*; + import mage.client.util.comparators.*; import mage.client.util.gui.TableSpinnerEditor; import mage.view.CardView; import mage.view.CardsView; - import mage.view.SimpleCardView; - import org.mage.card.arcane.CardPanel; import org.mage.card.arcane.ManaSymbolsCellRenderer; import javax.swing.*; @@ -37,12 +30,17 @@ import java.util.*; /** - * @author BetaSteward_at_googlemail.com + * Deck editor: grid mode for drafting (NOT a normal deck editor or sideboarding) + * TODO: combine with CardGrid.java + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ - public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid { + public class CardsList extends javax.swing.JPanel implements ICardGrid, CardEventProducer { protected final CardEventSource cardEventSource = new CardEventSource(); + private Dimension cardDimension; + private final List countLabels = new ArrayList<>(); // count label code copy-pasted from CardGrid.java private int rowHeight; private CardsView cards; private Map mageCards = new LinkedHashMap<>(); @@ -53,6 +51,7 @@ private TableModel mainModel; private JTable mainTable; private ICardGrid currentView; + private boolean isLoading; // disable events from comboboxes while updating /** * Creates new form Cards @@ -78,8 +77,8 @@ cardArea.removeMouseListener(ml); } for (Component comp : cardArea.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); } } cardArea.removeAll(); @@ -108,7 +107,7 @@ mainTable.setFont(GUISizeHelper.tableFont); mainTable.setRowHeight(GUISizeHelper.getTableRowHeight()); cardDimension = GUISizeHelper.editorCardDimension; - rowHeight = GUISizeHelper.editorCardOffsetSize; + rowHeight = GUISizeHelper.editorCardVertOffsetInStack; } private void makeTransparent() { @@ -157,27 +156,38 @@ chkPiles.setEnabled(true); } - cardArea.addMouseListener(this); - mainTable.setOpaque(false); + + // ENABLE double clicks in table mode mainTable.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { + // simulate mouse click on the card if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks e.consume(); - if (e.isAltDown()) { - handleAltDoubleClick(); - } else { - handleDoubleClick(); - } + handleTableDoubleClick(e); // TODO: replace on card's event source instead mouse listener } } }); - mainModel.setUpdateCountsCallback(new UpdateCountsCallback(lblCount, lblCreatureCount, lblLandCount, null, null, null, null)); + + // ENABLE popup menu for non card area + cardArea.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { + cardEventSource.fireEvent(null, e.getComponent(), e.getX(), e.getY(), ClientEventType.CARD_POPUP_MENU); + } + } + }); } - // if you use the deck ediot to build a free deck, numbers can be set directly in deck and sideboard + @Override + public CardEventSource getCardEventSource() { + return cardEventSource; + } + + // if you use the deck editor to build a free deck, numbers can be set directly in deck and sideboard public void setDeckEditorMode(DeckEditorMode mode) { if (mode == DeckEditorMode.FREE_BUILDING) { // activate spinner for card number change @@ -197,24 +207,13 @@ } } - public void handleDoubleClick() { + public void handleTableDoubleClick(MouseEvent e) { if (mainTable.getSelectedRowCount() > 0) { int[] n = mainTable.getSelectedRows(); List indexes = asList(n); Collections.reverse(indexes); for (Integer index : indexes) { - mainModel.doubleClick(index); - } - } - } - - public void handleAltDoubleClick() { - if (mainTable.getSelectedRowCount() > 0) { - int[] n = mainTable.getSelectedRows(); - List indexes = asList(n); - Collections.reverse(indexes); - for (Integer index : indexes) { - mainModel.altDoubleClick(index); + mainModel.doubleClick(index, e, false); } } } @@ -240,8 +239,13 @@ this.bigCard = bigCard; this.gameId = gameId; - cbSortBy.setSelectedItem(sortSetting.getSortBy()); - chkPiles.setSelected(sortSetting.isPilesToggle()); + isLoading = true; + try { + cbSortBy.setSelectedItem(sortSetting.getSortBy()); + chkPiles.setSelected(sortSetting.isPilesToggle()); + } finally { + isLoading = false; + } currentView.loadCards(showCards, sortSetting, bigCard, gameId); if (selectedRow >= 0) { selectedRow = Math.min(selectedRow, mainTable.getRowCount() - 1); @@ -260,13 +264,18 @@ @Override public void drawCards(SortSetting sortSetting) { + for (JLabel label : this.countLabels) { + cardArea.remove(label); + } + this.countLabels.clear(); + int maxWidth = this.getParent().getWidth(); int numColumns = maxWidth / cardDimension.width; int curColumn = 0; int curRow = 0; int maxRow = 0; int maxColumn = 0; - Comparator comparator = null; + CardViewComparator comparator = null; Map oldMageCards = mageCards; mageCards = new LinkedHashMap<>(); @@ -308,25 +317,43 @@ case CASTING_COST: comparator = new CardViewCostComparator(); break; + case UNSORTED: + comparator = new CardViewNoneComparator(); + break; + case EDH_POWER_LEVEL: + comparator = new CardViewEDHPowerLevelComparator(); + break; + default: + throw new IllegalArgumentException("Error, unknown sort settings in deck editor: " + sortSetting.getSortBy()); } - if (comparator != null) { - sortedCards.sort(new CardViewNameComparator()); - sortedCards.sort(comparator); - } + + sortedCards.sort(new CardViewNameComparator()); + sortedCards.sort(comparator); + CardView lastCard = null; + JLabel lastCountLabel = null; for (CardView card : sortedCards) { if (sortSetting.isPilesToggle()) { if (lastCard == null) { lastCard = card; + // new new count label + lastCountLabel = addNewCountLabel(curColumn); } - if (comparator != null) { - if (comparator.compare(card, lastCard) > 0) { - curColumn++; - maxRow = Math.max(maxRow, curRow); - curRow = 0; - } + + // create new column on different card sorting + if (comparator.compare(card, lastCard) != 0) { + curColumn++; + maxRow = Math.max(maxRow, curRow); + curRow = 0; + // add new count label + lastCountLabel = addNewCountLabel(curColumn); } - rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight); + + // update last count label stats + String description = comparator.getCategoryName(card); + DragCardGrid.updateCountLabel(lastCountLabel, curRow + 1, description); + + rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight + DragCardGrid.COUNT_LABEL_HEIGHT); setCardBounds(mageCards.get(card.getId()), rectangle); curRow++; @@ -353,6 +380,16 @@ this.setVisible(true); } + private JLabel addNewCountLabel(int columnNumber) { + JLabel label = DragCardGrid.createCountLabel(null); + this.countLabels.add(label); + cardArea.add(label, (Integer) 0); // draw on background + label.setLocation(columnNumber * cardDimension.width, 5); + label.setSize(cardDimension.width, DragCardGrid.COUNT_LABEL_HEIGHT); + label.setVisible(true); + return label; + } + private void updateCounts() { int landCount = 0; int creatureCount = 0; @@ -389,15 +426,16 @@ } private MageCard addCard(CardView card, BigCard bigCard, UUID gameId) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, PreferencesDialog.getRenderMode(), true); - cardArea.add(cardImg); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), cardDimension, gameId, true, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(this); cardImg.update(card); - cardImg.addMouseListener(this); + // card position calculated on parent call by drawCards + //cardImg.setCardBounds(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + cardArea.add(cardImg, (Integer) 10); // count label must be on layer 0 for background drawing return cardImg; } private void setCardBounds(MageCard card, Rectangle rectangle) { - card.setBounds(rectangle); card.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); cardArea.moveToFront(card); } @@ -605,13 +643,17 @@ }//GEN-LAST:event_jToggleListViewActionPerformed private void cbSortByActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSortByActionPerformed - sortSetting.setSortBy((SortBy) cbSortBy.getSelectedItem()); - drawCards(sortSetting); + if (!isLoading) { + sortSetting.setSortBy((SortBy) cbSortBy.getSelectedItem()); + drawCards(sortSetting); + } }//GEN-LAST:event_cbSortByActionPerformed private void chkPilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkPilesActionPerformed - sortSetting.setPilesToggle(chkPiles.isSelected()); - drawCards(sortSetting); + if (!isLoading) { + sortSetting.setPilesToggle(chkPiles.isSelected()); + drawCards(sortSetting); + } }//GEN-LAST:event_chkPilesActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables @@ -628,64 +670,6 @@ private javax.swing.JPanel panelControl; // End of variables declaration//GEN-END:variables - @Override - public void mouseClicked(MouseEvent e) { - } - - @Override - public void mousePressed(MouseEvent e) { - if (e.getClickCount() >= 1 && !e.isConsumed()) { - Object obj = e.getSource(); - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks - e.consume(); - if (obj instanceof Card) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } else if (obj instanceof MageCard) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } - } - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } - } - - @Override - public void mouseReleased(MouseEvent e) { - if (!e.isConsumed()) { - Object obj = e.getSource(); - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } - } - - private void checkMenu(MouseEvent Me, SimpleCardView card) { - if (Me.isPopupTrigger()) { - Me.consume(); - cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } public void setDisplayNoCopies(boolean value) { mainModel.setDisplayNoCopies(value); diff --git a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java index 84f41a22f51..0e67709188d 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java @@ -1,18 +1,11 @@ - - -/* - * DraftGrid.java - * - * Created on 7-Jan-2011, 6:23:39 PM - */ - package mage.client.cards; import mage.cards.CardDimensions; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; -import mage.client.util.CardViewRarityComparator; +import mage.client.util.comparators.CardViewRarityComparator; import mage.client.util.ClientEventType; import mage.client.util.Event; import mage.client.util.Listener; @@ -23,15 +16,15 @@ import mage.view.CardsView; import org.apache.log4j.Logger; import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.List; /** - * @author BetaSteward_at_googlemail.com + * Drafting: panel with the picks + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ -public class DraftGrid extends javax.swing.JPanel implements MouseListener { +public class DraftGrid extends javax.swing.JPanel implements CardEventProducer { private static final Logger logger = Logger.getLogger(DraftGrid.class); @@ -47,20 +40,44 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { initComponents(); markedCard = null; emptyGrid = true; + + // ENABLE picks and other actions + cardEventSource.addListener(new Listener() { + @Override + public void event(Event event) { + if (event.getEventType() == ClientEventType.CARD_DOUBLE_CLICK) { + logger.info("draft grid: catch double click"); + CardView card = (CardView) event.getSource(); + cardEventSource.fireEvent(card, ClientEventType.DRAFT_PICK_CARD); + hidePopup(); + AudioManager.playOnDraftSelect(); + } else if (event.getEventType() == ClientEventType.CARD_CLICK) { + logger.info("draft grid: catch single click"); + CardView card = (CardView) event.getSource(); + MageCard cardPanel = (MageCard) event.getComponent(); + if (markedCard != null) { + markedCard.setSelected(false); + } + cardEventSource.fireEvent(card, ClientEventType.DRAFT_MARK_CARD); + markedCard = cardPanel; + markedCard.setSelected(true); + repaint(); + } + } + }); } public void clear() { markedCard = null; - this.clearCardEventListeners(); for (Component comp : getComponents()) { - if (comp instanceof Card || comp instanceof MageCard) { + if (comp instanceof MageCard) { this.remove(comp); } } } public void loadBooster(CardsView booster, BigCard bigCard) { - if (booster instanceof CardsView && booster.isEmpty()) { + if (booster != null && booster.isEmpty()) { emptyGrid = true; } else { if (!emptyGrid) { @@ -103,13 +120,12 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { List sortedCards = new ArrayList<>(booster.values()); sortedCards.sort(new CardViewRarityComparator()); for (CardView card : sortedCards) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, dimension, null, true, true, PreferencesDialog.getRenderMode(), true); - cardImg.addMouseListener(this); - add(cardImg); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), dimension, null, true, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(this); cardImg.update(card); - rectangle.setLocation(curColumn * (cardDimension.getFrameWidth() + offsetX) + offsetX, curRow * (rectangle.height + offsetY) + offsetY); + this.add(cardImg); - cardImg.setBounds(rectangle); + rectangle.setLocation(curColumn * (cardDimension.getFrameWidth() + offsetX) + offsetX, curRow * (rectangle.height + offsetY) + offsetY); cardImg.setCardBounds(rectangle.x, rectangle.y, rectangle.width, rectangle.height); curColumn++; if (curColumn == numColumns) { @@ -127,10 +143,6 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { cardEventSource.addListener(listener); } - public void clearCardEventListeners() { - cardEventSource.clearListeners(); - } - private void hidePopup() { Plugins.instance.getActionCallback().mouseExited(null, null); } @@ -158,54 +170,14 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { }// //GEN-END:initComponents @Override - public void mouseClicked(MouseEvent e) { - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks - if (e.getButton() == MouseEvent.BUTTON1) { - Object obj = e.getSource(); - if (obj instanceof MageCard) { - this.cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.PICK_A_CARD); - this.hidePopup(); - AudioManager.playOnDraftSelect(); - } - } - } - - } - - @Override - public void mousePressed(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1 || e.getButton() == MouseEvent.BUTTON3) { // left or right click - Object obj = e.getSource(); - if (obj instanceof MageCard) { - if (this.markedCard != null) { - markedCard.setSelected(false); - } - this.cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.MARK_A_CARD); - markedCard = ((MageCard) obj); - markedCard.setSelected(true); - repaint(); - } - } - + public CardEventSource getCardEventSource() { + return cardEventSource; } public boolean isEmptyGrid() { return emptyGrid; } - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } - - // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 7c872fd3d5f..17810f92397 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -1,6 +1,7 @@ package mage.client.cards; import mage.cards.Card; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLayout; @@ -11,22 +12,23 @@ import mage.client.constants.Constants; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.*; +import mage.client.util.Event; +import mage.client.util.comparators.*; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SubType; import mage.constants.SuperType; +import mage.util.DebugUtil; import mage.util.RandomUtil; import mage.view.CardView; import mage.view.CardsView; import org.apache.log4j.Logger; import org.mage.card.arcane.CardRenderer; +import org.mage.card.arcane.ManaSymbols; import javax.swing.*; import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.List; import java.util.*; import java.util.regex.Matcher; @@ -34,12 +36,16 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; /** - * Created by StravantUser on 2016-09-20. + * @author StravantUser, JayDi85 */ -public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarget { +public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarget, CardEventProducer { + + private static final Logger logger = Logger.getLogger(DragCardGrid.class); + private static final String DOUBLE_CLICK_MODE_INFO = "Double click mode: %s"; - private static final Logger LOGGER = Logger.getLogger(DragCardGrid.class); private Constants.DeckEditorMode mode; + Listener cardListener; + MouseListener countLabelListener; // clicks on the count label @Override public Collection dragCardList() { @@ -66,7 +72,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (card.isSelected()) { stack.set(i, null); removeCardView(card); - eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD); + eventSource.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD); } } } @@ -88,6 +94,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg showDropPosition(e.getX(), e.getY()); } + @Override + public CardEventSource getCardEventSource() { + return this.eventSource; + } + private void showDropPosition(int x, int y) { // Clamp to region if (x < 0) { @@ -320,7 +331,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (CardView card : cards) { card.setSelected(true); addCardView(card, false); - eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD); + eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD); } layoutGrid(); repaintGrid(); @@ -361,21 +372,33 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg creatureCountLabel.setVisible(false); landCountLabel.setVisible(false); cardSizeSliderLabel.setVisible(false); + mouseDoubleClickMode.setVisible(false); } else { creatureCountLabel.setVisible(true); landCountLabel.setVisible(true); cardSizeSliderLabel.setVisible(true); + mouseDoubleClickMode.setVisible(true); } updateCounts(); } + private void updateMouseDoubleClicksInfo(boolean isHotKeyPressed) { + boolean gameMode = isHotKeyPressed + || mode != Constants.DeckEditorMode.FREE_BUILDING; + String oldText = mouseDoubleClickMode.getText(); + String newText = String.format(DOUBLE_CLICK_MODE_INFO, gameMode ? "MOVE" : "DELETE"); + if (!oldText.equals(newText)) { + mouseDoubleClickMode.setText(newText); + } + } + public void removeSelection() { for (List> gridRow : cardGrid) { for (List stack : gridRow) { for (int i = 0; i < stack.size(); ++i) { CardView card = stack.get(i); if (card.isSelected()) { - eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD); + eventSource.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD); stack.set(i, null); removeCardView(card); } @@ -406,13 +429,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg public void setDeckEditorMode(Constants.DeckEditorMode mode) { this.mode = mode; + updateMouseDoubleClicksInfo(false); } public enum Sort { - NONE("No Sort", (o1, o2) -> { - // Always equal, sort into the first row - return 0; - }), + NONE("No Sort", new CardViewNoneComparator()), CARD_TYPE("Card Type", new CardViewCardTypeComparator()), CMC("Converted Mana Cost", new CardViewCostComparator()), COLOR("Color", new CardViewColorComparator()), @@ -420,12 +441,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg RARITY("Rarity", new CardViewRarityComparator()), EDH_POWER_LEVEL("EDH Power Level", new CardViewEDHPowerLevelComparator()); - Sort(String text, Comparator comparator) { + Sort(String text, CardViewComparator comparator) { this.comparator = comparator; this.text = text; } - public Comparator getComparator() { + public CardViewComparator getComparator() { return comparator; } @@ -433,7 +454,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg return text; } - private final Comparator comparator; + private final CardViewComparator comparator; private final String text; } @@ -537,7 +558,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } // Constants - public static final int COUNT_LABEL_HEIGHT = 20; + public static final int COUNT_LABEL_HEIGHT = 40; // can contains 1 or 2 lines public static final int GRID_PADDING = 10; private static final ImageIcon INSERT_ROW_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_row.png")); @@ -547,7 +568,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg private final Map cardViews = new LinkedHashMap<>(); private final List allCards = new ArrayList<>(); - // Card listeners + // card listeners private final CardEventSource eventSource = new CardEventSource(); // Last big card @@ -561,6 +582,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg JButton analyseButton; JButton blingButton; JButton oldVersionButton; + JLabel mouseDoubleClickMode; // Popup for toolbar final JPopupMenu filterPopup; @@ -696,7 +718,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg cardSizeSlider.setValue(size); } - // Constructor public DragCardGrid() { // Make sure that the card grid is populated with at least one (empty) stack to begin with cardGrid = new ArrayList<>(); @@ -705,13 +726,55 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg setLayout(new BorderLayout()); setOpaque(false); - // Editting mode + // default game mode (real game mode will be set after all components init) this.mode = Constants.DeckEditorMode.LIMITED_BUILDING; // Content cardContent = new JLayeredPane(); cardContent.setLayout(null); cardContent.setOpaque(false); + + // ENABLE MOUSE CLICKS (cards, menu) + this.cardListener = event -> { + switch(event.getEventType()) { + case CARD_POPUP_MENU: + if (event.getSource() != null) { + // menu for card + CardView card = (CardView) event.getSource(); + MouseEvent me = event.getMouseEvent(); + if (!card.isSelected()) { + selectCard(card); + } + showCardRightClickMenu(card, me); + } + break; + + case CARD_CLICK: + if (event.getSource() != null) { + CardView card = (CardView) event.getSource(); + MouseEvent me = event.getMouseEvent(); + cardClicked(card, me); + } + break; + } + }; + eventSource.addListener(this.cardListener); + + // keyboard listener for ALT status update + // it requires GLOBAL listener + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addKeyEventDispatcher(new KeyEventDispatcher() { + @Override + public boolean dispatchKeyEvent(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ALT) { + updateMouseDoubleClicksInfo(e.isAltDown()); + } + return false; + } + }); + + + // ENABLE MULTI-CARDS SELECTION cardContent.addMouseListener(new MouseAdapter() { private boolean isDragging = false; @@ -739,6 +802,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg updateSelectionDrag(e.getX(), e.getY()); } }); + cardScroll = new JScrollPane(cardContent, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); @@ -757,6 +821,13 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg analyseButton = new JButton("M"); // "Mana" button blingButton = new JButton("B"); // "Bling" button oldVersionButton = new JButton("O"); // "Old version" button + mouseDoubleClickMode = new JLabel(DOUBLE_CLICK_MODE_INFO); + mouseDoubleClickMode.setToolTipText("Mouse modes for double clicks:" + + "
* <Double Click>: DELETE card from mainboard/sideboard (it works as MOVE in games);" + + "
* <ALT + Double Click>: MOVE card between mainboard/sideboard (default for games);" + + "
* Deck editor: use <ALT + Double Click> on cards list to add card to the sideboard instead mainboard." + ); + updateMouseDoubleClicksInfo(false); // Name and count label deckNameAndCountLabel = new JLabel(); @@ -782,6 +853,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg toolbarInner.add(analyseButton); toolbarInner.add(blingButton); toolbarInner.add(oldVersionButton); + toolbarInner.add(mouseDoubleClickMode); toolbar.add(toolbarInner, BorderLayout.WEST); JPanel sliderPanel = new JPanel(new GridBagLayout()); sliderPanel.setOpaque(false); @@ -809,12 +881,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg insertArrow = new JLabel(); insertArrow.setSize(20, 20); insertArrow.setVisible(false); - cardContent.add(insertArrow, 1000); + cardContent.add(insertArrow, (Integer) 1000); // Selection panel selectionPanel = new SelectionBox(); selectionPanel.setVisible(false); - cardContent.add(selectionPanel, new Integer(1001)); + cardContent.add(selectionPanel, (Integer) 1001); // Load separate creatures setting separateCreatures = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES, "false").equals("true"); @@ -965,6 +1037,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg @Override public void keyPressed(KeyEvent e) { + } }); @@ -975,17 +1048,14 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Analyse Mana (aka #blue pips, #islands, #white pips, #plains etc.) analyseButton.setToolTipText("Mana Analyser! Counts coloured/colourless mana costs. Counts land types."); - analyseButton.addActionListener(evt -> analyseDeck()); // Bling button - aka Add in a premium 'JR', 'MBP', 'CS' etc card blingButton.setToolTipText("Bling your deck! Select the original and added cards by selecting 'Multiples' in the selection options"); - blingButton.addActionListener(evt -> blingDeck()); // Old version button - Switch cards to the oldest non-promo printing. In case of multiples in a set, take the lowest card number. oldVersionButton.setToolTipText("Switch cards to the oldest non-promo printing"); - oldVersionButton.addActionListener(evt -> oldVersionDeck()); // Filter popup @@ -1032,11 +1102,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg sortMenu.add(separateButton); menu.add(sortMenu); - // Hook up to card content + // ENABLE popup menu on non card (e.g. show all or sorting) cardContent.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { + if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { for (Sort s : sortMenuItems.keySet()) { sortMenuItems.get(s).setSelected(cardSort == s); } @@ -1170,8 +1240,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg --col2; } - // avoids a null ref issue but only deals with symptom of problem. not sure how it gets to this state ever. see issue #3197 - // if (selectionDragStartCards == null) return; int curY = COUNT_LABEL_HEIGHT; for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) { int stackStartIndex; @@ -1638,7 +1706,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (acard.getName().equals(card.getName())) { CardView pimpedCard = new CardView(acard); addCardView(pimpedCard, false); - eventSource.fireEvent(pimpedCard, ClientEventType.ADD_SPECIFIC_CARD); + eventSource.fireEvent(pimpedCard, ClientEventType.DECK_ADD_SPECIFIC_CARD); pimpedCards.put(pimpedCard, 1); didModify = true; } @@ -1679,9 +1747,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (oldestCardInfo != null) { CardView oldestCardView = new CardView(oldestCardInfo.getMockCard()); this.removeCardView(card); - eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD); + eventSource.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD); this.addCardView(oldestCardView, false); - eventSource.fireEvent(oldestCardView, ClientEventType.ADD_SPECIFIC_CARD); + eventSource.fireEvent(oldestCardView, ClientEventType.DECK_ADD_SPECIFIC_CARD); newStack.add(oldestCardView); } else { newStack.add(card); @@ -1806,7 +1874,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (Map> tracked : trackedCards.values()) { for (List orphans : tracked.values()) { for (CardView orphan : orphans) { - LOGGER.info("Orphan when setting with layout: "); + logger.info("Orphan when setting with layout: "); sortIntoGrid(orphan); } } @@ -1902,48 +1970,23 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg updateCounts(); // Create the card view - final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true, PreferencesDialog.getRenderMode(), true); + final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new CardIconRenderSettings(), new Dimension(getCardWidth(), getCardHeight()), null, true, true, PreferencesDialog.getRenderMode(), true); + cardPanel.setCardContainerRef(this); cardPanel.update(card); + // cards bounds set in layoutGrid() cardPanel.setCardCaptionTopOffset(0); - // Remove mouse wheel listeners so that scrolling works - // Scrolling works on all areas without cards or by using the scroll bar, that's enough -// for (MouseWheelListener l : cardPanel.getMouseWheelListeners()) { -// cardPanel.removeMouseWheelListener(l); -// } - // Add a click listener for selection / drag start - cardPanel.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - // Select if not selected - if (!card.isSelected()) { - selectCard(card); - } - // Show menu - showCardRightClickMenu(card, e); - } else if (SwingUtilities.isLeftMouseButton(e)) { - if (e.getClickCount() == 1) { - cardClicked(card, e); - } else if (e.isAltDown()) { - eventSource.fireEvent(card, ClientEventType.ALT_DOUBLE_CLICK); - } else { - eventSource.fireEvent(card, ClientEventType.DOUBLE_CLICK); - } - } - } - }); - - // Add a motion listener to process drags + // ENABLE DRAG SUPPORT FOR CARDS + // TODO: rewrite mouseDragged in MageActionCallback, so it can support any drags, not hands only cardPanel.addMouseMotionListener(new MouseAdapter() { @Override public void mouseDragged(MouseEvent e) { if (!dragger.isDragging()) { // If the card isn't already selected, make sure it is if (!card.isSelected()) { - cardClicked(card, e); + selectCard(card); } - dragger.beginDrag(cardPanel, e); + dragger.handleDragStart(cardPanel, e); } } }); @@ -1954,7 +1997,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (duplicated) { sortIntoGrid(card); - eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD); + eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD); // Update layout layoutGrid(); repaintGrid(); @@ -2059,9 +2102,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // No card in this column? if (cardInColumn == null) { // Error, should not have an empty column - LOGGER.error("Empty column! " + currentColumn); + logger.error("Empty column! " + currentColumn); } else { - int res = cardSort.getComparator().compare(newCard, cardInColumn); + CardViewComparator cardComparator = cardSort.getComparator(); + int res = cardComparator.compare(newCard, cardInColumn); if (res <= 0) { // Insert into this col, but if less, then we need to create a new col here first if (res < 0) { @@ -2085,6 +2129,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } targetRow.get(targetRow.size() - 1).add(newCard); } + + // if it's a new deck then non creature cards can be added to second row, so fix empty rows here + trimGrid(); } /** @@ -2191,16 +2238,35 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg stackCountLabels.add(new ArrayList<>()); } if (stackCountLabels.get(rowIndex).size() <= colIndex) { - JLabel countLabel = new JLabel("", SwingConstants.CENTER); - countLabel.setForeground(Color.WHITE); - cardContent.add(countLabel, new Integer(0)); + // add new count label for the column + + // ENABLE cards auto-selection in the stack + if (this.countLabelListener == null) { + this.countLabelListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JLabel countLabel = (JLabel) e.getComponent(); + List cards = findCardStackByCountLabel(countLabel); + boolean selected = !cards.isEmpty() && cards.get(0).isSelected(); + cards.forEach(card -> { + card.setSelected(!selected); + cardViews.get(card.getId()).update(card); + }); + } + }; + } + + JLabel countLabel = DragCardGrid.createCountLabel(this.countLabelListener); + cardContent.add(countLabel, (Integer) 0); stackCountLabels.get(rowIndex).add(countLabel); } + JLabel countLabel = stackCountLabels.get(rowIndex).get(colIndex); if (stack.isEmpty()) { countLabel.setVisible(false); } else { - countLabel.setText(String.valueOf(stack.size())); + String description = cardSort.getComparator().getCategoryName(stack.get(0)); + DragCardGrid.updateCountLabel(countLabel, stack.size(), description); countLabel.setLocation(GRID_PADDING + (cardWidth + GRID_PADDING) * colIndex, currentY - COUNT_LABEL_HEIGHT); countLabel.setSize(cardWidth, COUNT_LABEL_HEIGHT); countLabel.setVisible(true); @@ -2231,6 +2297,57 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg //cardContent.setSize(maxWidth, currentY - COUNT_LABEL_HEIGHT + GRID_PADDING); } + public static JLabel createCountLabel(MouseListener mouseListener) { + JLabel countLabel = new JLabel("", JLabel.CENTER); + countLabel.setForeground(Color.WHITE); // TODO: add theme support + if (mouseListener != null) { + countLabel.addMouseListener(mouseListener); + } + return countLabel; + } + + public static void updateCountLabel(JLabel countLabel, int amount, String description) { + // two modes: + // * small: one line with count + // * big: two lines with count and description + String descHtml = ManaSymbols.replaceSymbolsWithHTML(description, ManaSymbols.Type.TABLE); + boolean smallMode = description.isEmpty(); + boolean supportCustomClicks = countLabel.getMouseListeners().length > 0 + && !(countLabel.getMouseListeners()[0] instanceof ToolTipManager); // ignore auto-added ToolTipManager for mouse over hints + + // border: 1px solid black + // white-space: nowrap; + countLabel.setText("" + + "
" + + "
" + amount + "
" + + (smallMode ? "" : "
" + descHtml + "
") + + "
" + + ""); + countLabel.setToolTipText("" + + amount + (smallMode ? "" : " - " + description) + + (supportCustomClicks ? "
Click on the count label to select/unselect cards stack." : "") + ); + countLabel.setVerticalAlignment(smallMode ? JLabel.CENTER : JLabel.TOP); + if (DebugUtil.GUI_DECK_EDITOR_DRAW_COUNT_LABEL_BORDER) { + countLabel.setBorder(BorderFactory.createLineBorder(Color.green)); + } + } + + private List findCardStackByCountLabel(JLabel countLabel) { + for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) { + List> gridRow = cardGrid.get(rowIndex); + for (int colIndex = 0; colIndex < gridRow.size(); ++colIndex) { + if (stackCountLabels.size() < rowIndex + || stackCountLabels.get(rowIndex).size() < colIndex + || !countLabel.equals(stackCountLabels.get(rowIndex).get(colIndex))) { + continue; + } + return gridRow.get(colIndex); + } + } + return new ArrayList<>(); + } + private static void makeButtonPopup(final AbstractButton button, final JPopupMenu popup) { button.addActionListener(e -> popup.show(button, 0, button.getHeight())); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardSource.java b/Mage.Client/src/main/java/mage/client/cards/DragCardSource.java index eb71b9a89de..584afffbbde 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardSource.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardSource.java @@ -5,10 +5,12 @@ import mage.view.CardView; import java.util.Collection; /** - * Created by StravantUser on 2016-09-22. + * @author StravantUser */ public interface DragCardSource { Collection dragCardList(); + void dragCardBegin(); + void dragCardEnd(DragCardTarget target); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardTarget.java b/Mage.Client/src/main/java/mage/client/cards/DragCardTarget.java index a656a4bc937..6466402b9fd 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardTarget.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardTarget.java @@ -6,11 +6,14 @@ import java.awt.event.MouseEvent; import java.util.Collection; /** - * Created by StravantUser on 2016-09-22. + * @author StravantUser */ public interface DragCardTarget { void dragCardEnter(MouseEvent e); + void dragCardMove(MouseEvent e); + void dragCardExit(MouseEvent e); + void dragCardDrop(MouseEvent e, DragCardSource source, Collection cards); } diff --git a/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java b/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java index 8372d1dbe38..c036b8498d9 100644 --- a/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/ICardGrid.java @@ -1,25 +1,29 @@ - - package mage.client.cards; -import java.util.UUID; import mage.client.deckeditor.SortSetting; import mage.client.util.Event; import mage.client.util.Listener; import mage.view.CardsView; +import java.util.UUID; + /** * Interface for card container. - * - * @author nantuko * + * @author nantuko */ public interface ICardGrid { void clearCardEventListeners(); + void addCardEventListener(Listener listener); + void drawCards(SortSetting sortSetting); + void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId); + void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge); + void refresh(); + int cardsSize(); } diff --git a/Mage.Client/src/main/java/mage/client/cards/Permanent.form b/Mage.Client/src/main/java/mage/client/cards/Permanent.form deleted file mode 100644 index dac4a517ffa..00000000000 --- a/Mage.Client/src/main/java/mage/client/cards/Permanent.form +++ /dev/null @@ -1,20 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/cards/Permanent.java b/Mage.Client/src/main/java/mage/client/cards/Permanent.java deleted file mode 100644 index 072531da710..00000000000 --- a/Mage.Client/src/main/java/mage/client/cards/Permanent.java +++ /dev/null @@ -1,263 +0,0 @@ - - -/* - * Permanent.java - * - * Created on Dec 22, 2009, 3:25:49 PM - */ - -package mage.client.cards; - -import mage.cards.CardDimensions; -import mage.cards.MagePermanent; -import mage.cards.Sets; -import mage.client.util.ClientDefaultSettings; -import mage.client.util.TransformedImageCache; -import mage.view.CounterView; -import mage.view.PermanentView; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import static mage.client.constants.Constants.DAMAGE_MAX_LEFT; -import static mage.client.constants.Constants.POWBOX_TEXT_MAX_TOP; - -/** - * @author BetaSteward_at_googlemail.com - */ -public class Permanent extends Card { - - protected PermanentView permanent; - - protected final List links = new ArrayList<>(); - protected boolean linked; - protected final BufferedImage tappedImage; - protected BufferedImage flippedImage; - - /** - * Creates new form Permanent - * - * @param permanent - * @param bigCard - * @param dimensions - * @param gameId - */ - public Permanent(PermanentView permanent, BigCard bigCard, CardDimensions dimensions, UUID gameId) { - super(permanent, bigCard, dimensions, gameId); - this.setSize(this.getPreferredSize()); - this.permanent = permanent; - tappedImage = new BufferedImage(ClientDefaultSettings.dimensions.getFrameHeight(), ClientDefaultSettings.dimensions.getFrameWidth(), BufferedImage.TYPE_INT_RGB); - } - - public UUID getPermanentId() { - return permanent.getId(); - } - - @Override - public List getLinks() { - return links; - } - - public boolean isLinked() { - return linked; - } - - public void setLinked(boolean linked) { - this.linked = linked; - } - - @Override - protected String getText(String cardType) { - StringBuilder sb = new StringBuilder(); - sb.append(super.getText(cardType)); - if (permanent.getOriginal() != null) { - sb.append("\n----- Originally -------\n"); - sb.append(permanent.getOriginal().getName()); - if (!permanent.getOriginal().getManaCost().isEmpty()) { - sb.append('\n').append(permanent.getOriginal().getManaCost()); - } - sb.append('\n').append(getType(permanent.getOriginal())); - if (permanent.getOriginal().getColor().hasColor()) { - sb.append('\n').append(permanent.getOriginal().getColor().toString()); - } - if (permanent.getOriginal().isCreature()) { - sb.append('\n').append(permanent.getOriginal().getPower()).append('/').append(permanent.getOriginal().getToughness()); - } else if (permanent.getOriginal().isPlanesWalker()) { - sb.append('\n').append(permanent.getOriginal().getLoyalty()); - } - for (String rule : getRules()) { - sb.append('\n').append(rule); - } - if (!permanent.getOriginal().getExpansionSetCode().isEmpty()) { - sb.append('\n').append(permanent.getCardNumber()).append(" - "); - sb.append('\n').append(Sets.getInstance().get(permanent.getOriginal().getExpansionSetCode()).getName()).append(" - "); - sb.append(permanent.getOriginal().getRarity() == null ? "none" : permanent.getOriginal().getRarity().toString()); - } -// sb.append("\n").append(card.getId()); - } - return sb.toString(); - - } - - @Override - protected List getRules() { - if (permanent.getCounters() != null) { - List rules = new ArrayList<>(permanent.getRules()); - for (CounterView counter : permanent.getCounters()) { - rules.add(counter.getCount() + " x " + counter.getName()); - } - return rules; - } else { - return permanent.getRules(); - } - } - - @Override - public void mousePressed(MouseEvent e) { - p = e.getPoint(); - e.consume(); - } - - @Override - public void mouseDragged(MouseEvent e) { - if (!linked) { - int dx = e.getX() - p.x; - int dy = e.getY() - p.y; - Rectangle r = this.getBounds(); - r.x += dx; - r.y += dy; - if (r.x < 0) { - r.x = 0; - } - if (r.y < 0) { - r.y = 0; - } - this.setBounds(r); - this.repaint(); - for (MagePermanent perm : links) { - r.x += 20; - r.y += 20; - perm.setBounds(r); - } - } - } - - @Override - public void mouseClicked(MouseEvent arg0) { - - super.mouseClicked(arg0); - } - - @Override - public void paintComponent(Graphics graphics) { - Graphics2D g2 = (Graphics2D) graphics; - this.setSize(this.getPreferredSize()); - if (permanent.isTapped()) { - this.getText().setVisible(false); - g2.drawImage(tappedImage, 0, 0, this); - } else { - this.getText().setVisible(true); - g2.drawImage(small, 0, 0, this); - } - - //Add a border, red if card currently has focus - if (isFocusOwner()) { - g2.setColor(Color.RED); - } else { - g2.setColor(Color.BLACK); - } - if (permanent.isTapped()) { - g2.drawRect(0, 0, ClientDefaultSettings.dimensions.getFrameHeight() - 1, ClientDefaultSettings.dimensions.getFrameWidth() - 1); - } else { - g2.drawRect(0, 0, ClientDefaultSettings.dimensions.getFrameWidth() - 1, ClientDefaultSettings.dimensions.getFrameHeight() - 1); - } - - } - - protected void generateTappedImage() { - Graphics2D g = (Graphics2D) tappedImage.getGraphics(); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g.drawImage(TransformedImageCache.getRotatedResizedImage(small, dimension.getFrameWidth(), dimension.getFrameHeight(), Math.toRadians(90.0)), 0, 0, this); - - g.dispose(); - } - - @Override - public void update(PermanentView permanent) { - this.permanent = permanent; - super.update(permanent); - if (permanent.getDamage() > 0) { - Graphics2D g = image.createGraphics(); - g.setColor(Color.RED); - g.drawString(Integer.toString(permanent.getDamage()), DAMAGE_MAX_LEFT, POWBOX_TEXT_MAX_TOP); - g.dispose(); - } - generateTappedImage(); - } - - @Override - public Dimension getPreferredSize() { - if (permanent != null && permanent.isTapped()) { - return new Dimension(ClientDefaultSettings.dimensions.getFrameHeight(), ClientDefaultSettings.dimensions.getFrameWidth()); - } else { - return new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); - } - } - - @Override - public Dimension getMinimumSize() { - return this.getPreferredSize(); - } - - public boolean overlaps(Rectangle r1) { - return this.getBounds().intersects(r1); - } - - @Override - public void mouseEntered(MouseEvent arg0) { - if (!tooltipShowing) { - if (tooltipPopup != null) { - tooltipPopup.hide(); - } - PopupFactory factory = PopupFactory.getSharedInstance(); - int x = (int) this.getLocationOnScreen().getX() + (permanent.isTapped() ? ClientDefaultSettings.dimensions.getFrameHeight() : ClientDefaultSettings.dimensions.getFrameWidth()); - int y = (int) this.getLocationOnScreen().getY() + 40; - tooltipPopup = factory.getPopup(this, tooltipText, x, y); - tooltipPopup.show(); - //hack to get tooltipPopup to resize to fit text - tooltipPopup.hide(); - tooltipPopup = factory.getPopup(this, tooltipText, x, y); - tooltipPopup.show(); - tooltipShowing = true; - } - } - - @Override - public PermanentView getOriginalPermanent() { - return permanent; - } - - /** - * This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - setLayout(null); - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - // End of variables declaration//GEN-END:variables - -} diff --git a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java index 4e121976c9a..6a06996ecb2 100644 --- a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java +++ b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java @@ -1,6 +1,6 @@ package mage.client.combat; -import mage.cards.MagePermanent; +import mage.cards.MageCard; import mage.client.MageFrame; import mage.client.game.PlayAreaPanel; import mage.client.util.SettingsManager; @@ -17,7 +17,7 @@ import java.util.Map; import java.util.UUID; /** - * @author noxx + * @author noxx, JayDi85 */ public enum CombatManager { @@ -27,8 +27,8 @@ public enum CombatManager { private final Map combatBlockers = new HashMap<>(); private int globalBlockersCount; // we need global counter as there are several combat groups - private static Color ARROW_COLOR_ATTACKER = Color.red; - private static Color ARROW_COLOR_BLOCKED_ATTACKER = Color.gray; + private static final Color ARROW_COLOR_ATTACKER = Color.red; + private static final Color ARROW_COLOR_BLOCKED_ATTACKER = Color.gray; private Point parentPoint; @@ -71,7 +71,7 @@ public enum CombatManager { private void drawAttacker(CombatGroupView group, CardView attacker, UUID gameId) { for (PlayAreaPanel pa2 : MageFrame.getGamePlayers(gameId).values()) { - MagePermanent attackerCard = pa2.getBattlefieldPanel().getPermanents().get(attacker.getId()); + MageCard attackerCard = pa2.getBattlefieldPanel().getPermanentPanels().get(attacker.getId()); if (attackerCard != null) { drawDefender(group, attackerCard, gameId); drawBlockers(group, attackerCard, gameId); @@ -79,7 +79,7 @@ public enum CombatManager { } } - private void drawDefender(CombatGroupView group, MagePermanent attackerCard, UUID gameId) { + private void drawDefender(CombatGroupView group, MageCard attackerCard, UUID gameId) { UUID defenderId = group.getDefenderId(); if (defenderId != null) { // if attacker was blocked then use another arrow color @@ -90,17 +90,17 @@ public enum CombatManager { // attack to player Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); - Point attackerPoint = attackerCard.getLocationOnScreen(); + Point attackerPoint = attackerCard.getCardLocationOnScreen().getCardPoint(); attackerPoint.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() - 20, attackColor, ArrowBuilder.Type.COMBAT); } else { // attack to planeswalker for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(defenderId); + MageCard permanent = pa.getBattlefieldPanel().getPermanentPanels().get(defenderId); if (permanent != null) { - Point target = permanent.getLocationOnScreen(); + Point target = permanent.getCardLocationOnScreen().getCardPoint(); target.translate(-parentPoint.x, -parentPoint.y); - Point attackerPoint = attackerCard.getLocationOnScreen(); + Point attackerPoint = attackerCard.getCardLocationOnScreen().getCardPoint(); attackerPoint.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() + 10, attackColor, ArrowBuilder.Type.COMBAT); } @@ -109,20 +109,20 @@ public enum CombatManager { } } - private void drawBlockers(CombatGroupView group, MagePermanent attackerCard, UUID gameId) { + private void drawBlockers(CombatGroupView group, MageCard attackerCard, UUID gameId) { for (CardView blocker : group.getBlockers().values()) { for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { - MagePermanent blockerCard = pa.getBattlefieldPanel().getPermanents().get(blocker.getId()); + MageCard blockerCard = pa.getBattlefieldPanel().getPermanentPanels().get(blocker.getId()); if (blockerCard != null) { parentPoint = getParentPoint(blockerCard); - Point blockerPoint = blockerCard.getLocationOnScreen(); + Point blockerPoint = blockerCard.getCardLocationOnScreen().getCardPoint(); blockerPoint.translate(-parentPoint.x, -parentPoint.y); - Point attackerPoint = attackerCard.getLocationOnScreen(); + Point attackerPoint = attackerCard.getCardLocationOnScreen().getCardPoint(); attackerPoint.translate(-parentPoint.x, -parentPoint.y); - double yRateA = (attackerCard.getSize().height / SettingsManager.instance.getCardSize().height); - double xRateA = (attackerCard.getSize().width / SettingsManager.instance.getCardSize().width); - double yRateB = (blockerCard.getSize().height / SettingsManager.instance.getCardSize().height); - double xRateB = (blockerCard.getSize().width / SettingsManager.instance.getCardSize().width); + double yRateA = (attackerCard.getCardLocation().getCardHeight() / SettingsManager.instance.getCardSize().height); + double xRateA = (attackerCard.getCardLocation().getCardWidth() / SettingsManager.instance.getCardSize().width); + double yRateB = (blockerCard.getCardLocation().getCardHeight() / SettingsManager.instance.getCardSize().height); + double xRateB = (blockerCard.getCardLocation().getCardWidth() / SettingsManager.instance.getCardSize().width); ArrowBuilder.getBuilder().addArrow(gameId, (int) blockerPoint.getX() + (int) (55 * xRateB), (int) blockerPoint.getY() + (int) (25 * xRateB), (int) attackerPoint.getX() + (int) (70 * xRateA), (int) attackerPoint.getY() + (int) (25 * yRateA), Color.blue, ArrowBuilder.Type.COMBAT); globalBlockersCount++; @@ -142,7 +142,7 @@ public enum CombatManager { combatBlockers.put(gameId, count); } - private Point getParentPoint(MagePermanent permanent) { + private Point getParentPoint(MageCard permanent) { if (parentPoint == null) { Component parentComponent = SwingUtilities.getRoot(permanent); parentPoint = parentComponent.getLocationOnScreen(); diff --git a/Mage.Client/src/main/java/mage/client/components/ColorPane.java b/Mage.Client/src/main/java/mage/client/components/ColorPane.java index 0592e31e3de..b10f2f6d26f 100644 --- a/Mage.Client/src/main/java/mage/client/components/ColorPane.java +++ b/Mage.Client/src/main/java/mage/client/components/ColorPane.java @@ -123,7 +123,7 @@ public class ColorPane extends JEditorPane { tooltipCounter = 0; } if (tooltipCounter > 0) { - Point location = new Point(getLocationOnScreen().x - container.getWidth(), MouseInfo.getPointerInfo().getLocation().y); + Point location = new Point(this.getLocationOnScreen().x - container.getWidth(), MouseInfo.getPointerInfo().getLocation().y); Component parentComponent = MageFrame.getInstance(); location = GuiDisplayUtil.keepComponentInsideParent(location, parentComponent.getLocationOnScreen(), container, parentComponent); container.setLocation(location); diff --git a/Mage.Client/src/main/java/mage/client/components/HoverButton.java b/Mage.Client/src/main/java/mage/client/components/HoverButton.java index 535a22d5cd1..e1b803028c8 100644 --- a/Mage.Client/src/main/java/mage/client/components/HoverButton.java +++ b/Mage.Client/src/main/java/mage/client/components/HoverButton.java @@ -15,8 +15,8 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; -import javax.swing.JPanel; -import javax.swing.Timer; +import javax.swing.*; + import mage.client.util.Command; /** @@ -261,7 +261,7 @@ public class HoverButton extends JPanel implements MouseListener { @Override public void mousePressed(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { + if (SwingUtilities.isLeftMouseButton(e)) { if (isEnabled() && observer != null) { observer.execute(); } diff --git a/Mage.Client/src/main/java/mage/client/components/StretchIcon.java b/Mage.Client/src/main/java/mage/client/components/StretchIcon.java new file mode 100644 index 00000000000..9845aedce26 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/StretchIcon.java @@ -0,0 +1,306 @@ +package mage.client.components; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.net.URL; + +/** + * An Icon that scales its image to fill the component area, excluding any border or insets, optionally maintaining the image's + * aspect ratio by padding and centering the scaled image horizontally or vertically. + *

+ * The class is a drop-in replacement for ImageIcon, except that the no-argument constructor is not supported. + *

+ * As the size of the Icon is determined by the size of the component in which it is displayed, StretchIcon must only be used + * in conjunction with a component and layout that does not depend on the size of the component's Icon. + *

+ * Source: https://stackoverflow.com/a/34514866/1276632 + * + * @author Darryl + * @version 1.1 01/15/2016 + */ +public class StretchIcon extends ImageIcon { + /** + * + */ + private static final long serialVersionUID = 1L; + /** + * Determines whether the aspect ratio of the image is maintained. Set to false to allow th image to distort to fill the + * component. + */ + protected boolean proportionate = true; + + /** + * Creates a StretchIcon from an array of bytes. + * + * @param imageData an array of pixels in an image format supported by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG + * @see ImageIcon#ImageIcon(byte[]) + */ + public StretchIcon(byte[] imageData) { + super(imageData); + } + + /** + * Creates a StretchIcon from an array of bytes with the specified behavior. + * + * @param imageData an array of pixels in an image format supported by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(byte[]) + */ + public StretchIcon(byte[] imageData, boolean proportionate) { + super(imageData); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from an array of bytes. + * + * @param imageData an array of pixels in an image format supported by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG + * @param description a brief textual description of the image + * @see ImageIcon#ImageIcon(byte[], String) + */ + public StretchIcon(byte[] imageData, String description) { + super(imageData, description); + } + + /** + * Creates a StretchIcon from an array of bytes with the specified behavior. + * + * @param imageData an array of pixels in an image format supported by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG + * @param description a brief textual description of the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(byte[]) + * @see ImageIcon#ImageIcon(byte[], String) + */ + public StretchIcon(byte[] imageData, String description, boolean proportionate) { + super(imageData, description); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the image. + * + * @param image the image + * @see ImageIcon#ImageIcon(Image) + */ + public StretchIcon(Image image) { + super(image); + } + + /** + * Creates a StretchIcon from the image with the specified behavior. + * + * @param image the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(Image) + */ + public StretchIcon(Image image, boolean proportionate) { + super(image); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the image. + * + * @param image the image + * @param description a brief textual description of the image + * @see ImageIcon#ImageIcon(Image, String) + */ + public StretchIcon(Image image, String description) { + super(image, description); + } + + /** + * Creates a StretchIcon from the image with the specified behavior. + * + * @param image the image + * @param description a brief textual description of the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(Image, String) + */ + public StretchIcon(Image image, String description, boolean proportionate) { + super(image, description); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the specified file. + * + * @param filename a String specifying a filename or path + * @see ImageIcon#ImageIcon(String) + */ + public StretchIcon(String filename) { + super(filename); + } + + /** + * Creates a StretchIcon from the specified file with the specified behavior. + * + * @param filename a String specifying a filename or path + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(String) + */ + public StretchIcon(String filename, boolean proportionate) { + super(filename); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the specified file. + * + * @param filename a String specifying a filename or path + * @param description a brief textual description of the image + * @see ImageIcon#ImageIcon(String, String) + */ + public StretchIcon(String filename, String description) { + super(filename, description); + } + + /** + * Creates a StretchIcon from the specified file with the specified behavior. + * + * @param filename a String specifying a filename or path + * @param description a brief textual description of the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(Image, String) + */ + public StretchIcon(String filename, String description, boolean proportionate) { + super(filename, description); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the specified URL. + * + * @param location the URL for the image + * @see ImageIcon#ImageIcon(URL) + */ + public StretchIcon(URL location) { + super(location); + } + + /** + * Creates a StretchIcon from the specified URL with the specified behavior. + * + * @param location the URL for the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(URL) + */ + public StretchIcon(URL location, boolean proportionate) { + super(location); + this.proportionate = proportionate; + } + + /** + * Creates a StretchIcon from the specified URL. + * + * @param location the URL for the image + * @param description a brief textual description of the image + * @see ImageIcon#ImageIcon(URL, String) + */ + public StretchIcon(URL location, String description) { + super(location, description); + } + + /** + * Creates a StretchIcon from the specified URL with the specified behavior. + * + * @param location the URL for the image + * @param description a brief textual description of the image + * @param proportionate true to retain the image's aspect ratio, false to allow distortion of the image to + * fill the component. + * @see ImageIcon#ImageIcon(URL, String) + */ + public StretchIcon(URL location, String description, boolean proportionate) { + super(location, description); + this.proportionate = proportionate; + } + + /** + * Paints the icon. The image is reduced or magnified to fit the component to which it is painted. + *

+ * If the proportion has not been specified, or has been specified as true, the aspect ratio of the image will be preserved + * by padding and centering the image horizontally or vertically. Otherwise the image may be distorted to fill the component it is + * painted to. + *

+ * If this icon has no image observer,this method uses the c component as the observer. + * + * @param c the component to which the Icon is painted. This is used as the observer if this icon has no image observer + * @param g the graphics context + * @param x not used. + * @param y not used. + * @see ImageIcon#paintIcon(Component, Graphics, int, int) + */ + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + Image image = getImage(); + if (image == null) { + return; + } + Insets insets = ((Container) c).getInsets(); + x = insets.left; + y = insets.top; + + int w = c.getWidth() - x - insets.right; + int h = c.getHeight() - y - insets.bottom; + + if (proportionate) { + int iw = image.getWidth(c); + int ih = image.getHeight(c); + + if ((iw * h) < (ih * w)) { + iw = (h * iw) / ih; + x += (w - iw) / 2; + w = iw; + } else { + ih = (w * ih) / iw; + y += (h - ih) / 2; + h = ih; + } + } + ImageObserver io = getImageObserver(); + + /* + * Added this code to generate nicer looking results when scaling. - bspkrs + * BEGIN CHANGES + */ + BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D g2d = bi.createGraphics(); + g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); + g2d.drawImage(image, 0, 0, w, h, io == null ? c : io); + g2d.dispose(); + /* + * END CHANGES + */ + + g.drawImage(bi, x, y, w, h, io == null ? c : io); + } + + /** + * Overridden to return 0. The size of this Icon is determined by the size of the component. + * + * @return 0 + */ + @Override + public int getIconWidth() { + return 0; + } + + /** + * Overridden to return 0. The size of this Icon is determined by the size of the component. + * + * @return 0 + */ + @Override + public int getIconHeight() { + return 0; + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java index 91227cc94b9..648c17bd622 100644 --- a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java +++ b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java @@ -158,7 +158,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener { rows.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent evt) { - if (evt.getButton() == MouseEvent.BUTTON1) { + if (SwingUtilities.isLeftMouseButton(evt)) { objectMouseClicked(evt); } } @@ -320,7 +320,6 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener { if (isSelected) { label.setIcon(new ImageIcon(rightImageHovered)); label.setForeground(SELECTED_COLOR); - //label.setBorder(BorderFactory.createLineBorder(BORDER_COLOR)); label.setBorder(BorderFactory.createEmptyBorder()); } else { label.setIcon(new ImageIcon(rightImage)); diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java index 81048c967c2..ed003cacaef 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java @@ -2,7 +2,6 @@ package mage.client.components.ext.dlg; import mage.client.components.ext.MessageDialogType; import mage.client.components.ext.dlg.impl.ChoiceDialog; -import mage.client.components.ext.dlg.impl.StackDialog; import javax.swing.*; import java.awt.*; @@ -10,15 +9,15 @@ import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; /** - * @author mw, noxx + * Game GUI: part of the old dialog system + * + * @author mw, noxx, JayDi85 */ public class DialogContainer extends JPanel { private static final int X_OFFSET = 30; private static final int Y_OFFSET = 30; private final BufferedImage shadow = null; - //private DialogManager.MTGDialogs dialogType; - //private DlgParams params; private Color backgroundColor = new Color(0, 255, 255, 60); private int alpha = 50; @@ -38,102 +37,61 @@ public class DialogContainer extends JPanel { drawContainer = true; switch (dialogType) { - case MESSAGE: - //backgroundColor = new Color(0, 255, 255, 60); + + case MESSAGE: { if (params.type == MessageDialogType.WARNING) { backgroundColor = new Color(255, 0, 0, 90); } else { backgroundColor = new Color(0, 0, 0, 90); } alpha = 0; - //MessageDlg dlg = new MessageDlg(params); - //add(dlg); - //dlg.setLocation(X_OFFSET + 10, Y_OFFSET); - //dlg.updateSize(params.rect.width, params.rect.height); - break; - case STACK: { - //backgroundColor = new Color(0, 255, 255, 60); - backgroundColor = new Color(0, 0, 0, 50); - alpha = 0; - StackDialog dlg = new StackDialog(params); - add(dlg); - dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); - //int width = Math.min(params.rect.width - 80, 600); - int width = params.rect.width; - int height = params.rect.height - 80; - dlg.updateSize(width, height); break; } - /* - else if (dialogType == DialogManager.MTGDialogs.COMBAT) { - backgroundColor = new Color(0, 0, 0, 60); - alpha = 0; - COMBAT dlg = new COMBAT(params); - add(dlg); - dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); - dlg.updateSize(params.rect.width - 80, params.rect.height - 80); - }*/ case CHOICE: { - - //backgroundColor = new Color(200, 200, 172, 120); - //backgroundColor = new Color(180, 150, 200, 120); - //backgroundColor = new Color(0, 255, 0, 60); - - //backgroundColor = new Color(139, 46, 173, 20); backgroundColor = new Color(0, 0, 0, 110); - //backgroundColor = new Color(139, 46, 173, 0); - alpha = 0; ChoiceDialog dlg = new ChoiceDialog(params, "Choose"); add(dlg); - //GameManager.getManager().setCurrentChoiceDlg(dlg); dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); dlg.updateSize(params.rect.width - 80, params.rect.height - 80); - break; } + case GRAVEYARD: { - backgroundColor = new Color(0, 0, 0, 110); - alpha = 0; ChoiceDialog dlg = new ChoiceDialog(params, "Graveyard"); add(dlg); dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); dlg.updateSize(params.rect.width - 80, params.rect.height - 80); - break; } + case EXILE: { - backgroundColor = new Color(250, 250, 250, 50); - alpha = 0; ChoiceDialog dlg = new ChoiceDialog(params, "Exile"); add(dlg); dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); dlg.updateSize(params.rect.width - 80, params.rect.height - 80); - break; } + case EMBLEMS: { - backgroundColor = new Color(0, 0, 50, 110); - alpha = 0; ChoiceDialog dlg = new ChoiceDialog(params, "Command Zone (Commander, Emblems and Planes)"); add(dlg); dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); dlg.updateSize(params.rect.width - 80, params.rect.height - 80); - break; } } } public void cleanUp() { - for (Component component:this.getComponents()) { + for (Component component : this.getComponents()) { if (component instanceof ChoiceDialog) { ((ChoiceDialog) component).cleanUp(); } diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogManager.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogManager.java index 6b1e3a82c08..000a522aef7 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogManager.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogManager.java @@ -15,10 +15,11 @@ import java.util.Map; import java.util.UUID; /** + * Game GUI: part of the old dialog system, transparent dialog with cards list (example: exile button on player's panel) + * * @author mw, noxx */ -public class DialogManager extends JComponent implements MouseListener, - MouseMotionListener { +public class DialogManager extends JComponent implements MouseListener, MouseMotionListener { private static final Map dialogManagers = new HashMap<>(); @@ -39,7 +40,7 @@ public class DialogManager extends JComponent implements MouseListener, } public enum MTGDialogs { - NONE, ABOUT, MESSAGE, STACK, ASSIGN_DAMAGE, MANA_CHOICE, CHOICE, EMBLEMS, GRAVEYARD, DialogContainer, COMBAT, + NONE, ABOUT, MESSAGE, ASSIGN_DAMAGE, MANA_CHOICE, CHOICE, EMBLEMS, GRAVEYARD, DialogContainer, COMBAT, CHOOSE_DECK, CHOOSE_COMMON, REVEAL, EXILE } @@ -107,75 +108,6 @@ public class DialogManager extends JComponent implements MouseListener, this.screen_height = screen_height; } - public void showStackDialog(CardsView cards, BigCard bigCard, FeedbackPanel feedbackPanel, UUID gameId) { - - int w = (int) (screen_width * 0.7); - //int h = (int) (screen_height * 0.5); - int h = 360; - - /*if (h < 200) { - h = 200; - }*/ - - if (w > 800) { - w = 800; - } - - int height = getHeight(); - int width = getWidth(); - - int x = ((width - w) / 2); - int y = ((height - h) / 2); - - DlgParams params = new DlgParams(); - params.rect = new Rectangle(x, y, w, h); - params.bigCard = bigCard; - params.gameId = gameId; - params.feedbackPanel = feedbackPanel; - params.setCards(cards); - dialogContainer = new DialogContainer(MTGDialogs.STACK, params); - dialogContainer.setVisible(true); - add(dialogContainer); - - this.currentDialog = MTGDialogs.DialogContainer; - - setDlgBounds(new Rectangle(x, y, w, h)); - - dialogContainer.showDialog(true); - - setVisible(true); - } - - public void showGraveyardDialog(CardsView cards, BigCard bigCard, UUID gameId) { - - int w = 720; - int h = 550; - - int height = getHeight(); - int width = getWidth(); - - int x = ((width - w) / 2); - int y = ((height - h) / 2); - - DlgParams params = new DlgParams(); - params.rect = new Rectangle(x, y, w, h); - params.bigCard = bigCard; - params.gameId = gameId; - //params.feedbackPanel = feedbackPanel; - params.setCards(cards); - dialogContainer = new DialogContainer(MTGDialogs.GRAVEYARD, params); - dialogContainer.setVisible(true); - add(dialogContainer); - - this.currentDialog = MTGDialogs.DialogContainer; - - setDlgBounds(new Rectangle(x, y, w, h)); - - dialogContainer.showDialog(true); - - setVisible(true); - } - public void showExileDialog(CardsView cards, BigCard bigCard, UUID gameId) { int w = 720; @@ -317,7 +249,7 @@ public class DialogManager extends JComponent implements MouseListener, @Override public void mousePressed(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { + if (SwingUtilities.isLeftMouseButton(e)) { j = (JComponent) getComponentAt(e.getX(), e.getY()); if (j instanceof DialogContainer) { diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/IDialogPanel.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/IDialogPanel.java index fa274ffca48..f8046bed62d 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/IDialogPanel.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/IDialogPanel.java @@ -8,33 +8,28 @@ import javax.swing.*; import java.awt.*; /** - * @author mw, noxx + * Game GUI: part of the old dialog system + * + * @author mw, noxx, JayDi85 */ public abstract class IDialogPanel extends JXPanel { - private DlgParams params; + private final DlgParams params; private Dimension cardDimension; public DlgParams getDlgParams() { return params; } - public void setDlgParams(DlgParams params) { - this.params = params; - } - public IDialogPanel(DlgParams params) { super(); this.params = params; } protected void updateSize(int newWidth, int newHeight) { - Rectangle r0 = getBounds(); - r0.width = newWidth; r0.height = newHeight; - setBounds(r0); } @@ -42,7 +37,6 @@ public abstract class IDialogPanel extends JXPanel { * Make inner component transparent. */ protected void makeTransparent() { - setOpaque(false); for (int i = 0; i < getComponentCount(); i++) { @@ -50,13 +44,12 @@ public abstract class IDialogPanel extends JXPanel { if (c instanceof AbstractButton && !(c instanceof JButton)) { ((AbstractButton) c).setContentAreaFilled(false); } else if (c instanceof ImageButton) { - ((AbstractButton) c).setContentAreaFilled(false); + ((AbstractButton) c).setContentAreaFilled(false); } } } protected void makeTransparent(JLayeredPane jLayeredPane) { - setOpaque(false); for (int i = 0; i < getComponentCount(); i++) { @@ -64,7 +57,7 @@ public abstract class IDialogPanel extends JXPanel { if (c instanceof AbstractButton && !(c instanceof JButton)) { ((AbstractButton) c).setContentAreaFilled(false); } else if (c instanceof ImageButton) { - ((AbstractButton) c).setContentAreaFilled(false); + ((AbstractButton) c).setContentAreaFilled(false); } } } diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java index df3e06416a0..cb61aa5e7be 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java @@ -1,6 +1,7 @@ package mage.client.components.ext.dlg.impl; import mage.cards.MageCard; +import mage.abilities.icon.CardIconRenderSettings; import mage.client.cards.BigCard; import mage.client.components.HoverButton; import mage.client.components.ext.ShadowLabel; @@ -15,7 +16,6 @@ import mage.client.util.SettingsManager; import mage.client.util.audio.AudioManager; import mage.view.CardView; import mage.view.CardsView; -import org.mage.card.arcane.CardPanel; import org.mage.plugins.card.utils.impl.ImageManagerImpl; import javax.swing.*; @@ -24,7 +24,9 @@ import java.util.ArrayList; import java.util.UUID; /** - * @author mw, noxx + * Game GUI: transparent dialog with cards list (example: exile button on player's panel) + * + * @author mw, noxx, JayDi85 */ public class ChoiceDialog extends IDialogPanel { @@ -74,25 +76,15 @@ public class ChoiceDialog extends IDialogPanel { in_a_row = 5; rows = 2; - /** - * Calculate max pages - */ + // calculate max pages maxPages = cards.size() / (in_a_row * rows); if (cards.size() % (in_a_row * rows) != 0) { maxPages++; } - /** - * Init - */ initialize(); } - /** - * This method initializes this - * - * @return void - */ private void initialize() { jTitle = new ShadowLabel(title, 14); jTitle.setBounds(new Rectangle(5, 4, 500, 16)); @@ -100,9 +92,7 @@ public class ChoiceDialog extends IDialogPanel { this.setLayout(null); - /** - * Components - */ + // components this.add(jTitle, null); this.add(getJButtonOK(), null); this.add(getJButtonPrevPage(), null); @@ -111,17 +101,14 @@ public class ChoiceDialog extends IDialogPanel { this.add(getJButtonCancel(), null); makeTransparent(); - /** - * Manage cards - */ - ///GameManager.getManager().resetChosenCards(); + // cards displayCards(params.getCards(), params.gameId, params.bigCard); } public void cleanUp() { for (Component comp : this.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); this.remove(comp); } } @@ -163,10 +150,11 @@ public class ChoiceDialog extends IDialogPanel { } CardView card = cardList.get(i); - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode(), true); - - cardImg.setLocation(dx, dy + j * (height + 30)); - add(cardImg); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(this); + cardImg.update(card); + cardImg.setCardBounds(dx, dy + j * (height + 30), width, height); + this.add(cardImg); dx += (width + 20); } diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java deleted file mode 100644 index 6175b4e8a8e..00000000000 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java +++ /dev/null @@ -1,184 +0,0 @@ -package mage.client.components.ext.dlg.impl; - -import mage.cards.MageCard; -import mage.client.cards.BigCard; -import mage.client.components.HoverButton; -import mage.client.components.ext.dlg.DialogContainer; -import mage.client.components.ext.dlg.DialogManager; -import mage.client.components.ext.dlg.DlgParams; -import mage.client.components.ext.dlg.IDialogPanel; -import mage.client.dialog.PreferencesDialog; -import mage.client.game.FeedbackPanel; -import mage.client.plugins.impl.Plugins; -import mage.client.util.Command; -import mage.client.util.SettingsManager; -import mage.view.CardView; -import mage.view.CardsView; -import mage.view.StackAbilityView; -import org.mage.plugins.card.utils.impl.ImageManagerImpl; - -import javax.swing.*; -import java.awt.*; -import java.util.UUID; - -/** - * @author mw, noxx - */ -public class StackDialog extends IDialogPanel { - - private static final long serialVersionUID = 1L; - private JLabel jTitle = null; - private JLabel jTitle2 = null; - private HoverButton jButtonAccept = null; - private HoverButton jButtonResponse = null; - - private JLayeredPane jLayeredPane; - private final FeedbackPanel feedbackPanel; - - private final UUID gameId; - - private static class CustomLabel extends JLabel { - - @Override - public void paintComponent(Graphics g) { - Graphics2D g2D = (Graphics2D) g; - g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - g2D.setColor(Color.black); - g2D.drawString(getText(), 1, 11); - g2D.setColor(Color.white); - g2D.drawString(getText(), 0, 10); - } - - private static final long serialVersionUID = 1L; - } - - /** - * This is the default constructor - */ - public StackDialog(DlgParams params) { - super(params); - this.feedbackPanel = params.feedbackPanel; - this.gameId = params.gameId; - initialize(); - displayStack(params.getCards(), params.gameId, params.bigCard); - } - - /** - * This method initializes this - * - * @return void - */ - private void initialize() { - - int w = getDlgParams().rect.width; - int h = getDlgParams().rect.height; - - jLayeredPane = new JLayeredPane(); - add(jLayeredPane); - jLayeredPane.setSize(w, h); - jLayeredPane.setVisible(true); - jLayeredPane.setOpaque(false); - - jTitle = new CustomLabel(); - jTitle.setBounds(new Rectangle(5, 3, w, 16)); - jTitle.setFont(new Font("Dialog", Font.BOLD, 14)); - jTitle.setText("Current stack: "); - - this.setLayout(null); - jLayeredPane.setLayout(null); - - jLayeredPane.add(jTitle, null); - //jLayeredPane.add(jTitle2, null); - jLayeredPane.add(getJButtonAccept(), null); - jLayeredPane.add(getJButtonResponse(), null); - - makeTransparent(jLayeredPane); - } - - private void displayStack(CardsView cards, UUID gameId, BigCard bigCard) { - - if (cards == null || cards.isEmpty()) { - return; - } - - /** - * Display spells and theis targets above them - */ - int dx = (SettingsManager.instance.getCardSize().width + 15) * (cards.size() - 1); - int dy = 30; - - for (CardView card : cards.values()) { - - if (card instanceof StackAbilityView) { - // replace ability by original card - CardView tmp = ((StackAbilityView) card).getSourceCard(); - tmp.overrideRules(card.getRules()); - tmp.setChoosable(card.isChoosable()); - tmp.setPlayable(card.isPlayable()); - tmp.setPlayableAmount(card.getPlayableAmount()); - tmp.setSelected(card.isSelected()); - tmp.setIsAbility(true); - tmp.overrideTargets(card.getTargets()); - tmp.overrideId(card.getId()); - card = tmp; - } - - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode(), true); - //cardImg.setBorder(BorderFactory.createLineBorder(Color.red)); - cardImg.setLocation(dx, dy); - - jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 1); - - dx -= (SettingsManager.instance.getCardSize().width + 15); - } - } - - private HoverButton getJButtonAccept() { - if (jButtonAccept == null) { - jButtonAccept = new HoverButton("", ImageManagerImpl.instance.getDlgAcceptButtonImage(), - ImageManagerImpl.instance.getDlgActiveAcceptButtonImage(), - ImageManagerImpl.instance.getDlgAcceptButtonImage(), - new Rectangle(60, 60)); - int w = getDlgParams().rect.width - 90; - int h = getDlgParams().rect.height - 90; - jButtonAccept.setBounds(new Rectangle(w / 2 - 80, h - 50, 60, 60)); - //jButtonAccept.setBorder(BorderFactory.createLineBorder(Color.red)); - - jButtonAccept.setObserver(new Command() { - @Override - public void execute() { - DialogManager.getManager(gameId).fadeOut((DialogContainer) getParent()); - //GameManager.getInputControl().getInput().selectButtonOK(); - StackDialog.this.feedbackPanel.doClick(); - } - - private static final long serialVersionUID = 1L; - }); - } - return jButtonAccept; - } - - private HoverButton getJButtonResponse() { - if (jButtonResponse == null) { - jButtonResponse = new HoverButton("", ImageManagerImpl.instance.getDlgCancelButtonImage(), - ImageManagerImpl.instance.getDlgActiveCancelButtonImage(), - ImageManagerImpl.instance.getDlgCancelButtonImage(), - new Rectangle(60, 60)); - int w = getDlgParams().rect.width - 90; - int h = getDlgParams().rect.height - 90; - jButtonResponse.setBounds(new Rectangle(w / 2 + 5, h - 48, 60, 60)); - - jButtonResponse.setObserver(new Command() { - @Override - public void execute() { - DialogManager.getManager(gameId).fadeOut((DialogContainer) getParent()); - } - - private static final long serialVersionUID = 1L; - }); - } - return jButtonResponse; - } -} \ No newline at end of file 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 58c358e86e3..e77cf06e974 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -1,8 +1,3 @@ -/* - * CardSelector.java - * - * Created on Feb 18, 2010, 2:49:03 PM - */ package mage.client.deckeditor; import mage.MageObject; @@ -1272,7 +1267,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene java.util.List indexes = asList(n); Collections.reverse(indexes); for (Integer index : indexes) { - mainModel.doubleClick(index); + // normal double click emulation + mainModel.doubleClick(index, null, false); } //if (!mode.equals(Constants.DeckEditorMode.Constructed)) if (limited) { @@ -1287,7 +1283,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene java.util.List indexes = asList(n); Collections.reverse(indexes); for (Integer index : indexes) { - mainModel.altDoubleClick(index); + // ALT double click emulation + mainModel.doubleClick(index, null, true); } //if (!mode.equals(Constants.DeckEditorMode.Constructed)) if (limited) { @@ -1537,7 +1534,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private javax.swing.JToggleButton tbWhite; // End of variables declaration//GEN-END:variables - private final mage.client.cards.CardGrid cardGrid; + private final mage.client.cards.CardGrid cardGrid; // grid for piles view mode (example: selected cards in drafting) @Override public void componentResized(ComponentEvent e) { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java index 40a635038e4..a62ae20b4ea 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java @@ -92,8 +92,8 @@ public class DeckArea extends javax.swing.JPanel { // Add to hidden and move to sideboard for (CardView card : cards) { hiddenCards.add(card.getId()); - maindeckVirtualEvent.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD); - sideboardVirtualEvent.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD); + maindeckVirtualEvent.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD); + sideboardVirtualEvent.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD); } loadDeck(lastDeck, lastBigCard); } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 11ac7fca8d2..bf14225bbd5 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -304,77 +304,92 @@ public class DeckEditorPanel extends javax.swing.JPanel { private void init() { //this.cardSelector.setVisible(true); this.panelLeft.setVisible(true); + + // TOP AREA: ENABLE ADDING/REMOVING BY DOUBLE CLICKS for (ICardGrid component : this.cardSelector.getCardGridComponents()) { - component.clearCardEventListeners(); + //component.clearCardEventListeners(); component.addCardEventListener((Listener) event -> { switch (event.getEventType()) { - 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 + + case CARD_DOUBLE_CLICK: { + boolean gameMode = mode != DeckEditorMode.FREE_BUILDING; + if (gameMode) { + // in game mode selector is used as sideboard, so the card must goes to deck all the time moveSelectorCardToDeck(event); + } else { + // edit mode + if (event.isMouseAltDown()) { + moveSelectorCardToSideboard(event); + } else { + moveSelectorCardToDeck(event); + } } break; - case REMOVE_MAIN: + } + + case DECK_REMOVE_SELECTION_MAIN: { DeckEditorPanel.this.deckArea.getDeckList().removeSelection(); break; - case REMOVE_SIDEBOARD: + } + + case DECK_REMOVE_SELECTION_SIDEBOARD: { DeckEditorPanel.this.deckArea.getSideboardList().removeSelection(); break; + } } refreshDeck(); }); } - this.deckArea.clearDeckEventListeners(); + + // BOTTOM AREA: ENABLE ADDING/REMOVING CARDS in MAINBOARD + // do not clear event listener - DragCardGrid already have own listeners for cards + //this.deckArea.clearDeckEventListeners(); this.deckArea.addDeckEventListener( + // card manipulation events + // warning, do not use drag or single click events here, it's already processing by DragCardGrid + (Listener) event -> { if (mode == DeckEditorMode.FREE_BUILDING) { switch (event.getEventType()) { - case DOUBLE_CLICK: { + case CARD_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; - } + Card card = deck.findCard(cardView.getId()); + if (card == null) { + return; } + + if (event.isMouseAltDown()) { + // ALT + double click: MOVE card from one deck to another + deck.getCards().remove(card); + deck.getSideboard().add(card); + } else { + // double click: DELETE card from deck + deck.getCards().remove(card); + } + hidePopup(); refreshDeck(); break; } + case SET_NUMBER: { setCardNumberToCardsList(event, deck.getCards()); break; } - case REMOVE_SPECIFIC_CARD: { + + case DECK_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; - } + Card card = deck.findCard(cardView.getId()); + if (card == null) { + return; } + + deck.getCards().remove(card); + storeTemporaryCard(card); break; } - case ADD_SPECIFIC_CARD: { + + case DECK_ADD_SPECIFIC_CARD: { SimpleCardView cardView = (CardView) event.getSource(); deck.getCards().add(retrieveTemporaryCard(cardView)); break; @@ -383,33 +398,36 @@ public class DeckEditorPanel extends javax.swing.JPanel { } else { // constructing phase or sideboarding during match -> card goes always to sideboard switch (event.getEventType()) { - case DOUBLE_CLICK: - case ALT_DOUBLE_CLICK: { + + case CARD_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; - } + Card card = deck.findCard(cardView.getId()); + if (card == null) { + return; } + + deck.getCards().remove(card); + deck.getSideboard().add(card); + cardSelector.loadSideboard(new ArrayList<>(deck.getSideboard()), this.bigCard); + hidePopup(); refreshDeck(); break; } - case REMOVE_SPECIFIC_CARD: { + + case DECK_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; - } + Card card = deck.findCard(cardView.getId()); + if (card == null) { + return; } + + deck.getCards().remove(card); + storeTemporaryCard(card); break; } - case ADD_SPECIFIC_CARD: { + + case DECK_ADD_SPECIFIC_CARD: { SimpleCardView cardView = (CardView) event.getSource(); deck.getCards().add(retrieveTemporaryCard(cardView)); break; @@ -417,95 +435,104 @@ public class DeckEditorPanel extends javax.swing.JPanel { } } }); - this.deckArea.clearSideboardEventListeners(); + + // BOTTOM AREA: ENABLE ADDING/REMOVING CARDS in SIDEBOARD + // do not clear event listener - DragCardGrid already have own listeners for cards + //this.deckArea.clearSideboardEventListeners(); this.deckArea.addSideboardEventListener( + // card manipulation events (Listener) event -> { if (mode == DeckEditorMode.FREE_BUILDING) { - // normal edit mode + // DECK EDITOR MODE switch (event.getEventType()) { - case DOUBLE_CLICK: - // remove card from sideboard (don't add it to deck) + case CARD_DOUBLE_CLICK: { 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; - } + Card card = deck.findSideboardCard(cardView.getId()); + if (card == null) { + return; } + + if (event.isMouseAltDown()) { + // ALT + double click: MOVE card from one deck to another + deck.getSideboard().remove(card); + deck.getCards().add(card); + } else { + // double click: DELETE card from deck + deck.getSideboard().remove(card); + } + 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; - } + + case DECK_REMOVE_SPECIFIC_CARD: { + SimpleCardView cardView = (SimpleCardView) event.getSource(); + Card card = deck.findSideboardCard(cardView.getId()); + if (card == null) { + return; } + + deck.getSideboard().remove(card); + storeTemporaryCard(card); break; } - case ADD_SPECIFIC_CARD: { - cardView = (CardView) event.getSource(); + + case DECK_ADD_SPECIFIC_CARD: { + SimpleCardView cardView = (CardView) event.getSource(); deck.getSideboard().add(retrieveTemporaryCard(cardView)); break; } } } else { - // construct phase or sideboarding during match + // GAME MODE + // constructing phase or sideboarding during match -> card goes always to main board switch (event.getEventType()) { - case REMOVE_SPECIFIC_CARD: { + + case DECK_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; - } + Card card = deck.findSideboardCard(cardView.getId()); + if (card == null) { + return; } + + deck.getSideboard().remove(card); + storeTemporaryCard(card); break; } - case ADD_SPECIFIC_CARD: { + + case DECK_ADD_SPECIFIC_CARD: { SimpleCardView cardView = (CardView) event.getSource(); deck.getSideboard().add(retrieveTemporaryCard(cardView)); break; } - case DOUBLE_CLICK: - case ALT_DOUBLE_CLICK: + + case CARD_DOUBLE_CLICK: { + // in games you can't delete cards, only moves from one deck to another 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; - } + Card card = deck.findSideboardCard(cardView.getId()); + if (card == null) { + return; } + + deck.getSideboard().remove(card); + deck.getCards().add(card); + hidePopup(); refreshDeck(); break; + } } } }); refreshDeck(true); + // auto-import dropped files from OS if (mode == DeckEditorMode.FREE_BUILDING) { setDropTarget(new DropTarget(this, new DnDDeckTargetListener() { @@ -562,26 +589,32 @@ public class DeckEditorPanel extends javax.swing.JPanel { 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())) { + boolean gameMode = mode != DeckEditorMode.FREE_BUILDING; + if (gameMode) { + // game: use existing real cards + for (Card sideCard : deck.getSideboard()) { + if (sideCard.getId().equals(cardView.getId())) { + card = sideCard; break; } } } else { + // editor: create mock card + CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber()); card = cardInfo != null ? cardInfo.getMockCard() : null; } + if (card != null) { deck.getCards().add(card); - if (mode == DeckEditorMode.SIDEBOARDING || mode == DeckEditorMode.LIMITED_BUILDING) { + if (gameMode) { + // game: move card from another board deck.getSideboard().remove(card); cardSelector.removeCard(card.getId()); cardSelector.setCardCount(deck.getSideboard().size()); cardSelector.refresh(); } + // card hint update if (cardInfoPane instanceof CardInfoPane) { ((CardInfoPane) cardInfoPane).setCard(new CardView(card), null); } @@ -590,12 +623,19 @@ public class DeckEditorPanel extends javax.swing.JPanel { } private void moveSelectorCardToSideboard(Event event) { + boolean gameMode = mode != DeckEditorMode.FREE_BUILDING; + if (gameMode) { + throw new IllegalArgumentException("ERROR, you can move card to sideboard from selector in game mode."); + } + 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); } + + // card hint update if (cardInfoPane instanceof CardInfoPane) { ((CardInfoPane) cardInfoPane).setCard(new CardView(card), null); } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckExportClipboardDialog.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckExportClipboardDialog.java index 52c7ffb03c4..2e83a36577f 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckExportClipboardDialog.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckExportClipboardDialog.java @@ -14,6 +14,8 @@ import java.io.ByteArrayOutputStream; import java.util.ArrayList; /** + * App GUI: deck export window, uses in deck editor + * * @author JayDi85 */ public class DeckExportClipboardDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportClipboardDialog.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportClipboardDialog.java index 5b7af3b3f64..74504860029 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportClipboardDialog.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportClipboardDialog.java @@ -13,6 +13,8 @@ import java.io.IOException; import java.util.Optional; /** + * App GUI: deck import window, uses in deck editor + * * @author JayDi85 */ public class DeckImportClipboardDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 5b426f77d60..95ab99fad12 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -1,5 +1,6 @@ package mage.client.deckeditor.collection.viewer; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.*; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; @@ -45,17 +46,17 @@ import static java.lang.Math.min; import static org.mage.plugins.card.images.DownloadPicturesService.getTokenCardUrls; /** - * Mage book with cards and page flipping. + * Card viewer (mage book) with cards and page flipping * - * @author nantuko + * @author nantuko, JayDi85 */ public class MageBook extends JComponent { private static final long serialVersionUID = 1L; public static final String LAYOUT_3X3 = "small"; - public static final String LAYOUT_4X4 = "big"; + private static final int CARD_CAPTION_OFFSET_Y = 8; // apply offset to see card names with mana icons at the same time public MageBook(BigCard bigCard) { super(); @@ -71,7 +72,6 @@ public class MageBook extends JComponent { setSize(conf.WIDTH, conf.HEIGHT); setPreferredSize(new Dimension(conf.WIDTH, conf.HEIGHT)); setMinimumSize(new Dimension(conf.WIDTH, conf.HEIGHT)); - //setBorder(BorderFactory.createLineBorder(Color.green)); jPanelLeft = getImagePanel(LEFT_PANEL_IMAGE_PATH, ImagePanelStyle.TILED); jPanelLeft.setPreferredSize(new Dimension(LEFT_RIGHT_PAGES_WIDTH, 0)); @@ -87,7 +87,6 @@ public class MageBook extends JComponent { Image image = ImageHelper.loadImage(LEFT_PAGE_BUTTON_IMAGE_PATH); pageLeft = new HoverButton(null, image, image, image, new Rectangle(64, 64)); - //pageLeft.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 0), 3, true)); //debug pageLeft.setBounds(0, 0, 64, 64); pageLeft.setVisible(false); pageLeft.setObserver(() -> { @@ -124,7 +123,6 @@ public class MageBook extends JComponent { // Top Panel (left page + (caption / stats) + right page jPanelTop = new JPanel(); jPanelTop.setLayout(new BorderLayout()); - // jPanelTop.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug jPanelTop.setPreferredSize(new Dimension(captionHeight, captionHeight)); jPanelCenter.add(jPanelTop, BorderLayout.NORTH); @@ -144,7 +142,6 @@ public class MageBook extends JComponent { // set's caption setCaption = new JLabel(); setCaption.setHorizontalAlignment(SwingConstants.CENTER); - //setCaption.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug setCaption.setFont(jLayeredPane.getFont().deriveFont(25f)); setCaption.setText("EMPTY CAPTION"); jPanelCaption.add(setCaption, BorderLayout.NORTH); @@ -152,7 +149,6 @@ public class MageBook extends JComponent { // set's info setInfo = new JLabel(); setInfo.setHorizontalAlignment(SwingConstants.CENTER); - //setCaption.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug setInfo.setFont(jLayeredPane.getFont().deriveFont(17f)); setInfo.setText("EMPTY STATS"); jPanelCaption.add(setInfo, BorderLayout.SOUTH); @@ -245,7 +241,6 @@ public class MageBook extends JComponent { for (int i = 0; i < min(conf.CARDS_PER_PAGE / 2, size); i++) { Card card = cards.get(i).getMockCard(); addCard(new CardView(card), bigCard, null, rectangle); - rectangle = CardPosition.translatePosition(i, rectangle, conf); } @@ -407,30 +402,29 @@ public class MageBook extends JComponent { if (cardDimension == null) { cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); } - final MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, PreferencesDialog.getRenderMode(), true); - cardImg.setBounds(rectangle); - jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10); + final MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, new CardIconRenderSettings(), cardDimension, gameId, true, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(jLayeredPane); cardImg.update(card); cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.getFrameWidth(), cardDimensions.getFrameHeight()); + jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10); - cardImg.setCardCaptionTopOffset(8); // card caption below real card caption to see full name even with mana icons + // card caption must be below real card caption to see full name even with mana icons + cardImg.setCardCaptionTopOffset(CARD_CAPTION_OFFSET_Y); // card number label JLabel cardNumber = new JLabel(); int dy = -5; // image panel have empty space in bottom (bug?), need to move label up - cardNumber.setBounds(rectangle.x, rectangle.y + cardImg.getHeight() + dy, cardDimensions.getFrameWidth(), 20); + cardNumber.setBounds(rectangle.x, rectangle.y + cardImg.getCardLocation().getCardHeight() + dy, cardDimensions.getFrameWidth(), 20); cardNumber.setHorizontalAlignment(SwingConstants.CENTER); - //cardNumber.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); cardNumber.setFont(jLayeredPane.getFont().deriveFont(jLayeredPane.getFont().getStyle() | Font.BOLD)); cardNumber.setText(card.getCardNumber()); jLayeredPane.add(cardNumber); - // draft rating label ( + // draft rating label JLabel draftRating = new JLabel(); dy = -5 * 2 + cardNumber.getHeight(); // under card number - draftRating.setBounds(rectangle.x, rectangle.y + cardImg.getHeight() + dy, cardDimensions.getFrameWidth(), 20); + draftRating.setBounds(rectangle.x, rectangle.y + cardImg.getCardLocation().getCardHeight() + dy, cardDimensions.getFrameWidth(), 20); draftRating.setHorizontalAlignment(SwingConstants.CENTER); - //draftRating.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 150), 3, true)); draftRating.setFont(jLayeredPane.getFont().deriveFont(jLayeredPane.getFont().getStyle() | Font.BOLD)); if (card.getOriginalCard() != null) { draftRating.setText("draft rating: " + RateCard.rateCard(card.getOriginalCard(), null)); @@ -448,8 +442,8 @@ public class MageBook extends JComponent { newToken.removeSummoningSickness(); PermanentView theToken = new PermanentView(newToken, null, null, null); theToken.setInViewerOnly(true); - final MageCard cardImg = Plugins.instance.getMagePermanent(theToken, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); - cardImg.setBounds(rectangle); + final MageCard cardImg = Plugins.instance.getMagePermanent(theToken, bigCard, new CardIconRenderSettings(), cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); + cardImg.setCardContainerRef(jLayeredPane); jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10); cardImg.update(theToken); cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.getFrameWidth(), cardDimensions.getFrameHeight()); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java index 2a004ef8c45..2725f9e0d2e 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java @@ -1,10 +1,11 @@ package mage.client.deckeditor.collection.viewer; -import javax.swing.*; - import mage.cards.repository.CardScanner; import mage.client.plugins.impl.Plugins; import org.mage.card.arcane.ManaSymbols; +import org.mage.card.arcane.SvgUtils; + +import javax.swing.*; /** * @author nantuko @@ -12,6 +13,7 @@ import org.mage.card.arcane.ManaSymbols; public class TestMageBook extends JFrame { public static void main(String[] args) { Plugins.instance.loadPlugins(); + SvgUtils.checkSvgSupport(); ManaSymbols.loadImages(); CardScanner.scan(); JFrame frame = new TestMageBook(); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java index 02c64870751..83185db8d8b 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/MageCardComparator.java @@ -1,45 +1,18 @@ -/* 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.table; import mage.cards.MageCard; +import mage.client.util.comparators.CardViewComparator; import mage.game.draft.RateCard; import mage.view.CardView; import org.apache.log4j.Logger; -import java.util.Comparator; - /** * {@link MageCard} comparator. Used to sort cards in Deck Editor Table View * pane. * * @author nantuko */ -public class MageCardComparator implements Comparator { +public class MageCardComparator implements CardViewComparator { private static final Logger logger = Logger.getLogger(MageCardComparator.class); @@ -122,5 +95,10 @@ public class MageCardComparator implements Comparator { } else { return bCom.compareTo(aCom); } - }// compare() + } + + @Override + public String getCategoryName(CardView sample) { + return ""; + } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java index 0607569a699..62b549e7ccd 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java @@ -293,22 +293,17 @@ public class TableModel extends AbstractTableModel implements ICardGrid { cardEventSource.fireEvent(card, ClientEventType.SET_NUMBER, number); } - public void doubleClick(int index) { + public void doubleClick(int index, MouseEvent e, boolean forceFakeAltDown) { CardView card = view.get(index); - cardEventSource.fireEvent(card, ClientEventType.DOUBLE_CLICK); - } - - public void altDoubleClick(int index) { - CardView card = view.get(index); - cardEventSource.fireEvent(card, ClientEventType.ALT_DOUBLE_CLICK); + cardEventSource.fireEvent(card, ClientEventType.CARD_DOUBLE_CLICK, e, forceFakeAltDown); } public void removeFromMainEvent(int index) { - cardEventSource.fireEvent(ClientEventType.REMOVE_MAIN); + cardEventSource.fireEvent(ClientEventType.DECK_REMOVE_SELECTION_MAIN); } public void removeFromSideEvent(int index) { - cardEventSource.fireEvent(ClientEventType.REMOVE_SIDEBOARD); + cardEventSource.fireEvent(ClientEventType.DECK_REMOVE_SELECTION_SIDEBOARD); } public void addListeners(final JTable table) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java index d93a3bf714d..4e1ac407315 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java @@ -7,6 +7,8 @@ import javax.swing.*; import java.awt.event.KeyEvent; /** + * App GUI: about window + * * @author JayDi85 */ public class AboutDialog extends MageDialog { @@ -14,7 +16,7 @@ public class AboutDialog extends MageDialog { private static String devsList = "BetaSteward, Noxx, Eugen.Rivniy, North, LevelX2, " + "Jeff, Plopman, dustinconrad, emerald000, fireshoes, lunaskyrise, " + "mnapoleon, jgod, LoneFox, drmDev, spjspj, TheElk801, L_J, JayDi85, " - + "jmharmon, Ketsuban, hitch17"; + + "jmharmon, Ketsuban, hitch17, weirddan455"; public AboutDialog() { initComponents(); diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java index c75ca0559d0..c833801f9fd 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -22,6 +22,8 @@ import java.util.SortedSet; import java.util.TreeSet; /** + * App GUI: adding new lands to the deck, uses in deck editor and drafting + * * @author BetaSteward_at_googlemail.com */ public class AddLandDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java index 4a133d4fdc5..4e9b78ed3f4 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java @@ -1,6 +1,8 @@ package mage.client.dialog; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; import java.util.EnumSet; import java.util.LinkedHashSet; @@ -9,6 +11,8 @@ import java.util.UUID; import javax.swing.*; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; +import javax.swing.plaf.basic.BasicInternalFrameUI; + import mage.client.cards.BigCard; import mage.client.util.GUISizeHelper; import mage.client.util.ImageHelper; @@ -23,6 +27,8 @@ import org.apache.log4j.Logger; import org.mage.plugins.card.utils.impl.ImageManagerImpl; /** + * Game GUI: popup windows with title like reveal, graveyard + * * @author BetaSteward_at_googlemail.com, JayDi85 */ public class CardInfoWindowDialog extends MageDialog { @@ -44,6 +50,22 @@ public class CardInfoWindowDialog extends MageDialog { this.positioned = false; initComponents(); + // ENABLE a minimizing window on double clicks + BasicInternalFrameUI ui = (BasicInternalFrameUI) this.getUI(); + ui.getNorthPane().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks + e.consume(); + try { + CardInfoWindowDialog.this.setIcon(!CardInfoWindowDialog.this.isIcon()); + } catch (PropertyVetoException exp) { + // ignore read only + } + } + } + }); + this.setModal(false); switch (this.showType) { case LOOKED_AT: @@ -62,7 +84,7 @@ public class CardInfoWindowDialog extends MageDialog { this.setFrameIcon(new ImageIcon(ImageHelper.getImageFromResources("/info/grave.png"))); this.setClosable(true); this.setDefaultCloseOperation(HIDE_ON_CLOSE); - addInternalFrameListener(new InternalFrameAdapter() { + this.addInternalFrameListener(new InternalFrameAdapter() { @Override public void internalFrameClosing(InternalFrameEvent e) { CardInfoWindowDialog.this.hideDialog(); diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 08d463bc674..f6ebd1aab19 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -28,6 +28,8 @@ import java.util.concurrent.TimeoutException; import static mage.client.dialog.PreferencesDialog.*; /** + * App GUI: connection windows + * * @author BetaSteward_at_googlemail.com */ public class ConnectDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java index cbf581d0c00..40685b70823 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java @@ -12,6 +12,8 @@ import java.util.HashMap; import java.util.Map; /** + * App GUI: download card images window + * * @author JayDi85 */ public class DownloadImagesDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java index f33f85d39a6..b64c152213a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java @@ -1,15 +1,7 @@ - - - -/* - * ErrorDialog.java - * - * Created on Dec 23, 2009, 11:01:32 AM - */ - package mage.client.dialog; /** + * Game GUI: error dialog * * @author BetaSteward_at_googlemail.com */ diff --git a/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java b/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java index ad7f4abc7a9..16433c22ff3 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java @@ -1,10 +1,3 @@ - - - /* - * GameEndDialog.java - * - * Created on Jul 31, 2013, 9:41:00 AM - */ package mage.client.dialog; import mage.client.MageFrame; @@ -27,6 +20,8 @@ import java.util.Calendar; /** + * Game GUI: end game window + * * @author LevelX2 */ public class GameEndDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index f95da5a77f7..1f305c233e4 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -11,6 +11,8 @@ import javax.swing.*; import java.util.UUID; /** + * App GUI: join to the new game window + * * @author BetaSteward_at_googlemail.com */ public class JoinTableDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index ed875b1a062..454becb0dd5 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.UUID; /** + * App GUI: create new GAME + * * @author BetaSteward_at_googlemail.com, JayDi85 */ public class NewTableDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index c9d96160b49..4e7933b553e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -34,6 +34,8 @@ import java.util.*; import java.util.stream.Collectors; /** + * App GUI: create new TOURNEY + * * @author BetaSteward_at_googlemail.com, JayDi85 */ public class NewTournamentDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java index c7ed93b891e..9aa36e19cab 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java @@ -13,8 +13,9 @@ import java.util.*; import java.util.List; /** - * @author JayDi85 - * @author Salco + * App GUI: fast search in the combobox, uses in deck editor (sets choosing) + * + * @author Salco, JayDi85 */ public class PickCheckBoxDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java index ddf82d60f37..768d9e62208 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java @@ -11,6 +11,8 @@ import mage.client.MageFrame; import mage.client.util.gui.MageDialogState; /** + * Game GUI: choosing one of the list's item + * * @author JayDi85 */ public class PickChoiceDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickNumberDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickNumberDialog.java index 98c80bb755b..274ab866d83 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickNumberDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickNumberDialog.java @@ -7,6 +7,8 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; /** + * Game GUI: choose number + * * @author BetaSteward_at_googlemail.com */ public class PickNumberDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickPileDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickPileDialog.java index 263f2694f76..4fc66f65e26 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickPileDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickPileDialog.java @@ -1,5 +1,6 @@ package mage.client.dialog; +import mage.cards.MageCard; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.cards.CardArea; @@ -11,7 +12,9 @@ import java.awt.*; import java.util.UUID; /** - * @author BetaSteward_at_googlemail.com + * Game GUI: pile choosing (select a 1 pile from a 2 piles) + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class PickPileDialog extends MageDialog { @@ -19,6 +22,7 @@ public class PickPileDialog extends MageDialog { private final CardArea pile2; private boolean pickedPile1 = false; + private boolean pickedOK = false; /** * Create the frame. @@ -51,20 +55,22 @@ public class PickPileDialog extends MageDialog { public void cleanUp() { for (Component comp : pile1.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); pile1.remove(comp); } } for (Component comp : pile2.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); pile2.remove(comp); } } } public void loadCards(String name, CardsView pile1, CardsView pile2, BigCard bigCard, UUID gameId) { + this.pickedOK = false; + this.pickedPile1 = false; this.title = name; this.pile1.loadCardsNarrow(pile1, bigCard, gameId); this.pile2.loadCardsNarrow(pile2, bigCard, gameId); @@ -81,23 +87,25 @@ public class PickPileDialog extends MageDialog { } this.makeWindowCentered(); - this.revalidate(); // TODO: remove? - this.repaint(); // TODO: remove? - this.setVisible(true); } private void btnPile1ActionPerformed(java.awt.event.ActionEvent evt) { pickedPile1 = true; + pickedOK = true; this.hideDialog(); } private void btnPile2ActionPerformed(java.awt.event.ActionEvent evt) { pickedPile1 = false; + pickedOK = true; this.hideDialog(); } public boolean isPickedPile1() { return this.pickedPile1; } + public boolean isPickedOK() { + return this.pickedOK; + } } diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 6a17922b9f2..e1a279311cd 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -2056,7 +2056,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 85c4c2ae872..700ae492809 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -323,11 +323,22 @@ public class PreferencesDialog extends javax.swing.JDialog { if (currentTheme == null) { currentTheme = ThemeType.valueByName(getCachedValue(KEY_THEME, "Default")); logger.info("Using GUI theme: " + currentTheme.getName()); + currentTheme.reload(); } return currentTheme; } + /** + * Set and reload current theme. App need restart to apply all new settings. + * + * @param newTheme + */ + public static void setCurrentTheme(ThemeType newTheme) { + currentTheme = newTheme; + currentTheme.reload(); + } + private final JFileChooser fc = new JFileChooser(); { @@ -656,7 +667,7 @@ public class PreferencesDialog extends javax.swing.JDialog { showFullImagePath.setSelected(true); showFullImagePath.setToolTipText("Show the path Xmage is expecting for this card's image (only displays if missing)"); showFullImagePath.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - showFullImagePath.setLabel("Display image path for missing images"); + showFullImagePath.setText("Display image path for missing images"); showFullImagePath.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { showFullImagePathActionPerformed(evt); @@ -1149,7 +1160,7 @@ public class PreferencesDialog extends javax.swing.JDialog { sliderCardSizeMinBattlefield.setPaintLabels(true); sliderCardSizeMinBattlefield.setPaintTicks(true); sliderCardSizeMinBattlefield.setSnapToTicks(true); - sliderCardSizeMinBattlefield.setToolTipText("The maximum size of permanents on the battlefield"); + sliderCardSizeMinBattlefield.setToolTipText("The minimum size of permanents on the battlefield"); sliderCardSizeMinBattlefield.setBorder(javax.swing.BorderFactory.createEtchedBorder()); sliderCardSizeMinBattlefield.setMinimumSize(new java.awt.Dimension(150, 40)); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -2766,7 +2777,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabsPanel.addTab("Themes", tabThemes); - saveButton.setLabel("Save"); + saveButton.setText("Save"); saveButton.setMaximumSize(new java.awt.Dimension(100, 30)); saveButton.setMinimumSize(new java.awt.Dimension(100, 30)); saveButton.setPreferredSize(new java.awt.Dimension(100, 30)); @@ -2777,7 +2788,7 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); - exitButton.setLabel("Exit"); + exitButton.setText("Exit"); exitButton.setMaximumSize(new java.awt.Dimension(100, 30)); exitButton.setMinimumSize(new java.awt.Dimension(100, 30)); exitButton.setPreferredSize(new java.awt.Dimension(100, 30)); diff --git a/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.form b/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.form deleted file mode 100644 index aa03e839ee4..00000000000 --- a/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.form +++ /dev/null @@ -1,75 +0,0 @@ - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.java b/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.java deleted file mode 100644 index 1b325045fe4..00000000000 --- a/Mage.Client/src/main/java/mage/client/dialog/QuestionDialog.java +++ /dev/null @@ -1,103 +0,0 @@ - - - -/* - * QuestionDialog.java - * - * Created on Dec 23, 2009, 11:01:32 AM - */ - -package mage.client.dialog; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class QuestionDialog extends MageDialog { - - private boolean answer; - - /** Creates new form QuestionDialog */ - public QuestionDialog() { - initComponents(); - } - - public void showDialog(String question) { - this.lblQuestion.setText(question); - this.setModal(true); - this.setVisible(true); - } - - public boolean getAnswer() { - return answer; - } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - lblQuestion = new javax.swing.JLabel(); - btnNo = new javax.swing.JButton(); - btnYes = new javax.swing.JButton(); - - lblQuestion.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - lblQuestion.setText("question"); - - btnNo.setText("No"); - btnNo.addActionListener(evt -> btnNoActionPerformed(evt)); - - btnYes.setText("Yes"); - btnYes.addActionListener(evt -> btnYesActionPerformed(evt)); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblQuestion, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(btnYes) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnNo))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(lblQuestion, javax.swing.GroupLayout.DEFAULT_SIZE, 35, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnNo) - .addComponent(btnYes)) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void btnYesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnYesActionPerformed - this.answer = true; - this.hideDialog(); - }//GEN-LAST:event_btnYesActionPerformed - - private void btnNoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNoActionPerformed - this.answer = false; - this.hideDialog(); - }//GEN-LAST:event_btnNoActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton btnNo; - private javax.swing.JButton btnYes; - private javax.swing.JLabel lblQuestion; - // End of variables declaration//GEN-END:variables - -} diff --git a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java index bebc65749ca..3bce596914e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java @@ -13,6 +13,10 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +/** + * App GUI: register new user on the server + * + */ public class RegisterUserDialog extends MageDialog { private static final Logger logger = Logger.getLogger(ConnectDialog.class); diff --git a/Mage.Client/src/main/java/mage/client/dialog/ResetPasswordDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ResetPasswordDialog.java index 5e99e421277..e13768c25d6 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ResetPasswordDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ResetPasswordDialog.java @@ -13,6 +13,10 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +/** + * App GUI: reset password on the server + * + */ public class ResetPasswordDialog extends MageDialog { private static final Logger logger = Logger.getLogger(ResetPasswordDialog.class); diff --git a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java index 3bcebfbc884..1bb46106f48 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java @@ -1,12 +1,6 @@ - - - /* - * ShowCardsDialog.java - * - * Created on 3-Feb-2010, 8:59:11 PM - */ package mage.client.dialog; + import mage.cards.MageCard; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.cards.CardArea; @@ -25,6 +19,8 @@ import java.util.UUID; /** + * Game GUI: choose target card from the cards list (example: exile and choose card to cast) + * * @author BetaSteward_at_googlemail.com */ public class ShowCardsDialog extends MageDialog { @@ -48,8 +44,8 @@ public void cleanUp() { cardArea.cleanUp(); for (Component comp : cardArea.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); + if (comp instanceof MageCard) { + ((MageCard) comp).cleanUp(); cardArea.remove(comp); } } diff --git a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java index 10c6d2bad09..7ebb872e589 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java @@ -1,10 +1,3 @@ - - - /* - * TableWaitingDialog.java - * - * Created on Dec 16, 2009, 10:27:44 AM - */ package mage.client.dialog; import java.awt.Dimension; @@ -40,6 +33,8 @@ import static mage.client.dialog.PreferencesDialog.KEY_TABLE_WAITING_COLUMNS_WID import static mage.client.dialog.PreferencesDialog.KEY_TABLES_DIVIDER_LOCATION_4; /** + * App GUI: waiting other players before join to a table + * * @author BetaSteward_at_googlemail.com */ public class TableWaitingDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form index 5f3f05dd822..a1b89d45fce 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -19,14 +19,15 @@ - + + + - + - @@ -34,9 +35,13 @@ + + + + - + @@ -56,12 +61,16 @@ + + - + + + @@ -127,5 +136,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 0ec715dc174..977f8ea9cdb 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -1,9 +1,12 @@ package mage.client.dialog; -import mage.cards.Card; -import mage.cards.CardGraphicInfo; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.icon.CardIconImpl; +import mage.abilities.icon.CardIconType; +import mage.abilities.icon.CardIconOrder; +import mage.abilities.icon.CardIconPosition; +import mage.cards.*; import mage.cards.decks.Deck; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -11,9 +14,14 @@ import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; import mage.client.cards.BigCard; +import mage.client.themes.ThemeType; +import mage.client.util.ClientEventType; +import mage.client.util.Event; import mage.client.util.GUISizeHelper; +import mage.client.util.Listener; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; +import mage.constants.Zone; import mage.game.Game; import mage.game.GameImpl; import mage.game.command.Emblem; @@ -21,9 +29,11 @@ import mage.game.command.Plane; import mage.game.match.MatchType; import mage.game.mulligan.Mulligan; import mage.game.mulligan.MulliganType; +import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.players.Player; import mage.players.StubPlayer; +import mage.util.RandomUtil; import mage.view.*; import org.apache.log4j.Logger; import org.mage.card.arcane.CardPanel; @@ -31,21 +41,20 @@ import org.mage.card.arcane.CardPanel; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.*; import java.util.List; -import java.util.Set; -import java.util.UUID; /** + * App GUI: debug only, testing card renders and manipulations + * * @author JayDi85 */ public class TestCardRenderDialog extends MageDialog { private static final Logger logger = Logger.getLogger(TestCardRenderDialog.class); float cardSizeMod = 1.0f; + private Game game = null; + Listener cardListener = null; public TestCardRenderDialog() { initComponents(); @@ -54,6 +63,24 @@ public class TestCardRenderDialog extends MageDialog { public void showDialog() { this.setModal(false); getRootPane().setDefaultButton(buttonCancel); + + // init render mode + this.comboRenderMode.setSelectedIndex(PreferencesDialog.getRenderMode()); + + // init themes list + this.comboTheme.setModel(new DefaultComboBoxModel(ThemeType.values())); + this.comboTheme.setSelectedItem(PreferencesDialog.getCurrentTheme()); + + // debug logs to show current component + /* + Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { + public void eventDispatched(AWTEvent e) { + logger.info("component: " + e.getSource()); + } + }, AWTEvent.MOUSE_EVENT_MASK); + */ + + // render cards reloadCards(); // windows settings @@ -75,12 +102,15 @@ public class TestCardRenderDialog extends MageDialog { this.removeDialog(); } - private PermanentView createPermanentCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage, boolean tapped) { + private PermanentView createPermanentCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage, boolean tapped, List extraAbilities) { CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber); ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code); CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(), new CardGraphicInfo(cardInfo.getFrameStyle(), cardInfo.usesVariousArt())); Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + if (extraAbilities != null) { + extraAbilities.forEach(ability -> card.addAbility(ability)); + } Set cardsList = new HashSet<>(); cardsList.add(card); @@ -154,59 +184,86 @@ public class TestCardRenderDialog extends MageDialog { } private void reloadCards() { + // apply selected theme (warning, it will be applied for all app, so can be bugged in other dialogs - but it's ok for debug) + PreferencesDialog.setCurrentTheme((ThemeType) comboTheme.getSelectedItem()); + cardsPanel.cleanUp(); cardsPanel.setCustomRenderMode(comboRenderMode.getSelectedIndex()); - cardsPanel.setCustomNeedFullPermanentRender(false); // to fix cropped/position bugged with PermanentView (CardArean do not support it as normal, see CardArea for info) + cardsPanel.setCustomNeedFullPermanentRender(true); // enable full battlefield render mode (it was bugged in test dialog so was disabled in old days, not it works fine) cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); cardsPanel.setCustomXOffsetBetweenCardsOrColumns(10); - cardsPanel.changeGUISize(); // reload new settings + cardsPanel.setCustomCardIconsPanelPosition(CardIconPosition.fromString((String) comboCardIconsPosition.getSelectedItem())); + cardsPanel.setCustomCardIconsPanelOrder(CardIconOrder.fromString((String) comboCardIconsOrder.getSelectedItem())); + cardsPanel.setCustomCardIconsMaxVisibleCount((Integer) spinnerCardIconsMaxVisible.getValue()); + int needAdditionalIcons = Math.min(99, Math.max(0, (Integer) spinnerCardIconsAdditionalAmount.getValue())); - // create custom mouse listener - cardsPanel.setCustomMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - cardsPanel.mouseClicked(e); // default + // reload new settings + cardsPanel.changeGUISize(); - // make cards chooseable or not - if (e.getSource() instanceof CardPanel) { - CardPanel panel = (CardPanel) e.getSource(); - panel.setChoosable(!panel.isChoosable()); - cardsPanel.redraw(); + // sample popup menus + JMenuItem item; + JPopupMenu popupCardMenu = new JPopupMenu(); + item = new JMenuItem("Card menu 1"); + popupCardMenu.add(item); + item = new JMenuItem("Card menu 2"); + popupCardMenu.add(item); + item = new JMenuItem("Card menu 3"); + popupCardMenu.add(item); + // + JPopupMenu popupPanelMenu = new JPopupMenu(); + item = new JMenuItem("Panel menu 1"); + popupPanelMenu.add(item); + item = new JMenuItem("Panel menu 2"); + popupPanelMenu.add(item); + item = new JMenuItem("Panel menu 3"); + popupPanelMenu.add(item); + + // init card listener for clicks, menu and other events + if (this.cardListener == null) { + this.cardListener = event -> { + switch(event.getEventType()) { + case CARD_CLICK: + case CARD_DOUBLE_CLICK: + handleCardClick(event); + break; + case CARD_POPUP_MENU: + if (event.getSource() != null) { + // card + handlePopupMenu(event, popupCardMenu); + } else { + // panel + handlePopupMenu(event, popupPanelMenu); + } + break; } - } + }; + cardsPanel.addCardEventListener(this.cardListener); + } - @Override - public void mousePressed(MouseEvent e) { - cardsPanel.mousePressed(e); // default - } - - @Override - public void mouseReleased(MouseEvent e) { - cardsPanel.mouseReleased(e); // default - } - - @Override - public void mouseEntered(MouseEvent e) { - cardsPanel.mouseEntered(e); // default - } - - @Override - public void mouseExited(MouseEvent e) { - cardsPanel.mouseExited(e); // default - } - }); - - Game game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20); + game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20); Deck deck = new Deck(); Player playerYou = new StubPlayer("player1", RangeOfInfluence.ALL); game.addPlayer(playerYou, deck); Player playerOpponent = new StubPlayer("player2", RangeOfInfluence.ALL); game.addPlayer(playerOpponent, deck); + List additionalIcons = Collections.singletonList(new SimpleStaticAbility(Zone.ALL, null)); + for (int i = 0; i < needAdditionalIcons; i++) { + String text = ""; + if (RandomUtil.nextBoolean()) { + if (RandomUtil.nextBoolean()) { + text = "75"; + } else { + text = "8"; + } + } + additionalIcons.get(0).addIcon(new CardIconImpl(CardIconType.PLAYABLE_COUNT, "test icon " + i + 1, text)); + } + List cardViews = new ArrayList<>(); /* // test morphed - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false)); // mountain - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false, null)); // mountain + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true, null)); // Judith, the Scourge Diva cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card) cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card) cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down @@ -215,22 +272,30 @@ public class TestCardRenderDialog extends MageDialog { //*/ /* //test emblems - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows - cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle - cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false, null)); // Noxious Groodion + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false, null)); // Knight of Sorrows + cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false, null)); // Huntmaster of the Fells, transforms + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false, null)); // Bedeck // Bedazzle + cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false, null)); // Conqueror's Galleon cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum //*/ - //* //test split, transform and mdf in hands + /* //test split, transform and mdf in hands cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior //*/ + //* //test card icons + cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears + cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms + cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, additionalIcons)); // Hinterland Drake + cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, additionalIcons)); // Kathari Remnant + + //*/ + // duplicate cards if (checkBoxGenerateManyCards.isSelected()) { while (cardViews.size() < 30) { @@ -248,6 +313,39 @@ public class TestCardRenderDialog extends MageDialog { cardsPanel.loadCards(view, big, game.getId()); } + private void handleCardClick(Event event) { + MageCard panel = (MageCard) event.getComponent(); + if (event.getEventType() == ClientEventType.CARD_DOUBLE_CLICK) { + // card tap + if (panel.getMainPanel() instanceof CardPanel) { + CardPanel main = (CardPanel) panel.getMainPanel(); + if (main.getGameCard() instanceof PermanentView) { + // new settings must be as a new copy -- it would activate the animations + PermanentView oldPermanent = (PermanentView) main.getGameCard(); + PermanentView newPermament = new PermanentView( + (Permanent) oldPermanent.getOriginalCard(), + game.getCard(oldPermanent.getOriginalCard().getId()), + UUID.randomUUID(), + game + ); + newPermament.overrideTapped(!oldPermanent.isTapped()); + main.update(newPermament); + } + } + } else { + // card choose + panel.setChoosable(!panel.isChoosable()); + } + cardsPanel.redraw(); + } + + private void handlePopupMenu(Event event, JPopupMenu popupMenu) { + //Point p = event.getComponent().getLocationOnScreen(); + Point p = MouseInfo.getPointerInfo().getLocation(); + popupMenu.show(this, 0, 0); // use relative coords + popupMenu.setLocation(p); // use screen coords + } + private int getCardWidth() { if (GUISizeHelper.editorCardDimension == null) { return 200; @@ -277,6 +375,17 @@ public class TestCardRenderDialog extends MageDialog { sliderSize = new javax.swing.JSlider(); labelSize = new javax.swing.JLabel(); checkBoxGenerateManyCards = new javax.swing.JCheckBox(); + panelCardIcons = new javax.swing.JPanel(); + labelCardIconsPosition = new javax.swing.JLabel(); + comboCardIconsPosition = new javax.swing.JComboBox<>(); + labelCardIconsMaxVisible = new javax.swing.JLabel(); + spinnerCardIconsMaxVisible = new javax.swing.JSpinner(); + labelCardIconsAdditionalAmount = new javax.swing.JLabel(); + spinnerCardIconsAdditionalAmount = new javax.swing.JSpinner(); + labelCardIconsOrder = new javax.swing.JLabel(); + comboCardIconsOrder = new javax.swing.JComboBox<>(); + labelTheme = new javax.swing.JLabel(); + comboTheme = new javax.swing.JComboBox<>(); buttonCancel.setText("Close"); buttonCancel.addActionListener(new java.awt.event.ActionListener() { @@ -294,7 +403,7 @@ public class TestCardRenderDialog extends MageDialog { labelRenderMode.setText("Render mode:"); - comboRenderMode.setModel(new javax.swing.DefaultComboBoxModel<>(new String[]{"MTGO", "Image"})); + comboRenderMode.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "MTGO", "Image" })); comboRenderMode.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { comboRenderModeItemStateChanged(evt); @@ -316,49 +425,141 @@ public class TestCardRenderDialog extends MageDialog { } }); + labelCardIconsPosition.setText("Card icons position:"); + + comboCardIconsPosition.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "TOP", "LEFT", "RIGHT", "BOTTOM", "CORNER_TOP_LEFT", "CORNER_TOP_RIGHT", "CORNER_BOTTOM_LEFT", "CORNER_BOTTOM_RIGHT" })); + comboCardIconsPosition.setSelectedIndex(1); + comboCardIconsPosition.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + comboCardIconsPositionItemStateChanged(evt); + } + }); + + labelCardIconsMaxVisible.setText("Max visible:"); + + spinnerCardIconsMaxVisible.setValue(3); + spinnerCardIconsMaxVisible.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + spinnerCardIconsMaxVisibleStateChanged(evt); + } + }); + + labelCardIconsAdditionalAmount.setText("Add additional icons:"); + + spinnerCardIconsAdditionalAmount.setValue(10); + spinnerCardIconsAdditionalAmount.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + spinnerCardIconsAdditionalAmountStateChanged(evt); + } + }); + + labelCardIconsOrder.setText("Order:"); + + comboCardIconsOrder.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "START", "CENTER", "END" })); + comboCardIconsOrder.setSelectedIndex(2); + comboCardIconsOrder.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + comboCardIconsOrderItemStateChanged(evt); + } + }); + + javax.swing.GroupLayout panelCardIconsLayout = new javax.swing.GroupLayout(panelCardIcons); + panelCardIcons.setLayout(panelCardIconsLayout); + panelCardIconsLayout.setHorizontalGroup( + panelCardIconsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelCardIconsLayout.createSequentialGroup() + .addComponent(labelCardIconsPosition) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboCardIconsPosition, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelCardIconsOrder) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboCardIconsOrder, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelCardIconsMaxVisible) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spinnerCardIconsMaxVisible, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelCardIconsAdditionalAmount) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spinnerCardIconsAdditionalAmount, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(188, Short.MAX_VALUE)) + ); + panelCardIconsLayout.setVerticalGroup( + panelCardIconsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelCardIconsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(comboCardIconsPosition, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelCardIconsPosition) + .addComponent(labelCardIconsMaxVisible) + .addComponent(spinnerCardIconsMaxVisible, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelCardIconsAdditionalAmount) + .addComponent(spinnerCardIconsAdditionalAmount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelCardIconsOrder) + .addComponent(comboCardIconsOrder, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + + labelTheme.setText("Theme:"); + + comboTheme.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "loading..." })); + comboTheme.setToolTipText("WARNING, selected theme will be applied to full app, not render dialog only"); + comboTheme.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + comboThemeItemStateChanged(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 748, Short.MAX_VALUE) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(cardsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(buttonReloadCards) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(labelRenderMode) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(labelSize) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkBoxGenerateManyCards) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(panelCardIcons, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(cardsPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(buttonReloadCards) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelRenderMode) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelTheme) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelSize) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(checkBoxGenerateManyCards) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) ); layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(buttonReloadCards) - .addComponent(labelRenderMode) - .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(labelSize)) - .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(checkBoxGenerateManyCards)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cardsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(buttonReloadCards) + .addComponent(labelRenderMode) + .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelSize) + .addComponent(comboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelTheme)) + .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(checkBoxGenerateManyCards)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(panelCardIcons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cardsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 401, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); pack(); @@ -373,7 +574,10 @@ public class TestCardRenderDialog extends MageDialog { }//GEN-LAST:event_buttonReloadCardsActionPerformed private void comboRenderModeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_comboRenderModeItemStateChanged - reloadCards(); + // render modes are loading on show dialog, so must ignore change event on startup + if (this.isVisible()) { + reloadCards(); + } }//GEN-LAST:event_comboRenderModeItemStateChanged private void sliderSizeStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sliderSizeStateChanged @@ -389,15 +593,49 @@ public class TestCardRenderDialog extends MageDialog { reloadCards(); }//GEN-LAST:event_checkBoxGenerateManyCardsItemStateChanged + private void comboCardIconsPositionItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_comboCardIconsPositionItemStateChanged + reloadCards(); + }//GEN-LAST:event_comboCardIconsPositionItemStateChanged + + private void spinnerCardIconsMaxVisibleStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerCardIconsMaxVisibleStateChanged + reloadCards(); + }//GEN-LAST:event_spinnerCardIconsMaxVisibleStateChanged + + private void spinnerCardIconsAdditionalAmountStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerCardIconsAdditionalAmountStateChanged + reloadCards(); + }//GEN-LAST:event_spinnerCardIconsAdditionalAmountStateChanged + + private void comboCardIconsOrderItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_comboCardIconsOrderItemStateChanged + reloadCards(); + }//GEN-LAST:event_comboCardIconsOrderItemStateChanged + + private void comboThemeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_comboThemeItemStateChanged + // themes list are loading on show dialog, so must ignore change event on startup + if (this.isVisible()) { + reloadCards(); + } + }//GEN-LAST:event_comboThemeItemStateChanged + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton buttonCancel; private javax.swing.JButton buttonReloadCards; private mage.client.cards.CardArea cardsPanel; private javax.swing.JCheckBox checkBoxGenerateManyCards; + private javax.swing.JComboBox comboCardIconsOrder; + private javax.swing.JComboBox comboCardIconsPosition; private javax.swing.JComboBox comboRenderMode; + private javax.swing.JComboBox comboTheme; + private javax.swing.JLabel labelCardIconsAdditionalAmount; + private javax.swing.JLabel labelCardIconsMaxVisible; + private javax.swing.JLabel labelCardIconsOrder; + private javax.swing.JLabel labelCardIconsPosition; private javax.swing.JLabel labelRenderMode; private javax.swing.JLabel labelSize; + private javax.swing.JLabel labelTheme; + private javax.swing.JPanel panelCardIcons; private javax.swing.JSlider sliderSize; + private javax.swing.JSpinner spinnerCardIconsAdditionalAmount; + private javax.swing.JSpinner spinnerCardIconsMaxVisible; // End of variables declaration//GEN-END:variables } diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestModalDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestModalDialog.java index fd2e785cbef..d59f029d59a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestModalDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestModalDialog.java @@ -7,6 +7,8 @@ import javax.swing.*; import java.awt.event.KeyEvent; /** + * App GUI: debug only, testing dialogs system + * * @author JayDi85 */ public class TestModalDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestModalSampleDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestModalSampleDialog.java index 19575ad9a08..1c0654445a5 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestModalSampleDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestModalSampleDialog.java @@ -7,6 +7,8 @@ import java.awt.*; import java.awt.event.KeyEvent; /** + * App GUI: debug only, sample dialog for testing dialogs system + * * @author JayDi85 */ public class TestModalSampleDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java index 368e6b3422b..f04af5dfd62 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java @@ -10,6 +10,8 @@ import javax.swing.plaf.basic.BasicInternalFrameUI; import java.awt.*; /** + * App GUI: confirm some actions from the user (example: close the app) + * * @author BetaSteward_at_googlemail.com */ public class UserRequestDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java index 11a30abb4d1..4c00fc76a2a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java @@ -28,6 +28,8 @@ import java.util.Scanner; import java.util.concurrent.TimeUnit; /** + * App GUI: show latest xmage news from the web page + * * @author JayDi85 */ public class WhatsNewDialog extends MageDialog { diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPane.java b/Mage.Client/src/main/java/mage/client/draft/DraftPane.java index 6d26f124e39..5e58b425604 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPane.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPane.java @@ -1,10 +1,3 @@ - - - /* - * DraftPane.java - * - * Created on Jan 7, 2011, 2:11:44 PM - */ package mage.client.draft; import java.awt.Component; @@ -16,6 +9,7 @@ import mage.client.MagePane; import mage.client.plugins.impl.Plugins; /** + * Game GUI: draft panel with scrolls * * @author BetaSteward_at_googlemail.com */ diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index f63d0853e6e..5c377eddddf 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -1,10 +1,3 @@ - - - /* - * DraftPanel.java - * - * Created on Jan 7, 2011, 2:15:48 PM - */ package mage.client.draft; import mage.cards.repository.CardInfo; @@ -21,10 +14,12 @@ import mage.client.util.gui.BufferedImageBuilder; import mage.constants.PlayerAction; import mage.view.*; + import org.apache.log4j.Logger; import javax.swing.Timer; import javax.swing.*; import java.awt.*; + import java.awt.dnd.DragSourceEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -35,10 +30,14 @@ import java.util.*; /** - * @author BetaSteward_at_googlemail.com + * Game GUI: draft panel for drafting game mode only + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class DraftPanel extends javax.swing.JPanel { + private static final Logger logger = Logger.getLogger(DraftPanel.class); + private UUID draftId; private Timer countdown; private int timeout; @@ -73,6 +72,9 @@ private static final CardsView EMPTY_VIEW = new CardsView(); + private Listener selectedCardsListener = null; + private Listener pickingCardsListener = null; + /** * Creates new form DraftPanel */ @@ -247,47 +249,52 @@ // upper area that shows the picks loadCardsToPickedCardsArea(draftPickView.getPicks()); - this.draftPicks.clearCardEventListeners(); - this.draftPicks.addCardEventListener((Listener) event -> { - if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { - if (event.getSource() != null) { - // Popup Menu Card - cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId(); - popupMenuCardPanel.show(event.getComponent(), event.getxPos(), event.getyPos()); - } else { - // Popup Menu area - popupMenuPickedArea.show(event.getComponent(), event.getxPos(), event.getyPos()); - } + // ENABLE clicks on selected/picked cards + if (this.selectedCardsListener == null) { + this.selectedCardsListener = event -> { + if (event.getEventType() == ClientEventType.CARD_POPUP_MENU) { + if (event.getSource() != null) { + // Popup Menu Card + cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId(); + popupMenuCardPanel.show(event.getComponent(), event.getxPos(), event.getyPos()); + } else { + // Popup Menu area + popupMenuPickedArea.show(event.getComponent(), event.getxPos(), event.getyPos()); } } - ); + }; + this.draftPicks.addCardEventListener(this.selectedCardsListener); + } // lower area that shows the booster - draftBooster.loadBooster(CardsViewUtil.convertSimple(draftPickView.getBooster()), bigCard); - this.draftBooster.clearCardEventListeners(); - this.draftBooster.addCardEventListener( - (Listener) event -> { - if (event.getEventType() == ClientEventType.PICK_A_CARD) { - SimpleCardView source = (SimpleCardView) event.getSource(); - DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden); - if (view != null) { - loadCardsToPickedCardsArea(view.getPicks()); - draftBooster.loadBooster(EMPTY_VIEW, bigCard); - Plugins.instance.getActionCallback().hideOpenComponents(); - setMessage("Waiting for other players"); - } - } - if (event.getEventType() == ClientEventType.MARK_A_CARD) { - SimpleCardView source = (SimpleCardView) event.getSource(); - SessionHandler.sendCardMark(draftId, source.getId()); + this.draftBooster.loadBooster(CardsViewUtil.convertSimple(draftPickView.getBooster()), bigCard); + if (this.pickingCardsListener == null) { + this.pickingCardsListener = event -> { + if (event.getEventType() == ClientEventType.DRAFT_PICK_CARD) { + logger.info("draft panel: catch pick card"); + SimpleCardView source = (SimpleCardView) event.getSource(); + DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden); + if (view != null) { + loadCardsToPickedCardsArea(view.getPicks()); + draftBooster.loadBooster(EMPTY_VIEW, bigCard); + Plugins.instance.getActionCallback().hideOpenComponents(); + setMessage("Waiting for other players"); } + } else if (event.getEventType() == ClientEventType.DRAFT_MARK_CARD) { + logger.info("draft panel: catch mark card"); + SimpleCardView source = (SimpleCardView) event.getSource(); + SessionHandler.sendCardMark(draftId, source.getId()); } - ); + }; + this.draftBooster.addCardEventListener(this.pickingCardsListener); + } + setMessage("Pick a card"); if (!AppUtil.isAppActive()) { MageTray.instance.displayMessage("Pick the next card."); MageTray.instance.blink(); } + countdown.stop(); this.timeout = draftPickView.getTimeout(); setTimeout(timeout); diff --git a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java index 1a86bde7e56..e3c939bae74 100644 --- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java @@ -1,22 +1,31 @@ package mage.client.game; +import mage.abilities.icon.CardIconRenderSettings; +import mage.cards.MageCard; import mage.cards.MagePermanent; +import mage.client.MageFrame; import mage.client.cards.BigCard; -import mage.client.cards.Permanent; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.ClientDefaultSettings; import mage.client.util.GUISizeHelper; import mage.client.util.audio.AudioManager; import mage.client.util.layout.CardLayoutStrategy; -import mage.client.util.layout.impl.OldCardLayoutStrategy; +import mage.client.util.layout.impl.CardLayoutStrategyImpl; +import mage.interfaces.callback.ClientCallback; +import mage.interfaces.callback.ClientCallbackMethod; +import mage.util.DebugUtil; import mage.view.CounterView; import mage.view.PermanentView; +import org.apache.log4j.Logger; import javax.swing.*; +import javax.swing.Timer; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import java.awt.*; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.List; @@ -24,11 +33,23 @@ import java.util.*; import java.util.Map.Entry; /** - * @author BetaSteward_at_googlemail.com + * Game GUI: battlefield panel (cards panel + scrollbars) + * + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class BattlefieldPanel extends javax.swing.JLayeredPane { - private final Map permanents = new LinkedHashMap<>(); + private static final Logger logger = Logger.getLogger(BattlefieldPanel.class); + + private static final int GAME_REDRAW_TIMEOUT_MS = 300; // timeout before game goes to redraw on scrollbars change + + // WARNING, permanents contains top level PANELS (cards), use getMainPanel for real MagePermanent (permanents) + // Source code logic and naming here: + // * MageCard card - top layer panel (example: permanent + icons layer + another layer); + // * MagePermanent permanent - original card panel with all data, but without additional panels like icons; + // * Only MagePermanent allows for panels here, so getMainPanel() must return MagePermanent all the time + private final Map permanents = new LinkedHashMap<>(); + private UUID gameId; private BigCard bigCard; private final Map uiComponentsList = new HashMap<>(); @@ -36,10 +57,11 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { protected Map battlefield; private Dimension cardDimension; - private JLayeredPane jPanel; - private JScrollPane jScrollPane; + private JLayeredPane jPanel; // cards + private JScrollPane jScrollPane; // scrollbars + private final CardLayoutStrategy layoutStrategy = new CardLayoutStrategyImpl(); - private final CardLayoutStrategy layoutStrategy = new OldCardLayoutStrategy(); + private javax.swing.Timer gameUpdateTimer; // timer for custom GUI re-drawing (example: update attack arrows) //private static int iCounter = 0; private boolean addedPermanent; @@ -57,6 +79,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { uiComponentsList.put("battlefieldPanel", this); initComponents(); uiComponentsList.put("jPanel", jPanel); + uiComponentsList.put("scrollPane", this.jScrollPane); setGUISize(); addComponentListener(new ComponentAdapter() { @@ -65,6 +88,12 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { updateSize(); } }); + + gameUpdateTimer = new Timer(GAME_REDRAW_TIMEOUT_MS, evt -> SwingUtilities.invokeLater(() -> { + gameUpdateTimer.stop(); + ClientCallback updateMessage = new ClientCallback(ClientCallbackMethod.GAME_REDRAW_GUI, gameId); + MageFrame.getInstance().processCallback(updateMessage); + })); } public void updateSize() { @@ -78,13 +107,16 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { } public void cleanUp() { + gameUpdateTimer.stop(); + for (Component c : this.jPanel.getComponents()) { - if (c instanceof Permanent || c instanceof MagePermanent) { - this.jPanel.remove(c); + if (c instanceof MageCard) { + if (((MageCard) c).getMainPanel() instanceof MagePermanent) { + this.jPanel.remove(c); + } } } permanents.clear(); - // Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values()); this.bigCard = null; } @@ -96,6 +128,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { private void setGUISize() { jScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); jScrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + // scrollbars speed changes on layout (depends on cards amount) cardDimension = GUISizeHelper.battlefieldCardMaxDimension; } @@ -108,6 +141,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { } public void update(Map battlefield) { + gameUpdateTimer.stop(); boolean changed = false; List permanentsToAdd = new ArrayList<>(); @@ -115,14 +149,15 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { if (!permanent.isPhasedIn()) { continue; } - MagePermanent oldMagePermanent = permanents.get(permanent.getId()); + MageCard oldFound = permanents.get(permanent.getId()); + MagePermanent oldMagePermanent = oldFound == null ? null : (MagePermanent) oldFound.getMainPanel(); if (oldMagePermanent == null) { permanentsToAdd.add(permanent); changed = true; } else { if (!changed) { changed = oldMagePermanent.getOriginalPermanent().isCreature() != permanent.isCreature(); - // Check if there was a chnage in the permanets that are the permanent attached to + // Check if there was a change in the permanets that are the permanent attached to if (!changed) { int attachments = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); int attachmentsBefore = oldMagePermanent.getLinks().size(); @@ -130,7 +165,8 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { changed = true; } else if (attachments > 0) { Set attachmentIds = new HashSet<>(permanent.getAttachments()); - for (MagePermanent magePermanent : oldMagePermanent.getLinks()) { + for (MageCard mageCard : oldMagePermanent.getLinks()) { + MagePermanent magePermanent = (MagePermanent) mageCard.getMainPanel(); if (!attachmentIds.contains(magePermanent.getOriginalPermanent().getId())) { // that means that the amount of attachments is the same // but they are different: @@ -161,7 +197,6 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { changed = true; } } - } oldMagePermanent.update(permanent); } @@ -184,8 +219,8 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { removedCreature = false; - for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext(); ) { - Entry entry = iterator.next(); + for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext(); ) { + Entry entry = iterator.next(); if (!battlefield.containsKey(entry.getKey()) || !battlefield.get(entry.getKey()).isPhasedIn()) { removePermanent(entry.getKey(), 1); iterator.remove(); @@ -221,28 +256,17 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { if (cardDimension == null) { cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); } - final MagePermanent perm = Plugins.instance.getMagePermanent(permanent, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); + final MageCard perm = Plugins.instance.getMagePermanent(permanent, bigCard, new CardIconRenderSettings(), cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); + perm.setCardContainerRef(jPanel); + perm.update(permanent); + // cards sizes changes in parent call by sortLayout + //perm.setCardBounds permanents.put(permanent.getId(), perm); - BattlefieldPanel.this.jPanel.add(perm, 10); - //this.jPanel.add(perm); - if (!Plugins.instance.isCardPluginLoaded()) { - moveToFront(perm); - perm.update(permanent); - } else { - moveToFront(jPanel); - Plugins.instance.onAddCard(perm, 1); - /*Thread t = new Thread(new Runnable() { - @Override - public void run() { - Plugins.getInstance().onAddCard(perm, count); - } - }); - synchronized (this) { - threads.add(t); - }*/ - } + this.jPanel.add(perm, (Integer) 10); + moveToFront(jPanel); + Plugins.instance.onAddCard(perm, 1); if (permanent.isArtifact()) { addedArtifact = true; @@ -254,24 +278,22 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { } private void removePermanent(UUID permanentId, final int count) { - for (Component c : this.jPanel.getComponents()) { - final Component comp = c; - if (comp instanceof Permanent) { - if (((Permanent) comp).getPermanentId().equals(permanentId)) { - comp.setVisible(false); - this.jPanel.remove(comp); - } - } else if (comp instanceof MagePermanent) { - if (((MagePermanent) comp).getOriginal().getId().equals(permanentId)) { - Thread t = new Thread(() -> { - Plugins.instance.onRemoveCard((MagePermanent) comp, count); - comp.setVisible(false); - BattlefieldPanel.this.jPanel.remove(comp); - }); - t.start(); - } - if (((MagePermanent) comp).getOriginal().isCreature()) { - removedCreature = true; + for (Component comp : this.jPanel.getComponents()) { + if (comp instanceof MageCard) { + MageCard mageCard = (MageCard) comp; + if (mageCard.getMainPanel() instanceof MagePermanent) { + MagePermanent magePermanent = (MagePermanent) mageCard.getMainPanel(); + if (magePermanent.getOriginal().getId().equals(permanentId)) { + Thread t = new Thread(() -> { + Plugins.instance.onRemoveCard(mageCard, count); + mageCard.setVisible(false); + this.jPanel.remove(mageCard); + }); + t.start(); + } + if (magePermanent.getOriginal().isCreature()) { + removedCreature = true; + } } } } @@ -282,7 +304,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { return false; } - public Map getPermanents() { + public Map getPermanentPanels() { return permanents; } @@ -293,12 +315,26 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { jPanel.setLayout(null); jPanel.setOpaque(false); jScrollPane = new JScrollPane(jPanel); + if (DebugUtil.GUI_GAME_DRAW_BATTLEFIELD_BORDER) { + jPanel.setBorder(BorderFactory.createLineBorder(Color.MAGENTA)); + jScrollPane.setBorder(BorderFactory.createLineBorder(Color.green)); + } Border empty = new EmptyBorder(0, 0, 0, 0); jScrollPane.setBorder(empty); jScrollPane.setViewportBorder(empty); jScrollPane.setOpaque(false); jScrollPane.getViewport().setOpaque(false); + jScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + if (gameUpdateTimer.isRunning()) { + gameUpdateTimer.restart(); + } else { + gameUpdateTimer.start(); + } + } + }); this.add(jScrollPane); } diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index aac7f629957..3fc47ef18c3 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -67,7 +67,8 @@ public class FeedbackPanel extends javax.swing.JPanel { int messageId, boolean gameNeedUserFeedback, TurnPhase gameTurnPhase) { synchronized (this) { if (messageId < this.lastMessageId) { - LOGGER.warn("ignoring message from later source: " + messageId + ", text=" + message); + // if too many warning messages here then look at GAME_REDRAW_GUI event logic + LOGGER.warn("catch un-synced message from later source (possible reason: connection or performance problems): " + messageId + ", text=" + message); return; } this.lastMessageId = messageId; @@ -76,7 +77,6 @@ public class FeedbackPanel extends javax.swing.JPanel { this.helper.setOriginalId(null); // reference to the feedback causing ability String lblText = addAdditionalText(message, options); this.helper.setTextArea(lblText); - //this.lblMessage.setText(lblText); this.mode = mode; switch (this.mode) { @@ -167,6 +167,12 @@ public class FeedbackPanel extends javax.swing.JPanel { } private void handleOptions(Map options) { + // clear already opened dialog (second request) + if (connectedDialog != null) { + connectedDialog.removeDialog(); + connectedDialog = null; + } + if (options != null) { if (options.containsKey("UI.left.btn.text")) { String text = (String) options.get("UI.left.btn.text"); diff --git a/Mage.Client/src/main/java/mage/client/game/FirstButtonMousePressedAction.java b/Mage.Client/src/main/java/mage/client/game/FirstButtonMousePressedAction.java index 881a6b1f13f..09beb946d50 100644 --- a/Mage.Client/src/main/java/mage/client/game/FirstButtonMousePressedAction.java +++ b/Mage.Client/src/main/java/mage/client/game/FirstButtonMousePressedAction.java @@ -2,6 +2,7 @@ package mage.client.game; import mage.client.components.KeyboundButton; +import javax.swing.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.function.Consumer; @@ -32,7 +33,7 @@ public class FirstButtonMousePressedAction extends MouseAdapter { KeyboundButton button = (KeyboundButton) e.getSource(); button.setTint(false); } - if (e.getButton() == MouseEvent.BUTTON1 && inside) { + if (SwingUtilities.isLeftMouseButton(e) && inside) { callback.accept(e); } } diff --git a/Mage.Client/src/main/java/mage/client/game/GamePane.java b/Mage.Client/src/main/java/mage/client/game/GamePane.java index 994205092ec..65fa3db7d82 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePane.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePane.java @@ -1,10 +1,3 @@ - - - /* - * GamePane.java - * - * Created on Dec 17, 2009, 9:34:10 AM - */ package mage.client.game; import java.awt.AWTEvent; @@ -14,6 +7,7 @@ import javax.swing.*; import mage.client.MagePane; /** + * Game GUI: game panel with scrollbars * * @author BetaSteward_at_googlemail.com */ diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 2151f75e623..937b2d1a816 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -1,6 +1,7 @@ package mage.client.game; import mage.cards.Card; +import mage.cards.MageCard; import mage.cards.action.ActionCallback; import mage.choices.Choice; import mage.client.MageFrame; @@ -25,9 +26,9 @@ import mage.client.util.gui.ArrowBuilder; import mage.client.util.gui.MageDialogState; import mage.constants.*; import mage.game.events.PlayerQueryEvent; +import mage.players.PlayableObjectsList; import mage.view.*; import org.apache.log4j.Logger; -import org.mage.card.arcane.CardPanel; import org.mage.plugins.card.utils.impl.ImageManagerImpl; import javax.swing.*; @@ -52,6 +53,8 @@ import static mage.client.dialog.PreferencesDialog.*; import static mage.constants.PlayerAction.*; /** + * Game GUI: main game panel with all controls + * * @author BetaSteward_at_googlemail.com, nantuko8, JayDi85 */ public final class GamePanel extends javax.swing.JPanel { @@ -59,7 +62,6 @@ public final class GamePanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(GamePanel.class); private static final String YOUR_HAND = "Your hand"; private static final int X_PHASE_WIDTH = 55; - private static final int STACK_MIN_CARDS_OFFSET_Y = 7; // TODO: Size bui GUISize value private static final String CMD_AUTO_ORDER_FIRST = "cmdAutoOrderFirst"; private static final String CMD_AUTO_ORDER_LAST = "cmdAutoOrderLast"; @@ -77,8 +79,8 @@ public final class GamePanel extends javax.swing.JPanel { private final Map graveyardWindows = new HashMap<>(); private final Map companion = new HashMap<>(); private final Map graveyards = new HashMap<>(); - private final ArrayList pickTarget = new ArrayList<>(); + private final ArrayList pickPile = new ArrayList<>(); private UUID gameId; private UUID playerId; // playerId of the player GamePane gamePane; @@ -114,6 +116,17 @@ public final class GamePanel extends javax.swing.JPanel { // popup menu for triggered abilities order private JPopupMenu popupMenuTriggerOrder; + // keep game data for updates/re-draws + // warning, it keeps updates from GAME_UPDATE events only and ignore another events with GameView + static class LastGameData { + GameView game; + boolean showPlayable; + Map options; + Set targets; + } + private final LastGameData lastGameData = new LastGameData(); + + public GamePanel() { initComponents = true; initComponents(); @@ -123,8 +136,6 @@ public final class GamePanel extends javax.swing.JPanel { this.feedbackPanel.setConnectedChatPanel(this.userChatPanel); - this.stackObjects.setMinOffsetY(STACK_MIN_CARDS_OFFSET_Y); - // Override layout (I can't edit generated code) this.setLayout(new BorderLayout()); final JLayeredPane jLayeredBackgroundPane = new JLayeredPane(); @@ -161,6 +172,7 @@ public final class GamePanel extends javax.swing.JPanel { } }); + // Resize the width of the stack area if the size of the play area is changed ComponentAdapter componentAdapterPlayField = new ComponentAdapter() { @Override @@ -179,7 +191,6 @@ public final class GamePanel extends javax.swing.JPanel { resizeTimer.stop(); setGUISize(); feedbackPanel.changeGUISize(); - })); pnlHelperHandButtonsStackArea.addComponentListener(componentAdapterPlayField); @@ -247,10 +258,10 @@ public final class GamePanel extends javax.swing.JPanel { companionDialog.cleanUp(); companionDialog.removeDialog(); } - for (ShowCardsDialog pickTargetDialog : pickTarget) { - pickTargetDialog.cleanUp(); - pickTargetDialog.removeDialog(); - } + + clearPickTargetDialogs(); + clearPickPileDialogs(); + Plugins.instance.getActionCallback().hideOpenComponents(); try { Component popupContainer = MageFrame.getUI().getComponent(MageComponents.POPUP_CONTAINER); @@ -262,6 +273,23 @@ public final class GamePanel extends javax.swing.JPanel { this.bigCard = null; } + private void clearPickTargetDialogs() { + for (ShowCardsDialog pickTargetDialog : this.pickTarget) { + pickTargetDialog.cleanUp(); + pickTargetDialog.removeDialog(); + } + this.pickTarget.clear(); + } + + private void clearPickPileDialogs() { + for (PickPileDialog pickPileDialog : this.pickPile) { + pickPileDialog.cleanUp(); + pickPileDialog.removeDialog(); + } + this.pickPile.clear(); + } + + public void changeGUISize() { initComponents = true; setGUISize(); @@ -290,6 +318,9 @@ public final class GamePanel extends javax.swing.JPanel { for (ShowCardsDialog showCardsDialog : pickTarget) { showCardsDialog.changeGUISize(); } + for (PickPileDialog pickPileDialog : pickPile) { + pickPileDialog.changeGUISize(); + } this.revalidate(); this.repaint(); @@ -300,21 +331,28 @@ public final class GamePanel extends javax.swing.JPanel { jSplitPane0.setDividerSize(GUISizeHelper.dividerBarSize); jSplitPane1.setDividerSize(GUISizeHelper.dividerBarSize); jSplitPane2.setDividerSize(GUISizeHelper.dividerBarSize); - stackObjects.setCardDimension(GUISizeHelper.handCardDimension); txtSpellsCast.setFont(new Font(GUISizeHelper.gameDialogAreaFont.getFontName(), Font.BOLD, GUISizeHelper.gameDialogAreaFont.getSize())); txtHoldPriority.setFont(new Font(GUISizeHelper.gameDialogAreaFont.getFontName(), Font.BOLD, GUISizeHelper.gameDialogAreaFont.getSize())); GUISizeHelper.changePopupMenuFont(popupMenuTriggerOrder); + // hand + stack panels + // the stack takes up a portion of the possible space (GUISizeHelper.stackWidth) + int newStackWidth = pnlHelperHandButtonsStackArea.getWidth() * GUISizeHelper.stackWidth / 100; - if (newStackWidth < 410) { - newStackWidth = 410; - } - Dimension newDimension = new Dimension(pnlHelperHandButtonsStackArea.getWidth() - newStackWidth, GUISizeHelper.handCardDimension.height + GUISizeHelper.scrollBarSize); + newStackWidth = Math.max(410, newStackWidth); + Dimension newDimension = new Dimension( + pnlHelperHandButtonsStackArea.getWidth() - newStackWidth, + MageActionCallback.getHandOrStackMargins(Zone.HAND).getHeight() + GUISizeHelper.handCardDimension.height + GUISizeHelper.scrollBarSize + ); handContainer.setPreferredSize(newDimension); handContainer.setMaximumSize(newDimension); - newDimension = new Dimension(newStackWidth, STACK_MIN_CARDS_OFFSET_Y + GUISizeHelper.handCardDimension.height + GUISizeHelper.scrollBarSize); + newDimension = new Dimension( + newStackWidth, + MageActionCallback.getHandOrStackMargins(Zone.STACK).getHeight() + GUISizeHelper.handCardDimension.height + GUISizeHelper.scrollBarSize + ); + stackObjects.setCardDimension(GUISizeHelper.handCardDimension); stackObjects.setPreferredSize(newDimension); stackObjects.setMinimumSize(newDimension); stackObjects.setMaximumSize(newDimension); @@ -633,24 +671,27 @@ public final class GamePanel extends javax.swing.JPanel { } public synchronized void updateGame(GameView game, boolean showPlayable, Map options, Set targets) { + keepLastGameData(game, showPlayable, options, targets); + prepareSelectableView(); + updateGame(); + } - prepareSelectableView(game, showPlayable, options, targets); - - if (playerId == null && game.getWatchedHands() == null) { + public synchronized void updateGame() { + if (playerId == null && lastGameData.game.getWatchedHands() == null) { this.handContainer.setVisible(false); } else { this.handContainer.setVisible(true); handCards.clear(); - if (game.getWatchedHands() != null) { - for (Map.Entry hand : game.getWatchedHands().entrySet()) { + if (lastGameData.game.getWatchedHands() != null) { + for (Map.Entry hand : lastGameData.game.getWatchedHands().entrySet()) { handCards.put(hand.getKey(), CardsViewUtil.convertSimple(hand.getValue(), loadedCards)); } } if (playerId != null) { - handCards.put(YOUR_HAND, game.getHand()); + handCards.put(YOUR_HAND, lastGameData.game.getHand()); // Get opponents hand cards if available (only possible for players) - if (game.getOpponentHands() != null) { - for (Map.Entry hand : game.getOpponentHands().entrySet()) { + if (lastGameData.game.getOpponentHands() != null) { + for (Map.Entry hand : lastGameData.game.getOpponentHands().entrySet()) { handCards.put(hand.getKey(), CardsViewUtil.convertSimple(hand.getValue(), loadedCards)); } } @@ -669,7 +710,7 @@ public final class GamePanel extends javax.swing.JPanel { if (playerId != null) { // set visible only if we have any other hand visible than ours btnSwitchHands.setVisible(handCards.size() > 1); - boolean change = (handCardsOfOpponentAvailable != (game.getOpponentHands() != null)); + boolean change = (handCardsOfOpponentAvailable != (lastGameData.game.getOpponentHands() != null)); if (change) { handCardsOfOpponentAvailable = !handCardsOfOpponentAvailable; if (handCardsOfOpponentAvailable) { @@ -683,45 +724,45 @@ public final class GamePanel extends javax.swing.JPanel { } } - if (game.getPhase() != null) { - this.txtPhase.setText(game.getPhase().toString()); + if (lastGameData.game.getPhase() != null) { + this.txtPhase.setText(lastGameData.game.getPhase().toString()); } else { this.txtPhase.setText(""); } - if (game.getStep() != null) { - updatePhases(game.getStep()); - this.txtStep.setText(game.getStep().toString()); + if (lastGameData.game.getStep() != null) { + updatePhases(lastGameData.game.getStep()); + this.txtStep.setText(lastGameData.game.getStep().toString()); } else { logger.debug("Step is empty"); this.txtStep.setText(""); } - if (game.getSpellsCastCurrentTurn() > 0 && PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_SHOW_STORM_COUNTER, "true").equals("true")) { + if (lastGameData.game.getSpellsCastCurrentTurn() > 0 && PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_SHOW_STORM_COUNTER, "true").equals("true")) { this.txtSpellsCast.setVisible(true); - this.txtSpellsCast.setText(' ' + Integer.toString(game.getSpellsCastCurrentTurn()) + ' '); + this.txtSpellsCast.setText(' ' + Integer.toString(lastGameData.game.getSpellsCastCurrentTurn()) + ' '); } else { this.txtSpellsCast.setVisible(false); } - this.txtActivePlayer.setText(game.getActivePlayerName()); - this.txtPriority.setText(game.getPriorityPlayerName()); - this.txtTurn.setText(Integer.toString(game.getTurn())); + this.txtActivePlayer.setText(lastGameData.game.getActivePlayerName()); + this.txtPriority.setText(lastGameData.game.getPriorityPlayerName()); + this.txtTurn.setText(Integer.toString(lastGameData.game.getTurn())); List possibleAttackers = new ArrayList<>(); - if (options != null && options.containsKey(Constants.Option.POSSIBLE_ATTACKERS)) { - if (options.get(Constants.Option.POSSIBLE_ATTACKERS) instanceof List) { - possibleAttackers.addAll((List) options.get(Constants.Option.POSSIBLE_ATTACKERS)); + if (lastGameData.options != null && lastGameData.options.containsKey(Constants.Option.POSSIBLE_ATTACKERS)) { + if (lastGameData.options.get(Constants.Option.POSSIBLE_ATTACKERS) instanceof List) { + possibleAttackers.addAll((List) lastGameData.options.get(Constants.Option.POSSIBLE_ATTACKERS)); } } List possibleBlockers = new ArrayList<>(); - if (options != null && options.containsKey(Constants.Option.POSSIBLE_BLOCKERS)) { - if (options.get(Constants.Option.POSSIBLE_BLOCKERS) instanceof List) { - possibleBlockers.addAll((List) options.get(Constants.Option.POSSIBLE_BLOCKERS)); + if (lastGameData.options != null && lastGameData.options.containsKey(Constants.Option.POSSIBLE_BLOCKERS)) { + if (lastGameData.options.get(Constants.Option.POSSIBLE_BLOCKERS) instanceof List) { + possibleBlockers.addAll((List) lastGameData.options.get(Constants.Option.POSSIBLE_BLOCKERS)); } } - for (PlayerView player : game.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { if (players.containsKey(player.getPlayerId())) { if (!possibleAttackers.isEmpty()) { for (UUID permanentId : possibleAttackers) { @@ -737,7 +778,7 @@ public final class GamePanel extends javax.swing.JPanel { } } } - players.get(player.getPlayerId()).update(game, player, targets); + players.get(player.getPlayerId()).update(lastGameData.game, player, lastGameData.targets); if (player.getPlayerId().equals(playerId)) { skipButtons.updateFromPlayer(player); } @@ -778,7 +819,7 @@ public final class GamePanel extends javax.swing.JPanel { sb.append("Playing: "); } boolean first = true; - for (PlayerView player : game.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { if (first) { first = false; } else { @@ -790,10 +831,10 @@ public final class GamePanel extends javax.swing.JPanel { gamePane.setTitle(sb.toString()); } - GameManager.instance.setStackSize(game.getStack().size()); - displayStack(game, bigCard, feedbackPanel, gameId); + GameManager.instance.setStackSize(lastGameData.game.getStack().size()); + displayStack(lastGameData.game, bigCard, feedbackPanel, gameId); - for (ExileView exile : game.getExile()) { + for (ExileView exile : lastGameData.game.getExile()) { if (!exiles.containsKey(exile.getId())) { CardInfoWindowDialog newExile = new CardInfoWindowDialog(ShowType.EXILE, exile.getName()); exiles.put(exile.getId(), newExile); @@ -803,16 +844,16 @@ public final class GamePanel extends javax.swing.JPanel { exiles.get(exile.getId()).loadCards(exile, bigCard, gameId); } - showRevealed(game); - showLookedAt(game); - showCompanion(game); - if (!game.getCombat().isEmpty()) { - CombatManager.instance.showCombat(game.getCombat(), gameId); + showRevealed(lastGameData.game); + showLookedAt(lastGameData.game); + showCompanion(lastGameData.game); + if (!lastGameData.game.getCombat().isEmpty()) { + CombatManager.instance.showCombat(lastGameData.game.getCombat(), gameId); } else { CombatManager.instance.hideCombat(gameId); } - for (PlayerView player : game.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { if (player.hasLeft() && !playersWhoLeft.get(player.getPlayerId())) { PlayAreaPanel playerLeftPanel = players.get(player.getPlayerId()); playersWhoLeft.put(player.getPlayerId(), true); @@ -1230,34 +1271,43 @@ public final class GamePanel extends javax.swing.JPanel { this.feedbackPanel.getFeedback(FeedbackMode.QUESTION, question, false, options, messageId, true, gameView.getPhase()); } - private void prepareSelectableView(GameView gameView, boolean showPlayable, Map options, Set targets) { - // make cards/perm selectable/chooseable/playable - // playable must be used for ask dialog only (priority and mana pay) + private void keepLastGameData(GameView game, boolean showPlayable, Map options, Set targets) { + lastGameData.game = game; + lastGameData.showPlayable = showPlayable; + lastGameData.options = options; + lastGameData.targets = targets; + } + + private void prepareSelectableView() { + // make cards/perm selectable/chooseable/playable update game data updates + if (lastGameData.game == null) { + return; + } Zone needZone = Zone.ALL; - if (options != null && options.containsKey("targetZone")) { - needZone = (Zone) options.get("targetZone"); + if (lastGameData.options != null && lastGameData.options.containsKey("targetZone")) { + needZone = (Zone) lastGameData.options.get("targetZone"); } List needChoosen; - if (options != null && options.containsKey("chosen")) { - needChoosen = (List) options.get("chosen"); + if (lastGameData.options != null && lastGameData.options.containsKey("chosen")) { + needChoosen = (List) lastGameData.options.get("chosen"); } else { needChoosen = new ArrayList<>(); } Set needSelectable; - if (targets != null) { - needSelectable = targets; + if (lastGameData.targets != null) { + needSelectable = lastGameData.targets; } else { needSelectable = new HashSet<>(); } - Map needPlayable; - if (showPlayable && gameView.getCanPlayObjects() != null) { - needPlayable = gameView.getCanPlayObjects(); + PlayableObjectsList needPlayable; + if (lastGameData.showPlayable && lastGameData.game.getCanPlayObjects() != null) { + needPlayable = lastGameData.game.getCanPlayObjects(); } else { - needPlayable = new HashMap<>(); + needPlayable = new PlayableObjectsList(); } if (needChoosen.isEmpty() && needSelectable.isEmpty() && needPlayable.isEmpty()) { @@ -1266,23 +1316,22 @@ public final class GamePanel extends javax.swing.JPanel { // hand if (needZone == Zone.HAND || needZone == Zone.ALL) { - for (CardView card : gameView.getHand().values()) { + for (CardView card : lastGameData.game.getHand().values()) { if (needSelectable.contains(card.getId())) { card.setChoosable(true); } if (needChoosen.contains(card.getId())) { card.setSelected(true); } - if (needPlayable.containsKey(card.getId())) { - card.setPlayable(true); - card.setPlayableAmount(needPlayable.get(card.getId())); + if (needPlayable.containsObject(card.getId())) { + card.setPlayableStats(needPlayable.getStats(card.getId())); } } } // stack if (needZone == Zone.STACK || needZone == Zone.ALL) { - for (Map.Entry card : gameView.getStack().entrySet()) { + for (Map.Entry card : lastGameData.game.getStack().entrySet()) { if (needSelectable.contains(card.getKey())) { card.getValue().setChoosable(true); } @@ -1290,16 +1339,15 @@ public final class GamePanel extends javax.swing.JPanel { card.getValue().setSelected(true); } // users can activate abilities of the spell on the stack (example: Lightning Storm); - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } // battlefield if (needZone == Zone.BATTLEFIELD || needZone == Zone.ALL) { - for (PlayerView player : gameView.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { for (Map.Entry perm : player.getBattlefield().entrySet()) { if (needSelectable.contains(perm.getKey())) { perm.getValue().setChoosable(true); @@ -1307,9 +1355,8 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(perm.getKey())) { perm.getValue().setSelected(true); } - if (needPlayable.containsKey(perm.getKey())) { - perm.getValue().setPlayable(true); - perm.getValue().setPlayableAmount(needPlayable.get(perm.getKey())); + if (needPlayable.containsObject(perm.getKey())) { + perm.getValue().setPlayableStats(needPlayable.getStats(perm.getKey())); } } } @@ -1317,7 +1364,7 @@ public final class GamePanel extends javax.swing.JPanel { // graveyard if (needZone == Zone.GRAVEYARD || needZone == Zone.ALL) { - for (PlayerView player : gameView.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { for (Map.Entry card : player.getGraveyard().entrySet()) { if (needSelectable.contains(card.getKey())) { card.getValue().setChoosable(true); @@ -1325,17 +1372,31 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(card.getKey())) { card.getValue().setSelected(true); } - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } } // exile - if (needZone == Zone.HAND || needZone == Zone.ALL) { - for (ExileView exile : gameView.getExile()) { + if (needZone == Zone.EXILED || needZone == Zone.ALL) { + // exile from player panel + for (PlayerView player : lastGameData.game.getPlayers()) { + for (CardView card : player.getExile().values()) { + if (needSelectable.contains(card.getId())) { + card.setChoosable(true); + } + if (needChoosen.contains(card.getId())) { + card.setSelected(true); + } + if (needPlayable.containsObject(card.getId())) { + card.setPlayableStats(needPlayable.getStats(card.getId())); + } + } + } + // exile from windows + for (ExileView exile : lastGameData.game.getExile()) { for (Map.Entry card : exile.entrySet()) { if (needSelectable.contains(card.getKey())) { card.getValue().setChoosable(true); @@ -1343,9 +1404,8 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(card.getKey())) { card.getValue().setSelected(true); } - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } @@ -1353,7 +1413,7 @@ public final class GamePanel extends javax.swing.JPanel { // command if (needZone == Zone.COMMAND || needZone == Zone.ALL) { - for (PlayerView player : gameView.getPlayers()) { + for (PlayerView player : lastGameData.game.getPlayers()) { for (CommandObjectView com : player.getCommandObjectList()) { if (needSelectable.contains(com.getId())) { com.setChoosable(true); @@ -1361,16 +1421,15 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(com.getId())) { com.setSelected(true); } - if (needPlayable.containsKey(com.getId())) { - com.setPlayable(true); - com.setPlayableAmount(needPlayable.get(com.getId())); + if (needPlayable.containsObject(com.getId())) { + com.setPlayableStats(needPlayable.getStats(com.getId())); } } } } // revealed - for (RevealedView rev : gameView.getRevealed()) { + for (RevealedView rev : lastGameData.game.getRevealed()) { for (Map.Entry card : rev.getCards().entrySet()) { if (needSelectable.contains(card.getKey())) { card.getValue().setChoosable(true); @@ -1378,15 +1437,14 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(card.getKey())) { card.getValue().setSelected(true); } - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } // companion - for (RevealedView rev : gameView.getCompanion()) { + for (RevealedView rev : lastGameData.game.getCompanion()) { for (Map.Entry card : rev.getCards().entrySet()) { if (needSelectable.contains(card.getKey())) { card.getValue().setChoosable(true); @@ -1394,19 +1452,17 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(card.getKey())) { card.getValue().setSelected(true); } - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } // looked at - for (LookedAtView look : gameView.getLookedAt()) { + for (LookedAtView look : lastGameData.game.getLookedAt()) { for (Map.Entry card : look.getCards().entrySet()) { - if (needPlayable.containsKey(card.getKey())) { - card.getValue().setPlayable(true); - card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + if (needPlayable.containsObject(card.getKey())) { + card.getValue().setPlayableStats(needPlayable.getStats(card.getKey())); } } } @@ -1417,14 +1473,14 @@ public final class GamePanel extends javax.swing.JPanel { * the pick triggered ability) * * @param message - * @param cardView + * @param cardsView * @param gameView * @param targets * @param required * @param options * @param messageId */ - public void pickTarget(String message, CardsView cardView, GameView gameView, Set targets, boolean required, Map options, int messageId) { + public void pickTarget(String message, CardsView cardsView, GameView gameView, Set targets, boolean required, Map options, int messageId) { PopUpMenuType popupMenuType = null; if (options != null) { if (options.containsKey("queryType")) { @@ -1446,13 +1502,15 @@ public final class GamePanel extends javax.swing.JPanel { Map options0 = options == null ? new HashMap<>() : options; ShowCardsDialog dialog = null; - if (cardView != null && !cardView.isEmpty()) { - dialog = showCards(message, cardView, required, options0, popupMenuType); + if (cardsView != null && !cardsView.isEmpty()) { + // clear old dialogs before the new + clearPickTargetDialogs(); + dialog = showCards(message, cardsView, required, options0, popupMenuType); options0.put("dialog", dialog); } this.feedbackPanel.getFeedback(required ? FeedbackMode.INFORM : FeedbackMode.CANCEL, message, gameView.getSpecial(), options0, messageId, true, gameView.getPhase()); if (dialog != null) { - this.pickTarget.add(dialog); // TODO: 01.01.2018, JayDi85: why feedbackPanel saved to pickTarget list? Need to research + this.pickTarget.add(dialog); } } @@ -1581,9 +1639,17 @@ public final class GamePanel extends javax.swing.JPanel { public void pickPile(String message, CardsView pile1, CardsView pile2) { hideAll(); + + // remove old dialogs before the new + clearPickPileDialogs(); + PickPileDialog pickPileDialog = new PickPileDialog(); + this.pickPile.add(pickPileDialog); + pickPileDialog.loadCards(message, pile1, pile2, bigCard, gameId); - SessionHandler.sendPlayerBoolean(gameId, pickPileDialog.isPickedPile1()); + if (pickPileDialog.isPickedOK()) { + SessionHandler.sendPlayerBoolean(gameId, pickPileDialog.isPickedPile1()); + } pickPileDialog.cleanUp(); pickPileDialog.removeDialog(); } @@ -2453,7 +2519,7 @@ public final class GamePanel extends javax.swing.JPanel { } private void mouseClickPhaseBar(MouseEvent evt) { - if (evt.getButton() == MouseEvent.BUTTON1) { // Left button + if (SwingUtilities.isLeftMouseButton(evt)) { PreferencesDialog.main(new String[]{PreferencesDialog.OPEN_PHASES_TAB}); // TODO: add event handler on preferences closed and refresh game data from server } @@ -2542,18 +2608,15 @@ public final class GamePanel extends javax.swing.JPanel { // Event listener for the ShowCardsDialog private Listener getShowCardsEventListener(final ShowCardsDialog dialog) { return event -> { - if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { - if (event.getComponent() != null && event.getComponent() instanceof CardPanel) { - JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu(); + if (event.getEventType() == ClientEventType.CARD_POPUP_MENU) { + if (event.getComponent() != null && event.getComponent() instanceof MageCard) { + JPopupMenu menu = ((MageCard) event.getComponent()).getPopupMenu(); if (menu != null) { cardViewPopupMenu = ((CardView) event.getSource()); menu.show(event.getComponent(), event.getxPos(), event.getyPos()); } } } - if (event.getEventType() == ClientEventType.ACTION_CONSUMED) { - dialog.removeDialog(); - } }; } @@ -2594,9 +2657,14 @@ public final class GamePanel extends javax.swing.JPanel { default: break; } + + // TODO: 2021-01-23 why it here? Can be removed? for (ShowCardsDialog dialog : pickTarget) { dialog.removeDialog(); } + for (PickPileDialog dialog : pickPile) { + dialog.removeDialog(); + } } private void initPopupMenuTriggerOrder() { diff --git a/Mage.Client/src/main/java/mage/client/game/HandPanel.java b/Mage.Client/src/main/java/mage/client/game/HandPanel.java index d2a9e1d09b9..ce7720e2e02 100644 --- a/Mage.Client/src/main/java/mage/client/game/HandPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HandPanel.java @@ -12,22 +12,19 @@ import mage.view.CardsView; public class HandPanel extends JPanel { - private static final int HAND_MIN_CARDS_OFFSET_Y = -10; - public HandPanel() { initComponents(); changeGUISize(); } public void initComponents() { - hand = new mage.client.cards.Cards(true); - hand.setMinOffsetY(HAND_MIN_CARDS_OFFSET_Y); - hand.setCardDimension(GUISizeHelper.handCardDimension); - jPanel = new JPanel(); jScrollPane1 = new JScrollPane(jPanel); jScrollPane1.getViewport().setBackground(new Color(0, 0, 0, 0)); + hand = new mage.client.cards.Cards(true, jScrollPane1); + hand.setCardDimension(GUISizeHelper.handCardDimension); + jPanel.setLayout(new GridBagLayout()); // centers hand jPanel.setBackground(new Color(0, 0, 0, 0)); jPanel.add(hand); @@ -49,7 +46,7 @@ public class HandPanel extends JPanel { hand.setBackgroundColor(new Color(0, 0, 0, 0)); hand.setVisibleIfEmpty(false); hand.setBorder(EMPTY_BORDER); - hand.setZone(Zone.HAND.toString()); + hand.setZone(Zone.HAND); } public void cleanUp() { @@ -63,6 +60,7 @@ public class HandPanel extends JPanel { private void setGUISize() { jScrollPane1.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); jScrollPane1.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + jScrollPane1.getHorizontalScrollBar().setUnitIncrement(GUISizeHelper.getCardsScrollbarUnitInc(GUISizeHelper.handCardDimension.width)); hand.setCardDimension(GUISizeHelper.handCardDimension); hand.changeGUISize(); } diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index 0a94eab63af..b7f0c686db8 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -186,13 +186,13 @@ public class HelperPanel extends JPanel { MouseListener checkPopupAdapter = new MouseAdapter() { @Override - public void mousePressed(MouseEvent me) { - checkPopupMenu(me); + public void mousePressed(MouseEvent e) { + checkPopupMenu(e); } @Override - public void mouseReleased(MouseEvent me) { - checkPopupMenu(me); + public void mouseReleased(MouseEvent e) { + checkPopupMenu(e); } }; @@ -230,26 +230,26 @@ public class HelperPanel extends JPanel { dialogTextArea.addMouseListener(new MouseAdapter() { @Override - public void mouseEntered(MouseEvent me) { + public void mouseEntered(MouseEvent e) { ToolTipManager.sharedInstance().setDismissDelay(100 * 1000); UIManager.put("info", Color.DARK_GRAY); } @Override - public void mouseExited(MouseEvent me) { + public void mouseExited(MouseEvent e) { ToolTipManager.sharedInstance().setDismissDelay(Constants.TOOLTIPS_DELAY_MS); UIManager.put("info", tooltipBackground); } }); } - private void checkPopupMenu(MouseEvent me) { - if (me.isPopupTrigger() + private void checkPopupMenu(MouseEvent e) { + if (e.isPopupTrigger() && originalId != null) { // only Yes/No requests from abilities can be automated - JButton source = (JButton) me.getSource(); + JButton source = (JButton) e.getSource(); if (source.getActionCommand().startsWith(QUESTION.toString())) { - showPopupMenu(me.getComponent(), source.getActionCommand()); - me.consume(); + showPopupMenu(e.getComponent(), source.getActionCommand()); + e.consume(); } } } diff --git a/Mage.Client/src/main/java/mage/client/game/ManaPool.java b/Mage.Client/src/main/java/mage/client/game/ManaPool.java index e9ca4927f47..62a4561e763 100644 --- a/Mage.Client/src/main/java/mage/client/game/ManaPool.java +++ b/Mage.Client/src/main/java/mage/client/game/ManaPool.java @@ -1,11 +1,3 @@ - - -/* - * ManaPool.java - * - * Created on Dec 22, 2009, 9:52:00 AM - */ - package mage.client.game; import mage.view.ManaPoolView; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index f7c2c2b35fe..6d6ead40a66 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -421,19 +421,19 @@ public class PlayAreaPanel extends javax.swing.JPanel { battlefieldPanel.getMainPanel().addMouseListener(new MouseAdapter() { @Override - public void mouseReleased(MouseEvent Me) { - this.checkMenu(Me); + public void mouseReleased(MouseEvent e) { + this.checkMenu(e); } // neccessary for linux and mac systems @Override - public void mousePressed(MouseEvent Me) { - this.checkMenu(Me); + public void mousePressed(MouseEvent e) { + this.checkMenu(e); } - private void checkMenu(MouseEvent Me) { - if (Me.isPopupTrigger() && playingMode) { - popupMenu.show(Me.getComponent(), Me.getX(), Me.getY()); + private void checkMenu(MouseEvent e) { + if (e.isPopupTrigger() && playingMode) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); @@ -474,19 +474,19 @@ public class PlayAreaPanel extends javax.swing.JPanel { battlefieldPanel.getMainPanel().addMouseListener(new MouseAdapter() { @Override - public void mouseReleased(MouseEvent Me) { - this.checkMenu(Me); + public void mouseReleased(MouseEvent e) { + this.checkMenu(e); } // neccessary for linux and mac systems @Override - public void mousePressed(MouseEvent Me) { - this.checkMenu(Me); + public void mousePressed(MouseEvent e) { + this.checkMenu(e); } - private void checkMenu(MouseEvent Me) { - if (Me.isPopupTrigger() && playingMode) { - popupMenu.show(Me.getComponent(), Me.getX(), Me.getY()); + private void checkMenu(MouseEvent e) { + if (e.isPopupTrigger() && playingMode) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index d8004aecca8..428e9612c1c 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -36,7 +36,7 @@ import java.util.*; import static mage.constants.Constants.*; /** - * Enhanced player pane. + * Game GUI: player panel with avatar and icons * * @author nantuko, JayDi85 */ @@ -150,7 +150,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { // can play if (gameView.getCanPlayObjects() != null && !gameView.getCanPlayObjects().isEmpty()) { for (CardView card : cards) { - if (gameView.getCanPlayObjects().containsKey(card.getId())) { + if (gameView.getCanPlayObjects().containsObject(card.getId())) { return true; } } @@ -247,7 +247,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { Color commandColor = Color.BLACK; for (CommandObjectView com : player.getCommandObjectList()) { - if (game != null && game.getCanPlayObjects() != null && game.getCanPlayObjects().containsKey(com.getId())) { + if (game != null && game.getCanPlayObjects() != null && game.getCanPlayObjects().containsObject(com.getId())) { commandColor = activeValueColor; break; } @@ -545,7 +545,6 @@ public class PlayerPanelExt extends javax.swing.JPanel { cheat.addActionListener(e -> btnCheatActionPerformed(e)); zonesPanel = new JPanel(); - //zonesPanel.setBorder(BorderFactory.createLineBorder(Color.red)); zonesPanel.setPreferredSize(new Dimension(100, 60)); zonesPanel.setSize(100, 60); zonesPanel.setLayout(null); diff --git a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java index 8040ed6d662..5fc6bcbdf39 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java @@ -1,7 +1,7 @@ package mage.client.plugins; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; -import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; import mage.client.cards.BigCard; import mage.view.CardView; @@ -23,9 +23,9 @@ public interface MagePlugins { JComponent updateTablePanel(Map ui); - MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, int renderMode, boolean needFullPermanentRender); + MageCard getMagePermanent(PermanentView card, BigCard bigCard, CardIconRenderSettings cardIconRenderSettings, Dimension dimension, UUID gameId, boolean loadImage, int renderMode, boolean needFullPermanentRender); - MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode, boolean needFullPermanentRender); + MageCard getMageCard(CardView card, BigCard bigCard, CardIconRenderSettings cardIconRenderSettings, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode, boolean needFullPermanentRender); boolean isThemePluginLoaded(); @@ -33,7 +33,7 @@ public interface MagePlugins { boolean isCounterPluginLoaded(); - int sortPermanents(Map ui, Map permanents, boolean topRow); + int sortPermanents(Map ui, Map cards, boolean topRow); void downloadSymbols(); @@ -41,9 +41,9 @@ public interface MagePlugins { void addGamesPlayed(); - void onAddCard(MagePermanent card, int count); + void onAddCard(MageCard card, int count); - void onRemoveCard(MagePermanent card, int count); + void onRemoveCard(MageCard card, int count); JComponent getCardInfoPane(); diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 13232664739..78578a80841 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -1,28 +1,31 @@ package mage.client.plugins.adapters; import mage.cards.MageCard; +import mage.cards.MageCardSpace; import mage.cards.action.ActionCallback; import mage.cards.action.TransferData; import mage.client.MageFrame; import mage.client.MagePane; import mage.client.SessionHandler; import mage.client.cards.BigCard; +import mage.client.cards.CardEventProducer; import mage.client.components.MageComponents; import mage.client.dialog.PreferencesDialog; import mage.client.game.GamePane; import mage.client.plugins.impl.Plugins; +import mage.client.util.ClientEventType; import mage.client.util.DefaultActionCallback; import mage.client.util.gui.ArrowBuilder; import mage.client.util.gui.ArrowUtil; import mage.client.util.gui.GuiDisplayUtil; import mage.components.CardInfoPane; import mage.constants.EnlargeMode; +import mage.constants.Zone; import mage.utils.ThreadUtils; import mage.view.CardView; import mage.view.PermanentView; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXPanel; -import org.mage.card.arcane.CardPanel; import org.mage.plugins.card.images.ImageCache; import javax.swing.*; @@ -41,14 +44,26 @@ import java.util.concurrent.TimeUnit; * Class that handles the callbacks from the card panels to mage to display big * card images from the cards the mouse hovers on. Also handles tooltip text * window. + *

+ * Only ONE action callback possible for the app + *

+ * If you want to process card events in your component then use CardEventProducer, see example with mouseClicked here * - * @author Nantuko, noxx + * @author Nantuko, noxx, JayDi85 */ public class MageActionCallback implements ActionCallback { - private static final Logger LOGGER = Logger.getLogger(ActionCallback.class); - public static final int GAP_X = 5; - public static final double COMPARE_GAP_X = 30; + private static final Logger logger = Logger.getLogger(ActionCallback.class); + + // hand and stack panel sizes (without scrolls) + public static final int HAND_CARDS_BETWEEN_GAP_X = 5; // space between cards in hand // TODO: make it gui's sizeable + public static final int STACK_CARDS_BETWEEN_GAP_X = 5; // space between cards in hand // TODO: make it gui's sizeable + public static final MageCardSpace HAND_CARDS_MARGINS = new MageCardSpace(10, 10, 5, 5); // no needs bottom space for scrolls, it's already calced in parent panel + public static final MageCardSpace STACK_CARDS_MARGINS = new MageCardSpace(10, 10, 5, 5); + + // effect of moving the cards apart while dragging another card above it + // higher value -> the effect will appear earlier (depends on the card size) + public static final int HAND_CARDS_COMPARE_GAP_X = 30; public static final int GO_DOWN_ON_DRAG_Y_OFFSET = 0; public static final int GO_UP_ON_DRAG_Y_OFFSET = 0; @@ -56,7 +71,6 @@ public class MageActionCallback implements ActionCallback { public static final int MIN_X_OFFSET_REQUIRED = 20; private Popup tooltipPopup; - private JPopupMenu jPopupMenu; private BigCard bigCard; private CardView tooltipCard; @@ -66,7 +80,6 @@ public class MageActionCallback implements ActionCallback { private int tooltipDelay; enum EnlargedWindowState { - CLOSED, NORMAL, ROTATED } @@ -79,12 +92,12 @@ public class MageActionCallback implements ActionCallback { private static final ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1); private ScheduledFuture hideTimeout; - private CardPanel prevCardPanel; + private MageCard prevCardPanel; private boolean startedDragging; - private boolean isDragging; + private boolean isDragging; // TODO: remove drag hand code to the hand panels private Point initialCardPos; private Point initialMousePos; - private final Set cardPanels = new HashSet<>(); + private final Set draggingCards = new HashSet<>(); public MageActionCallback() { enlargeMode = EnlargeMode.NORMAL; @@ -101,44 +114,57 @@ public class MageActionCallback implements ActionCallback { } @Override - public void mouseClicked(MouseEvent e, TransferData data) { + public void mouseClicked(MouseEvent e, TransferData data, boolean doubleClick) { + // send mouse clicked event to the card's area and other cards list components for processing + if (e.isConsumed()) { + return; + } + if (data.getComponent().getCardContainer() instanceof CardEventProducer) { + ClientEventType clickType = doubleClick ? ClientEventType.CARD_DOUBLE_CLICK : ClientEventType.CARD_CLICK; + CardEventProducer cardContainer = (CardEventProducer) data.getComponent().getCardContainer(); + mage.client.util.Event clientEvent = new mage.client.util.Event( + data.getComponent().getOriginal(), clickType, + 0, e.getX(), e.getY(), data.getComponent(), e, false + ); + cardContainer.getCardEventSource().fireEvent(clientEvent); + } } @Override public void mouseEntered(MouseEvent e, final TransferData data) { this.popupData = data; - handleOverNewView(data); + handleMouseMoveOverNewCard(data); } - private void showTooltipPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { - if (data.getComponent() != null) { - tooltipDelay = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_DELAY, 300); - if (tooltipDelay == 0) { - return; - } + private void startCardHintPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + + tooltipDelay = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_DELAY, 300); + if (tooltipDelay == 0) { + return; } if (cardInfoPane == null) { - PopupFactory factory = PopupFactory.getSharedInstance(); + // create new popup if (data.getLocationOnScreen() == null) { - if (data.getComponent() == null) { - return; - } - data.setLocationOnScreen(data.getComponent().getLocationOnScreen()); + data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint()); } + PopupFactory factory = PopupFactory.getSharedInstance(); data.getPopupText().updateText(); - tooltipPopup = factory.getPopup(data.getComponent(), data.getPopupText(), (int) data.getLocationOnScreen().getX() + data.getPopupOffsetX(), (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() + 40); + tooltipPopup = factory.getPopup(cardPanel, data.getPopupText(), (int) data.getLocationOnScreen().getX() + data.getPopupOffsetX(), (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() + 40); tooltipPopup.show(); // hack to get popup to resize to fit text tooltipPopup.hide(); - tooltipPopup = factory.getPopup(data.getComponent(), data.getPopupText(), (int) data.getLocationOnScreen().getX() + data.getPopupOffsetX(), (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() + 40); + tooltipPopup = factory.getPopup(cardPanel, data.getPopupText(), (int) data.getLocationOnScreen().getX() + data.getPopupOffsetX(), (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() + 40); tooltipPopup.show(); } else { - sumbitShowPopupTask(data, parentComponent, parentPoint); + showCardHintPopup(data, parentComponent, parentPoint); } } - private void sumbitShowPopupTask(final TransferData data, final Component parentComponent, final Point parentPoint) { + private void showCardHintPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + ThreadUtils.threadPool2.submit(new Runnable() { @Override public void run() { @@ -158,7 +184,7 @@ public class MageActionCallback implements ActionCallback { ((CardInfoPane) popupInfo).setCard(data.getCard(), popupContainer); showPopup(popupContainer, popupInfo); } catch (InterruptedException e) { - LOGGER.error("Can't show card tooltip", e); + logger.error("Can't show card tooltip", e); Thread.currentThread().interrupt(); } } @@ -171,7 +197,7 @@ public class MageActionCallback implements ActionCallback { } if (data.getLocationOnScreen() == null) { - data.setLocationOnScreen(data.getComponent().getLocationOnScreen()); + data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint()); } Point location = new Point((int) data.getLocationOnScreen().getX() + data.getPopupOffsetX() - 40, (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() - 40); @@ -188,7 +214,8 @@ public class MageActionCallback implements ActionCallback { @Override public void mousePressed(MouseEvent e, TransferData data) { - data.getComponent().requestFocusInWindow(); + MageCard cardPanel = data.getComponent().getTopPanelRef(); + cardPanel.requestFocusInWindow(); // for some reason sometime mouseRelease happens before numerous Mouse_Dragged events // that results in not finished dragging @@ -197,83 +224,88 @@ public class MageActionCallback implements ActionCallback { isDragging = false; startedDragging = false; prevCardPanel = null; - cardPanels.clear(); + draggingCards.clear(); Point mouse = new Point(e.getX(), e.getY()); SwingUtilities.convertPointToScreen(mouse, data.getComponent()); initialMousePos = new Point((int) mouse.getX(), (int) mouse.getY()); - initialCardPos = data.getComponent().getLocation(); + initialCardPos = cardPanel.getCardLocation().getCardPoint(); // Closes popup & enlarged view if a card/Permanent is selected hideTooltipPopup(); } @Override - public void mouseReleased(MouseEvent e, TransferData transferData) { - CardPanel card = ((CardPanel) transferData.getComponent()); - if (e.isPopupTrigger() /*&& card.getPopupMenu() != null*/) { + public void mouseReleased(MouseEvent e, TransferData data) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + if (e.isPopupTrigger()) { hideTooltipPopup(); - } else if (card.getZone() != null && card.getZone().equalsIgnoreCase("hand")) { + } else if (cardPanel.getZone() == Zone.HAND) { + // drag end int maxXOffset = 0; if (isDragging) { Point mouse = new Point(e.getX(), e.getY()); - SwingUtilities.convertPointToScreen(mouse, transferData.getComponent()); + SwingUtilities.convertPointToScreen(mouse, data.getComponent()); maxXOffset = Math.abs((int) (mouse.getX() - initialMousePos.x)); } - clearDragging(card); + clearDragging(cardPanel); this.startedDragging = false; if (maxXOffset < MIN_X_OFFSET_REQUIRED) { // we need this for protection from small card movements - transferData.getComponent().requestFocusInWindow(); - DefaultActionCallback.instance.mouseClicked(transferData.getGameId(), transferData.getCard()); + // default click simulation // TODO: replace to normal clicked events (change dialogs) + cardPanel.requestFocusInWindow(); + DefaultActionCallback.instance.mouseClicked(data.getGameId(), data.getCard()); // Closes popup & enlarged view if a card/Permanent is selected hideTooltipPopup(); } e.consume(); } else { - transferData.getComponent().requestFocusInWindow(); - DefaultActionCallback.instance.mouseClicked(transferData.getGameId(), transferData.getCard()); + // default click simulation + cardPanel.requestFocusInWindow(); + DefaultActionCallback.instance.mouseClicked(data.getGameId(), data.getCard()); // Closes popup & enlarged view if a card/Permanent is selected hideTooltipPopup(); e.consume(); } } - private void clearDragging(CardPanel card) { - if (this.startedDragging && prevCardPanel != null && card != null) { - for (Component component : card.getCardArea().getComponents()) { - if (component instanceof CardPanel) { - if (cardPanels.contains(component)) { - component.setLocation(component.getLocation().x, component.getLocation().y - GO_DOWN_ON_DRAG_Y_OFFSET); + private void clearDragging(MageCard clearCard) { + if (this.startedDragging && prevCardPanel != null && clearCard != null) { + // distribute cards between cards container and a drag container + for (Component comp : clearCard.getCardContainer().getComponents()) { + if (comp instanceof MageCard) { + MageCard realCard = (MageCard) comp; + if (draggingCards.contains(realCard)) { + realCard.setCardLocation(realCard.getCardLocation().getCardX(), realCard.getCardLocation().getCardY() - GO_DOWN_ON_DRAG_Y_OFFSET); } } } - card.setLocation(card.getLocation().x, card.getLocation().y + GO_UP_ON_DRAG_Y_OFFSET); - sort(card, card.getCardArea(), true); - cardPanels.clear(); + clearCard.setCardLocation(clearCard.getCardLocation().getCardX(), clearCard.getCardLocation().getCardY() + GO_UP_ON_DRAG_Y_OFFSET); + sortHandCards(clearCard, clearCard.getCardContainer(), true); + draggingCards.clear(); } prevCardPanel = null; } @Override - public void mouseMoved(MouseEvent e, TransferData transferData) { + public void mouseMoved(MouseEvent e, TransferData data) { if (!Plugins.instance.isCardPluginLoaded()) { return; } - if (!popupData.getCard().equals(transferData.getCard())) { - this.popupData = transferData; - handleOverNewView(transferData); - + if (!popupData.getCard().equals(data.getCard())) { + this.popupData = data; + handleMouseMoveOverNewCard(data); } if (bigCard == null) { return; } - handlePopup(transferData); + updateCardHints(data); } @Override - public void mouseDragged(MouseEvent e, TransferData transferData) { - CardPanel cardPanel = ((CardPanel) transferData.getComponent()); - if (cardPanel.getZone() == null || !cardPanel.getZone().equalsIgnoreCase("hand")) { + public void mouseDragged(MouseEvent e, TransferData data) { + // start the dragging + MageCard cardPanel = data.getComponent().getTopPanelRef(); + if (cardPanel.getZone() != Zone.HAND) { // drag'n'drop is allowed for HAND zone only return; } @@ -283,18 +315,19 @@ public class MageActionCallback implements ActionCallback { } isDragging = true; prevCardPanel = cardPanel; - Point cardPanelLocationOld = cardPanel.getLocation(); + + Point cardPanelLocationOld = cardPanel.getCardLocation().getCardPoint(); Point mouse = new Point(e.getX(), e.getY()); - SwingUtilities.convertPointToScreen(mouse, transferData.getComponent()); - int xOffset = cardPanel.getXOffset(cardPanel.getCardWidth()); - int newX = Math.max(initialCardPos.x + (int) (mouse.getX() - initialMousePos.x) - xOffset, 0); + SwingUtilities.convertPointToScreen(mouse, data.getComponent()); + int xOffset = 0; // starting position + int newX = Math.max(initialCardPos.x + (int) (mouse.getX() - initialMousePos.x) - xOffset, 0); // TODO: fix cardPanel.setCardBounds( newX, - cardPanelLocationOld.y + cardPanel.getCardYOffset(), - cardPanel.getCardWidth(), - cardPanel.getCardHeight()); - cardPanel.getCardArea().setComponentZOrder(cardPanel, 0); - sort(cardPanel, cardPanel.getCardArea(), false); + cardPanelLocationOld.y, + cardPanel.getCardLocation().getCardWidth(), + cardPanel.getCardLocation().getCardHeight()); + cardPanel.getCardContainer().setComponentZOrder(cardPanel, 0); + sortHandCards(cardPanel, cardPanel.getCardContainer(), false); if (!this.startedDragging) { this.startedDragging = true; @@ -308,55 +341,106 @@ public class MageActionCallback implements ActionCallback { } else { hideAll(null); } - ///clearDragging((CardPanel)data.component); + ///clearDragging((MageCard)data.component); } - private void sort(CardPanel card, JPanel container, boolean sortSource) { - java.util.List cards = new ArrayList<>(); - for (Component component : container.getComponents()) { - if (component instanceof CardPanel) { - if (!component.equals(card)) { - if (!cardPanels.contains(component)) { - component.setLocation(component.getLocation().x, component.getLocation().y + GO_DOWN_ON_DRAG_Y_OFFSET); + @Override + public void popupMenuCard(MouseEvent e, TransferData data) { + // send popup menu request over card + if (e.isConsumed()) { + return; + } + e.consume(); + if (data.getComponent().getCardContainer() instanceof CardEventProducer) { + CardEventProducer area = (CardEventProducer) data.getComponent().getCardContainer(); + mage.client.util.Event clientEvent = new mage.client.util.Event( + data.getComponent().getOriginal(), ClientEventType.CARD_POPUP_MENU, + 0, e.getX(), e.getY(), data.getComponent(), e, false + ); + area.getCardEventSource().fireEvent(clientEvent); + } + } + + @Override + public void popupMenuPanel(MouseEvent e, Component sourceComponent) { + // over non card component + if (e.isConsumed()) { + return; + } + e.consume(); + if (sourceComponent instanceof CardEventProducer) { + CardEventProducer area = (CardEventProducer) sourceComponent; + // card param must be empty + mage.client.util.Event clientEvent = new mage.client.util.Event( + null, ClientEventType.CARD_POPUP_MENU, + 0, e.getX(), e.getY(), e.getComponent(), e, false + ); + area.getCardEventSource().fireEvent(clientEvent); + } + } + + private void sortHandCards(MageCard card, Container container, boolean sortSource) { + java.util.List cards = new ArrayList<>(); + + // distribute cards between cards container and a drag container + for (Component comp : container.getComponents()) { + if (comp instanceof MageCard) { + MageCard realCard = (MageCard) comp; + if (!realCard.equals(card)) { + if (!draggingCards.contains(realCard)) { + realCard.setCardLocation(realCard.getCardLocation().getCardX(), realCard.getCardLocation().getCardY() + GO_DOWN_ON_DRAG_Y_OFFSET); } - cardPanels.add((CardPanel) component); + draggingCards.add(realCard); } else if (!startedDragging) { - component.setLocation(component.getLocation().x, component.getLocation().y - GO_UP_ON_DRAG_Y_OFFSET); + realCard.setCardLocation(realCard.getCardLocation().getCardX(), realCard.getCardLocation().getCardY() - GO_DOWN_ON_DRAG_Y_OFFSET); } - cards.add((CardPanel) component); + cards.add(realCard); } } - sortLayout(cards, card, sortSource); + + sortAndAnimateDraggingHandCards(cards, card, sortSource); } - private void sortLayout(List cards, CardPanel source, boolean includeSource) { - source.getLocation().x -= COMPARE_GAP_X; // this creates nice effect + private void sortAndAnimateDraggingHandCards(List cards, MageCard source, boolean includeSource) { + // special offset, allows to switch with first card + source.setCardLocation(source.getCardLocation().getCardX() - HAND_CARDS_COMPARE_GAP_X, source.getCardLocation().getCardY()); - cards.sort(Comparator.comparingInt(cp -> cp.getLocation().x)); + // sorting card components, so the effect above will be applied too + cards.sort(Comparator.comparingInt(cp -> cp.getCardLocation().getCardX())); - int dx = 0; + // WARNING, must be same sort code as Cards->layoutCards (if not then hand cards will be messed after drag) + int dx = MageActionCallback.getHandOrStackMargins(source.getZone()).getLeft(); // starting position boolean createdGapForSource = false; - for (Component component : cards) { + for (MageCard component : cards) { + // use real component locations, not a card's if (!includeSource) { + // create special hole between cards to put dragging card into it if (!component.equals(source)) { - component.setLocation(dx, component.getLocation().y); - dx += ((CardPanel) component).getCardWidth() + GAP_X; + component.setCardLocation(dx, component.getCardLocation().getCardY()); + dx += component.getCardLocation().getCardWidth() + MageActionCallback.getHandOrStackBetweenGapX(source.getZone()); // once dx is bigger than source's x position // we need to create a gap for the source card // but only once - if (!createdGapForSource && (dx + COMPARE_GAP_X) > source.getLocation().x) { + if (!createdGapForSource && (dx + HAND_CARDS_COMPARE_GAP_X) > source.getCardLocation().getCardX()) { createdGapForSource = true; - dx += ((CardPanel) component).getCardWidth() + GAP_X; + int gapOffset = component.getCardLocation().getCardWidth() + MageActionCallback.getHandOrStackBetweenGapX(source.getZone()); + dx += gapOffset; + // workaround to apply gap on the first card (if you drag over first card) + if (cards.get(0).equals(source) && cards.size() > 1 && cards.get(1).equals(component)) { + component.setCardLocation(component.getCardLocation().getCardX() + gapOffset, component.getCardLocation().getCardY()); + } } } } else { - component.setLocation(dx, component.getLocation().y); - dx += ((CardPanel) component).getCardWidth() + GAP_X; + component.setCardLocation(dx, component.getCardLocation().getCardY()); + dx += component.getCardLocation().getCardWidth() + MageActionCallback.getHandOrStackBetweenGapX(source.getZone()); } } } - private void handleOverNewView(TransferData data) { + private void handleMouseMoveOverNewCard(TransferData data) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + // Prevent to show tooltips from panes not in front MagePane topPane = MageFrame.getTopMost(null); if (topPane instanceof GamePane) { @@ -367,11 +451,11 @@ public class MageActionCallback implements ActionCallback { hideTooltipPopup(); cancelTimeout(); - Component parentComponent = SwingUtilities.getRoot(data.getComponent()); + Component parentComponent = SwingUtilities.getRoot(cardPanel); Point parentPoint = parentComponent.getLocationOnScreen(); if (data.getLocationOnScreen() == null) { - data.setLocationOnScreen(data.getComponent().getLocationOnScreen()); + data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint()); } ArrowUtil.drawArrowsForTargets(data, parentPoint); @@ -381,22 +465,23 @@ public class MageActionCallback implements ActionCallback { ArrowUtil.drawArrowsForEnchantPlayers(data, parentPoint); tooltipCard = data.getCard(); - showTooltipPopup(data, parentComponent, parentPoint); + startCardHintPopup(data, parentComponent, parentPoint); } - private void handlePopup(TransferData transferData) { - MageCard mageCard = (MageCard) transferData.getComponent(); + private void updateCardHints(TransferData data) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + if (!popupTextWindowOpen - || !Objects.equals(mageCard.getOriginal().getId(), bigCard.getCardId())) { + || !Objects.equals(cardPanel.getOriginal().getId(), bigCard.getCardId())) { if (bigCard.getWidth() > 0) { synchronized (MageActionCallback.class) { - if (!popupTextWindowOpen || !Objects.equals(mageCard.getOriginal().getId(), bigCard.getCardId())) { + if (!popupTextWindowOpen || !Objects.equals(cardPanel.getOriginal().getId(), bigCard.getCardId())) { if (!popupTextWindowOpen) { bigCard.resetCardId(); } popupTextWindowOpen = true; - Image image = mageCard.getImage(); - displayCardInfo(mageCard, image, bigCard); + Image image = cardPanel.getImage(); + displayCardInfo(cardPanel, image, bigCard); } } } else { @@ -404,7 +489,7 @@ public class MageActionCallback implements ActionCallback { } if (enlargedWindowState != EnlargedWindowState.CLOSED) { cancelTimeout(); - displayEnlargedCard(mageCard.getOriginal(), transferData); + displayEnlargedCard(cardPanel.getOriginal(), data); } } } @@ -419,9 +504,6 @@ public class MageActionCallback implements ActionCallback { if (tooltipPopup != null) { tooltipPopup.hide(); } - if (jPopupMenu != null) { - jPopupMenu.setVisible(false); - } try { if (SessionHandler.getSession() == null) { return; @@ -429,7 +511,7 @@ public class MageActionCallback implements ActionCallback { Component popupContainer = MageFrame.getUI().getComponent(MageComponents.POPUP_CONTAINER); popupContainer.setVisible(false); } catch (InterruptedException e) { - LOGGER.error("Can't hide card tooltip", e); + logger.error("Can't hide card tooltip", e); Thread.currentThread().interrupt(); } } @@ -456,22 +538,22 @@ public class MageActionCallback implements ActionCallback { } @Override - public void mouseWheelMoved(MouseWheelEvent e, TransferData transferData) { + public void mouseWheelMoved(MouseWheelEvent e, TransferData data) { int notches = e.getWheelRotation(); if (enlargedWindowState != EnlargedWindowState.CLOSED) { // same move direction will be ignored, opposite direction closes the enlarged window if (enlargeredViewOpened != null && new Date().getTime() - enlargeredViewOpened.getTime() > 1000) { // if the opening is back more than 1 seconds close anyway hideEnlargedCard(); - handleOverNewView(transferData); + handleMouseMoveOverNewCard(data); } else if (enlargeMode == EnlargeMode.NORMAL) { if (notches > 0) { hideEnlargedCard(); - handleOverNewView(transferData); + handleMouseMoveOverNewCard(data); } } else if (notches < 0) { hideEnlargedCard(); - handleOverNewView(transferData); + handleMouseMoveOverNewCard(data); } return; } @@ -526,11 +608,13 @@ public class MageActionCallback implements ActionCallback { } } catch (InterruptedException e) { - LOGGER.warn("Can't hide enlarged card", e); + logger.warn("Can't hide enlarged card", e); } } - private void displayEnlargedCard(final CardView cardView, final TransferData transferData) { + private void displayEnlargedCard(final CardView cardView, final TransferData data) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + ThreadUtils.threadPool3.submit(() -> { if (cardView == null) { return; @@ -539,7 +623,7 @@ public class MageActionCallback implements ActionCallback { if (enlargedWindowState == EnlargedWindowState.CLOSED) { return; } - + MageComponents mageComponentCardPreviewContainer; MageComponents mageComponentCardPreviewPane; if (cardView.isToRotate()) { @@ -559,17 +643,16 @@ public class MageActionCallback implements ActionCallback { } final Component popupContainer = MageFrame.getUI().getComponent(mageComponentCardPreviewContainer); Component cardPreviewPane = MageFrame.getUI().getComponent(mageComponentCardPreviewPane); - Component parentComponent = SwingUtilities.getRoot(transferData.getComponent()); + Component parentComponent = SwingUtilities.getRoot(cardPanel); if (cardPreviewPane != null && parentComponent != null) { Point parentPoint = parentComponent.getLocationOnScreen(); - transferData.setLocationOnScreen(transferData.getComponent().getLocationOnScreen()); - Point location = new Point((int) transferData.getLocationOnScreen().getX() + transferData.getPopupOffsetX() - 40, (int) transferData.getLocationOnScreen().getY() + transferData.getPopupOffsetY() - 40); + data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint()); + Point location = new Point((int) data.getLocationOnScreen().getX() + data.getPopupOffsetX() - 40, (int) data.getLocationOnScreen().getY() + data.getPopupOffsetY() - 40); location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, cardPreviewPane, parentComponent); location.translate(-parentPoint.x, -parentPoint.y); popupContainer.setLocation(location); popupContainer.setVisible(true); - - MageCard mageCard = (MageCard) transferData.getComponent(); + Image image = null; switch (enlargeMode) { case COPY: @@ -593,17 +676,17 @@ public class MageActionCallback implements ActionCallback { break; } if (image == null) { - image = mageCard.getImage(); + image = cardPanel.getImage(); } // shows the card in the popup Container - displayCardInfo(mageCard, image, (BigCard) cardPreviewPane); - + displayCardInfo(cardPanel, image, (BigCard) cardPreviewPane); + } else { - LOGGER.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName()); + logger.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName()); } - + } catch (Exception e) { - LOGGER.warn("Problem dring display of enlarged card", e); + logger.warn("Problem dring display of enlarged card", e); } }); } @@ -641,4 +724,20 @@ public class MageActionCallback implements ActionCallback { hideTimeout.cancel(false); } } + + public static MageCardSpace getHandOrStackMargins(Zone zone) { + if (zone == Zone.HAND) { + return HAND_CARDS_MARGINS; + } else { + return STACK_CARDS_MARGINS; + } + } + + public static int getHandOrStackBetweenGapX(Zone zone) { + if (zone == Zone.HAND) { + return HAND_CARDS_BETWEEN_GAP_X; + } else { + return STACK_CARDS_BETWEEN_GAP_X; + } + } } diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index 8c5d98e47b3..6c7a30abc13 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -1,16 +1,13 @@ package mage.client.plugins.impl; +import mage.abilities.icon.CardIconRenderSettings; import mage.cards.MageCard; -import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; import mage.client.MageFrame; import mage.client.cards.BigCard; -import mage.client.cards.Card; -import mage.client.cards.Permanent; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.MagePlugins; import mage.client.plugins.adapters.MageActionCallback; -import mage.client.util.ClientDefaultSettings; import mage.interfaces.PluginException; import mage.interfaces.plugin.CardPlugin; import mage.interfaces.plugin.CounterPlugin; @@ -21,6 +18,7 @@ import net.xeoh.plugins.base.PluginManager; import net.xeoh.plugins.base.impl.PluginManagerFactory; import net.xeoh.plugins.base.util.uri.ClassURI; import org.apache.log4j.Logger; +import org.mage.card.arcane.MageLayer; import org.mage.plugins.card.CardPluginImpl; import org.mage.plugins.theme.ThemePluginImpl; @@ -42,7 +40,7 @@ public enum Plugins implements MagePlugins { private static PluginManager pm; private ThemePlugin themePlugin = null; - private CardPlugin cardPlugin = null; + private CardPlugin cardPlugin = null; // required by game private CounterPlugin counterPlugin = null; private static final MageActionCallback mageActionCallback = new MageActionCallback(); private final Map sortingOptions = new HashMap<>(); @@ -93,27 +91,39 @@ public enum Plugins implements MagePlugins { } @Override - public MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, int renderMode, boolean needFullPermanentRender) { + public MageCard getMagePermanent(PermanentView card, BigCard bigCard, CardIconRenderSettings cardIconRenderSettings, Dimension dimension, UUID gameId, boolean loadImage, int renderMode, boolean needFullPermanentRender) { + MageCard mageCard; if (cardPlugin != null) { mageActionCallback.refreshSession(); mageActionCallback.setCardPreviewComponent(bigCard); - return cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); + mageCard = cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); } else { - return new Permanent(card, bigCard, ClientDefaultSettings.dimensions, gameId); + throw new IllegalArgumentException("Card's plugin must be loaded"); } + return createLayeredCard(mageCard, dimension, cardIconRenderSettings); } @Override - public MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode, boolean needFullPermanentRender) { + public MageCard getMageCard(CardView card, BigCard bigCard, CardIconRenderSettings cardIconRenderSettings, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode, boolean needFullPermanentRender) { + // Card icons panels must be put outside of the card like MTG Arena. + // So for compatibility purposes: keep free space for icons and change card dimention + MageCard mageCard; if (cardPlugin != null) { if (previewable) { mageActionCallback.refreshSession(); mageActionCallback.setCardPreviewComponent(bigCard); } - return cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); + mageCard = cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); } else { - return new Card(card, bigCard, ClientDefaultSettings.dimensions, gameId); + throw new IllegalArgumentException("Card's plugin must be loaded"); } + return createLayeredCard(mageCard, dimension, cardIconRenderSettings); + } + + private MageCard createLayeredCard(MageCard mageCard, Dimension dimension, CardIconRenderSettings cardIconRenderSettings) { + MageLayer mageLayer = new MageLayer(mageCard, cardIconRenderSettings); + mageLayer.setCardBounds(0, 0, dimension.width, dimension.height); // default size + return mageLayer; } @Override @@ -122,9 +132,9 @@ public enum Plugins implements MagePlugins { } @Override - public int sortPermanents(Map ui, Map permanents, boolean topRow) { + public int sortPermanents(Map ui, Map cards, boolean topRow) { if (this.cardPlugin != null) { - return this.cardPlugin.sortPermanents(ui, permanents, PreferencesDialog.getCachedValue("nonLandPermanentsInOnePile", "false").equals("true"), topRow); + return this.cardPlugin.sortPermanents(ui, cards, PreferencesDialog.getCachedValue("nonLandPermanentsInOnePile", "false").equals("true"), topRow); } return -1; } @@ -176,14 +186,14 @@ public enum Plugins implements MagePlugins { } @Override - public void onAddCard(MagePermanent card, int count) { + public void onAddCard(MageCard card, int count) { if (this.cardPlugin != null) { this.cardPlugin.onAddCard(card, count); } } @Override - public void onRemoveCard(MagePermanent card, int count) { + public void onRemoveCard(MageCard card, int count) { if (this.cardPlugin != null) { this.cardPlugin.onRemoveCard(card, count); } diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 3cdf9fb93e4..7a12eda92e2 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -49,31 +49,41 @@ public class CallbackClientImpl implements CallbackClient { try { logger.debug(callback.getMessageId() + " -- " + callback.getMethod()); switch (callback.getMethod()) { + case START_GAME: { TableClientMessage message = (TableClientMessage) callback.getData(); GameManager.instance.setCurrentPlayerUUID(message.getPlayerId()); gameStarted(message.getGameId(), message.getPlayerId()); break; } + case START_TOURNAMENT: { TableClientMessage message = (TableClientMessage) callback.getData(); tournamentStarted(message.getGameId(), message.getPlayerId()); break; } + case START_DRAFT: { TableClientMessage message = (TableClientMessage) callback.getData(); draftStarted(message.getGameId(), message.getPlayerId()); break; } - case REPLAY_GAME: + + case REPLAY_GAME: { replayGame(callback.getObjectId()); break; - case SHOW_TOURNAMENT: + } + + case SHOW_TOURNAMENT: { showTournament(callback.getObjectId()); break; - case WATCHGAME: + } + + case WATCHGAME: { watchGame(callback.getObjectId()); break; + } + case CHATMESSAGE: { ChatMessage message = (ChatMessage) callback.getData(); // Drop messages from ignored users @@ -117,7 +127,8 @@ public class CallbackClientImpl implements CallbackClient { } break; } - case SERVER_MESSAGE: + + case SERVER_MESSAGE: { if (callback.getData() != null) { ChatMessage message = (ChatMessage) callback.getData(); if (message.getColor() == ChatMessage.MessageColor.RED) { @@ -127,11 +138,14 @@ public class CallbackClientImpl implements CallbackClient { } } break; + } + case JOINED_TABLE: { TableClientMessage message = (TableClientMessage) callback.getData(); joinedTable(message.getRoomId(), message.getTableId(), message.getFlag()); break; } + case REPLAY_INIT: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -139,6 +153,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case REPLAY_DONE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -146,6 +161,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case REPLAY_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -153,6 +169,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_INIT: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -161,6 +178,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_OVER: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -175,9 +193,12 @@ public class CallbackClientImpl implements CallbackClient { } break; } - case GAME_ERROR: + + case GAME_ERROR: { frame.showErrorDialog("Game Error", (String) callback.getData()); break; + } + case GAME_ASK: { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -187,6 +208,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_TARGET: // e.g. Pick triggered ability { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -199,6 +221,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_SELECT: { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -209,6 +232,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_CHOOSE_ABILITY: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { @@ -217,6 +241,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_CHOOSE_PILE: { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -226,6 +251,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_CHOOSE_CHOICE: { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -237,6 +263,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_PLAY_MANA: { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -246,6 +273,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_PLAY_XMANA: { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -256,6 +284,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_GET_AMOUNT: { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -267,6 +296,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case GAME_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -277,17 +307,31 @@ public class CallbackClientImpl implements CallbackClient { } break; } - case END_GAME_INFO: - MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData()); + case GAME_REDRAW_GUI: { + // re-draw game's gui elements like attack arrows + // uses for client side only (example: update after scrollbars support) + GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { + panel.updateGame(); + } break; - case SHOW_USERMESSAGE: + } + + case END_GAME_INFO: { + MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData()); + break; + } + + case SHOW_USERMESSAGE: { List messageData = (List) callback.getData(); if (messageData.size() == 2) { JOptionPane.showMessageDialog(null, messageData.get(1), messageData.get(0), JOptionPane.WARNING_MESSAGE); } break; - case GAME_INFORM: + } + + case GAME_INFORM: { if (callback.getMessageId() > gameInformMessageId) { { GameClientMessage message = (GameClientMessage) callback.getData(); @@ -297,13 +341,14 @@ public class CallbackClientImpl implements CallbackClient { panel.inform(message.getMessage(), message.getGameView(), callback.getMessageId()); } } -// no longer needed because phase skip handling on server side now + // no longer needed because phase skip handling on server side now } else { logger.warn(new StringBuilder("message out of sequence - ignoring").append("MessageId = ").append(callback.getMessageId()).append(" method = ").append(callback.getMethod())); - //logger.warn("message out of sequence - ignoring"); } gameInformMessageId = messageId; break; + } + case GAME_INFORM_PERSONAL: { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); @@ -313,6 +358,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case SIDEBOARD: { TableClientMessage message = (TableClientMessage) callback.getData(); DeckView deckView = message.getDeck(); @@ -324,6 +370,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case VIEW_LIMITED_DECK: { TableClientMessage message = (TableClientMessage) callback.getData(); DeckView deckView = message.getDeck(); @@ -331,6 +378,7 @@ public class CallbackClientImpl implements CallbackClient { viewLimitedDeck(deck, message.getTableId(), message.getTime()); break; } + case CONSTRUCT: { TableClientMessage message = (TableClientMessage) callback.getData(); DeckView deckView = message.getDeck(); @@ -338,9 +386,13 @@ public class CallbackClientImpl implements CallbackClient { construct(deck, message.getTableId(), message.getTime()); break; } - case DRAFT_OVER: + + case DRAFT_OVER: { MageFrame.removeDraft(callback.getObjectId()); break; + } + + case DRAFT_INIT: case DRAFT_PICK: { DraftClientMessage message = (DraftClientMessage) callback.getData(); DraftPanel panel = MageFrame.getDraft(callback.getObjectId()); @@ -349,6 +401,7 @@ public class CallbackClientImpl implements CallbackClient { } break; } + case DRAFT_UPDATE: { DraftPanel panel = MageFrame.getDraft(callback.getObjectId()); DraftClientMessage message = (DraftClientMessage) callback.getData(); @@ -357,23 +410,25 @@ public class CallbackClientImpl implements CallbackClient { } break; } - case DRAFT_INIT: { - DraftClientMessage message = (DraftClientMessage) callback.getData(); - DraftPanel panel = MageFrame.getDraft(callback.getObjectId()); - if (panel != null) { - panel.loadBooster(message.getDraftPickView()); - } + + case TOURNAMENT_INIT: { break; } - case TOURNAMENT_INIT: - break; - case USER_REQUEST_DIALOG: + + case USER_REQUEST_DIALOG: { frame.showUserRequestDialog((UserRequestMessage) callback.getData()); break; - default: + } + + default: { break; + } + } + + // sync message for server side events only + if (!callback.getMethod().isClientSideMessage()) { + messageId = callback.getMessageId(); } - messageId = callback.getMessageId(); } catch (Exception ex) { handleException(ex); } @@ -551,6 +606,10 @@ public class CallbackClientImpl implements CallbackClient { private void handleException(Exception ex) { logger.fatal("Client error\n", ex); - frame.showError("Error: " + ex.getMessage()); + String errorMessage = ex.getMessage(); + if (errorMessage == null || errorMessage.isEmpty() || errorMessage.equals("Null")) { + errorMessage = ex.getClass().getSimpleName() + " - look server logs for more details"; + } + frame.showError("Server's error: " + errorMessage); } } diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index f962b067987..75370abed30 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -1,11 +1,3 @@ - - -/* - * TablePlayerPanel.java - * - * Created on 9-May-2010, 11:43:03 AM - */ - package mage.client.table; import mage.cards.decks.importer.DeckImporter; diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index a7e2d7b4e28..9540185bc15 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -1,11 +1,3 @@ - - -/* - * TournamentPlayerPanel.java - * - * Created on Jan 28, 2011, 1:50:29 PM - */ - package mage.client.table; import mage.cards.decks.DeckCardLists; diff --git a/Mage.Client/src/main/java/mage/client/themes/ThemeType.java b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java index 56b57493bd6..db90d27585e 100644 --- a/Mage.Client/src/main/java/mage/client/themes/ThemeType.java +++ b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java @@ -1,7 +1,13 @@ package mage.client.themes; +import mage.abilities.hint.HintUtils; +import org.mage.card.arcane.SvgUtils; + import java.awt.*; +/** + * @author 18ths, JayDi85 + */ public enum ThemeType { // https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/_nimbusDefaults.html DEFAULT("Default", @@ -21,7 +27,11 @@ public enum ThemeType { null, // mageToolbar new Color(200, 200, 180, 200), // playerPanel_inactiveBackgroundColor new Color(200, 255, 200, 200), // playerPanel_activeBackgroundColor - new Color(131, 94, 83, 200) // playerPanel_deadBackgroundColor + new Color(131, 94, 83, 200), // playerPanel_deadBackgroundColor + // card icons + new Color(169, 176, 190), + Color.black, + new Color(51, 98, 140) ), GREY("Grey", "grey-theme/", @@ -40,7 +50,11 @@ public enum ThemeType { null, // mageToolbar new Color(172, 172, 172, 200), // playerPanel_inactiveBackgroundColor new Color(180, 234, 180, 200), // playerPanel_activeBackgroundColor - new Color(99, 99, 99, 200) // playerPanel_deadBackgroundColor + new Color(99, 99, 99, 200), // playerPanel_deadBackgroundColor + // card icons + new Color(158, 158, 158), + Color.black, + Color.black ), SUNSET_VAPORWAVE("Vaporwave Sunset", "16bit-theme/", @@ -59,6 +73,10 @@ public enum ThemeType { new Color(192, 166, 232), new Color(243, 233, 164), new Color(204, 236, 201), + new Color(106, 0, 255), + // card icons + new Color(246, 136, 158), + Color.black, new Color(106, 0, 255) ), COFFEE("Coffee", @@ -78,7 +96,11 @@ public enum ThemeType { new Color(219, 193, 172), // mageToolbar new Color(219, 193, 172), new Color(204, 236, 201), - new Color(99, 72, 50, 255) + new Color(99, 72, 50, 255), + // card icons + new Color(219, 193, 172), + Color.black, + new Color(97, 27, 0) ), ISLAND("Island", "island-theme/", @@ -97,7 +119,11 @@ public enum ThemeType { new Color(172, 195, 219), // mageToolbar new Color(172, 195, 219), new Color(204, 236, 201), - new Color(50, 68, 99, 255) + new Color(50, 68, 99, 255), + // card icons + new Color(172, 197, 219), + Color.black, + new Color(0, 78, 97) ); private final String name; @@ -118,6 +144,10 @@ public enum ThemeType { private final Color playerPanel_inactiveBackgroundColor; private final Color playerPanel_activeBackgroundColor; private final Color playerPanel_deadBackgroundColor; + // card icons settings (example: flying icon) + private final Color cardIconsFillColor; + private final Color cardIconsStrokeColor; + private final Color cardIconsTextColor; ThemeType(String name, String path, @@ -136,7 +166,10 @@ public enum ThemeType { Color mageToolbar, Color playerPanel_inactiveBackgroundColor, Color playerPanel_activeBackgroundColor, - Color playerPanel_deadBackgroundColor + Color playerPanel_deadBackgroundColor, + Color cardIconsFillColor, + Color cardIconsStrokeColor, + Color cardIconsTextColor ) { this.name = name; this.path = path; @@ -156,6 +189,9 @@ public enum ThemeType { this.playerPanel_activeBackgroundColor = playerPanel_activeBackgroundColor; this.playerPanel_deadBackgroundColor = playerPanel_deadBackgroundColor; this.playerPanel_inactiveBackgroundColor = playerPanel_inactiveBackgroundColor; + this.cardIconsFillColor = cardIconsFillColor; + this.cardIconsStrokeColor = cardIconsStrokeColor; + this.cardIconsTextColor = cardIconsTextColor; } @Override @@ -267,4 +303,46 @@ public enum ThemeType { return getBackgroundPath(); } } + + public Color getCardIconsFillColor() { + return this.cardIconsFillColor; + } + + public Color getCardIconsStrokeColor() { + return cardIconsStrokeColor; + } + + public Color getCardIconsTextColor() { + return cardIconsTextColor; + } + + public String getCardIconsResourcePath(String resourceName) { + return "/card/icons/" + resourceName; + } + + public String getCardIconsCssFile() { + return "card-icons-svg-settings.css"; + } + + public String getCardIconsCssSettings() { + String fillColorVal = HintUtils.colorToHtml(this.getCardIconsFillColor()); + String strokeColorVal = HintUtils.colorToHtml(this.getCardIconsStrokeColor()); + + return String.format("" + + "fill: %s;" + + "stroke: %s;" + + "stroke-width: 0.5;" // px + + "stroke-opacity: 0.7;", // 1 = 100% + fillColorVal, + strokeColorVal + ); + } + + /** + * Prepare theme settings and files before using. Call it on app loading or after theme changed + */ + public void reload() { + // reload card icons css file (run it all the time, even on svg unsupport mode) + SvgUtils.prepareCss(this.getCardIconsCssFile(), this.getCardIconsCssSettings(), true); + } } \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java b/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java index b6cdf04b34b..e859263632d 100644 --- a/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java +++ b/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java @@ -1,10 +1,3 @@ - - - /* - * TournamentPane.java - * - * Created on 22-Jan-2011, 11:41:47 PM - */ package mage.client.tournament; import java.util.UUID; diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.form b/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.form deleted file mode 100644 index d455016e3f0..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.form +++ /dev/null @@ -1,46 +0,0 @@ - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.java b/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.java deleted file mode 100644 index bb3dbf739d6..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatDialog.java +++ /dev/null @@ -1,127 +0,0 @@ - - -/* - * COMBAT.java - * - * Created on Feb 10, 2010, 3:35:02 PM - */ - -//package mage.client.dialog; -package mage.client.unusedFiles; - -import mage.client.cards.BigCard; -import mage.view.CombatGroupView; - -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyVetoException; -import java.util.List; -import java.util.UUID; -import mage.client.dialog.MageDialog; -import org.apache.log4j.Logger; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CombatDialog extends MageDialog { - - private static final Logger logger = Logger.getLogger(CombatDialog.class); - - private UUID gameId; - private BigCard bigCard; - private int lastX = 500; - private int lastY = 300; - - /** Creates new form COMBAT */ - public CombatDialog() { - - JPanel contentPane = new JPanel() { - private static final long serialVersionUID = -8283955788355547309L; - - @Override - public void paintComponent(Graphics g) { - g.setColor(new Color(50, 50, 50, 100)); - g.fillRect(0, 0, getWidth(), getHeight()); - } - }; - setContentPane(contentPane); - - initComponents(); - this.setModal(false); - - combatArea.setOpaque(false); - jScrollPane1.setOpaque(false); - jScrollPane1.getViewport().setOpaque(false); - getRootPane().setOpaque(false); - - //setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); - } - - public void init(UUID gameId, BigCard bigCard) { - this.gameId = gameId; - this.bigCard = bigCard; - } - - public void showDialog(List combat) { - combatArea.removeAll(); - for (CombatGroupView group: combat) { - CombatGroup combatGroup = new CombatGroup(); - combatGroup.init(gameId, bigCard); - combatGroup.update(group); - combatGroup.setVisible(true); - combatArea.add(combatGroup); - combatGroup.revalidate(); - } - try { - this.setSelected(true); - } catch (PropertyVetoException ex) { - logger.error(null, ex); - } - pack(); - this.revalidate(); - this.repaint(); - if (!this.isVisible()) { - this.setVisible(true); - this.setLocation(lastX, lastY); - } - } - - @Override - public void hideDialog() { - this.lastX = this.getX(); - this.lastY = this.getY(); - super.hideDialog(); - } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - combatArea = new javax.swing.JPanel(); - - setResizable(true); - setTitle("Combat"); - setNormalBounds(new java.awt.Rectangle(400, 200, 410, 307)); - setVisible(true); - getContentPane().setLayout(new java.awt.BorderLayout()); - - jScrollPane1.setViewportView(combatArea); - - getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER); - - pack(); - }// //GEN-END:initComponents - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JPanel combatArea; - private javax.swing.JScrollPane jScrollPane1; - // End of variables declaration//GEN-END:variables - -} diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.form b/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.form deleted file mode 100644 index cfe13fcba76..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.form +++ /dev/null @@ -1,58 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.java b/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.java deleted file mode 100644 index eb8cd77a44f..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/CombatGroup.java +++ /dev/null @@ -1,96 +0,0 @@ - - - /* - * CombatGroup.java - * - * Created on Feb 10, 2010, 3:36:55 PM - */ -package mage.client.unusedFiles; - -//package mage.client.game; -import java.util.UUID; -import mage.client.cards.BigCard; -import mage.client.util.ClientDefaultSettings; -import mage.client.util.GUISizeHelper; -import mage.view.CombatGroupView; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CombatGroup extends javax.swing.JPanel { - - private UUID gameId; - private BigCard bigCard; - - /** - * Creates new form CombatGroup - */ - public CombatGroup() { - initComponents(); - attackers.setDontDisplayTapped(true); - } - - public void init(UUID gameId, BigCard bigCard) { - this.gameId = gameId; - this.bigCard = bigCard; - } - - public void update(CombatGroupView combatGroup) { - this.lblDefender.setText(combatGroup.getDefenderName()); - attackers.setCardDimension(GUISizeHelper.otherZonesCardDimension); - this.attackers.loadCards(combatGroup.getAttackers(), bigCard, gameId, true); - - blockers.setCardDimension(GUISizeHelper.otherZonesCardDimension); - this.blockers.loadCards(combatGroup.getBlockers(), bigCard, gameId, true); - - this.attackers.setVisible(true); - this.blockers.setVisible(true); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - blockers = new mage.client.cards.Cards(); - attackers = new mage.client.cards.Cards(); - lblDefender = new javax.swing.JLabel(); - - blockers.setPreferredSize(new java.awt.Dimension(ClientDefaultSettings.dimensions.getFrameWidth() + 8, ClientDefaultSettings.dimensions.getFrameHeight() + 25)); - - attackers.setPreferredSize(new java.awt.Dimension(ClientDefaultSettings.dimensions.getFrameWidth() + 8, ClientDefaultSettings.dimensions.getFrameHeight() + 25)); - - lblDefender.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - lblDefender.setText("Defender"); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblDefender, javax.swing.GroupLayout.DEFAULT_SIZE, 69, Short.MAX_VALUE) - .addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblDefender) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); - }// //GEN-END:initComponents - - // Variables declaration - do not modify//GEN-BEGIN:variables - private mage.client.cards.Cards attackers; - private mage.client.cards.Cards blockers; - private javax.swing.JLabel lblDefender; - // End of variables declaration//GEN-END:variables - -} diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/DelayedViewerThread.java b/Mage.Client/src/main/java/mage/client/unusedFiles/DelayedViewerThread.java deleted file mode 100644 index 89976b2669f..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/DelayedViewerThread.java +++ /dev/null @@ -1,57 +0,0 @@ - -package mage.client.unusedFiles; -//package mage.client.thread; - -import java.awt.*; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - - -public class DelayedViewerThread extends Thread { - private static final DelayedViewerThread instance = new DelayedViewerThread(); - - public static DelayedViewerThread getInstance() { - return instance; - } - - private final Map delayedViewers; - - protected DelayedViewerThread() { - delayedViewers = new HashMap<>(); - start(); - } - - public synchronized void show(Component component, long delay) { - delayedViewers.put(component, System.currentTimeMillis() + delay); - notifyAll(); - } - - public synchronized void hide(Component component) { - delayedViewers.remove(component); - component.setVisible(false); - } - - @Override - public synchronized void run() { - while (true) { - try { - if (delayedViewers.isEmpty()) { - wait(); - } - final long time = System.currentTimeMillis(); - for (Iterator it = delayedViewers.keySet().iterator(); it.hasNext();) { - Component component = it.next(); - final long delayedTime = delayedViewers.get(component); - if (delayedTime <= time) { - component.setVisible(true); - it.remove(); - } - } - wait(100); - } catch (final InterruptedException ex) { - System.out.println("Interrupted : " + ex.getMessage()); - } - } - } -} diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.form b/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.form deleted file mode 100644 index 92c0c74d182..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.form +++ /dev/null @@ -1,156 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.java b/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.java deleted file mode 100644 index 26833d6a54f..00000000000 --- a/Mage.Client/src/main/java/mage/client/unusedFiles/PlayerPanel.java +++ /dev/null @@ -1,181 +0,0 @@ - - - /* - * PlayerPanel.java - * - * Created on Nov 18, 2009, 3:01:31 PM - */ -package mage.client.unusedFiles; -//package mage.client.game; - -import java.awt.Color; -import java.util.UUID; - -import mage.client.SessionHandler; -import mage.client.cards.BigCard; -import mage.client.dialog.ShowCardsDialog; -import mage.view.PlayerView; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class PlayerPanel extends javax.swing.JPanel { - - private UUID playerId; - private UUID gameId; - private PlayerView player; - - private ShowCardsDialog graveyard; - private BigCard bigCard; - - /** - * Creates new form PlayerPanel - */ - public PlayerPanel() { - initComponents(); - } - - public void init(UUID gameId, UUID playerId, BigCard bigCard) { - this.gameId = gameId; - this.playerId = playerId; - this.bigCard = bigCard; - } - - public void update(PlayerView player) { - this.player = player; - this.txtLife.setText(Integer.toString(player.getLife())); - this.txtHand.setText(Integer.toString(player.getHandCount())); - this.txtLibrary.setText(Integer.toString(player.getLibraryCount())); - this.btnGrave.setText(Integer.toString(player.getGraveyard().size())); - this.btnPlayerName.setText(player.getName()); - if (player.isActive()) { - this.btnPlayerName.setBackground(Color.DARK_GRAY); - } else if (player.hasLeft()) { - this.btnPlayerName.setBackground(Color.RED); - } else { - this.btnPlayerName.setBackground(Color.LIGHT_GRAY); - } - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - btnPlayerName = new javax.swing.JButton(); - lblLife = new javax.swing.JLabel(); - lblLibrary = new javax.swing.JLabel(); - lblHand = new javax.swing.JLabel(); - txtLife = new javax.swing.JLabel(); - txtLibrary = new javax.swing.JLabel(); - txtHand = new javax.swing.JLabel(); - lblGrave = new javax.swing.JLabel(); - btnGrave = new javax.swing.JButton(); - - setName("Form"); // NOI18N - - btnPlayerName.setText("Player Name"); // NOI18N - btnPlayerName.setName("btnPlayerName"); // NOI18N - btnPlayerName.addActionListener(evt -> btnPlayerNameActionPerformed(evt)); - - lblLife.setLabelFor(txtLife); - lblLife.setText("Life:"); // NOI18N - lblLife.setName("lblLife"); // NOI18N - - lblLibrary.setLabelFor(txtLibrary); - lblLibrary.setText("Library:"); // NOI18N - lblLibrary.setName("lblLibrary"); // NOI18N - - lblHand.setLabelFor(txtHand); - lblHand.setText("Hand:"); // NOI18N - lblHand.setName("lblHand"); // NOI18N - - txtLife.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); - txtLife.setName("txtLife"); // NOI18N - - txtLibrary.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); - txtLibrary.setName("txtLibrary"); // NOI18N - - txtHand.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); - txtHand.setName("txtHand"); // NOI18N - - lblGrave.setLabelFor(btnGrave); - lblGrave.setText("Grave:"); // NOI18N - lblGrave.setName("lblGrave"); // NOI18N - - btnGrave.setName("btnGrave"); // NOI18N - btnGrave.addActionListener(evt -> btnGraveActionPerformed(evt)); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblLife, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 42, Short.MAX_VALUE) - .addComponent(lblGrave, javax.swing.GroupLayout.DEFAULT_SIZE, 42, Short.MAX_VALUE) - .addComponent(lblHand, javax.swing.GroupLayout.DEFAULT_SIZE, 42, Short.MAX_VALUE) - .addComponent(lblLibrary, javax.swing.GroupLayout.DEFAULT_SIZE, 42, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(txtLife, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE) - .addComponent(txtLibrary, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE) - .addComponent(txtHand, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE) - .addComponent(btnGrave, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE)) - .addContainerGap()) - .addComponent(btnPlayerName, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(btnPlayerName, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(txtLife, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblLife, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(txtLibrary, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblLibrary, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(txtHand, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) - .addComponent(lblHand)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblGrave) - .addComponent(btnGrave, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ); - }// //GEN-END:initComponents - - private void btnPlayerNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayerNameActionPerformed - SessionHandler.sendPlayerUUID(gameId, playerId); - }//GEN-LAST:event_btnPlayerNameActionPerformed - - private void btnGraveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGraveActionPerformed - if (graveyard == null) { - graveyard = new ShowCardsDialog(); - } - graveyard.loadCards(player.getName() + " graveyard", player.getGraveyard(), bigCard, gameId, false, null, null, null); - }//GEN-LAST:event_btnGraveActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton btnGrave; - private javax.swing.JButton btnPlayerName; - private javax.swing.JLabel lblGrave; - private javax.swing.JLabel lblHand; - private javax.swing.JLabel lblLibrary; - private javax.swing.JLabel lblLife; - private javax.swing.JLabel txtHand; - private javax.swing.JLabel txtLibrary; - private javax.swing.JLabel txtLife; - // End of variables declaration//GEN-END:variables - -} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java deleted file mode 100644 index 70b7bc1ae37..00000000000 --- a/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.client.util; - -import java.util.Comparator; -import mage.view.CardView; - -/** - * - * @author LevelX2 - */ -public class CardViewCardTypeComparator implements Comparator { - - @Override - public int compare(CardView o1, CardView o2) { - return o1.getCardTypes().toString().compareTo(o2.getCardTypes().toString()); - } - -} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewColorComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewColorComparator.java deleted file mode 100644 index dac7bd6cd41..00000000000 --- a/Mage.Client/src/main/java/mage/client/util/CardViewColorComparator.java +++ /dev/null @@ -1,19 +0,0 @@ - - -package mage.client.util; - -import java.util.Comparator; -import mage.view.CardView; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CardViewColorComparator implements Comparator { - - @Override - public int compare(CardView o1, CardView o2) { - return o1.getColor().compareTo(o2.getColor()); - } - -} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java deleted file mode 100644 index 60ce0dda9ab..00000000000 --- a/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java +++ /dev/null @@ -1,19 +0,0 @@ - -package mage.client.util; - -import java.util.Comparator; -import mage.utils.CardColorUtil; -import mage.view.CardView; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CardViewColorIdentityComparator implements Comparator { - - @Override - public int compare(CardView o1, CardView o2) { - return CardColorUtil.getColorIdentitySortValue(o1.getManaCost(), o1.getColor(), o1.getRules()) - - CardColorUtil.getColorIdentitySortValue(o2.getManaCost(), o2.getColor(), o2.getRules()); - } -} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewNameComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewNameComparator.java deleted file mode 100644 index 47612edd449..00000000000 --- a/Mage.Client/src/main/java/mage/client/util/CardViewNameComparator.java +++ /dev/null @@ -1,19 +0,0 @@ - - -package mage.client.util; - -import java.util.Comparator; -import mage.view.CardView; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CardViewNameComparator implements Comparator { - - @Override - public int compare(CardView o1, CardView o2) { - return o1.getName().compareTo(o2.getName()); - } - -} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/ClientEventType.java b/Mage.Client/src/main/java/mage/client/util/ClientEventType.java index 252707f26cb..c695d94a9b0 100644 --- a/Mage.Client/src/main/java/mage/client/util/ClientEventType.java +++ b/Mage.Client/src/main/java/mage/client/util/ClientEventType.java @@ -1,20 +1,22 @@ package mage.client.util; +/** + * @author JayDi85 + */ public enum ClientEventType { + CARD_CLICK, + CARD_DOUBLE_CLICK, + CARD_POPUP_MENU, // right click on windows // TODO: split into two events: CARD_ and PANEL_ + // SET_NUMBER, - ACTION_CONSUMED, - DOUBLE_CLICK, - ALT_DOUBLE_CLICK, - REMOVE_MAIN, - REMOVE_SIDEBOARD, - SHOW_POP_UP_MENU, - REMOVE_SPECIFIC_CARD, - ADD_SPECIFIC_CARD, - PICK_A_CARD, - MARK_A_CARD, + // + DECK_REMOVE_SELECTION_MAIN, + DECK_REMOVE_SELECTION_SIDEBOARD, + DECK_REMOVE_SPECIFIC_CARD, + DECK_ADD_SPECIFIC_CARD, + // + DRAFT_PICK_CARD, + DRAFT_MARK_CARD, + // PLAYER_TYPE_CHANGED - - - - } diff --git a/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java b/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java index 1789c6ef23b..1e7b753f66d 100644 --- a/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java @@ -1,16 +1,16 @@ package mage.client.util; -import java.util.UUID; - import mage.client.SessionHandler; import mage.view.CardView; +import java.util.UUID; + public enum DefaultActionCallback { instance; - public void mouseClicked(UUID gameId, CardView card) { + public void mouseClicked(UUID gameId, CardView card) { if (gameId != null) { if (card.isAbility() && card.getAbility() != null) { SessionHandler.sendPlayerUUID(gameId, card.getAbility().getId()); diff --git a/Mage.Client/src/main/java/mage/client/util/Event.java b/Mage.Client/src/main/java/mage/client/util/Event.java index 90306aa14d4..c2cc5ff6599 100644 --- a/Mage.Client/src/main/java/mage/client/util/Event.java +++ b/Mage.Client/src/main/java/mage/client/util/Event.java @@ -1,42 +1,47 @@ - - package mage.client.util; -import java.awt.Component; +import java.awt.*; +import java.awt.event.MouseEvent; import java.io.Serializable; /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class Event implements Serializable { - private final Object source; - private final Component component; + + private final Object source; // can be null for non card events like popup from panel + private final Component component; // component that raise the event (example: MageCard) private final ClientEventType eventType; private final int number; private final int xPos; private final int yPos; + private final MouseEvent mouseEvent; + // force alt mouse state for fake mouse event (example: card's selector can add card to deck by panel's button instead double click) + private final boolean mouseAltDown; + public Event(Object source, ClientEventType eventType) { this(source, eventType, 0); } - + public Event(Object source, ClientEventType eventType, int number) { + this(source, eventType, number, 0, 0, null); + } + + public Event(Object source, ClientEventType eventType, int number, int xPos, int yPos, Component component) { + this(source, eventType, number, xPos, yPos, component, null, false); + } + + public Event(Object source, ClientEventType eventType, int number, int xPos, int yPos, Component component, + MouseEvent mouseEvent, boolean mouseAltDown) { this.source = source; this.eventType = eventType; this.number = number; - this.xPos = 0; - this.yPos = 0; - this.component = null; - } - - public Event(Object source, ClientEventType eventType, int xPos, int yPos, Component component) { - this.source = source; - this.eventType = eventType; - this.number =0; this.xPos = xPos; this.yPos = yPos; this.component = component; + this.mouseEvent = mouseEvent; + this.mouseAltDown = mouseAltDown; } public Object getSource() { @@ -62,5 +67,21 @@ public class Event implements Serializable { public Component getComponent() { return component; } - -} + + public MouseEvent getMouseEvent() { + if (this.mouseEvent == null) { + // GUI must catch a correct events, so look at stack trace and find a problem code with event generation + throw new IllegalArgumentException("Error, found empty mouse event."); + } + + return mouseEvent; + } + + public boolean isMouseAltDown() { + if (mouseEvent != null) { + return mouseEvent.isAltDown(); + } else { + return mouseAltDown; + } + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/EventSource.java b/Mage.Client/src/main/java/mage/client/util/EventSource.java index 0698f3575e3..59c4b3ab9a7 100644 --- a/Mage.Client/src/main/java/mage/client/util/EventSource.java +++ b/Mage.Client/src/main/java/mage/client/util/EventSource.java @@ -1,16 +1,12 @@ - - package mage.client.util; import java.io.Serializable; /** - * * @author BetaSteward_at_googlemail.com */ public interface EventSource extends Serializable { - void addListener(Listener listener); - void clearListeners(); + void clearListeners(); } diff --git a/Mage.Client/src/main/java/mage/client/util/GUISizeHelper.java b/Mage.Client/src/main/java/mage/client/util/GUISizeHelper.java index 3f187e19f40..d8cf3a5a99e 100644 --- a/Mage.Client/src/main/java/mage/client/util/GUISizeHelper.java +++ b/Mage.Client/src/main/java/mage/client/util/GUISizeHelper.java @@ -1,23 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.client.util; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.util.Locale; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; import mage.client.MageFrame; import mage.client.dialog.PreferencesDialog; import org.mage.card.arcane.CardRenderer; +import javax.swing.*; +import java.awt.*; +import java.util.Locale; + /** - * * @author LevelX2 */ public final class GUISizeHelper { @@ -66,7 +57,7 @@ public final class GUISizeHelper { public static int gameDialogButtonWidth; public static Dimension handCardDimension; - public static int stackWidth; + public static int stackWidth; // percent public static Dimension otherZonesCardDimension; public static int otherZonesCardVerticalOffset; @@ -75,7 +66,7 @@ public final class GUISizeHelper { public static Dimension battlefieldCardMaxDimension; public static Dimension editorCardDimension; - public static int editorCardOffsetSize; + public static int editorCardVertOffsetInStack; public static int enlargedImageHeight; public static int getTableRowHeight() { @@ -88,6 +79,11 @@ public final class GUISizeHelper { return new java.awt.Font("Arial", 0, fontSize); } + public static Font getCardFont() { + // default font type for some card panels (each render mode uses it's own font sizes) + return new Font("Arial", Font.PLAIN, 14); + } + public static void changeGUISize() { calculateGUISizes(); MageFrame.getInstance().changeGUISize(); @@ -158,9 +154,9 @@ public final class GUISizeHelper { int editorCardSize = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_CARD_EDITOR_SIZE, 14); editorCardDimension = new Dimension(CARD_IMAGE_WIDTH * editorCardSize / 42, CARD_IMAGE_HEIGHT * editorCardSize / 42); if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_FALLBACK, "false").equals("false")) { - editorCardOffsetSize = CardRenderer.getCardTopHeight(editorCardDimension.width); + editorCardVertOffsetInStack = CardRenderer.getCardTopHeight(editorCardDimension.width); } else { - editorCardOffsetSize = 2 * PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_CARD_OFFSET_SIZE, 14) - 10; + editorCardVertOffsetInStack = 2 * PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_CARD_OFFSET_SIZE, 14) - 10; } enlargedImageHeight = 25 * PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_ENLARGED_IMAGE_SIZE, 20); @@ -186,4 +182,14 @@ public final class GUISizeHelper { } return text; } + + /** + * Return scrollbar settings, so user can scroll it more faster for bigger cards + * + * @param cardSize card's wight or height (depends on vertical or horizontal scrollbar) + * @return + */ + public static int getCardsScrollbarUnitInc(int cardSize) { + return Math.max(8, cardSize / 4); + } } diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCardTypeComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCardTypeComparator.java new file mode 100644 index 00000000000..53eef66e51b --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCardTypeComparator.java @@ -0,0 +1,25 @@ +package mage.client.util.comparators; + +import mage.constants.CardType; +import mage.view.CardView; + +import java.util.stream.Collectors; + +/** + * @author LevelX2 + */ +public class CardViewCardTypeComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return o1.getCardTypes().toString().compareTo(o2.getCardTypes().toString()); + } + + @Override + public String getCategoryName(CardView sample) { + return sample.getCardTypes() + .stream() + .map(CardType::toString) + .collect(Collectors.joining(", ")); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorComparator.java new file mode 100644 index 00000000000..78a21115e26 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorComparator.java @@ -0,0 +1,28 @@ +package mage.client.util.comparators; + +import mage.view.CardView; + +import java.util.stream.Collectors; + +/** + * @author BetaSteward_at_googlemail.com, JayDi85 + */ +public class CardViewColorComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return o1.getColor().compareTo(o2.getColor()); + } + + @Override + public String getCategoryName(CardView sample) { + String res = sample.getColor().getColors() + .stream() + .map(c -> "{" + c.getOneColoredManaSymbol() + "}") + .collect(Collectors.joining("")); + if (res.isEmpty()) { + res = "{C}"; // colorless + } + return res; + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java new file mode 100644 index 00000000000..f644f69eee8 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewColorIdentityComparator.java @@ -0,0 +1,33 @@ +package mage.client.util.comparators; + +import mage.filter.FilterMana; +import mage.util.ManaUtil; +import mage.view.CardView; + +/** + * @author BetaSteward_at_googlemail.com, JayDi85 + */ +public class CardViewColorIdentityComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return calcHash(o1) - calcHash(o2); + } + + @Override + public String getCategoryName(CardView sample) { + String res = calcIdentity(sample).toString(); + if (res.isEmpty()) { + res = "{C}"; // colorless + } + return res; + } + + public static FilterMana calcIdentity(CardView cardView) { + return ManaUtil.getColorIdentity(cardView.getColor(), cardView.getManaCost(), cardView.getRules(), null); + } + + public static int calcHash(CardView cardView) { + return ManaUtil.getColorIdentityHash(calcIdentity(cardView)); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewComparator.java new file mode 100644 index 00000000000..e6fd8d60708 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewComparator.java @@ -0,0 +1,14 @@ +package mage.client.util.comparators; + +import mage.view.CardView; + +import java.util.Comparator; + +/** + * @author JayDi85 + */ +public interface CardViewComparator extends Comparator { + + String getCategoryName(CardView sample); + +} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java similarity index 50% rename from Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java rename to Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java index 00452c6a3df..4fdf0dc01f8 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewCostComparator.java @@ -1,18 +1,19 @@ +package mage.client.util.comparators; -package mage.client.util; - -import java.util.Comparator; import mage.view.CardView; /** - * * @author BetaSteward_at_googlemail.com */ -public class CardViewCostComparator implements Comparator { +public class CardViewCostComparator implements CardViewComparator { @Override public int compare(CardView o1, CardView o2) { return Integer.compare(o1.getConvertedManaCost(), o2.getConvertedManaCost()); } + @Override + public String getCategoryName(CardView sample) { + return "CMC: " + sample.getConvertedManaCost(); + } } diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewEDHPowerLevelComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java similarity index 98% rename from Mage.Client/src/main/java/mage/client/util/CardViewEDHPowerLevelComparator.java rename to Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java index 9d4f614020a..34105d8f145 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewEDHPowerLevelComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java @@ -1,15 +1,23 @@ +package mage.client.util.comparators; -package mage.client.util; - -import java.util.Comparator; -import java.util.Locale; import mage.view.CardView; +import java.util.Locale; + /** - * * @author spjspj */ -public class CardViewEDHPowerLevelComparator implements Comparator { +public class CardViewEDHPowerLevelComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return Integer.compare(getPowerLevel(o1), getPowerLevel(o2)); + } + + @Override + public String getCategoryName(CardView sample) { + return "EDH: " + getPowerLevel(sample); + } private int getPowerLevel(CardView card) { @@ -506,10 +514,4 @@ public class CardViewEDHPowerLevelComparator implements Comparator { } return thisMaxPower; } - - @Override - public int compare(CardView o1, CardView o2) { - return Integer.compare(getPowerLevel(o1), getPowerLevel(o2)); - } - } diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNameComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNameComparator.java new file mode 100644 index 00000000000..fd0019e654f --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNameComparator.java @@ -0,0 +1,19 @@ +package mage.client.util.comparators; + +import mage.view.CardView; + +/** + * @author BetaSteward_at_googlemail.com + */ +public class CardViewNameComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return o1.getName().compareTo(o2.getName()); + } + + @Override + public String getCategoryName(CardView sample) { + return sample.getName(); + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNoneComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNoneComparator.java new file mode 100644 index 00000000000..f3a73b538eb --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewNoneComparator.java @@ -0,0 +1,19 @@ +package mage.client.util.comparators; + +import mage.view.CardView; + +/** + * @author JayDi85 + */ +public class CardViewNoneComparator implements CardViewComparator { + + @Override + public int compare(CardView o1, CardView o2) { + return 0; + } + + @Override + public String getCategoryName(CardView sample) { + return ""; + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewRarityComparator.java similarity index 63% rename from Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java rename to Mage.Client/src/main/java/mage/client/util/comparators/CardViewRarityComparator.java index 77bd9ff23f9..3f8566ebcd4 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewRarityComparator.java @@ -1,14 +1,12 @@ -package mage.client.util; +package mage.client.util.comparators; import mage.constants.Rarity; import mage.view.CardView; -import java.util.Comparator; - /** * @author BetaSteward_at_googlemail.com */ -public class CardViewRarityComparator implements Comparator { +public class CardViewRarityComparator implements CardViewComparator { @Override public int compare(CardView o1, CardView o2) { @@ -21,4 +19,8 @@ public class CardViewRarityComparator implements Comparator { ); } + @Override + public String getCategoryName(CardView sample) { + return sample.getRarity().toString(); + } } \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java index 243b47dc7c7..77131246d83 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java @@ -1,6 +1,6 @@ package mage.client.util.gui; -import mage.cards.MagePermanent; +import mage.cards.MageCard; import mage.cards.action.TransferData; import mage.client.MageFrame; import mage.client.game.PlayAreaPanel; @@ -12,7 +12,7 @@ import java.awt.*; import java.util.UUID; /** - * @author noxx + * @author noxx, JayDi85 */ public final class ArrowUtil { @@ -25,9 +25,9 @@ public final class ArrowUtil { me.translate(-parentPoint.x, -parentPoint.y); UUID uuid = data.getCard().getPairedCard(); for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + MageCard permanent = pa.getBattlefieldPanel().getPermanentPanels().get(uuid); if (permanent != null) { - Point target = permanent.getLocationOnScreen(); + Point target = permanent.getCardLocationOnScreen().getCardPoint(); target.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(data.getGameId(), (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.green, ArrowBuilder.Type.PAIRED); } @@ -41,9 +41,9 @@ public final class ArrowUtil { me.translate(-parentPoint.x, -parentPoint.y); for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { for (UUID uuid : data.getCard().getBandedCards()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + MageCard permanent = pa.getBattlefieldPanel().getPermanentPanels().get(uuid); if (permanent != null) { - Point target = permanent.getLocationOnScreen(); + Point target = permanent.getCardLocationOnScreen().getCardPoint(); target.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(data.getGameId(), (int) me.getX() + 55, (int) me.getY() + 25, (int) target.getX() + 60, (int) target.getY() + 35, Color.yellow, ArrowBuilder.Type.BANDED); } @@ -77,9 +77,9 @@ public final class ArrowUtil { me.translate(-parentPoint.x, -parentPoint.y); UUID uuid = data.getCard().getParentId(); for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + MageCard permanent = pa.getBattlefieldPanel().getPermanentPanels().get(uuid); if (permanent != null) { - Point source = permanent.getLocationOnScreen(); + Point source = permanent.getCardLocationOnScreen().getCardPoint(); source.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(data.getGameId(), (int) source.getX() + 40, (int) source.getY() + 10, (int) me.getX() + 35, (int) me.getY() + 20, Color.blue, ArrowBuilder.Type.SOURCE); } @@ -106,9 +106,9 @@ public final class ArrowUtil { } for (PlayAreaPanel panel : MageFrame.getGamePlayers(data.getGameId()).values()) { - MagePermanent permanent = panel.getBattlefieldPanel().getPermanents().get(uuid); + MageCard permanent = panel.getBattlefieldPanel().getPermanentPanels().get(uuid); if (permanent != null) { - Point target = permanent.getLocationOnScreen(); + Point target = permanent.getCardLocationOnScreen().getCardPoint(); target.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(data.getGameId(), (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.red, ArrowBuilder.Type.TARGET); continue; diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index b73b9ee132d..18ecf4b509c 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -84,7 +84,6 @@ public final class GuiDisplayUtil { descriptionPanel.setVisible(false); descriptionPanel.setLayout(null); - //descriptionPanel.setBorder(BorderFactory.createLineBorder(Color.green)); JButton j = new JButton(""); j.setBounds(0, 0, width, height); j.setBackground(Color.black); diff --git a/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java index 40d9677ceff..ae7fbcd767f 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java @@ -13,5 +13,5 @@ public interface CardLayoutStrategy { void onAdd(BattlefieldPanel jLayeredPane); - void doLayout(BattlefieldPanel jLayeredPane, int battlefieldWidth); + void doLayout(BattlefieldPanel battlefieldPanel, int battlefieldWidth); } diff --git a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/impl/CardLayoutStrategyImpl.java similarity index 50% rename from Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java rename to Mage.Client/src/main/java/mage/client/util/layout/impl/CardLayoutStrategyImpl.java index af9547c15c7..34624dc3f26 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/impl/CardLayoutStrategyImpl.java @@ -6,30 +6,23 @@ import java.awt.Rectangle; import java.util.Map; import java.util.UUID; import javax.swing.JLayeredPane; -import mage.cards.MagePermanent; + +import mage.cards.MageCard; import mage.client.game.BattlefieldPanel; import mage.client.plugins.impl.Plugins; import mage.client.util.layout.CardLayoutStrategy; import mage.view.PermanentView; /** - * Card layout for client version 1.3.0 and earlier. + * Card layouts code * - * Save it here for a while. - * - * @author noxx + * @author noxx, JayDi85 */ -public class OldCardLayoutStrategy implements CardLayoutStrategy { +public class CardLayoutStrategyImpl implements CardLayoutStrategy { - /** - * This offset is used once to shift all attachments - */ - private static final int ATTACHMENTS_MIN_DX_OFFSET = 12; - - /** - * This offset is used for each attachment - */ - private static final int ATTACHMENT_MIN_DY_OFFSET = 12; + private static final int ATTACHMENTS_OFFSET_ALL_X = 12; // offset to shift all attachments + private static final int ATTACHMENTS_OFFSET_SINGLE_Y = 12; + private static final int ATTACHMENTS_MAX_COLUMNS = 10; // after 10 attachments no more offsets (will be "hide") final class AttachmentLayoutInfos { @@ -59,100 +52,99 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { } @Override - public void doLayout(BattlefieldPanel battlefieldPanel, int width) { - Map permanents = battlefieldPanel.getPermanents(); + public void doLayout(BattlefieldPanel battlefieldPanel, int battlefieldWidth) { + Map cards = battlefieldPanel.getPermanentPanels(); JLayeredPane mainPanel = battlefieldPanel.getMainPanel(); // does the basic layout of rows and colums - int height = Plugins.instance.sortPermanents(battlefieldPanel.getUiComponentsList(), permanents, battlefieldPanel.isTopPanelBattlefield()); + int height = Plugins.instance.sortPermanents(battlefieldPanel.getUiComponentsList(), cards, battlefieldPanel.isTopPanelBattlefield()); - mainPanel.setPreferredSize(new Dimension(width - 30, height)); + mainPanel.setPreferredSize(new Dimension(battlefieldWidth - 30, height)); for (PermanentView permanent : battlefieldPanel.getBattlefield().values()) { if (permanent.getAttachments() != null && !permanent.isAttachedTo()) { // Layout only permanents that are not attached to other permanents itself - groupAttachments(battlefieldPanel, mainPanel, permanents, permanent); + groupAttachments(battlefieldPanel, mainPanel, cards, permanent); } } - } - private void groupAttachments(BattlefieldPanel battlefieldPanel, JLayeredPane mainPanel, Map permanents, PermanentView permanentWithAttachmentsView) { - MagePermanent permWithAttachments = permanents.get(permanentWithAttachmentsView.getId()); - if (permWithAttachments == null) { + private void groupAttachments(BattlefieldPanel battlefieldPanel, JLayeredPane mainPanel, Map cards, PermanentView permanentWithAttachmentsView) { + MageCard cardWithAttachments = cards.get(permanentWithAttachmentsView.getId()); + if (cardWithAttachments == null) { return; } - // Calculate how many vertical columns are needed and number of attachements - AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(0, battlefieldPanel, permanents, permanentWithAttachmentsView); - int position = battlefieldPanel.getPosition(permWithAttachments); // relative position within the layer - // permWithAttachments.getLinks().clear(); - Rectangle rectangleBaseCard = permWithAttachments.getBounds(); - if (!Plugins.instance.isCardPluginLoaded()) { - permWithAttachments.getLinks().clear(); - for (UUID attachmentId : permanentWithAttachmentsView.getAttachments()) { - MagePermanent link = permanents.get(attachmentId); - if (link != null) { - permWithAttachments.getLinks().add(link); - rectangleBaseCard.translate(20, 20); - link.setBounds(rectangleBaseCard); - battlefieldPanel.setPosition(link, ++position); - } - } - } else { - layoutAttachements(rectangleBaseCard.getX(), attachmentLayoutInfos.getColumns(), - attachmentLayoutInfos.getAttachments(), permanentWithAttachmentsView, permanents, battlefieldPanel, mainPanel, rectangleBaseCard); - mainPanel.setComponentZOrder(permWithAttachments, 0); - } + // calculate how many vertical columns are needed and number of attachements + AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(0, battlefieldPanel, cards, permanentWithAttachmentsView); + // group by columns + layoutAttachements( + cardWithAttachments.getCardLocation().getCardX(), + attachmentLayoutInfos.getColumns(), + attachmentLayoutInfos.getAttachments(), + permanentWithAttachmentsView, + cards, + battlefieldPanel, + mainPanel, + cardWithAttachments.getCardLocation().getCardBounds() + ); + mainPanel.setComponentZOrder(cardWithAttachments, 0); } - private void layoutAttachements(double baseX, // basic x position - int maxCul, // number of attachments levels + private void layoutAttachements(int startingCardX, + int maxColumnLevels, int ZOrder, PermanentView permanentWithAttachmentsView, - Map permanents, + Map cards, BattlefieldPanel battlefieldPanel, JLayeredPane mainPanel, - Rectangle rectangleBaseCard) { - - MagePermanent permWithAttachments = permanents.get(permanentWithAttachmentsView.getId()); - if (permWithAttachments == null) { + Rectangle lastAttachmentRect + ) { + // put attachments to the next level (take lastAttachmentRect and apply offsets) + MageCard cardWithAttachments = cards.get(permanentWithAttachmentsView.getId()); + if (cardWithAttachments == null) { return; } int col = getVerticalCul(permanentWithAttachmentsView, battlefieldPanel); // from right to left [2][1][0] int currentAttachmentCol = col + 1; - permWithAttachments.getLinks().clear(); - int VerticalIndex = permanentWithAttachmentsView.getAttachments().size(); + cardWithAttachments.getLinks().clear(); + int verticalIndex = permanentWithAttachmentsView.getAttachments().size(); for (UUID attachmentId : permanentWithAttachmentsView.getAttachments()) { + // put child attachments of the attachment PermanentView attachedPermanentView = battlefieldPanel.getBattlefield().get(attachmentId); if (attachedPermanentView != null && attachedPermanentView.getAttachments() != null && !attachedPermanentView.getAttachments().isEmpty()) { - layoutAttachements(baseX, maxCul, ZOrder, attachedPermanentView, permanents, battlefieldPanel, mainPanel, rectangleBaseCard); + layoutAttachements(startingCardX, maxColumnLevels, ZOrder, attachedPermanentView, cards, battlefieldPanel, mainPanel, lastAttachmentRect); } - MagePermanent attachedPermanent = permanents.get(attachmentId); - if (attachedPermanent != null) { - // reset x position + // put attachment + MageCard attachedCard = cards.get(attachmentId); + if (attachedCard != null) { + // x position Point point = new Point(); - point.setLocation(baseX + (maxCul - currentAttachmentCol) * Math.max(permWithAttachments.getWidth() / 10, ATTACHMENTS_MIN_DX_OFFSET), rectangleBaseCard.getY()); - rectangleBaseCard.setLocation(point); + point.setLocation(startingCardX + (maxColumnLevels - currentAttachmentCol) * Math.max(cardWithAttachments.getCardLocation().getCardWidth() / ATTACHMENTS_MAX_COLUMNS, ATTACHMENTS_OFFSET_ALL_X), lastAttachmentRect.getY()); + lastAttachmentRect.setLocation(point); - attachedPermanent.setBounds(rectangleBaseCard); // set position first to the same as of the permanent it is attached to - permWithAttachments.getLinks().add(attachedPermanent); - int dyOffset = Math.max(permWithAttachments.getHeight() / 10, ATTACHMENT_MIN_DY_OFFSET); // calculate y offset - if (VerticalIndex == 1) { - rectangleBaseCard.translate(Math.max(permWithAttachments.getWidth() / 10, ATTACHMENTS_MIN_DX_OFFSET), dyOffset); // do it once + // set position first to the same as of the permanent it is attached to + attachedCard.setCardLocation(lastAttachmentRect.x, lastAttachmentRect.y); + + // y position + cardWithAttachments.getLinks().add(attachedCard); + int dyOffset = Math.max(cardWithAttachments.getCardLocation().getCardHeight() / ATTACHMENTS_MAX_COLUMNS, ATTACHMENTS_OFFSET_SINGLE_Y); + if (verticalIndex == 1) { + lastAttachmentRect.translate(Math.max(cardWithAttachments.getCardLocation().getCardWidth() / ATTACHMENTS_MAX_COLUMNS, ATTACHMENTS_OFFSET_ALL_X), dyOffset); } else { - rectangleBaseCard.translate(0, dyOffset); + lastAttachmentRect.translate(0, dyOffset); } - permWithAttachments.setBounds(rectangleBaseCard); - battlefieldPanel.moveToFront(attachedPermanent); - battlefieldPanel.moveToFront(permWithAttachments); - mainPanel.setComponentZOrder(attachedPermanent, ZOrder--); - VerticalIndex--; + cardWithAttachments.setCardLocation(lastAttachmentRect.x, lastAttachmentRect.y); + + battlefieldPanel.moveToFront(attachedCard); + battlefieldPanel.moveToFront(cardWithAttachments); + mainPanel.setComponentZOrder(attachedCard, ZOrder--); + verticalIndex--; } } } - private AttachmentLayoutInfos calculateNeededNumberOfVerticalColumns(int currentCol, BattlefieldPanel battlefieldPanel, Map permanents, PermanentView permanentWithAttachmentsView) { + private AttachmentLayoutInfos calculateNeededNumberOfVerticalColumns(int currentCol, BattlefieldPanel battlefieldPanel, Map cards, PermanentView permanentWithAttachmentsView) { int maxCol = ++currentCol; int attachments = 0; for (UUID attachmentId : permanentWithAttachmentsView.getAttachments()) { @@ -160,7 +152,7 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { if (attachedPermanent != null) { attachments++; if (attachedPermanent.getAttachments() != null && !attachedPermanent.getAttachments().isEmpty()) { - AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(currentCol, battlefieldPanel, permanents, attachedPermanent); + AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(currentCol, battlefieldPanel, cards, attachedPermanent); if (attachmentLayoutInfos.getColumns() > maxCol) { maxCol = attachmentLayoutInfos.getColumns(); attachments += attachmentLayoutInfos.getAttachments(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java index d5d679aa7b9..d6e1c4bad84 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java @@ -1,10 +1,11 @@ package org.mage.card.arcane; +import mage.cards.MageCard; + +import javax.swing.*; import java.awt.*; import java.util.Timer; import java.util.TimerTask; -import javax.swing.*; -import mage.cards.MagePermanent; public abstract class Animation { @@ -18,7 +19,7 @@ public abstract class Animation { private static CardPanel enlargedAnimationPanel; private static final Object enlargeLock = new Object(); - private TimerTask timerTask; + private final TimerTask timerTask; private FrameTimer frameTimer; private long elapsed; @@ -115,53 +116,59 @@ public abstract class Animation { } } - public static void tapCardToggle(final CardPanel panel, final MagePermanent parent, final boolean tapped, final boolean flipped) { + public static void tapCardToggle(final CardPanel source, final boolean tapped, final boolean flipped) { + CardPanel mainPanel = source; + MageCard parentPanel = mainPanel.getTopPanelRef(); + new Animation(300) { @Override protected void start() { - parent.onBeginAnimation(); + parentPanel.onBeginAnimation(); } @Override protected void update(float percentage) { if (tapped) { - panel.setTappedAngle(CardPanel.TAPPED_ANGLE * percentage); + mainPanel.setTappedAngle(CardPanel.TAPPED_ANGLE * percentage); // reverse movement if untapping - if (!panel.isTapped()) { - panel.setTappedAngle(CardPanel.TAPPED_ANGLE - panel.getTappedAngle()); + if (!mainPanel.isTapped()) { + mainPanel.setTappedAngle(CardPanel.TAPPED_ANGLE - mainPanel.getTappedAngle()); } } if (flipped) { - panel.setFlippedAngle(CardPanel.FLIPPED_ANGLE * percentage); - if (!panel.isFlipped()) { - panel.setFlippedAngle(CardPanel.FLIPPED_ANGLE - panel.getFlippedAngle()); + mainPanel.setFlippedAngle(CardPanel.FLIPPED_ANGLE * percentage); + if (!mainPanel.isFlipped()) { + mainPanel.setFlippedAngle(CardPanel.FLIPPED_ANGLE - mainPanel.getFlippedAngle()); } } - panel.repaint(); + parentPanel.repaint(); } @Override protected void end() { if (tapped) { - panel.setTappedAngle(panel.isTapped() ? CardPanel.TAPPED_ANGLE : 0); + mainPanel.setTappedAngle(mainPanel.isTapped() ? CardPanel.TAPPED_ANGLE : 0); } if (flipped) { - panel.setFlippedAngle(panel.isFlipped() ? CardPanel.FLIPPED_ANGLE : 0); + mainPanel.setFlippedAngle(mainPanel.isFlipped() ? CardPanel.FLIPPED_ANGLE : 0); } - parent.onEndAnimation(); - parent.repaint(); + parentPanel.onEndAnimation(); + parentPanel.repaint(); } }; } - public static void transformCard(final CardPanel panel, final MagePermanent parent, final boolean transformed) { + public static void transformCard(final CardPanel source) { + + CardPanel mainPanel = source; + MageCard parentPanel = mainPanel.getTopPanelRef(); new Animation(600) { private boolean state = false; @Override protected void start() { - parent.onBeginAnimation(); + parentPanel.onBeginAnimation(); } @Override @@ -169,48 +176,51 @@ public abstract class Animation { double p = percentage * 2; if (percentage > 0.5) { if (!state) { - parent.toggleTransformed(); + parentPanel.toggleTransformed(); } state = true; p = (p - 0.5) * 2; } if (!state) { - panel.transformAngle = Math.max(0.01, 1 - p); + mainPanel.transformAngle = Math.max(0.01, 1 - p); } else { - panel.transformAngle = Math.max(0.01, p - 1); + mainPanel.transformAngle = Math.max(0.01, p - 1); } - panel.repaint(); + parentPanel.repaint(); } @Override protected void end() { if (!state) { - parent.toggleTransformed(); + parentPanel.toggleTransformed(); } state = true; - panel.transformAngle = 1; + mainPanel.transformAngle = 1; - parent.onEndAnimation(); - parent.repaint(); + parentPanel.onEndAnimation(); + parentPanel.repaint(); } }; } public static void moveCardToPlay(final int startX, final int startY, final int startWidth, final int endX, final int endY, - final int endWidth, final CardPanel animationPanel, final CardPanel placeholder, final JLayeredPane layeredPane, - final int speed) { + final int endWidth, final CardPanel cardToAnimate, final CardPanel placeholder, final JLayeredPane layeredPane, + final int speed) { + CardPanel cardPanel = (CardPanel) cardToAnimate.getMainPanel(); + MageCard mainPanel = cardToAnimate.getTopPanelRef(); + UI.invokeLater(() -> { final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO); final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO); final float a = 2f; final float sqrta = (float) Math.sqrt(1 / a); - animationPanel.setCardBounds(startX, startY, startWidth, startHeight); - animationPanel.setAnimationPanel(true); - Container parent = animationPanel.getParent(); + mainPanel.setCardBounds(startX, startY, startWidth, startHeight); + cardPanel.setAnimationPanel(true); + Container parent = mainPanel.getParent(); if (parent != null && !parent.equals(layeredPane)) { - layeredPane.add(animationPanel); - layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER); + layeredPane.add(mainPanel); + layeredPane.setLayer(mainPanel, JLayeredPane.MODAL_LAYER); } new Animation(700) { @@ -241,7 +251,7 @@ public abstract class Animation { } currentX -= currentWidth / 2; currentY -= currentHeight / 2; - animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); + mainPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); } @Override @@ -249,11 +259,11 @@ public abstract class Animation { EventQueue.invokeLater(() -> { if (placeholder != null) { placeholder.setDisplayEnabled(true); - placeholder.transferResources(animationPanel); + placeholder.transferResources(cardPanel); } - animationPanel.setVisible(false); - animationPanel.repaint(); - layeredPane.remove(animationPanel); + mainPanel.setVisible(false); + mainPanel.repaint(); + layeredPane.remove(mainPanel); }); } }; @@ -261,18 +271,21 @@ public abstract class Animation { } public static void moveCard(final int startX, final int startY, final int startWidth, final int endX, final int endY, - final int endWidth, final CardPanel animationPanel, final CardPanel placeholder, final JLayeredPane layeredPane, - final int speed) { + final int endWidth, final MageCard cardToAnimate, final CardPanel placeholder, final JLayeredPane layeredPane, + final int speed) { + CardPanel cardPanel = (CardPanel) cardToAnimate.getMainPanel(); + MageCard mainPanel = cardToAnimate.getTopPanelRef(); + UI.invokeLater(() -> { final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO); final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO); - animationPanel.setCardBounds(startX, startY, startWidth, startHeight); - animationPanel.setAnimationPanel(true); - Container parent = animationPanel.getParent(); + mainPanel.setCardBounds(startX, startY, startWidth, startHeight); + cardPanel.setAnimationPanel(true); + Container parent = mainPanel.getParent(); if (parent != null && !parent.equals(layeredPane)) { - layeredPane.add(animationPanel); - layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER); + layeredPane.add(mainPanel); + layeredPane.setLayer(mainPanel, JLayeredPane.MODAL_LAYER); } new Animation(speed) { @@ -282,7 +295,7 @@ public abstract class Animation { int currentY = startY + Math.round((endY - startY) * percentage); int currentWidth = startWidth + Math.round((endWidth - startWidth) * percentage); int currentHeight = startHeight + Math.round((endHeight - startHeight) * percentage); - animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); + mainPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); } @Override @@ -290,11 +303,11 @@ public abstract class Animation { EventQueue.invokeLater(() -> { if (placeholder != null) { placeholder.setDisplayEnabled(true); - placeholder.transferResources(animationPanel); + placeholder.transferResources(cardPanel); } - animationPanel.setVisible(false); - animationPanel.repaint(); - layeredPane.remove(animationPanel); + mainPanel.setVisible(false); + mainPanel.repaint(); + layeredPane.remove(mainPanel); }); } }; @@ -327,7 +340,7 @@ public abstract class Animation { protected void update(float percentage) { int currentWidth = startWidth + Math.round((endWidth - startWidth) * percentage); int currentHeight = startHeight + Math.round((endHeight - startHeight) * percentage); - Point startPos = SwingUtilities.convertPoint(overPanel.getParent(), overPanel.getCardLocation(), layeredPane); + Point startPos = SwingUtilities.convertPoint(overPanel.getParent(), overPanel.getCardLocation().getCardPoint(), layeredPane); int centerX = startPos.x + Math.round(endWidth / 2f); int centerY = startPos.y + Math.round(endHeight / 2f); int currentX = Math.max(0, centerX - Math.round(currentWidth / 2f)); @@ -353,7 +366,7 @@ public abstract class Animation { } } - public static void showCard(final MagePermanent card, int count) { + public static void showCard(final MageCard card, int count) { if (count == 0) { return; } @@ -376,7 +389,7 @@ public abstract class Animation { }; } - public static void hideCard(final MagePermanent card, int count) { + public static void hideCard(final MageCard card, int count) { if (count == 0) { return; } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 638f1e99850..9d82d7f82d1 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -1,16 +1,13 @@ package org.mage.card.arcane; -import mage.cards.MagePermanent; -import mage.cards.TextPopup; +import mage.cards.*; import mage.cards.action.ActionCallback; import mage.cards.action.TransferData; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; +import mage.client.util.GUISizeHelper; import mage.client.util.audio.AudioManager; -import mage.constants.CardType; -import mage.constants.EnlargeMode; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.constants.*; import mage.view.AbilityView; import mage.view.CardView; import mage.view.PermanentView; @@ -24,19 +21,22 @@ import java.awt.event.*; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; +import java.util.TimerTask; import java.util.UUID; /** * Main class for drawing Mage card object. * - * @author arcane, nantuko, noxx + * WARNING, if you want to catch mouse events then use cardEventSource and related code. You can't use outer listeners. + * + * @author arcane, nantuko, noxx, JayDi85 */ @SuppressWarnings({"unchecked", "rawtypes"}) -public abstract class CardPanel extends MagePermanent implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener { +public abstract class CardPanel extends MagePermanent implements ComponentListener, MouseListener, MouseMotionListener, MouseWheelListener { private static final long serialVersionUID = -3272134219262184410L; - private static final Logger LOGGER = Logger.getLogger(CardPanel.class); + private static final Logger logger = Logger.getLogger(CardPanel.class); public static final double TAPPED_ANGLE = Math.PI / 2; public static final double FLIPPED_ANGLE = Math.PI; @@ -57,7 +57,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private double tappedAngle = 0; private double flippedAngle = 0; - private final List links = new ArrayList<>(); + private final List links = new ArrayList<>(); public final JPanel buttonPanel; private JButton dayNightButton; @@ -65,7 +65,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private boolean displayEnabled = true; private boolean isAnimationPanel; - private int cardXOffset, cardYOffset, cardWidth, cardHeight; + private int cardWidth, cardHeight; private int symbolWidth; private boolean isSelected; @@ -82,7 +82,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private boolean isPermanent; private boolean hasSickness; - private String zone; + private Zone zone; + + // mouse clicks + private int mouseClicksCount = 0; + private java.util.Timer mouseResetTimer = null; + static private final int MOUSE_DOUBLE_CLICK_RESET_MS = 200; // Permanent and card renders are different (another sizes and positions of panel, tapped, etc -- that's weird) // Some card view components support only permanents (BattlefieldPanel), but another support only cards (CardArea) @@ -95,7 +100,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private boolean transformed; private boolean animationInProgress = false; - private JPanel cardArea; + private Container cardContainer; + private MageCard topPanel; // default offset, e.g. for battlefield private int yCardCaptionOffsetPercent = 8; // card caption offset (use for moving card caption view center, below mana icons -- for more good UI) @@ -104,6 +110,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private JPopupMenu popupMenu; public CardPanel(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { + // warning, it can be used under MageLayer so make all rotates or other card manipulation as parent + // Store away params this.setGameCard(newGameCard); this.callback = callback; @@ -154,7 +162,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, if (animationInProgress || isTapped() || isPermanent) { return; } - Animation.transformCard(CardPanel.this, CardPanel.this, true); + Animation.transformCard(this); }); // Add it @@ -183,6 +191,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, setOpaque(false); // JPanel event listeners + + // all listeneres to process mouse and another events addMouseListener(this); addMouseMotionListener(this); addMouseWheelListener(this); @@ -201,7 +211,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, @Override public void doLayout() { // Position transform and show source buttons - buttonPanel.setLocation(cardXOffset, cardYOffset); + buttonPanel.setLocation(0, 0); buttonPanel.setSize(cardWidth, cardHeight); int x = cardWidth / 20; int y = cardHeight / 10; @@ -255,12 +265,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, public abstract void transferResources(CardPanel panel); @Override - public void setZone(String zone) { + public void setZone(Zone zone) { this.zone = zone; } @Override - public String getZone() { + public Zone getZone() { return zone; } @@ -290,15 +300,21 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } @Override - public List getLinks() { + public List getLinks() { return links; } + @Override + public MageCardSpace getOuterSpace() { + return MageCardSpace.empty; + } + @Override public void setChoosable(boolean isChoosable) { this.isChoosable = isChoosable; } + @Override public boolean isChoosable() { return this.isChoosable; } @@ -312,8 +328,18 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } @Override - public void setCardAreaRef(JPanel cardArea) { - this.cardArea = cardArea; + public void setCardContainerRef(Container cardContainer) { + this.cardContainer = cardContainer; + } + + @Override + public void setTopPanelRef(MageCard topPanel) { + this.topPanel = topPanel; + } + + @Override + public MageCard getTopPanelRef() { + return this.topPanel; } public void setShowCastingCost(boolean showCastingCost) { @@ -333,25 +359,9 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, @Override public void paint(Graphics g) { - if (!displayEnabled) { - return; - } - if (!isValid()) { - super.validate(); - } - Graphics2D g2d = (Graphics2D) g; - if (transformAngle < 1) { - float edgeOffset = (cardWidth + cardXOffset) / 2f; - g2d.translate(edgeOffset * (1 - transformAngle), 0); - g2d.scale(transformAngle, 1); - } - if (getTappedAngle() + getFlippedAngle() > 0) { - g2d = (Graphics2D) g2d.create(); - float edgeOffset = cardWidth / 2f; - double angle = getTappedAngle() + (Math.abs(getFlippedAngle() - FLIPPED_ANGLE) < 0.001 ? 0 : getFlippedAngle()); - g2d.rotate(angle, cardXOffset + edgeOffset, cardYOffset + cardHeight - edgeOffset); - } - super.paint(g2d); + // card rotating implemented by top layer panel + // TODO: is CardPanel can be used without MageLayer? + super.paint(g); } @Override @@ -373,70 +383,34 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, @Override public void setCardBounds(int x, int y, int cardWidth, int cardHeight) { if (cardWidth == this.cardWidth && cardHeight == this.cardHeight) { - setBounds(x - cardXOffset, y - cardYOffset, getWidth(), getHeight()); - return; - } - - this.cardWidth = cardWidth; - this.symbolWidth = cardWidth / 7; - this.cardHeight = cardHeight; - if (this.isPermanent && needFullPermanentRender) { - int rotCenterX = Math.round(cardWidth / 2f); - int rotCenterY = cardHeight - rotCenterX; - int rotCenterToTopCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_TOP_CORNER); - int rotCenterToBottomCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_BOTTOM_CORNER); - int xOffset = getXOffset(cardWidth); - int yOffset = getYOffset(cardWidth, cardHeight); - cardXOffset = -xOffset; - cardYOffset = -yOffset; - int width = -xOffset + rotCenterX + rotCenterToTopCorner; - int height = -yOffset + rotCenterY + rotCenterToBottomCorner; - setBounds(x + xOffset, y + yOffset, width, height); + // coords changed + //noinspection deprecation + setBounds(x, y, getWidth(), getHeight()); } else { - cardXOffset = 0; - cardYOffset = 0; - int width = cardXOffset * 2 + cardWidth; - int height = cardYOffset * 2 + cardHeight; - setBounds(x - cardXOffset, y - cardYOffset, width, height); + // coords + sizes changed + this.cardWidth = cardWidth; + this.symbolWidth = cardWidth / 7; + this.cardHeight = cardHeight; + // no needs in size settings here - all outer/draw spaces calcs by top parent panel + //noinspection deprecation + setBounds(x, y, cardWidth, cardHeight); } } - public int getXOffset(int cardWidth) { - if (this.isPermanent && needFullPermanentRender) { - int rotCenterX = Math.round(cardWidth / 2f); - int rotCenterToBottomCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_BOTTOM_CORNER); - int xOffset = rotCenterX - rotCenterToBottomCorner; - return xOffset; - } else { - return cardXOffset; - } - } - - public final int getYOffset(int cardWidth, int cardHeight) { - if (this.isPermanent && needFullPermanentRender) { - int rotCenterX = Math.round(cardWidth / 2f); - int rotCenterY = cardHeight - rotCenterX; - int rotCenterToTopCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_TOP_CORNER); - int yOffset = rotCenterY - rotCenterToTopCorner; - return yOffset; - } else { - return cardYOffset; - } - - } - public final int getCardX() { - return getX() + cardXOffset; + return getX() + this.getOuterSpace().getLeft(); } public final int getCardY() { - return getY() + cardYOffset; + return getY() + this.getOuterSpace().getTop(); } + @Override public final int getCardWidth() { return cardWidth; } + @Override public final int getCardHeight() { return cardHeight; } @@ -445,13 +419,6 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, return symbolWidth; } - public final Point getCardLocation() { - Point p = getLocation(); - p.x += cardXOffset; - p.y += cardYOffset; - return p; - } - public final CardView getCard() { return this.getGameCard(); } @@ -466,12 +433,38 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, return alpha; } - public final int getCardXOffset() { - return cardXOffset; - } + @Override + public MageCardAnimationSettings getAnimationSettings(int offsetX, int offsetY, float cardBoundWidth, float cardBoundHeight) { + // card panel can be rotated after tap so send drawning settings to rotate parent panel too + MageCardAnimationSettings settings = new MageCardAnimationSettings(); - public final int getCardYOffset() { - return cardYOffset; + // display + settings.withVisible(this.displayEnabled); + + // TODO: remove cardXOffset and cardYOffset + + // animate tap + if (getTappedAngle() + getFlippedAngle() > 0) { + // Rectangle rotation to keep bottom left corner + // Algorithm logic: + // 1. Take the start and the final figure positions (example: vertical and horizontal) + // 2. Find share figure between start/end positions; + // 3. Find center of the share figure; + // 4. Rotate from that center. + // Rotate center schema: https://user-images.githubusercontent.com/8344157/104398558-6981b500-5568-11eb-9e97-5c16926d481b.png + double angle = getTappedAngle() + (Math.abs(getFlippedAngle() - FLIPPED_ANGLE) < 0.001 ? 0 : getFlippedAngle()); + float edgeOffset = cardBoundWidth / 2f; + settings.withRotate(angle, offsetX + edgeOffset, offsetY + cardBoundHeight - edgeOffset); + } + + // animate transform (shrink/flip animation) + if (transformAngle < 1) { + float edgeOffset = cardBoundWidth / 2f; + settings.withTranslate((offsetX + edgeOffset) * (1 - transformAngle), 0); + settings.withScale(transformAngle, 1); + } + + return settings; } @Override @@ -542,14 +535,14 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, boolean needsTapping = isTapped() != ((PermanentView) card).isTapped(); boolean needsFlipping = isFlipped() != ((PermanentView) card).isFlipped(); if (needsTapping || needsFlipping) { - Animation.tapCardToggle(this, this, needsTapping, needsFlipping); + Animation.tapCardToggle(this, needsTapping, needsFlipping); } if (needsTapping && ((PermanentView) card).isTapped()) { AudioManager.playTapPermanent(); } boolean needsTranforming = isTransformed() != card.isTransformed(); if (needsTranforming) { - Animation.transformCard(this, this, card.isTransformed()); + Animation.transformCard(this); } } @@ -589,27 +582,6 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } } - @Override - public boolean contains(int x, int y) { - return containsThis(x, y, true); - } - - public boolean containsThis(int x, int y, boolean root) { - Point component = getLocation(); - - int cx = getCardX() - component.x; - int cy = getCardY() - component.y; - int cw = cardWidth; - int ch = cardHeight; - if (isTapped()) { - cy = ch - cw + cx; - ch = cw; - cw = cardHeight; - } - - return x >= cx && x <= cx + cw && y >= cy && y <= cy + ch; - } - @Override public CardView getOriginal() { return this.getGameCard(); @@ -617,6 +589,43 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, @Override public void mouseClicked(MouseEvent e) { + data.setComponent(this); + data.setCard(this.getGameCard()); + data.setGameId(this.gameId); + + // popup menu processing + if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { + callback.popupMenuCard(e, data); + return; + } + + // double clicks processing, see https://stackoverflow.com/questions/4051659/identifying-double-click-in-java + // logic: run timer to reset clicks counter + mouseClicksCount = e.getClickCount(); + if (mouseClicksCount > 1) { + // forced to double click + if (mouseResetTimer != null) { + mouseResetTimer.cancel(); + } + callback.mouseClicked(e, data, true); + } else { + // can be single or double click, start the reset timer + if (mouseResetTimer != null) { + mouseResetTimer.cancel(); + } + mouseResetTimer = new java.util.Timer("mouseResetTimer", false); + mouseResetTimer.schedule(new TimerTask() { + @Override + public void run() { + if (mouseClicksCount == 1) { + callback.mouseClicked(e, data, false); + } else if (mouseClicksCount > 1) { + callback.mouseClicked(e, data, true); + } + mouseClicksCount = 0; + } + }, MOUSE_DOUBLE_CLICK_RESET_MS); + } } @Override @@ -626,7 +635,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } if (!tooltipShowing) { synchronized (this) { - if (!tooltipShowing) { + if (!tooltipShowing) { // TODO: remove tooltip showing to callback processing code, not here TransferData transferData = getTransferDataForMouseEntered(); if (this.isShowing()) { tooltipShowing = true; @@ -637,31 +646,15 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } } - @Override - public void mouseDragged(MouseEvent e) { - data.setComponent(this); - callback.mouseDragged(e, data); - } - - @Override - public void mouseMoved(MouseEvent e) { - if (getGameCard().hideInfo()) { - return; - } - data.setComponent(this); - callback.mouseMoved(e, data); - } - - @Override + @Override public void mouseExited(MouseEvent e) { if (getGameCard().hideInfo()) { return; } - if (tooltipShowing) { synchronized (this) { if (tooltipShowing) { - tooltipShowing = false; + tooltipShowing = false; // TODO: same, move code for callback processing data.setComponent(this); data.setCard(this.getGameCard()); data.setPopupText(tooltipText); @@ -681,21 +674,55 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, @Override public void mouseReleased(MouseEvent e) { + data.setComponent(this); + data.setCard(this.getGameCard()); + data.setGameId(this.gameId); callback.mouseReleased(e, data); } + @Override + public void mouseDragged(MouseEvent e) { + data.setComponent(this); + data.setCard(this.getGameCard()); + data.setGameId(this.gameId); + callback.mouseDragged(e, data); + } + + @Override + public void mouseMoved(MouseEvent e) { + if (getGameCard().hideInfo()) { + return; + } + data.setComponent(this); + data.setCard(this.getGameCard()); + data.setGameId(this.gameId); + callback.mouseMoved(e, data); + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if (getGameCard().hideInfo()) { + return; + } + data.setComponent(this); + data.setCard(this.getGameCard()); + data.setGameId(this.gameId); + callback.mouseWheelMoved(e, data); + } + /** * Prepares data to be sent to action callback on client side. * * @return */ private TransferData getTransferDataForMouseEntered() { + MageCard cardPanel = this.getTopPanelRef(); data.setComponent(this); data.setCard(this.getGameCard()); data.setPopupText(tooltipText); data.setGameId(this.gameId); - data.setLocationOnScreen(data.getComponent().getLocationOnScreen()); // we need this for popup - data.setPopupOffsetX(isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP); + data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint()); // we need this for popup + data.setPopupOffsetX(isTapped() ? cardHeight + POPUP_X_GAP : cardWidth + POPUP_X_GAP); data.setPopupOffsetY(40); return data; } @@ -760,7 +787,9 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, public void update(PermanentView card) { this.hasSickness = card.hasSummoningSickness(); this.showCopySourceButton.setVisible(card.isCopy()); - update((CardView) card); + + // must update from top layer (e.g. card icons) + this.getTopPanelRef().update(card); } @Override @@ -771,12 +800,6 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, throw new IllegalStateException("Is not permanent."); } - @Override - public void updateCallback(ActionCallback callback, UUID gameId) { - this.callback = callback; - this.gameId = gameId; - } - public void setTransformed(boolean transformed) { this.transformed = transformed; } @@ -798,7 +821,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, dayNightButton.setIcon(new ImageIcon(night)); } if (this.getGameCard().getSecondCardFace() == null) { - LOGGER.error("no second side for card to transform!"); + logger.error("no second side for card to transform!"); return; } if (!isPermanent) { // use only for custom transformation (when pressing day-night button) @@ -829,16 +852,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } @Override - public void mouseWheelMoved(MouseWheelEvent e) { - if (getGameCard().hideInfo()) { - return; - } - data.setComponent(this); - callback.mouseWheelMoved(e, data); - } - - public JPanel getCardArea() { - return cardArea; + public Container getCardContainer() { + return cardContainer; } @Override @@ -922,4 +937,32 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, public void setFlippedAngle(double flippedAngle) { this.flippedAngle = flippedAngle; } + + @Override + public boolean contains(int x, int y) { + // if you need a mouse related features in the tapped state then implement contains here (see MageLayer for info) + // example: you want a working button + //return super.contains(x, y); + + // Swing uses relative coords here (0,0 is component's top left corner) + MageCardLocation needLocation = this.getCardLocation(); + Rectangle normalRect = new Rectangle( + 0, + 0, + needLocation.getCardWidth(), + needLocation.getCardHeight() + ); + Rectangle animatedRect = MageLayer.animateCoords(this, normalRect); + return animatedRect.contains(x, y); + } + + @Override + public Font getFont() { + Font res = super.getFont(); + if (res == null) { + // workaround: sometimes the card panels haven't default font + res = GUISizeHelper.getCardFont(); + } + return res; + } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelAttributes.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelAttributes.java index da6646de851..0904a528b87 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelAttributes.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelAttributes.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; /** diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java similarity index 85% rename from Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java rename to Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java index f510efabdca..65ee832b7a7 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java @@ -1,6 +1,7 @@ package org.mage.card.arcane; import mage.MageInt; +import mage.cards.MageCardLocation; import mage.cards.action.ActionCallback; import mage.client.constants.Constants; import mage.client.dialog.PreferencesDialog; @@ -11,11 +12,11 @@ import mage.components.ImagePanel; import mage.components.ImagePanelStyle; import mage.constants.AbilityType; import mage.constants.SubType; +import mage.util.DebugUtil; import mage.view.CardView; import mage.view.CounterView; import mage.view.PermanentView; import mage.view.StackAbilityView; -import org.apache.log4j.Logger; import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.mage.plugins.card.images.ImageCache; import org.mage.plugins.card.utils.impl.ImageManagerImpl; @@ -27,18 +28,14 @@ import java.util.StringTokenizer; import java.util.UUID; /** - * Class for drawing the mage card object by using a form based JComponent - * approach + * Render mode: IMAGE * * @author arcane, nantuko, noxx, stravant, JayDi85 */ -@SuppressWarnings({"unchecked", "rawtypes"}) -public class CardPanelComponentImpl extends CardPanel { +public class CardPanelRenderModeImage extends CardPanel { private static final long serialVersionUID = -3272134219262184411L; - private static final Logger LOGGER = Logger.getLogger(CardPanelComponentImpl.class); - private static final int WIDTH_LIMIT = 90; // card width limit to create smaller counter private static final float ROUNDED_CORNER_SIZE = 0.1f; @@ -47,10 +44,6 @@ public class CardPanelComponentImpl extends CardPanel { private static final int TEXT_GLOW_SIZE = 6; private static final float TEXT_GLOW_INTENSITY = 3f; - // size to show icons and text (help to see full size card without text) - private static final int CARD_MIN_SIZE_FOR_ICONS = 60; - private static final int CARD_MAX_SIZE_FOR_ICONS = 200; - // text min size for image render mode private static final int CARD_TITLE_FONT_MIN_SIZE = 13; private static final int CARD_PT_FONT_MIN_SIZE = 17; @@ -58,9 +51,10 @@ public class CardPanelComponentImpl extends CardPanel { public final ScaledImagePanel imagePanel; private ImagePanel overlayPanel; - private JPanel iconPanel; - private JButton typeButton; - private JPanel ptPanel; + // triggered/activated/permanent icon + private JPanel typeIconPanel; + private JButton typeIconButton; + private final JPanel ptPanel; private JPanel counterPanel; private JLabel loyaltyCounterLabel; @@ -83,7 +77,7 @@ public class CardPanelComponentImpl extends CardPanel { private boolean hasImage = false; private boolean displayTitleAnyway; - private boolean displayFullImagePath; + private final boolean displayFullImagePath; private final static SoftValuesLoadingCache IMAGE_CACHE; @@ -95,12 +89,12 @@ public class CardPanelComponentImpl extends CardPanel { this.overlayPanel = overlayPanel; } - public JPanel getIconPanel() { - return iconPanel; + public JPanel getTypeIconPanel() { + return typeIconPanel; } - public void setIconPanel(JPanel iconPanel) { - this.iconPanel = iconPanel; + public void setTypeIconPanel(JPanel typeIconPanel) { + this.typeIconPanel = typeIconPanel; } public JPanel getCounterPanel() { @@ -113,6 +107,7 @@ public class CardPanelComponentImpl extends CardPanel { static class Key { + final Insets border; final int width; final int height; final int cardWidth; @@ -126,7 +121,8 @@ public class CardPanelComponentImpl extends CardPanel { final boolean canAttack; final boolean canBlock; - public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack, boolean canBlock) { + public Key(Insets border, int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack, boolean canBlock) { + this.border = border; this.width = width; this.height = height; this.cardWidth = cardWidth; @@ -144,6 +140,10 @@ public class CardPanelComponentImpl extends CardPanel { @Override public int hashCode() { int hash = 3; + hash = 19 * hash + this.border.left; + hash = 19 * hash + this.border.right; + hash = 19 * hash + this.border.top; + hash = 19 * hash + this.border.bottom; hash = 19 * hash + this.width; hash = 19 * hash + this.height; hash = 19 * hash + this.cardWidth; @@ -171,6 +171,18 @@ public class CardPanelComponentImpl extends CardPanel { return false; } final Key other = (Key) obj; + if (this.border.left != other.border.left) { + return false; + } + if (this.border.right != other.border.right) { + return false; + } + if (this.border.top != other.border.top) { + return false; + } + if (this.border.bottom != other.border.bottom) { + return false; + } if (this.width != other.width) { return false; } @@ -209,7 +221,7 @@ public class CardPanelComponentImpl extends CardPanel { } static { - IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelComponentImpl::createImage)); + IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelRenderModeImage::createImage)); } static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage) { @@ -220,27 +232,29 @@ public class CardPanelComponentImpl extends CardPanel { } private static class CardSizes { + + // from bigger to smaller Rectangle rectFull; Rectangle rectSelection; Rectangle rectBorder; Rectangle rectCard; - CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight) { + CardSizes(Insets border, int offsetX, int offsetY, int fullWidth, int fullHeight) { - int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE); - int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE); - int realSelectionSizeX = Math.round(fullWidth * SELECTION_BORDER_SIZE); - int realSelectionSizeY = Math.round(fullWidth * SELECTION_BORDER_SIZE); + int realBorderSizeX = Math.max(1, Math.round(fullWidth * BLACK_BORDER_SIZE)); + int realBorderSizeY = Math.max(1, Math.round(fullHeight * BLACK_BORDER_SIZE)); + int realSelectionSizeX = Math.max(1, Math.round(fullWidth * SELECTION_BORDER_SIZE)); + int realSelectionSizeY = Math.max(1, Math.round(fullHeight * SELECTION_BORDER_SIZE)); // card full size = select border + black border + real card - rectFull = new Rectangle(offsetX, offsetY, fullWidth, fullHeight); + rectFull = new Rectangle(offsetX + border.left, offsetY + border.top, fullWidth - border.left - border.right, fullHeight - border.top - border.bottom); rectSelection = new Rectangle(rectFull.x, rectFull.y, rectFull.width, rectFull.height); rectBorder = new Rectangle(rectSelection.x + realSelectionSizeX, rectSelection.y + realSelectionSizeY, rectSelection.width - 2 * realSelectionSizeX, rectSelection.height - 2 * realSelectionSizeY); rectCard = new Rectangle(rectBorder.x + realBorderSizeX, rectBorder.y + realBorderSizeY, rectBorder.width - 2 * realBorderSizeX, rectBorder.height - 2 * realBorderSizeY); } } - public CardPanelComponentImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { + public CardPanelRenderModeImage(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { // Call to super super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender); @@ -290,8 +304,6 @@ public class CardPanelComponentImpl extends CardPanel { // Title Text titleText = new GlowText(); setTitle(getGameCard()); -// int fontSize = (int) cardHeight / 11; -// titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); titleText.setForeground(Color.white); titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); titleText.setWrap(true); @@ -326,7 +338,9 @@ public class CardPanelComponentImpl extends CardPanel { // Imagel panel imagePanel = new ScaledImagePanel(); - imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); + if (DebugUtil.GUI_RENDER_IMAGE_DRAW_IMAGE_BORDER) { + imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); + } add(imagePanel); // Do we need to load? @@ -338,21 +352,21 @@ public class CardPanelComponentImpl extends CardPanel { } private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) { - setIconPanel(new JPanel()); - getIconPanel().setLayout(null); - getIconPanel().setOpaque(false); - add(getIconPanel()); + setTypeIconPanel(new JPanel()); + getTypeIconPanel().setLayout(null); + getTypeIconPanel().setOpaque(false); + add(getTypeIconPanel()); - typeButton = new JButton(""); - typeButton.setLocation(2, 2); - typeButton.setSize(25, 25); + typeIconButton = new JButton(""); + typeIconButton.setLocation(2, 2); + typeIconButton.setSize(25, 25); - getIconPanel().setVisible(true); - typeButton.setIcon(new ImageIcon(bufferedImage)); + getTypeIconPanel().setVisible(true); + typeIconButton.setIcon(new ImageIcon(bufferedImage)); if (toolTipText != null) { - typeButton.setToolTipText(toolTipText); + typeIconButton.setToolTipText(toolTipText); } - getIconPanel().add(typeButton); + getTypeIconPanel().add(typeIconButton); } @Override @@ -388,8 +402,8 @@ public class CardPanelComponentImpl extends CardPanel { @Override public void transferResources(final CardPanel panelAbstract) { - if (panelAbstract instanceof CardPanelComponentImpl) { - CardPanelComponentImpl panel = (CardPanelComponentImpl) panelAbstract; + if (panelAbstract instanceof CardPanelRenderModeImage) { + CardPanelRenderModeImage panel = (CardPanelRenderModeImage) panelAbstract; synchronized (panel.imagePanel) { if (panel.imagePanel.hasImage()) { setImage(panel.imagePanel.getSrcImage()); @@ -416,27 +430,35 @@ public class CardPanelComponentImpl extends CardPanel { g2d.setComposite(composite); } + // draw background (selected/chooseable/playable) + MageCardLocation cardLocation = getCardLocation(); g2d.drawImage( IMAGE_CACHE.getOrThrow( - new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(), + new Key(getInsets(), + cardLocation.getCardWidth(), cardLocation.getCardHeight(), + cardLocation.getCardWidth(), cardLocation.getCardHeight(), + 0, + 0, hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack(), getGameCard().isCanBlock())), - 0, 0, null); + 0, 0, cardLocation.getCardWidth(), cardLocation.getCardHeight(), null); g2d.dispose(); } private static BufferedImage createImage(Key key) { - int cardWidth = key.cardWidth; - int cardHeight = key.cardHeight; + // draw background image with selection + Insets componentBorder = key.border; + int renderWidth = key.cardWidth; + int renderHeight = key.cardHeight; int cardXOffset = key.cardXOffset; int cardYOffset = key.cardYOffset; - BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(renderWidth, renderHeight); Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // card full size = select border + black border + real card - CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight); + CardSizes sizes = new CardSizes(componentBorder, cardXOffset, cardYOffset, renderWidth, renderHeight); // corners for selection and for border int cornerSizeSelection = Math.max(4, Math.round(sizes.rectSelection.width * ROUNDED_CORNER_SIZE)); @@ -446,6 +468,7 @@ public class CardPanelComponentImpl extends CardPanel { // draw selection if (key.isSelected) { + // TODO: add themes color support g2d.setColor(Color.green); g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection); } else if (key.isChoosable) { @@ -456,7 +479,7 @@ public class CardPanelComponentImpl extends CardPanel { g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection); } - // draw attack or block border (?inner part of selection?) + // draw attack or block border (?inner part of a selection?) if (key.canAttack || key.canBlock) { g2d.setColor(new Color(255, 50, 50, 230)); g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection); @@ -473,13 +496,6 @@ public class CardPanelComponentImpl extends CardPanel { } // draw real card by component (see imagePanel and other layout's items) - - //TODO:uncomment - /* - if (gameCard.isAttacking()) { - g2d.setColor(new Color(200,10,10,200)); - g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize); - }*/ g2d.dispose(); return image; @@ -489,10 +505,8 @@ public class CardPanelComponentImpl extends CardPanel { protected void paintChildren(Graphics g) { super.paintChildren(g); - CardSizes realCard = new CardSizes(getCardXOffset(), getCardYOffset(), getCardWidth(), getCardHeight()); - /* - // draw recs for debug + // debug draw recs // full card g.setColor(new Color(255, 0, 0)); @@ -522,8 +536,10 @@ public class CardPanelComponentImpl extends CardPanel { int manaMarginRight = Math.round(22f / 672f * getCardWidth()); int manaMarginTop = Math.round(24f / 936f * getCardHeight()); - int manaX = getCardXOffset() + getCardWidth() - manaMarginRight - manaWidth; - int manaY = getCardYOffset() + manaMarginTop; + int cardOffsetX = 0; + int cardOffsetY = 0; + int manaX = cardOffsetX + getCardWidth() - manaMarginRight - manaWidth; + int manaY = cardOffsetY + manaMarginTop; ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), ModernCardRenderer.MANA_ICONS_TEXT_COLOR, symbolMarginX); } @@ -547,12 +563,12 @@ public class CardPanelComponentImpl extends CardPanel { public void doLayout() { super.doLayout(); - int cardWidth = getCardWidth(); - int cardHeight = getCardHeight(); - int cardXOffset = getCardXOffset(); - int cardYOffset = getCardYOffset(); + int cardWidth = getCardLocation().getCardWidth(); // must use current panel sizes to scale real image + int cardHeight = getCardLocation().getCardHeight(); + int cardXOffset = 0; + int cardYOffset = 0; - CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight); + CardSizes sizes = new CardSizes(getInsets(), cardXOffset, cardYOffset, cardWidth, cardHeight); // origin card without selection Rectangle realCardSize = sizes.rectBorder; @@ -566,10 +582,11 @@ public class CardPanelComponentImpl extends CardPanel { getOverlayPanel().setVisible(false); } - if (getIconPanel() != null) { - getIconPanel().setLocation(realCardSize.x, realCardSize.y); - getIconPanel().setSize(realCardSize.width, realCardSize.height); + if (getTypeIconPanel() != null) { + getTypeIconPanel().setLocation(realCardSize.x, realCardSize.y); + getTypeIconPanel().setSize(realCardSize.width, realCardSize.height); } + if (getCounterPanel() != null) { getCounterPanel().setLocation(realCardSize.x, realCardSize.y); getCounterPanel().setSize(realCardSize.width, realCardSize.height); @@ -591,10 +608,6 @@ public class CardPanelComponentImpl extends CardPanel { // TITLE - //old version - text hide on small fonts, why? - //int fontHeight = Math.round(cardHeight * (26f / 672)); - //boolean showText = (!isAnimationPanel() && fontHeight < 12); - boolean showText = !isAnimationPanel() && canShowCardIcons(cardWidth, hasImage); titleText.setVisible(showText); ptText1.setVisible(showText && !ptText1.getText().isEmpty()); @@ -609,7 +622,7 @@ public class CardPanelComponentImpl extends CardPanel { // margins from card black border to text, not need? text show up good without margins int titleMarginLeft = 0; //Math.round(28f / 672f * cardWidth); int titleMarginRight = 0; - int titleMarginTop = 0 + Math.round(getCardCaptionTopOffset() / 100f * cardHeight);//Math.round(28f / 936f * cardHeight); + int titleMarginTop = Math.round(getCardCaptionTopOffset() / 100f * cardHeight);//Math.round(28f / 936f * cardHeight); int titleMarginBottom = 0; titleText.setBounds( imagePanel.getX() + titleMarginLeft, @@ -778,11 +791,7 @@ public class CardPanelComponentImpl extends CardPanel { setTitle(getGameCard()); // Summoning Sickness overlay - if (hasSickness() && getGameCard().isCreature() && isPermanent()) { - getOverlayPanel().setVisible(true); - } else { - getOverlayPanel().setVisible(false); - } + getOverlayPanel().setVisible(hasSickness() && getGameCard().isCreature() && isPermanent()); // Update counters panel if (getCounterPanel() != null) { @@ -852,7 +861,6 @@ public class CardPanelComponentImpl extends CardPanel { otherCounterLabel.setVisible(false); getCounterPanel().setVisible(false); } - } private static ImageIcon getCounterImageWithAmount(int amount, BufferedImage image, int cardWidth) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java similarity index 95% rename from Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java rename to Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java index 1a714d273e8..00315d0532e 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java @@ -11,7 +11,6 @@ import mage.view.CardView; import mage.view.CounterView; import mage.view.PermanentView; import mage.view.StackAbilityView; -import org.apache.log4j.Logger; import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.mage.plugins.card.images.ImageCache; @@ -21,9 +20,10 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -public class CardPanelRenderImpl extends CardPanel { - - private static final Logger LOGGER = Logger.getLogger(CardPanelRenderImpl.class); +/** + * Render mode: MTGO + */ +public class CardPanelRenderModeMTGO extends CardPanel { private static boolean cardViewEquals(CardView a, CardView b) { if (a == b) { @@ -200,7 +200,7 @@ public class CardPanelRenderImpl extends CardPanel { final ImageKey other = (ImageKey) object; // Compare - if ((artImage != null) != (other.artImage != null)) { + if ((artImage == null) == (other.artImage != null)) { return false; } if (width != other.width) { @@ -241,8 +241,8 @@ public class CardPanelRenderImpl extends CardPanel { private BufferedImage cardImage; private CardRenderer cardRenderer; - public CardPanelRenderImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, - final boolean foil, Dimension dimension, boolean needFullPermanentRender) { + public CardPanelRenderModeMTGO(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, + final boolean foil, Dimension dimension, boolean needFullPermanentRender) { // Call to super super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender); @@ -255,8 +255,8 @@ public class CardPanelRenderImpl extends CardPanel { @Override public void transferResources(CardPanel panel) { - if (panel instanceof CardPanelRenderImpl) { - CardPanelRenderImpl impl = (CardPanelRenderImpl) panel; + if (panel instanceof CardPanelRenderModeMTGO) { + CardPanelRenderModeMTGO impl = (CardPanelRenderModeMTGO) panel; // Use the art image and current rendered image from the card artImage = impl.artImage; @@ -285,16 +285,15 @@ public class CardPanelRenderImpl extends CardPanel { } // And draw the image we now have - g.drawImage(cardImage, getCardXOffset(), getCardYOffset(), null); + int cardOffsetX = 0; + int cardOffsetY = 0; + g.drawImage(cardImage, cardOffsetX, cardOffsetY, null); } - /** - * Create an appropriate card renderer for the - */ /** * Render the card to a new BufferedImage at it's current dimensions * - * @return + * @return image */ private BufferedImage renderCard() { int cardWidth = getCardWidth(); @@ -411,6 +410,7 @@ public class CardPanelRenderImpl extends CardPanel { } private BufferedImage getFaceDownImage() { + // TODO: add download default images if (isPermanent()) { if (((PermanentView) getGameCard()).isMorphed()) { return ImageCache.getMorphImage(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java index 4a78b7437d7..0c206ee8aee 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java @@ -4,7 +4,7 @@ import mage.cards.ArtRect; import mage.view.CardView; /** - * Created by StravantUser on 2017-03-30. + * @author StravantUser */ public class CardRendererFactory { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index 632f9be42f9..baea45d0e5b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -1,10 +1,16 @@ package org.mage.card.arcane; import mage.MageInt; +import mage.util.DebugUtil; import mage.view.CardView; import mage.view.PermanentView; import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Iterator; @@ -253,4 +259,90 @@ public final class CardRendererUtils { return defaultColor; } + /** + * Reduce rect by percent (add empty space from all sides and keep rect position) + * Example usage: reduce rect to fit auto-size text + * + * @param rect + * @param reduceFactor + * @return + */ + public static Rectangle reduceRect(Rectangle rect, float reduceFactor) { + float newWidth = rect.width * reduceFactor; + float newHeight = rect.height * reduceFactor; + int offsetX = Math.round((rect.width - newWidth) / 2f); + int offsetY = Math.round((rect.height - newHeight) / 2f); + return new Rectangle(rect.x + offsetX, rect.y + offsetY, Math.round(newWidth), Math.round(newHeight)); + } + + /** + * Draw a String centered in the middle of a rectangle. + * + * @param g2d The graphics instance + * @param text The string to draw + * @param rect The rectangle to center the text in + * @param font + * @param isAutoScaleFont if the text is too big then it will scale a font to fit it in the rect + */ + public static void drawCenteredText(Graphics2D g2d, String text, Rectangle rect, Font font, boolean isAutoScaleFont) { + if (DebugUtil.GUI_RENDER_CENTERED_TEXT_DRAW_DEBUG_LINES) { + g2d.drawLine(rect.x, rect.y + rect.height / 2, rect.x + rect.width, rect.y + rect.height / 2); + g2d.drawLine(rect.x + rect.width / 2, rect.y, rect.x + rect.width / 2, rect.y + rect.height); + } + + // https://stackoverflow.com/a/23730104/1276632 + Font affectedFont = font; + if (isAutoScaleFont) { + affectedFont = scaleFont(g2d, text, rect, font); + } + + g2d.setFont(affectedFont); + FontRenderContext frc = g2d.getFontRenderContext(); + GlyphVector gv = affectedFont.createGlyphVector(frc, text); + Rectangle2D box = gv.getVisualBounds(); + float offsetX = (float) (((rect.getWidth() - box.getWidth()) / 2d) + (-box.getX())); + float offsetY = (float) (((rect.getHeight() - box.getHeight()) / 2d) + (-box.getY())); + + g2d.drawString(text, rect.x + offsetX, rect.y + offsetY); + } + + /** + * Auto scale font to fit current text inside the rect (e.g. decrease font size for too big text) + * + * @param g2d graphics context + * @param text text to draw + * @param font base font + * @param rect the bounds for fitting the string + * @return a scaled font + */ + private static Font scaleFont(Graphics2D g2d, String text, Rectangle rect, Font font) { + // https://stackoverflow.com/a/876266/1276632 + FontRenderContext frc = g2d.getFontRenderContext(); + + double needWidth = rect.getWidth(); + double needHeight = rect.getHeight(); + + float fontMinSize = 1f; + float fontMaxSize = 1000f; + Font scaledFont = font; + float scaledFontSize = scaledFont.getSize(); + + while (fontMaxSize - fontMinSize > 1f) { + scaledFont = scaledFont.deriveFont(scaledFontSize); + + TextLayout layout = new TextLayout(text, scaledFont, frc); + float currentWidth = layout.getVisibleAdvance(); + LineMetrics metrics = scaledFont.getLineMetrics(text, frc); + float currentHeight = metrics.getHeight(); + + if ((currentWidth > needWidth) || (currentHeight > needHeight)) { + fontMaxSize = scaledFontSize; + } else { + fontMinSize = scaledFontSize; + } + scaledFontSize = (fontMinSize + fontMaxSize) / 2f; + } + + return scaledFont.deriveFont((float) Math.floor(scaledFontSize)); + } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/MageLayer.java b/Mage.Client/src/main/java/org/mage/card/arcane/MageLayer.java new file mode 100644 index 00000000000..296b5325699 --- /dev/null +++ b/Mage.Client/src/main/java/org/mage/card/arcane/MageLayer.java @@ -0,0 +1,624 @@ +package org.mage.card.arcane; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconCategory; +import mage.abilities.icon.CardIconRenderSettings; +import mage.abilities.icon.system.PlayableCountIcon; +import mage.cards.MageCard; +import mage.cards.MageCardAnimationSettings; +import mage.cards.MageCardLocation; +import mage.cards.MageCardSpace; +import mage.client.cards.CardIconsPanel; +import mage.client.cards.CardIconsPanelFactory; +import mage.constants.Zone; +import mage.util.DebugUtil; +import mage.view.CardView; +import org.apache.log4j.Logger; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Additional layer for mage cards (example: layer with card icons). + * One MageCard can have multiple layers in the future. + *

+ * WARNING, all added listeners goes to card's main panel (mouse, events, etc) + * + * @author JayDi85 + */ +public class MageLayer extends MageCard { + + private static final Logger logger = Logger.getLogger(MageLayer.class); + + JLayeredPane mainContainer; + JPanel mainLayerCard; + JPanel mainLayerIcons; + JPanel mainLayerDebug = null; + + MageCard mainPanel; + + // empty spaces to control real card size in the center + JPanel spaceLeft; + JPanel spaceRight; + JPanel spaceTop; + JPanel spaceBottom; + + // drawing spaces, you must ignore it in animations and other calcs + MageCardSpace lastOuterSpace = MageCardSpace.empty; + + // card icons + CardIconRenderSettings iconsRender; + List iconsPanels = new ArrayList<>(); // for calcs only + CardIconsPanel iconsDebugPanel; + CardIconsPanel iconsAbilitiesPanel; + CardIconsPanel iconsPlayablePanel; + + public MageLayer(MageCard mainPanel, CardIconRenderSettings iconsRender) { + this.mainPanel = mainPanel; + this.mainPanel.setTopPanelRef(this); + + // component structure (border layout): + // - main container: JLayeredPane (center) + // * layer with card: custom size, border layout + // - main panel + spaces + // * layer with icons: custom size + // * layer with debug drawing + + // component + this.setLayout(new BorderLayout()); + this.setOpaque(false); + + // main container + this.mainContainer = new JLayeredPane(); + this.mainContainer.setOpaque(false); + this.add(this.mainContainer, BorderLayout.CENTER); + + // card layer + this.mainLayerCard = new JPanel(new BorderLayout()); + this.mainLayerCard.setOpaque(false); + this.mainContainer.add(this.mainLayerCard, (Integer) 0); + // main panel + spaces + this.mainLayerCard.add(this.mainPanel, BorderLayout.CENTER); + this.initEmptySpaces(); + + // icons layer + this.mainLayerIcons = new JPanel(null); + this.mainLayerIcons.setOpaque(false); + this.mainContainer.add(this.mainLayerIcons, (Integer) 10); + + // debug layer + if (DebugUtil.GUI_CARD_DRAW_MOUSE_CONTAINS_BOUNDS) { + this.mainLayerDebug = new JPanel(null); + this.mainLayerDebug.setOpaque(false); + this.mainLayerDebug.setBorder(BorderFactory.createLineBorder(Color.MAGENTA)); + this.mainContainer.add(this.mainLayerDebug, (Integer) 20); + } + + // init icons panels, real icons and sizes will added by setCardBounds and setSizes + this.iconsRender = iconsRender; + this.initCardIconsPanels(); + + // Warning, you must ignore outer/draw spaces, use getCardLocation to find a real component and card size/position + // If you inherits a MageLayer then you must implements contains(int x, int y) for correct mouse events + + if (DebugUtil.GUI_CARD_DRAW_OUTER_BORDER) { + this.setBorder(BorderFactory.createLineBorder(Color.red)); + } + if (DebugUtil.GUI_CARD_DRAW_INNER_BORDER) { + this.mainPanel.setBorder(BorderFactory.createLineBorder(Color.green)); + } + } + + private void initEmptySpaces() { + this.spaceLeft = new JPanel(null); + this.spaceLeft.setOpaque(false); + this.mainLayerCard.add(this.spaceLeft, BorderLayout.WEST); + // + this.spaceRight = new JPanel(null); + this.spaceRight.setOpaque(false); + this.mainLayerCard.add(this.spaceRight, BorderLayout.EAST); + // + this.spaceTop = new JPanel(null); + this.spaceTop.setOpaque(false); + this.mainLayerCard.add(this.spaceTop, BorderLayout.NORTH); + // + this.spaceBottom = new JPanel(null); + this.spaceBottom.setOpaque(false); + this.mainLayerCard.add(this.spaceBottom, BorderLayout.SOUTH); + } + + private void initCardIconsPanels() { + this.iconsDebugPanel = null; + this.iconsAbilitiesPanel = null; + this.iconsPlayablePanel = null; + + if (this.iconsRender.isDebugMode()) { + // DEBUG MODE -- only one debug panel + this.iconsDebugPanel = CardIconsPanelFactory.createDebugPanel(this.iconsRender); + this.iconsPanels.add(this.iconsDebugPanel); + } else { + // NORMAL mode -- multiple panels + this.iconsAbilitiesPanel = CardIconsPanelFactory.createAbilitiesPanel(); + this.iconsPanels.add(this.iconsAbilitiesPanel); + this.iconsPlayablePanel = CardIconsPanelFactory.createPlayablePanel(); + this.iconsPanels.add(this.iconsPlayablePanel); + } + + this.iconsPanels.forEach(panel -> this.mainLayerIcons.add(panel)); + } + + private void setEmptySpaces(int left, int right, int top, int bottom) { + this.setEmptySpaces(new MageCardSpace(left, right, top, bottom)); + } + + private void setEmptySpaces(MageCardSpace space) { + Border border = space.getDebugColor() == null ? null : BorderFactory.createLineBorder(space.getDebugColor()); + + this.spaceLeft.setPreferredSize(new Dimension(space.getLeft(), 0)); + this.spaceLeft.setBorder(border); + // + this.spaceRight.setPreferredSize(new Dimension(space.getRight(), 0)); + this.spaceRight.setBorder(border); + // + this.spaceTop.setPreferredSize(new Dimension(0, space.getTop())); + this.spaceTop.setBorder(border); + // + this.spaceBottom.setPreferredSize(new Dimension(0, space.getBottom())); + this.spaceBottom.setBorder(border); + } + + @Override + public MageCard getMainPanel() { + return mainPanel.getMainPanel(); + } + + @Override + public void onBeginAnimation() { + mainPanel.onBeginAnimation(); + } + + @Override + public void onEndAnimation() { + mainPanel.onEndAnimation(); + } + + @Override + public boolean isTapped() { + return mainPanel.isTapped(); + } + + @Override + public boolean isFlipped() { + return mainPanel.isFlipped(); + } + + @Override + public void setAlpha(float transparency) { + mainPanel.setAlpha(transparency); + } + + @Override + public float getAlpha() { + return mainPanel.getAlpha(); + } + + @Override + public CardView getOriginal() { + return mainPanel.getOriginal(); + } + + @Override + public void setCardCaptionTopOffset(int yOffsetPercent) { + mainPanel.setCardCaptionTopOffset(yOffsetPercent); + } + + /** + * Scale inner card to draw additional icons or something (example: card icons in outer space) + * + * @param renderWidth + * @param renderHeight + * @return + */ + private MageCardSpace getAdditionalSpaces(int renderWidth, int renderHeight) { + return new MageCardSpace(0, 0, Math.round(renderHeight * 0f), 0); + } + + @Override + public void setCardBounds(int x, int y, int width, int height) { + // base idea: child layers should not know about parent layer + // + // render logic: + // * scale the current layer to fit additional elemenst like icons + // * draw child layer with new sizes + // + // animation logic (maybe it can be change in the future): + // * animation implemented as g2d graphic context scale in Paint() method + // * all layers and elements must be moved as one object + // * only the main panel (child) can do a calcs for the animation (so send parent sizes to recalc it) + + // if (this.getTopPanelRef() == this && this.getOriginal().getName().equals("Kathari Remnant")) { // for debug only + if (this.getTopPanelRef() == this) { // TODO: is it support multi layer drawing? + + // scale inner card and create space for additional drawing like icons + MageCardSpace innerSpace = getAdditionalSpaces(width, height); + + // extra space for animation and other drawing + // WTF, I'm tired with render calcs, so make BIG draw spaces for any needs + MageCardSpace outerSpace = new MageCardSpace(width * 2, width * 2, height * 2, height * 2); + //MageCardSpace outerSpace = new MageCardSpace(50, 30, 150, 20); + this.lastOuterSpace = outerSpace; + + // construct new spaces (outer + inner) + MageCardSpace fullSpace = MageCardSpace.combine(innerSpace, outerSpace).withDebugColor(innerSpace.getDebugColor()); + this.setEmptySpaces(fullSpace); + //noinspection deprecation - it's ok to use inner setBounds here + this.setBounds(x - outerSpace.getLeft(), y - outerSpace.getTop(), width + outerSpace.getWidth(), height + outerSpace.getHeight()); + mainPanel.setCardBounds(x + innerSpace.getLeft(), y + innerSpace.getTop(), width - innerSpace.getWidth(), height - innerSpace.getHeight()); + } else { + this.setEmptySpaces(0, 0, 0, 0); + //noinspection deprecation - it's ok to use inner setBounds here + this.setBounds(x, y, width, height); + mainPanel.setCardBounds(x, y, width, height); + } + + MageCardLocation location = this.getCardLocation(); + + // panel sizes + this.mainLayerCard.setBounds(0, 0, location.getComponentWidth(), location.getComponentHeight()); + this.mainLayerIcons.setBounds(0, 0, location.getComponentWidth(), location.getComponentHeight()); + + // icons sizes + Rectangle cardSize = new Rectangle(location.getCardRelativeX(), location.getCardRelativeY(), location.getCardWidth(), location.getCardHeight()); + iconsPanels.forEach(panel -> { + panel.updateSizes(cardSize); + }); + } + + @Override + public void update(CardView card) { + // icons update + updateCardIcons(card); + + // card update + mainPanel.update(card); + } + + private void updateCardIcons(CardView card) { + Map> newIcons = new HashMap<>(); + this.iconsPanels.forEach(panel -> newIcons.put(panel, new ArrayList<>())); + + List allIcons = new ArrayList<>(); + // main icons + allIcons.addAll(card.getCardIcons()); + // playable icons + if (card.getPlayableStats().getPlayableImportantAmount() > 0) { + allIcons.add(new PlayableCountIcon(card.getPlayableStats())); + } + + // create panels + allIcons.forEach(cardIcon -> { + CardIconCategory category = cardIcon.getIconType().getCategory(); + // debug must take all icons + if (iconsDebugPanel != null) { + newIcons.get(iconsDebugPanel).add(cardIcon); + } + if (iconsPlayablePanel != null && category == CardIconCategory.PLAYABLE_COUNT) { + newIcons.get(iconsPlayablePanel).add(cardIcon); + } + if (iconsAbilitiesPanel != null && category == CardIconCategory.ABILITY) { + newIcons.get(iconsAbilitiesPanel).add(cardIcon); + } + }); + this.iconsPanels.forEach(panel -> panel.updateIcons(newIcons.get(panel))); + } + + @Override + public void updateArtImage() { + mainPanel.updateArtImage(); + } + + @Override + public Image getImage() { + return mainPanel.getImage(); + } + + @Override + public void setZone(Zone zone) { + mainPanel.setZone(zone); + } + + @Override + public Zone getZone() { + return mainPanel.getZone(); + } + + @Override + public void toggleTransformed() { + mainPanel.toggleTransformed(); + } + + @Override + public boolean isTransformed() { + return mainPanel.isTransformed(); + } + + @Override + public void showCardTitle() { + mainPanel.showCardTitle(); + } + + @Override + public void setSelected(boolean selected) { + mainPanel.setSelected(selected); + } + + @Override + public void setCardContainerRef(Container cardContainer) { + mainPanel.setCardContainerRef(cardContainer); + } + + @Override + public void setTopPanelRef(MageCard mageCard) { + mainPanel.setTopPanelRef(mageCard); + } + + @Override + public MageCard getTopPanelRef() { + return mainPanel.getTopPanelRef(); + } + + @Override + public Container getCardContainer() { + return mainPanel.getCardContainer(); + } + + @Override + public void setChoosable(boolean isChoosable) { + mainPanel.setChoosable(isChoosable); + } + + @Override + public boolean isChoosable() { + return mainPanel.isChoosable(); + } + + @Override + public void setPopupMenu(JPopupMenu popupMenu) { + mainPanel.setPopupMenu(popupMenu); + } + + @Override + public JPopupMenu getPopupMenu() { + return mainPanel.getPopupMenu(); + } + + @Override + public void cleanUp() { + mainPanel.cleanUp(); + } + + @Override + public int getCardWidth() { + return mainPanel.getCardWidth(); + } + + @Override + public int getCardHeight() { + return mainPanel.getCardHeight(); + } + + @Override + public MageCardAnimationSettings getAnimationSettings(int offsetX, int offsetY, float cardBoundWidth, float cardBoundHeight) { + return mainPanel.getAnimationSettings(offsetX, offsetY, cardBoundWidth, cardBoundHeight); + } + + @Override + public List getLinks() { + return mainPanel.getLinks(); + } + + @Override + public MageCardSpace getOuterSpace() { + return this.lastOuterSpace; + } + + @Override + public MageCardLocation getCardLocation() { + // TODO: is it support multi layers? + if (this.getTopPanelRef() == this) { + //noinspection deprecation (it's ok to call native getLocation here) + return new MageCardLocation(this.getLocation(), this.getOuterSpace(), this.getBounds()); + } else { + return super.getCardLocation(); + } + } + + @Override + public void setCardLocation(int x, int y) { + // TODO: is it support multi layers? + if (this.getTopPanelRef() == this) { + // see setCardBounds for more coords cals + //noinspection deprecation - it's ok to use inner setLocation here + this.setLocation(x - lastOuterSpace.getLeft(), y - lastOuterSpace.getTop()); + } else { + this.getTopPanelRef().setCardLocation(x, y); + } + } + + @Override + public MageCardLocation getCardLocationOnScreen() { + // TODO: is it support multi layers? + if (this.getTopPanelRef() == this) { + //noinspection deprecation - it's ok to use inner getLocation here + return new MageCardLocation(this.getLocationOnScreen(), this.getOuterSpace(), this.getBounds()); + } else { + return super.getCardLocationOnScreen(); + } + } + + // ADDITIONAL METHODS FROM real components (e.g. set bounds or other things) + // TODO: move it to interface for require? + + @Override + public int hashCode() { + return mainPanel.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public synchronized void addMouseListener(MouseListener l) { + //super.addMouseListener(l); + mainPanel.addMouseListener(l); + } + + @Override + public synchronized void removeMouseListener(MouseListener l) { + //super.removeMouseListener(l); + mainPanel.removeMouseListener(l); + } + + @Override + public synchronized void addMouseMotionListener(MouseMotionListener l) { + //super.addMouseMotionListener(l); + mainPanel.addMouseMotionListener(l); + } + + @Override + public synchronized void removeMouseMotionListener(MouseMotionListener l) { + //super.removeMouseMotionListener(l); + mainPanel.removeMouseMotionListener(l); + } + + @Override + public synchronized void addMouseWheelListener(MouseWheelListener l) { + //super.addMouseWheelListener(l); + mainPanel.addMouseWheelListener(l); + } + + @Override + public synchronized void removeMouseWheelListener(MouseWheelListener l) { + //super.removeMouseWheelListener(l); + mainPanel.removeMouseWheelListener(l); + } + + @Override + public void paint(Graphics g) { + // inner card panel can decide about transform/scale settings (example: tapped), + // so the top parent layer must be scaled too + + // real card can be put on any parent layer's position (see outer spaces), so the render logic here: + // * find real card sizes (without outer spaces) + // * calc new sizes for the animation/rotation + // * apply outer spaces to calculated sizes + // TODO: is it support multi layers? + int offsetX = this.getOuterSpace().getLeft(); + int offsetY = this.getOuterSpace().getTop(); + int extraWidth = this.getOuterSpace().getWidth(); + int extraHeight = this.getOuterSpace().getHeight(); + Rectangle componentRect = this.getCardLocation().getComponentBounds(); + MageCardAnimationSettings settings = getAnimationSettings( + offsetX, + offsetY, + componentRect.width - extraWidth, + componentRect.height - extraHeight + ); + + if (!settings.isVisible()) { + return; + } + + if (!isValid() || !mainPanel.isValid()) { + mainPanel.validate(); + super.validate(); + } + + Graphics2D g2d = (Graphics2D) g; + settings.doTransforms(g2d); + + super.paint(g); + } + + @Override + public boolean contains(int x, int y) { + // TODO: is it work with multi layer? + // Mouse coords checking to find a child component under the mouse (example: show card hint on mouse over or button click) + // Swing uses relative coords here (0,0 is component's top left corner) + // WARNING, when you fail a parent coord check then all childs goes to ignore (example: top layer miss check, + // then no card panel get it and no card hints on mouse over) + MageCardLocation needLocation = this.getCardLocation(); + + // TODO: added contains support for icons hint + // implement idea: use custom "contains" methods for all components structure: from top layer to icon label + // another implement idea: save last AffineTransforms from paint method, translate it to current component and check coords (most accurate method) + + // extra size for icons (workaround to fix tooltips over icons) + Rectangle iconsOffset = new Rectangle(0, 0); + if (this.iconsPanels.stream().anyMatch(Component::isVisible)) { + CardIconsPanel samplePanel = this.iconsPanels.stream().findFirst().get(); + iconsOffset.x = -samplePanel.getHalfSize(); + iconsOffset.y = -samplePanel.getHalfSize(); + iconsOffset.height = samplePanel.getHalfSize(); + iconsOffset.width = samplePanel.getHalfSize(); + } + + Rectangle normalRect = new Rectangle( + needLocation.getCardRelativeX() + iconsOffset.x, + needLocation.getCardRelativeY() + iconsOffset.y, + needLocation.getCardWidth() + iconsOffset.width, + needLocation.getCardHeight() + iconsOffset.height + ); + Rectangle animatedRect = animateCoords(this, normalRect); + + // debug draw just for color info, real draw will be transformed/animated with card, so you can look at draw rect + if (DebugUtil.GUI_CARD_DRAW_MOUSE_CONTAINS_BOUNDS) { + this.mainLayerDebug.setBounds(animatedRect.x, animatedRect.y, animatedRect.width, animatedRect.height); + if (animatedRect.contains(x, y)) { + this.mainLayerDebug.setBorder(BorderFactory.createLineBorder(Color.green)); + } else { + this.mainLayerDebug.setBorder(BorderFactory.createLineBorder(Color.MAGENTA)); + } + } + + return animatedRect.contains(x, y); + } + + public static Rectangle animateCoords(MageCard card, Rectangle normalRect) { + int needX = normalRect.x; + int needY = normalRect.y; + int needW = normalRect.width; + int needH = normalRect.height; + int cx = needX; + int cy = needY; + int cw = needW; + int ch = needH; + + if (card.isTapped()) { + // TODO: add rotate support for non 90 angles in the future + // rotate by 90 only + // example before: + // * coord: 50, 150 + // * size: 126 x 176 + // example after: + // * coord: 50, 150 + 176 - 126 + // * size: 176 x 126 + cx = needX; + cy = needY + needH - needW; + cw = needH; + ch = needW; + } + + return new Rectangle(cx, cy, cw, ch); + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index 5dc9ca187af..4c09becc729 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -1,23 +1,5 @@ package org.mage.card.arcane; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.FilteredImageSource; -import java.awt.image.ImageProducer; -import java.awt.image.RGBImageFilter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Pattern; -import java.util.stream.IntStream; -import javax.imageio.ImageIO; -import javax.swing.*; import mage.abilities.hint.HintUtils; import mage.cards.repository.CardInfo; import mage.cards.repository.ExpansionRepository; @@ -30,23 +12,30 @@ import mage.client.util.ImageHelper; import mage.client.util.gui.BufferedImageBuilder; import mage.client.util.gui.GuiDisplayUtil; import mage.constants.Rarity; -import mage.utils.StreamUtils; -import org.apache.batik.anim.dom.SVGDOMImplementation; -import org.apache.batik.transcoder.TranscoderException; -import org.apache.batik.transcoder.TranscoderInput; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.TranscodingHints; -import org.apache.batik.transcoder.image.ImageTranscoder; -import org.apache.batik.util.SVGConstants; import org.apache.log4j.Logger; import org.mage.plugins.card.utils.CardImageUtils; -import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Pattern; +import java.util.stream.IntStream; public final class ManaSymbols { - private static final Logger LOGGER = Logger.getLogger(ManaSymbols.class); - private static final Map> manaImages = new HashMap<>(); + private static final Logger logger = Logger.getLogger(ManaSymbols.class); + private static final String CSS_FILE_NAME = "mana-svg-settings.css"; + private static final String CSS_ADDITIONAL_SETTINGS = ""; + + private static final Map> manaImages = new HashMap<>(); private static final Map> setImages = new ConcurrentHashMap<>(); private static final Set onlyMythics = new HashSet<>(); @@ -81,55 +70,20 @@ public final class ManaSymbols { private static final Pattern REPLACE_SYMBOLS_PATTERN = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}"); private static final String[] symbols = new String[]{ - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", - "B", "BG", "BR", "BP", "2B", - "G", "GU", "GW", "GP", "2G", - "R", "RG", "RW", "RP", "2R", - "S", "T", "Q", - "U", "UB", "UR", "UP", "2U", - "W", "WB", "WU", "WP", "2W", - "X", "C", "E"}; + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "B", "BG", "BR", "BP", "2B", + "G", "GU", "GW", "GP", "2G", + "R", "RG", "RW", "RP", "2R", + "S", "T", "Q", + "U", "UB", "UR", "UP", "2U", + "W", "WB", "WU", "WP", "2W", + "X", "C", "E"}; private static final JLabel labelRender = new JLabel(); // render mana text - private static String getSvgPathToCss() { - return getImagesDir() + File.separator + "temp" + File.separator + "batic-svg-settings.css"; - } - - private static void prepareSvg(Boolean forceToCreateCss) { - File f = new File(getSvgPathToCss()); - - if (forceToCreateCss || !f.exists()) { - - // Rendering hints can't be set programatically, so - // we override defaults with a temporary stylesheet. - // These defaults emphasize quality and precision, and - // are more similar to the defaults of other SVG viewers. - // SVG documents can still override these defaults. - String css = "svg {" - + "shape-rendering: geometricPrecision;" - + "text-rendering: geometricPrecision;" - + "color-rendering: optimizeQuality;" - + "image-rendering: optimizeQuality;" - + "}"; - - FileWriter w = null; - try { - f.getParentFile().mkdirs(); - f.createNewFile(); - w = new FileWriter(f); - w.write(css); - } catch (Throwable e) { - LOGGER.error("Can't create css file for svg", e); - } finally { - StreamUtils.closeQuietly(w); - } - } - } - public static void loadImages() { - LOGGER.info("Loading symbols..."); + logger.info("Loading symbols..."); // TODO: delete files rename jpg->gif (it was for backward compatibility for one of the old version?) renameSymbols(getResourceSymbolsPath(ResourceSymbolSize.SMALL)); @@ -138,8 +92,8 @@ public final class ManaSymbols { //renameSymbols(getSymbolsPath(ResourceSymbolSize.SVG)); // not need // TODO: remove medium sets files to "medium" folder like symbols above? - // prepare svg settings - prepareSvg(true); + // prepare svg's css settings + SvgUtils.prepareCss(CSS_FILE_NAME, CSS_ADDITIONAL_SETTINGS, true); // preload symbol images loadSymbolImages(15); @@ -164,7 +118,7 @@ public final class ManaSymbols { ImageIO.write(image, "png", newFile); } } catch (Exception e) { - LOGGER.warn("Can't generate png image for symbol:" + symbol); + logger.warn("Can't generate png image for symbol:" + symbol); } } } @@ -173,7 +127,7 @@ public final class ManaSymbols { java.util.List setCodes = ExpansionRepository.instance.getSetCodes(); if (setCodes == null) { // the cards db file is probaly not included in the client. It will be created after the first connect to a server. - LOGGER.warn("No db information for sets found. Connect to a server to create database file on client side. Then try to restart the client."); + logger.warn("No db information for sets found. Connect to a server to create database file on client side. Then try to restart the client."); return; } for (String set : setCodes) { @@ -270,136 +224,16 @@ public final class ManaSymbols { } } - public static BufferedImage loadSVG(File svgFile, int resizeToWidth, int resizeToHeight, boolean useShadow) throws IOException { - // debug: disable shadow gen, need to test it - useShadow = false; - - // load SVG image - // base loader code: https://stackoverflow.com/questions/11435671/how-to-get-a-buffererimage-from-a-svg - // resize code: https://vibranttechie.wordpress.com/2015/05/15/svg-loading-to-javafx-stage-and-auto-scaling-when-stage-resize/ - if (useShadow && ((resizeToWidth <= 0) || (resizeToHeight <= 0))) { - throw new IllegalArgumentException("Must use non zero sizes for shadow."); - } - - final BufferedImage[] imagePointer = new BufferedImage[1]; - - // css settings for svg - prepareSvg(false); - File cssFile = new File(getSvgPathToCss()); - - TranscodingHints transcoderHints = new TranscodingHints(); - - // resize - int shadowX = 0; - int shadowY = 0; - if (useShadow) { - // shadow size (16px image: 1px left, 2px bottom) - shadowX = 1 * Math.round(1f / 16f * resizeToWidth); - shadowY = 2 * Math.round(1f / 16f * resizeToHeight); - resizeToWidth = resizeToWidth - shadowX; - resizeToHeight = resizeToHeight - shadowY; - } - - if (resizeToWidth > 0) { - transcoderHints.put(ImageTranscoder.KEY_WIDTH, (float) resizeToWidth); //your image width - } - if (resizeToHeight > 0) { - transcoderHints.put(ImageTranscoder.KEY_HEIGHT, (float) resizeToHeight); //your image height - } - - transcoderHints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE); - transcoderHints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION, - SVGDOMImplementation.getDOMImplementation()); - transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, - SVGConstants.SVG_NAMESPACE_URI); - transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, "svg"); - transcoderHints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, cssFile.toURI().toString()); - - try { - TranscoderInput input = new TranscoderInput(new FileInputStream(svgFile)); - ImageTranscoder t = new ImageTranscoder() { - - @Override - public BufferedImage createImage(int w, int h) { - return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - } - - @Override - public void writeImage(BufferedImage image, TranscoderOutput out) - throws TranscoderException { - imagePointer[0] = image; - } - }; - t.setTranscodingHints(transcoderHints); - t.transcode(input, null); - } catch (Exception e) { - throw new IOException("Couldn't convert svg file: " + svgFile + " , reason: " + e.getMessage()); - } - - BufferedImage originImage = imagePointer[0]; - - if (useShadow && (originImage.getWidth() > 0)) { - // draw shadow - // origin image was reduces in sizes to fit shadow - // see https://stackoverflow.com/a/40833715/1276632 - - // a filter which converts all colors except 0 to black - ImageProducer prod = new FilteredImageSource(originImage.getSource(), new RGBImageFilter() { - @Override - public int filterRGB(int x, int y, int rgb) { - if (rgb == 0) { - return 0; - } else { - return 0xff000000; - } - } - }); - // create whe black image - Image shadow = Toolkit.getDefaultToolkit().createImage(prod); - // result - BufferedImage result = new BufferedImage(originImage.getWidth() + shadowX, originImage.getHeight() + shadowY, originImage.getType()); - Graphics2D g = (Graphics2D) result.getGraphics(); - // draw shadow with offset (left bottom) - g.drawImage(shadow, -1 * shadowX, shadowY, null); - // draw original image - g.drawImage(originImage, 0, 0, null); - return result; - } else { - // return origin image without shadow - return originImage; - } - - /* - BufferedImage base = GraphicsUtilities.createCompatibleTranslucentImage(w, h); - Graphics2D g2 = base.createGraphics(); - g2.setColor(Color.WHITE); - g2.fillRoundRect(0, 0, image.getWidth(), image.getHeight(), 10, 10); - g2.dispose(); - - ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, - Color.GRAY); - return renderer.createShadow(base); - */ - //imagePointer[0]; - } - public static File getSymbolFileNameAsSVG(String symbol) { return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg"); } - private static BufferedImage loadSymbolAsSVG(String symbol, int resizeToWidth, int resizeToHeight) { - - File sourceFile = getSymbolFileNameAsSVG(symbol); - return loadSymbolAsSVG(sourceFile, resizeToWidth, resizeToHeight); - } - - private static BufferedImage loadSymbolAsSVG(File sourceFile, int resizeToWidth, int resizeToHeight) { + private static BufferedImage loadSymbolAsSVG(InputStream svgFile, String svgInfo, int resizeToWidth, int resizeToHeight) { try { // no need to resize svg (lib already do it on load) - return loadSVG(sourceFile, resizeToWidth, resizeToHeight, true); - + return SvgUtils.loadSVG(svgFile, svgInfo, CSS_FILE_NAME, CSS_ADDITIONAL_SETTINGS, resizeToWidth, resizeToHeight, true); } catch (Exception e) { - LOGGER.error("Can't load svg symbol: " + sourceFile.getPath() + " , reason: " + e.getMessage()); + logger.error("Can't load svg symbol: " + svgInfo + " , reason: " + e.getMessage()); return null; } } @@ -441,7 +275,7 @@ public final class ManaSymbols { } } } catch (IOException e) { - LOGGER.error("Can't load gif symbol: " + sourceFile.getPath()); + logger.error("Can't load gif symbol: " + sourceFile.getPath()); return null; } @@ -463,9 +297,16 @@ public final class ManaSymbols { File file; // svg - file = getSymbolFileNameAsSVG(symbol); - if (file.exists()) { - image = loadSymbolAsSVG(file, size, size); + if (SvgUtils.haveSvgSupport()) { + file = getSymbolFileNameAsSVG(symbol); + if (file.exists()) { + try { + InputStream fileStream = new FileInputStream(file); + image = loadSymbolAsSVG(fileStream, file.getPath(), size, size); + } catch (FileNotFoundException e) { + // it's ok to hide error + } + } } // gif @@ -501,7 +342,7 @@ public final class ManaSymbols { } if (!errorInfo.isEmpty()) { - LOGGER.warn("Symbols can't be load for size " + size + ": " + errorInfo); + logger.warn("Symbols can't be load for size " + size + ": " + errorInfo); } manaImages.put(size, sizedSymbols); @@ -527,7 +368,7 @@ public final class ManaSymbols { } }); } catch (IOException e) { - LOGGER.error("Couldn't rename mana symbols on " + path, e); + logger.error("Couldn't rename mana symbols on " + path, e); } } @@ -604,7 +445,7 @@ public final class ManaSymbols { loadSymbolImages(symbolWidth); } - // TODO: replace with jlabel render (look at table rendere)? + // TODO: replace with jlabel render (look at table renderer)? /* // NEW version with component draw @@ -677,7 +518,6 @@ public final class ManaSymbols { labelRender.setVerticalAlignment(SwingConstants.CENTER); labelRender.setForeground(symbolsTextColor); labelRender.setHorizontalAlignment(SwingConstants.CENTER); - //labelRender.setBorder(new LineBorder(new Color(125, 250, 250), 1)); // debug draw // fix font size for mana text // work for labels WITHOUT borders @@ -727,6 +567,7 @@ public final class ManaSymbols { CHAT, DIALOG, TOOLTIP, + CARD_ICON_HINT } private static String filePathToUrl(String path) { @@ -739,6 +580,13 @@ public final class ManaSymbols { } } + /** + * Replace images/icons code by real html links. Uses in many places. + * + * @param value + * @param type + * @return + */ public static synchronized String replaceSymbolsWithHTML(String value, Type type) { // mana cost to HTML images (urls to files) @@ -757,6 +605,7 @@ public final class ManaSymbols { case TOOLTIP: symbolSize = GUISizeHelper.symbolTooltipSize; break; + case CARD_ICON_HINT: default: symbolSize = 11; break; @@ -786,7 +635,7 @@ public final class ManaSymbols { replaced = replaced.replace(CardInfo.SPLIT_MANA_SEPARATOR_FULL, CardInfo.SPLIT_MANA_SEPARATOR_RENDER); replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll( "$1$2 0) { + transcoderHints.put(ImageTranscoder.KEY_WIDTH, (float) resizeToWidth); //your image width + } + if (resizeToHeight > 0) { + transcoderHints.put(ImageTranscoder.KEY_HEIGHT, (float) resizeToHeight); //your image height + } + + transcoderHints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE); + transcoderHints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION, + SVGDOMImplementation.getDOMImplementation()); + transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, + SVGConstants.SVG_NAMESPACE_URI); + transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, "svg"); + transcoderHints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, cssFile.toURI().toString()); + + try { + TranscoderInput input = new TranscoderInput(svgFile); + ImageTranscoder t = new ImageTranscoder() { + + @Override + public BufferedImage createImage(int w, int h) { + return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + } + + @Override + public void writeImage(BufferedImage image, TranscoderOutput out) { + imagePointer[0] = image; + } + }; + t.setTranscodingHints(transcoderHints); + t.transcode(input, null); + } catch (Exception e) { + throw new IOException("Can't load svg file: " + svgInfo + " , reason: " + e.getMessage()); + } + + BufferedImage originImage = imagePointer[0]; + + if (useShadow && (originImage.getWidth() > 0)) { + // draw shadow + // origin image was reduces in sizes to fit shadow + // see https://stackoverflow.com/a/40833715/1276632 + + // a filter which converts all colors except 0 to black + ImageProducer prod = new FilteredImageSource(originImage.getSource(), new RGBImageFilter() { + @Override + public int filterRGB(int x, int y, int rgb) { + if (rgb == 0) { + return 0; + } else { + return 0xff000000; + } + } + }); + // create whe black image + Image shadow = Toolkit.getDefaultToolkit().createImage(prod); + // result + BufferedImage result = new BufferedImage(originImage.getWidth() + shadowX, originImage.getHeight() + shadowY, originImage.getType()); + Graphics2D g = (Graphics2D) result.getGraphics(); + // draw shadow with offset (left bottom) + g.drawImage(shadow, -1 * shadowX, shadowY, null); + // draw original image + g.drawImage(originImage, 0, 0, null); + return result; + } else { + // return origin image without shadow + return originImage; + } + + /* + BufferedImage base = GraphicsUtilities.createCompatibleTranslucentImage(w, h); + Graphics2D g2 = base.createGraphics(); + g2.setColor(Color.WHITE); + g2.fillRoundRect(0, 0, image.getWidth(), image.getHeight(), 10, 10); + g2.dispose(); + + ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, + Color.GRAY); + return renderer.createShadow(base); + */ + //imagePointer[0]; + } + + /** + * Check if the current system support svg (some linux systems can have compatibility problems due to different java/svg libs) + *

+ * Call it on app's start + * + * @return true on support, also save result for haveSvgSupport + */ + public static boolean checkSvgSupport() { + // usa sample icon for svg support testing + // direct call, no needs in cache + BufferedImage sampleImage = ImageManagerImpl.instance.getCardIcon(FlyingAbilityIcon.instance.getIconType().getResourceName(), 32); + haveSvgSupport = (sampleImage != null && sampleImage.getWidth() > 0); + if (!haveSvgSupport) { + logger.warn("WARNING, your system doesn't support svg images, so card icons will be disabled. Please, make a bug report in the github."); + } + return haveSvgSupport; + } + + public static boolean haveSvgSupport() { + return haveSvgSupport; + } +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java index 27fc01cd62e..12d1a127c1a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java @@ -17,7 +17,7 @@ import java.util.regex.Pattern; */ public final class TextboxRuleParser { - private static final Logger LOGGER = Logger.getLogger(CardPanel.class); + private static final Logger LOGGER = Logger.getLogger(TextboxRuleParser.class); private static final Pattern BasicManaAbility = Pattern.compile("\\{T\\}: Add \\{(\\w)\\}\\."); private static final Pattern LevelAbilityPattern = Pattern.compile("Level (\\d+)-?(\\d*)(\\+?)"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 1fa978e3710..c5246b1bd00 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -1,5 +1,6 @@ package org.mage.plugins.card; +import mage.cards.MageCard; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; import mage.client.util.GUISizeHelper; @@ -45,8 +46,8 @@ public class CardPluginImpl implements CardPlugin { private static final Logger LOGGER = Logger.getLogger(CardPluginImpl.class); - private static final int GUTTER_Y = 15; - private static final int GUTTER_X = 5; + private static final int GUTTER_Y = 15; // top offset before cards + private static final int GUTTER_X = 15; // left offset before cards static final float EXTRA_CARD_SPACING_X = 0.04f; private static final float CARD_SPACING_Y = 0.03f; private static final float STACK_SPACING_X = 0.07f; @@ -54,9 +55,10 @@ public class CardPluginImpl implements CardPlugin { private static final float ATTACHMENT_SPACING_Y = 0.13f; private static final int landStackMax = 5; - // private int cardWidthMin = 50, cardWidthMax = Constants.CARD_SIZE_FULL.width; private int cardWidthMin = (int) GUISizeHelper.battlefieldCardMinDimension.getWidth(); private int cardWidthMax = (int) GUISizeHelper.battlefieldCardMaxDimension.getWidth(); + // card width increment for auto-size searching (bigger value - faster draw speed on screen size, but not as accurate) + private static final int CARD_WIDTH_AUTO_FIT_INCREMENT = 10; private static final boolean stackVertical = false; @@ -98,12 +100,12 @@ public class CardPluginImpl implements CardPlugin { * Temporary card rendering shim. Split card rendering isn't implemented * yet, so use old component based rendering for the split cards. */ - private CardPanel makePanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) { + private CardPanel makeCardPanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) { switch (renderMode) { case 0: - return new CardPanelRenderImpl(view, gameId, loadImage, callback, isFoil, dimension, needFullPermanentRender); + return new CardPanelRenderModeMTGO(view, gameId, loadImage, callback, isFoil, dimension, needFullPermanentRender); case 1: - return new CardPanelComponentImpl(view, gameId, loadImage, callback, isFoil, dimension, needFullPermanentRender); + return new CardPanelRenderModeImage(view, gameId, loadImage, callback, isFoil, dimension, needFullPermanentRender); default: throw new IllegalStateException("Unknown render mode " + renderMode); @@ -111,43 +113,45 @@ public class CardPluginImpl implements CardPlugin { } @Override - public MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) { - CardPanel cardPanel = makePanel(permanent, gameId, loadImage, callback, false, dimension, renderMode, needFullPermanentRender); + public MageCard getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) { + CardPanel cardPanel = makeCardPanel(permanent, gameId, loadImage, callback, false, dimension, renderMode, needFullPermanentRender); cardPanel.setShowCastingCost(true); return cardPanel; } @Override - public MagePermanent getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) { - CardPanel cardPanel = makePanel(cardView, gameId, loadImage, callback, false, dimension, renderMode, needFullPermanentRender); + public MageCard getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) { + CardPanel cardPanel = makeCardPanel(cardView, gameId, loadImage, callback, false, dimension, renderMode, needFullPermanentRender); cardPanel.setShowCastingCost(true); return cardPanel; } @Override - public int sortPermanents(Map ui, Map permanents, boolean nonPermanentsOwnRow, boolean topPanel) { - //TODO: add caching + public int sortPermanents(Map ui, Map cards, boolean nonPermanentsOwnRow, boolean topPanel) { //requires to find out is position have been changed that includes: //adding/removing permanents, type change + // must return new height, so battlefield scrolls can be enabled on too big sizes + if (ui == null) { - throw new RuntimeException("Error: no components"); - } - JComponent component = ui.get("battlefieldPanel"); - - if (component == null) { - throw new RuntimeException("Error: battlefieldPanel is missing"); + throw new RuntimeException("No battlefield ui for layout"); } - JLayeredPane battlefieldPanel = (JLayeredPane) component; - JComponent jPanel = ui.get("jPanel"); + JLayeredPane battlefieldPanel = (JLayeredPane) ui.get("battlefieldPanel"); + JComponent cardsPanel = ui.get("jPanel"); + JScrollPane scrollPane = (JScrollPane) ui.get("scrollPane"); + if (battlefieldPanel == null || cardsPanel == null || scrollPane == null) { + throw new RuntimeException("No battlefield components for layout"); + } Row rowAllLands = new Row(); outerLoop: // - for (MagePermanent permanent : permanents.values()) { - if (!permanent.isLand() || permanent.isCreature()) { + for (MageCard card : cards.values()) { + MagePermanent perm = (MagePermanent) card.getMainPanel(); // all cards must be MagePermanent on battlefield + + if (!perm.isLand() || perm.isCreature()) { continue; } @@ -155,35 +159,36 @@ public class CardPluginImpl implements CardPlugin { // Find already added lands with the same name. for (int i = 0, n = rowAllLands.size(); i < n; i++) { + // stack contains main card panel, but for any size/order manipulation you must use top layer panel Stack stack = rowAllLands.get(i); - MagePermanent firstPanel = stack.get(0); - if (firstPanel.getOriginal().getName().equals(permanent.getOriginal().getName())) { + MagePermanent firstPanelPerm = stack.get(0); + if (firstPanelPerm.getOriginal().getName().equals(perm.getOriginal().getName())) { - if (!empty(firstPanel.getOriginalPermanent().getAttachments())) { + if (!empty(firstPanelPerm.getOriginalPermanent().getAttachments())) { // Put this land to the left of lands with the same name and attachments. insertIndex = i; break; } - List counters = firstPanel.getOriginalPermanent().getCounters(); + List counters = firstPanelPerm.getOriginalPermanent().getCounters(); if (counters != null && !counters.isEmpty()) { // don't put to first panel if it has counters insertIndex = i; break; } - if (!empty(permanent.getOriginalPermanent().getAttachments()) || stack.size() == landStackMax) { + if (!empty(perm.getOriginalPermanent().getAttachments()) || stack.size() == landStackMax) { // If this land has attachments or the stack is full, put it to the right. insertIndex = i + 1; continue; } - counters = permanent.getOriginalPermanent().getCounters(); + counters = perm.getOriginalPermanent().getCounters(); if (counters != null && !counters.isEmpty()) { // if a land has counter, put it to the right insertIndex = i + 1; continue; } // Add to stack. - stack.add(0, permanent); + stack.add(0, perm); continue outerLoop; } if (insertIndex != -1) { @@ -193,22 +198,22 @@ public class CardPluginImpl implements CardPlugin { Stack stack = new Stack(); - if (permanent.getOriginalPermanent().getAttachments() != null - && !permanent.getOriginalPermanent().getAttachments().isEmpty() - && !permanent.getOriginalPermanent().isAttachedTo()) { + if (perm.getOriginalPermanent().getAttachments() != null + && !perm.getOriginalPermanent().getAttachments().isEmpty() + && !perm.getOriginalPermanent().isAttachedTo()) { // get the number of all attachements and sub attachments - AttachmentLayoutInfos ali = calculateNeededNumberOfVerticalColumns(0, permanents, permanent); + AttachmentLayoutInfos ali = calculateNeededNumberOfVerticalColumns(0, cards, card); stack.setMaxAttachedCount(ali.getAttachments()); stack.setAttachmentColumns(ali.getColumns()); } - stack.add(permanent); + stack.add(perm); rowAllLands.add(insertIndex == -1 ? rowAllLands.size() : insertIndex, stack); } - Row rowAllCreatures = new Row(permanents, RowType.creature); - Row rowAllOthers = new Row(permanents, RowType.other); - Row rowAllAttached = new Row(permanents, RowType.attached); + Row rowAllCreatures = new Row(cards, RowType.creature); + Row rowAllOthers = new Row(cards, RowType.other); + Row rowAllAttached = new Row(cards, RowType.attached); boolean othersOnTheRight = true; if (nonPermanentsOwnRow) { @@ -217,6 +222,7 @@ public class CardPluginImpl implements CardPlugin { rowAllOthers.clear(); } + // try to auto-fit cards cardWidth = cardWidthMax; Rectangle rect = battlefieldPanel.getVisibleRect(); playAreaWidth = rect.width; @@ -226,7 +232,7 @@ public class CardPluginImpl implements CardPlugin { // calculate values based on the card size that is changing with every iteration cardHeight = Math.round(cardWidth * CardPanel.ASPECT_RATIO); extraCardSpacingX = Math.round(cardWidth * EXTRA_CARD_SPACING_X); - cardSpacingX = cardHeight - cardWidth + extraCardSpacingX; + cardSpacingX = cardHeight - cardWidth + extraCardSpacingX; // need space for tap animation (horizontal position) cardSpacingY = Math.round(cardHeight * CARD_SPACING_Y); stackSpacingX = stackVertical ? 0 : Math.round(cardWidth * STACK_SPACING_X); stackSpacingY = Math.round(cardHeight * STACK_SPACING_Y); @@ -248,7 +254,6 @@ public class CardPluginImpl implements CardPlugin { addOthersIndex = rows.size(); wrap(lands, rows, rows.size()); wrap(others, rows, rows.size()); - } // Store the current rows and others. @@ -277,9 +282,7 @@ public class CardPluginImpl implements CardPlugin { if (creatures.isEmpty() && lands.isEmpty() && others.isEmpty()) { break; } - //FIXME: -1 is too slow. why not binary search? - cardWidth -= 3; - + cardWidth -= CARD_WIDTH_AUTO_FIT_INCREMENT; } // Get size of all the rows. @@ -297,7 +300,7 @@ public class CardPluginImpl implements CardPlugin { maxRowWidth = Math.max(maxRowWidth, x); } - // Position all card panels. + // Position all card panels y = GUTTER_Y; for (Row row : rows) { int rowBottom = 0; @@ -312,21 +315,21 @@ public class CardPluginImpl implements CardPlugin { } } for (int panelIndex = 0, panelCount = stack.size(); panelIndex < panelCount; panelIndex++) { - MagePermanent panel = stack.get(panelIndex); + MagePermanent panelPerm = stack.get(panelIndex); // it's original card panel, but you must change top layer int stackPosition = panelCount - panelIndex - 1; - if (jPanel != null) { - jPanel.setComponentZOrder(panel, panelIndex); + if (cardsPanel != null) { + cardsPanel.setComponentZOrder(panelPerm.getTopPanelRef(), panelIndex); } int panelX = x + (stackPosition * stackSpacingX); int panelY = y + (stackPosition * stackSpacingY); try { // may cause: // java.lang.IllegalArgumentException: illegal component position 26 should be less then 26 - battlefieldPanel.moveToFront(panel); + battlefieldPanel.moveToFront(panelPerm.getTopPanelRef()); } catch (Exception e) { e.printStackTrace(); } - panel.setCardBounds(panelX, panelY, cardWidth, cardHeight); + panelPerm.getTopPanelRef().setCardBounds(panelX, panelY, cardWidth, cardHeight); } rowBottom = Math.max(rowBottom, y + stack.getHeight()); x += stack.getWidth(); @@ -337,11 +340,14 @@ public class CardPluginImpl implements CardPlugin { // we need this only for defining card size // attached permanents will be handled separately for (Stack stack : rowAllAttached) { - for (MagePermanent panel : stack) { - panel.setCardBounds(0, 0, cardWidth, cardHeight); + for (MagePermanent panelPerm : stack) { + panelPerm.getTopPanelRef().setCardBounds(0, 0, cardWidth, cardHeight); } } + // scrollbars speed + scrollPane.getVerticalScrollBar().setUnitIncrement(GUISizeHelper.getCardsScrollbarUnitInc(cardHeight)); + return y; } @@ -351,7 +357,7 @@ public class CardPluginImpl implements CardPlugin { private int wrap(Row sourceRow, List rows, int insertIndex) { // The cards are sure to fit (with vertical scrolling) at the minimum card width. - boolean allowHeightOverflow = cardWidth == cardWidthMin; + boolean allowHeightOverflow = (cardWidth <= cardWidthMin); Row currentRow = new Row(); for (int i = 0, n = sourceRow.size() - 1; i <= n; i++) { @@ -413,15 +419,17 @@ public class CardPluginImpl implements CardPlugin { return height - cardSpacingY + GUTTER_Y * 2; } - private AttachmentLayoutInfos calculateNeededNumberOfVerticalColumns(int currentCol, Map permanents, MagePermanent permanentWithAttachments) { + private AttachmentLayoutInfos calculateNeededNumberOfVerticalColumns(int currentCol, Map cards, MageCard cardWithAttachments) { int maxCol = ++currentCol; int attachments = 0; - for (UUID attachmentId : permanentWithAttachments.getOriginalPermanent().getAttachments()) { - MagePermanent attachedPermanent = permanents.get(attachmentId); - if (attachedPermanent != null) { + MagePermanent permWithAttachments = (MagePermanent) cardWithAttachments.getMainPanel(); + for (UUID attachmentId : permWithAttachments.getOriginalPermanent().getAttachments()) { + MageCard attachedCard = cards.get(attachmentId); + if (attachedCard != null) { attachments++; - if (attachedPermanent.getOriginalPermanent().getAttachments() != null && !attachedPermanent.getOriginalPermanent().getAttachments().isEmpty()) { - AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(currentCol, permanents, attachedPermanent); + MagePermanent attachedPerm = (MagePermanent) attachedCard.getMainPanel(); + if (attachedPerm.getOriginalPermanent().getAttachments() != null && !attachedPerm.getOriginalPermanent().getAttachments().isEmpty()) { + AttachmentLayoutInfos attachmentLayoutInfos = calculateNeededNumberOfVerticalColumns(currentCol, cards, attachedCard); if (attachmentLayoutInfos.getColumns() > maxCol) { maxCol = attachmentLayoutInfos.getColumns(); attachments += attachmentLayoutInfos.getAttachments(); @@ -435,16 +443,16 @@ public class CardPluginImpl implements CardPlugin { private enum RowType { land, creature, other, attached; - public boolean isType(MagePermanent card) { + public boolean isType(MagePermanent permanent) { switch (this) { case land: - return card.isLand(); + return permanent.isLand(); case creature: - return card.isCreature(); + return permanent.isCreature(); case other: - return !card.isLand() && !card.isCreature(); + return !permanent.isLand() && !permanent.isCreature(); case attached: - return card.getOriginalPermanent().isAttachedToPermanent(); + return permanent.getOriginalPermanent().isAttachedToPermanent(); default: throw new RuntimeException("Unhandled type: " + this); } @@ -459,24 +467,26 @@ public class CardPluginImpl implements CardPlugin { super(16); } - public Row(Map permanents, RowType type) { + public Row(Map cards, RowType type) { this(); - addAll(permanents, type); + addAll(cards, type); } - private void addAll(Map permanents, RowType type) { - for (MagePermanent permanent : permanents.values()) { - if (!type.isType(permanent)) { + private void addAll(Map cards, RowType type) { + for (MageCard card : cards.values()) { + MagePermanent perm = (MagePermanent) card.getMainPanel(); + + if (!type.isType(perm)) { continue; } // all attached permanents are grouped separately later - if (type != RowType.attached && RowType.attached.isType(permanent)) { + if (type != RowType.attached && RowType.attached.isType(perm)) { continue; } Stack stack = new Stack(); - stack.add(permanent); - if (permanent.getOriginalPermanent().getAttachments() != null) { - AttachmentLayoutInfos ali = calculateNeededNumberOfVerticalColumns(0, permanents, permanent); + stack.add(perm); + if (perm.getOriginalPermanent().getAttachments() != null) { + AttachmentLayoutInfos ali = calculateNeededNumberOfVerticalColumns(0, cards, card); stack.setMaxAttachedCount(ali.getAttachments()); stack.setAttachmentColumns(ali.getColumns()); } @@ -666,7 +676,7 @@ public class CardPluginImpl implements CardPlugin { } @Override - public void onAddCard(MagePermanent card, int count) { + public void onAddCard(MageCard card, int count) { if (card != null) { Animation.showCard(card, count > 0 ? count : 1); try { @@ -680,7 +690,7 @@ public class CardPluginImpl implements CardPlugin { } @Override - public void onRemoveCard(MagePermanent card, int count) { + public void onRemoveCard(MageCard card, int count) { if (card != null) { Animation.hideCard(card, count > 0 ? count : 1); try { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/AbstractBoundBean.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/AbstractBoundBean.java index 0aa044940a6..40a17e56822 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/AbstractBoundBean.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/AbstractBoundBean.java @@ -1,9 +1,3 @@ -/** - * AbstractBoundBean.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/BoundBean.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/BoundBean.java index 1269cba48a4..df36c13b57b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/BoundBean.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/BoundBean.java @@ -1,9 +1,3 @@ -/** - * BoundBean.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/EventListenerList.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/EventListenerList.java index ebd130b353f..e072318afc7 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/EventListenerList.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/EventListenerList.java @@ -1,8 +1,3 @@ -/** - * EventListenerList.java - * - * Created on 08.04.2010 - */ package org.mage.plugins.card.dl.beans; import com.google.common.base.Function; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/PropertyChangeSupport.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/PropertyChangeSupport.java index 26574a23033..80016180e98 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/PropertyChangeSupport.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/PropertyChangeSupport.java @@ -1,14 +1,5 @@ -/** - * PropertyChangeSupport.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans; - - - /** * The class PropertyChangeSupport. * diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java index 63ab9809d60..4c53da5e3e5 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java @@ -1,9 +1,3 @@ -/** - * ListenableCollections.java - * - * Created on 25.04.2010 - */ - package org.mage.plugins.card.dl.beans.collections; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperties.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperties.java index e52ce5c89d7..52d61189c8a 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperties.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperties.java @@ -1,9 +1,3 @@ -/** - * AbstractProperties.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties; import java.util.ArrayList; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperty.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperty.java index 796afc96748..7f5f1819045 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperty.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/AbstractProperty.java @@ -1,9 +1,3 @@ -/** - * AbstractProperty.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/CompoundProperties.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/CompoundProperties.java index aa9350feaee..666716ed80d 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/CompoundProperties.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/CompoundProperties.java @@ -1,9 +1,3 @@ -/** - * CompoundProperties.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Properties.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Properties.java index 084928510f2..f0135fbe16e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Properties.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Properties.java @@ -1,9 +1,3 @@ -/** - * Properties.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Property.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Property.java index ce9a67bdd0f..137e6d86bf9 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Property.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/Property.java @@ -1,9 +1,3 @@ -/** - * Property.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperties.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperties.java index eb299872dc2..9230e7de133 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperties.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperties.java @@ -1,9 +1,3 @@ -/** - * BoundProperties.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties.basic; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperty.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperty.java index 254b2955749..0028d09569e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperty.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/basic/BasicProperty.java @@ -1,9 +1,3 @@ -/** - * BasicProperty.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans.properties.basic; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperties.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperties.java index 318bba5da95..004aa9f5258 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperties.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperties.java @@ -1,9 +1,3 @@ -/** - * BoundProperties.java - * - * Created on 24.08.2010 - */ - package org.mage.plugins.card.dl.beans.properties.bound; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperty.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperty.java index 3e4951292c1..02706904e61 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperty.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/BoundProperty.java @@ -1,9 +1,3 @@ -/** - * BasicProperty.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans.properties.bound; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeListListener.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeListListener.java index 260f38ca438..514a954789f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeListListener.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeListListener.java @@ -1,17 +1,8 @@ -/** - * PropertyChangeListListener.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans.properties.bound; import org.mage.plugins.card.dl.beans.PropertyChangeSupport; import org.mage.plugins.card.dl.beans.collections.ListenableCollections.ListListener; - - - /** * The class PropertyChangeListListener. * diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeMapListener.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeMapListener.java index 65d14ebf420..6fbe171679f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeMapListener.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeMapListener.java @@ -1,9 +1,3 @@ -/** - * PropertyChangeMapListener.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans.properties.bound; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeSetListener.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeSetListener.java index de2c9bde429..827e8f3c3c5 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeSetListener.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/beans/properties/bound/PropertyChangeSetListener.java @@ -1,9 +1,3 @@ -/** - * PropertyChangeSetListener.java - * - * Created on 16.07.2010 - */ - package org.mage.plugins.card.dl.beans.properties.bound; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java index 224a1b036e6..23349d55789 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java @@ -1,19 +1,11 @@ -/** - * AbstractLaternaBean.java - *

- * Created on 25.08.2010 - */ - package org.mage.plugins.card.dl.lm; - import org.apache.log4j.Logger; import org.mage.plugins.card.dl.beans.AbstractBoundBean; import org.mage.plugins.card.dl.beans.EventListenerList; import org.mage.plugins.card.dl.beans.properties.Properties; import org.mage.plugins.card.dl.beans.properties.bound.BoundProperties; - /** * The class AbstractLaternaBean. * diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/DirectLinksForDownload.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/DirectLinksForDownload.java index bcd23f226c3..7f1934014c1 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/DirectLinksForDownload.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/DirectLinksForDownload.java @@ -1,9 +1,3 @@ -/** - * GathererSymbols.java - *

- * Created on 25.08.2010 - */ - package org.mage.plugins.card.dl.sources; import mage.client.constants.Constants; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java index d222a5929e4..963c0ac1d2c 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java @@ -1,8 +1,3 @@ -/** - * GathererSymbols.java - * - * Created on 25.08.2010 - */ package org.mage.plugins.card.dl.sources; import com.google.common.collect.AbstractIterator; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 0b3b528436b..f4144d63a94 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -13,6 +13,7 @@ import net.java.truevfs.access.TFileOutputStream; import org.apache.log4j.Logger; import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.utils.CardImageUtils; +import org.mage.plugins.card.utils.impl.ImageManagerImpl; import javax.imageio.ImageIO; import java.awt.*; @@ -45,14 +46,18 @@ public final class ImageCache { private static final SoftValuesLoadingCache IMAGE_CACHE; private static final SoftValuesLoadingCache FACE_IMAGE_CACHE; + private static final SoftValuesLoadingCache CARD_ICONS_CACHE; /** * Common pattern for keys. See ImageCache.getKey for structure info */ private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)"); + private static final Pattern CARD_ICON_KEY_PATTERN = Pattern.compile("(.*)#(.*)"); static { - // softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference (by default, strong references are used). Softly-referenced objects will be garbage-collected in a globally least-recently-used manner, in response to memory demand. + // softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference + // (by default, strong references are used). Softly-referenced objects will be garbage-collected in a + // globally least-recently-used manner, in response to memory demand. IMAGE_CACHE = SoftValuesLoadingCache.from(new Function() { @Override public BufferedImage apply(String key) { @@ -219,10 +224,33 @@ public final class ImageCache { } } }); + + CARD_ICONS_CACHE = SoftValuesLoadingCache.from(key -> { + try { + Matcher m = CARD_ICON_KEY_PATTERN.matcher(key); + + if (m.matches()) { + int cardSize = Integer.parseInt(m.group(1)); + String resourceName = m.group(2); + BufferedImage image = ImageManagerImpl.instance.getCardIcon(resourceName, cardSize); + return image; + } else { + throw new RuntimeException("Wrong card icons image key format: " + key); + } + } catch (Exception ex) { + if (ex instanceof ComputationException) { + throw (ComputationException) ex; + } else { + throw new ComputationException(ex); + } + } + }); } public static void clearCache() { IMAGE_CACHE.invalidateAll(); + FACE_IMAGE_CACHE.invalidateAll(); + CARD_ICONS_CACHE.invalidateAll(); } public static String getFilePath(CardView card, int width) { @@ -335,7 +363,7 @@ public final class ImageCache { BufferedImage cornerImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); // corner - float ROUNDED_CORNER_SIZE = 0.11f; // see CardPanelComponentImpl + float ROUNDED_CORNER_SIZE = 0.11f; // see CardPanelRenderModeImage int cornerSizeBorder = Math.max(4, Math.round(image.getWidth() * ROUNDED_CORNER_SIZE)); // corner mask @@ -394,13 +422,14 @@ public final class ImageCache { return getImage(getKey(card, card.getName(), "")); } - // public static BufferedImage getImageFaceOriginal(CardView card) { -// return getFaceImage(getFaceKey(card, card.getName(), card.getExpansionSetCode())); -// } public static BufferedImage getImageOriginalAlternateName(CardView card) { return getImage(getKey(card, card.getAlternateName(), "")); } + public static BufferedImage getCardIconImage(String resourceName, int iconSize) { + return getCardIconImage(getCardIconKey(resourceName, iconSize)); + } + /** * Returns the Image corresponding to the key */ @@ -432,6 +461,18 @@ public final class ImageCache { } } + private static BufferedImage getCardIconImage(String key) { + try { + return CARD_ICONS_CACHE.getOrNull(key); + } catch (ComputationException ex) { + if (ex.getCause() instanceof NullPointerException) { + return null; + } + LOGGER.error(ex, ex); + return null; + } + } + /** * Returns the Image corresponding to the key only if it already exists in * the cache. @@ -461,13 +502,9 @@ public final class ImageCache { return name + '#' + set + "####"; } -// /** -// * Returns the map key for the flip image of a card, without any suffixes for the image size. -// */ -// private static String getKeyAlternateName(CardView card, String alternateName) { -// return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#" -// + (card.getTokenSetCode() == null ? "":card.getTokenSetCode()); -// } + private static String getCardIconKey(String resourceName, int size) { + return size + "#" + resourceName; + } /** * Load image from file diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java index ec5c4c485ad..54991bd6a86 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/ImageManager.java @@ -1,50 +1,83 @@ package org.mage.plugins.card.utils; import java.awt.*; +import java.awt.image.BufferedImage; public interface ImageManager { Image getAppImage(); + Image getAppSmallImage(); + Image getAppFlashedImage(); Image getSicknessImage(); + Image getDayImage(); + Image getNightImage(); - + Image getTokenIconImage(); + Image getTriggeredAbilityImage(); + Image getActivatedAbilityImage(); + Image getLookedAtImage(); + Image getRevealedImage(); + Image getExileImage(); + Image getCopyInformIconImage(); + Image getCounterImageViolet(); + Image getCounterImageRed(); + Image getCounterImageGreen(); + Image getCounterImageGrey(); Image getDlgAcceptButtonImage(); + Image getDlgActiveAcceptButtonImage(); + Image getDlgCancelButtonImage(); + Image getDlgActiveCancelButtonImage(); + Image getDlgPrevButtonImage(); + Image getDlgActivePrevButtonImage(); + Image getDlgNextButtonImage(); + Image getDlgActiveNextButtonImage(); Image getSwitchHandsButtonImage(); + Image getStopWatchButtonImage(); + Image getConcedeButtonImage(); + Image getCancelSkipButtonImage(); + Image getSkipNextTurnButtonImage(); + Image getSkipEndTurnButtonImage(); + Image getSkipMainButtonImage(); + Image getSkipStackButtonImage(); + Image getSkipEndStepBeforeYourTurnButtonImage(); + Image getSkipYourNextTurnButtonImage(); + Image getToggleRecordMacroButtonImage(); + BufferedImage getCardIcon(String resourceName, int size); Image getPhaseImage(String phase); } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java index 0aff4722d98..7b3cbd9be80 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java @@ -1,36 +1,34 @@ package org.mage.plugins.card.utils.impl; -import java.awt.Color; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.CropImageFilter; -import java.awt.image.FilteredImageSource; -import java.awt.image.WritableRaster; +import mage.client.dialog.PreferencesDialog; +import mage.client.util.gui.BufferedImageBuilder; +import org.apache.log4j.Logger; +import org.mage.card.arcane.SvgUtils; +import org.mage.plugins.card.utils.ImageManager; +import org.mage.plugins.card.utils.Transparency; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.*; +import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import javax.imageio.ImageIO; - -import mage.client.dialog.PreferencesDialog; -import mage.client.util.gui.BufferedImageBuilder; -import org.mage.plugins.card.utils.ImageManager; -import org.mage.plugins.card.utils.Transparency; public enum ImageManagerImpl implements ImageManager { instance; + private static final Logger logger = Logger.getLogger(ImageManagerImpl.class); + ImageManagerImpl() { init(); } public void init() { String[] phases = {"Untap", "Upkeep", "Draw", "Main1", - "Combat_Start", "Combat_Attack", "Combat_Block", "Combat_Damage", "Combat_End", - "Main2", "Cleanup", "Next_Turn"}; + "Combat_Start", "Combat_Attack", "Combat_Block", "Combat_Damage", "Combat_End", + "Main2", "Cleanup", "Next_Turn"}; phasesImages = new HashMap<>(); for (String name : phases) { Image image = getImageFromResource( @@ -224,6 +222,7 @@ public enum ImageManagerImpl implements ImageManager { return imageDlgAcceptButton; } + @Override public Image getDlgActiveAcceptButtonImage() { if (imageDlgActiveAcceptButton == null) { @@ -363,6 +362,22 @@ public enum ImageManagerImpl implements ImageManager { return imageToggleRecordMacroButton; } + @Override + public BufferedImage getCardIcon(String resourceName, int size) { + // icon must be same, but color can be changed by themes + InputStream data = ImageManager.class.getResourceAsStream(PreferencesDialog.getCurrentTheme().getCardIconsResourcePath(resourceName)); + try { + // no need to resize svg (lib already do it on load) + return SvgUtils.loadSVG(data, "card icon = " + resourceName, + PreferencesDialog.getCurrentTheme().getCardIconsCssFile(), + PreferencesDialog.getCurrentTheme().getCardIconsCssSettings(), + size, size, false); + } catch (Exception e) { + logger.error("Can't load card icon: " + resourceName + " , reason: " + e.getMessage(), e); + return null; + } + } + protected static Image getImageFromResourceTransparent(String path, Color mask, Rectangle rec) { BufferedImage image; Image imageCardTransparent; diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/alarm-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/alarm-fill.svg new file mode 100644 index 00000000000..4933d6ef1be --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/alarm-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/bell-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/bell-fill.svg new file mode 100644 index 00000000000..8bed646f0fa --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/bell-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/circle-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/circle-fill.svg new file mode 100644 index 00000000000..5829b0d5372 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/clock-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/clock-fill.svg new file mode 100644 index 00000000000..57f53646e2d --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/clock-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/cloud-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/cloud-fill.svg new file mode 100644 index 00000000000..96843a967c2 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/cloud-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-circle-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-circle-fill.svg new file mode 100644 index 00000000000..006789e66a9 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-circle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-square-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-square-fill.svg new file mode 100644 index 00000000000..0d6a7fce77e --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-triangle-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-triangle-fill.svg new file mode 100644 index 00000000000..ee77e0daca4 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/exclamation-triangle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/eye-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/eye-fill.svg new file mode 100644 index 00000000000..b85e1e3dd8f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/eye-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/gear-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/gear-fill.svg new file mode 100644 index 00000000000..9865bfdce79 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/gear-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/grid-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/grid-fill.svg new file mode 100644 index 00000000000..8e26fc5d6b5 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/grid-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/heart-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/heart-fill.svg new file mode 100644 index 00000000000..93699ca940e --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/heart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/hexagon-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/hexagon-fill.svg new file mode 100644 index 00000000000..c87e1179a52 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/hexagon-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/lightning-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/lightning-fill.svg new file mode 100644 index 00000000000..f07ccfdc0ed --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/lightning-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/link.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/link.svg new file mode 100644 index 00000000000..df35bc8a1e6 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/link.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/question-square-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/question-square-fill.svg new file mode 100644 index 00000000000..6d7ff31616f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/question-square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill-check.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill-check.svg new file mode 100644 index 00000000000..2ede6759d4b --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill.svg new file mode 100644 index 00000000000..0b0107db799 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-slash-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-slash-fill.svg new file mode 100644 index 00000000000..d0a99738aa7 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shield-slash-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/shift-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shift-fill.svg new file mode 100644 index 00000000000..3f7de95b962 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/shift-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/square-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/square-fill.svg new file mode 100644 index 00000000000..4ad0edfc2c4 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/square-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/bootstrap/triangle-fill.svg b/Mage.Client/src/main/resources/card/icons/original/bootstrap/triangle-fill.svg new file mode 100644 index 00000000000..03a2d5d5a2c --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/bootstrap/triangle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/anchor.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/anchor.svg new file mode 100644 index 00000000000..b3172addf8d --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/anchor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/ankh.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/ankh.svg new file mode 100644 index 00000000000..33978161e07 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/ankh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/bolt.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/bolt.svg new file mode 100644 index 00000000000..8d658e38b02 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/bolt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/chess-rook.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/chess-rook.svg new file mode 100644 index 00000000000..0ef354472fd --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/chess-rook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/cog.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/cog.svg new file mode 100644 index 00000000000..c372171f41d --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/cog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/crosshairs.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/crosshairs.svg new file mode 100644 index 00000000000..333cc447b57 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/crosshairs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/dharmachakra.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dharmachakra.svg new file mode 100644 index 00000000000..d39ef328370 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dharmachakra.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/dice-six.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dice-six.svg new file mode 100644 index 00000000000..266a42b4e03 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dice-six.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/dizzy.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dizzy.svg new file mode 100644 index 00000000000..082107e8323 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/dizzy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-circle.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-circle.svg new file mode 100644 index 00000000000..a5ab18e9bdb --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-triangle.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-triangle.svg new file mode 100644 index 00000000000..9ab8e44eb6b --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/exclamation-triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/expand-arrows-alt.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/expand-arrows-alt.svg new file mode 100644 index 00000000000..5da5d978547 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/expand-arrows-alt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/eye.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/eye.svg new file mode 100644 index 00000000000..d9e3aee1e22 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/feather-alt.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/feather-alt.svg new file mode 100644 index 00000000000..3bacd571ad2 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/feather-alt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/flask.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/flask.svg new file mode 100644 index 00000000000..d8bb0edaf72 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/flask.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/frog.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/frog.svg new file mode 100644 index 00000000000..f1babed3f36 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/frog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/ghost.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/ghost.svg new file mode 100644 index 00000000000..d7856553f6c --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/ghost.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/grimace.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/grimace.svg new file mode 100644 index 00000000000..b20497359a5 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/grimace.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/hashtag.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/hashtag.svg new file mode 100644 index 00000000000..05928bccd7f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/hashtag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/heart.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/heart.svg new file mode 100644 index 00000000000..c4ff89ce72f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/khanda.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/khanda.svg new file mode 100644 index 00000000000..15322a8f9f1 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/khanda.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/lightbulb.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/lightbulb.svg new file mode 100644 index 00000000000..5ec88768fee --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/lightbulb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/link.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/link.svg new file mode 100644 index 00000000000..ccc2f886d19 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/mars-double.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/mars-double.svg new file mode 100644 index 00000000000..5ec56c5795c --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/mars-double.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull-crossbones.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull-crossbones.svg new file mode 100644 index 00000000000..02437dd2dbe --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull-crossbones.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull.svg new file mode 100644 index 00000000000..ce97af6ea6e --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/skull.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/tooth.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/tooth.svg new file mode 100644 index 00000000000..4eb939aa5be --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/tooth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-monster.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-monster.svg new file mode 100644 index 00000000000..3dddb62482d --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-monster.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-pickup.svg b/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-pickup.svg new file mode 100644 index 00000000000..84df4e34c0f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/original/fontawesome/truck-pickup.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mage.Client/src/main/resources/card/icons/prepared/ankh.svg b/Mage.Client/src/main/resources/card/icons/prepared/ankh.svg new file mode 100644 index 00000000000..72623a8e26d --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/ankh.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/chess-rook.svg b/Mage.Client/src/main/resources/card/icons/prepared/chess-rook.svg new file mode 100644 index 00000000000..75c8ca83e0e --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/chess-rook.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/cog.svg b/Mage.Client/src/main/resources/card/icons/prepared/cog.svg new file mode 100644 index 00000000000..569421c4c72 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/cog.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/expand-arrows-alt.svg b/Mage.Client/src/main/resources/card/icons/prepared/expand-arrows-alt.svg new file mode 100644 index 00000000000..7070b860ae8 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/expand-arrows-alt.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/feather-alt.svg b/Mage.Client/src/main/resources/card/icons/prepared/feather-alt.svg new file mode 100644 index 00000000000..ccda5cfa190 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/feather-alt.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/flask.svg b/Mage.Client/src/main/resources/card/icons/prepared/flask.svg new file mode 100644 index 00000000000..8c77432aaf0 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/flask.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/grid-fill.svg b/Mage.Client/src/main/resources/card/icons/prepared/grid-fill.svg new file mode 100644 index 00000000000..01a71700cf6 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/grid-fill.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/grimace.svg b/Mage.Client/src/main/resources/card/icons/prepared/grimace.svg new file mode 100644 index 00000000000..6e5aebf5a4f --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/grimace.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/khanda.svg b/Mage.Client/src/main/resources/card/icons/prepared/khanda.svg new file mode 100644 index 00000000000..ed01430df45 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/khanda.svg @@ -0,0 +1,15 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/link.svg b/Mage.Client/src/main/resources/card/icons/prepared/link.svg new file mode 100644 index 00000000000..905ce967fe3 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/link.svg @@ -0,0 +1,12 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/mars-double.svg b/Mage.Client/src/main/resources/card/icons/prepared/mars-double.svg new file mode 100644 index 00000000000..11d54a0ac06 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/mars-double.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/mars.svg b/Mage.Client/src/main/resources/card/icons/prepared/mars.svg new file mode 100644 index 00000000000..4d2b370d68c --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/mars.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/skull-crossbones.svg b/Mage.Client/src/main/resources/card/icons/prepared/skull-crossbones.svg new file mode 100644 index 00000000000..4374a2d1449 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/skull-crossbones.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/square-fill.svg b/Mage.Client/src/main/resources/card/icons/prepared/square-fill.svg new file mode 100644 index 00000000000..8f1b88827dd --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/square-fill.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/prepared/truck-monster.svg b/Mage.Client/src/main/resources/card/icons/prepared/truck-monster.svg new file mode 100644 index 00000000000..1d0ea802c33 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/prepared/truck-monster.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/Mage.Client/src/main/resources/card/icons/readme-icons.txt b/Mage.Client/src/main/resources/card/icons/readme-icons.txt new file mode 100644 index 00000000000..fe20a859de5 --- /dev/null +++ b/Mage.Client/src/main/resources/card/icons/readme-icons.txt @@ -0,0 +1,19 @@ +Requirements for icon files: +* free open source license like creative commons; +* SVG format; +* icon must have square proportional (with 1-2 pixels free space on the border); +* icon must use small view box (example: 16 x 16 px). If you use big image scale then you can't see ingame stroke effect on icon +* icon must be painted with glyph/solid style, filled by black (black-white-none colors possible): + * white color keeps; + * black color replaced by themed color; + * non color is transparent; +* don't use strokes (xmage uses it itself); + + +Folder structure: +* original - keep original icons by sources, don't edit it; +* prepared - prepared icons, use it in your code (CardIconType), see requirements above; + +Icons sources: + * bootstrap icons: https://github.com/twbs/icons + * font awesome: https://github.com/FortAwesome/Font-Awesome \ No newline at end of file diff --git a/Mage.Client/src/test/java/mage/client/game/StartMultiGamesTest.java b/Mage.Client/src/test/java/mage/client/game/StartMultiGamesTest.java index e8ec74705c0..1818a667b0a 100644 --- a/Mage.Client/src/test/java/mage/client/game/StartMultiGamesTest.java +++ b/Mage.Client/src/test/java/mage/client/game/StartMultiGamesTest.java @@ -1,13 +1,15 @@ package mage.client.game; -import java.util.concurrent.TimeUnit; -import javax.swing.*; import mage.client.MageFrame; import mage.client.components.MageComponents; import mage.client.components.MageUI; import org.apache.log4j.Logger; +import org.junit.Assert; import org.junit.Ignore; +import javax.swing.*; +import java.util.concurrent.TimeUnit; + /** * @author ayratn */ @@ -22,7 +24,7 @@ public class StartMultiGamesTest { private static final Integer GAME_START_COUNT = 10; private MageFrame frame = null; - private Object sync = new Object(); + private final Object sync = new Object(); private MageUI ui; public static void main(String[] argv) throws Exception { @@ -46,11 +48,16 @@ public class StartMultiGamesTest { Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.fatal(null, e)); SwingUtilities.invokeLater(() -> { synchronized (sync) { - frame = new MageFrame(); + try { + frame = new MageFrame(); + } catch (Throwable e) { + Assert.fail("Can't start client app"); + } frame.setVisible(true); sync.notifyAll(); } }); + synchronized (sync) { if (frame == null) { sync.wait(); diff --git a/Mage.Common/src/main/java/mage/cards/MageCard.java b/Mage.Common/src/main/java/mage/cards/MageCard.java index 4054c5bcb65..8061b5391e1 100644 --- a/Mage.Common/src/main/java/mage/cards/MageCard.java +++ b/Mage.Common/src/main/java/mage/cards/MageCard.java @@ -1,13 +1,30 @@ package mage.cards; -import mage.cards.action.ActionCallback; +import mage.constants.Zone; import mage.view.CardView; +import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; -import java.util.UUID; +import java.util.List; -public abstract class MageCard extends JPanel { +/** + * @author JayDi85 + */ +public abstract class MageCard extends JLayeredPane { + + private static final Logger logger = Logger.getLogger(MageCard.class); + + /** + * Return real MageCard panel (example: card icons uses MageLayerCard to implement additional panels) + *

+ * If you need callback from card then use CardEventSource from form (search for example, e.g. card clicks processing) + * + * @return + */ + public MageCard getMainPanel() { + return this; + } private static final long serialVersionUID = 6089945326434301879L; @@ -36,11 +53,9 @@ public abstract class MageCard extends JPanel { public abstract Image getImage(); - public abstract void setZone(String zone); + public abstract void setZone(Zone zone); - public abstract String getZone(); - - public abstract void updateCallback(ActionCallback callback, UUID gameId); + public abstract Zone getZone(); public abstract void toggleTransformed(); @@ -50,11 +65,124 @@ public abstract class MageCard extends JPanel { public abstract void setSelected(boolean selected); - public abstract void setCardAreaRef(JPanel cardArea); + /** + * Set link to cards list panel that contains the card component (can process mouse events from the card) + * + * @param cardContainer + */ + public abstract void setCardContainerRef(Container cardContainer); + + public abstract void setTopPanelRef(MageCard mageCard); + + public abstract MageCard getTopPanelRef(); + + public abstract Container getCardContainer(); public abstract void setChoosable(boolean isChoosable); + public abstract boolean isChoosable(); + public abstract void setPopupMenu(JPopupMenu popupMenu); public abstract JPopupMenu getPopupMenu(); + + public abstract void cleanUp(); + + public abstract int getCardWidth(); + + public abstract int getCardHeight(); + + public abstract MageCardAnimationSettings getAnimationSettings(int offsetX, int offsetY, float cardBoundWidth, float cardBoundHeight); + + public abstract List getLinks(); + + public abstract MageCardSpace getOuterSpace(); + + /** + * Return top layer component location without outer/draw space (real card) + * + * @return + */ + public MageCardLocation getCardLocation() { + return getTopPanelRef().getCardLocation(); + } + + /** + * Set card location (top left corner of the main card panel). All calls goes to top layer panel. + * + * @param x + * @param y + */ + public void setCardLocation(int x, int y) { + setLocation(x, y); + } + + /** + * Return top layer component location without outer/draw space (real card) + * + * @return + */ + public MageCardLocation getCardLocationOnScreen() { + return getTopPanelRef().getCardLocationOnScreen(); + } + + @Override + @Deprecated // default getLocationOnScreen for Swing engine only, use getCardLocationOnScreen instead + public Point getLocationOnScreen() { + return super.getLocationOnScreen(); + } + + @Override + @Deprecated // default getLocation for Swing engine only, use getCardLocation instead + public Point getLocation() { + return super.getLocation(); + } + + @Override + @Deprecated // default setLocation for inner usage only, call setCardLocation instead + public void setLocation(int x, int y) { + super.setLocation(x, y); + } + + @Override + @Deprecated // default setLocation for inner usage only, call setCardLocation instead + public void setLocation(Point p) { + super.setLocation(p); + } + + @Override + @Deprecated // default getBounds for Swing engine only, use getCardLocation instead + public Rectangle getBounds() { + return super.getBounds(); + } + + @Override + @Deprecated // default setBounds for inner usage only, call setCardBounds instead + public void setBounds(Rectangle r) { + super.setBounds(r); + } + + @Override + @Deprecated // default setBounds for inner usage only, call setCardBounds instead + public void setBounds(int x, int y, int width, int height) { + super.setBounds(x, y, width, height); + } + + @Override + @Deprecated // default getHeight for inner usage only, call getCardLocation instead + public int getHeight() { + return super.getHeight(); + } + + @Override + @Deprecated // default getWidth for inner usage only, call getCardLocation instead + public int getWidth() { + return super.getWidth(); + } + + @Override + @Deprecated // default getSize for inner usage only, call getCardLocation.getCardWidth instead + public Dimension getSize() { + return super.getSize(); + } } diff --git a/Mage.Common/src/main/java/mage/cards/MageCardAnimationSettings.java b/Mage.Common/src/main/java/mage/cards/MageCardAnimationSettings.java new file mode 100644 index 00000000000..26de90a657b --- /dev/null +++ b/Mage.Common/src/main/java/mage/cards/MageCardAnimationSettings.java @@ -0,0 +1,104 @@ +package mage.cards; + +import java.awt.*; + +/** + * Contains animation/scale settings to send from card panel to parent panel + * + * @author JayDi85 + */ +public class MageCardAnimationSettings { + + private boolean visible = true; + + private double translateX = 0; + private double translateY = 0; + + private double scaleX = 0; + private double scaleY = 0; + + private double rotateTheta = 0; + private double rotateX = 0; + private double rotateY = 0; + + public MageCardAnimationSettings withVisible(boolean visible) { + this.visible = visible; + return this; + } + + public MageCardAnimationSettings withTranslate(double x, double y) { + this.translateX = x; + this.translateY = y; + return this; + } + + public MageCardAnimationSettings withScale(double x, double y) { + this.scaleX = x; + this.scaleY = y; + return this; + } + + public MageCardAnimationSettings withRotate(double rotateTheta, double x, double y) { + this.rotateTheta = rotateTheta; + this.rotateX = x; + this.rotateY = y; + return this; + } + + public boolean isVisible() { + return visible; + } + + /** + * Move the position (uses for animation/rotation in multi layer cards) + * + * @param offsetX + * @param offsetY + */ + public void applyOffsets(int offsetX, int offsetY) { + + // translate + if (translateX != 0 || translateY != 0) { + translateX += offsetX; + translateY += offsetY; + } + + // scale don't have offsets + + // rotate + if (rotateTheta != 0 || rotateX != 0 || rotateY != 0) { + rotateX += offsetX; + rotateY += offsetY; + } + } + + /** + * Transform draw context (example: card rotate) + * Draw settings calculates by child panel, but drawing and animation goes from parent, so you must use scale factor + * + * @param g2d graphic context of the card's top layer + */ + public void doTransforms(Graphics2D g2d) { + if (!visible) { + return; + } + + double factorX = 1; + double factorY = 1; + + // translate + if (translateX != 0 || translateY != 0) { + g2d.translate(translateX * factorX, translateY * factorY); + } + + // scale + if (scaleX != 0 || scaleY != 0) { + g2d.scale(scaleX * factorX, scaleY * factorY); + } + + // rotate + if (rotateTheta != 0 || rotateX != 0 || rotateY != 0) { + g2d.rotate(rotateTheta, rotateX * factorX, rotateY * factorY); // rotateTheta don't have scale factor + } + } +} diff --git a/Mage.Common/src/main/java/mage/cards/MageCardLocation.java b/Mage.Common/src/main/java/mage/cards/MageCardLocation.java new file mode 100644 index 00000000000..ad9b338f1ee --- /dev/null +++ b/Mage.Common/src/main/java/mage/cards/MageCardLocation.java @@ -0,0 +1,88 @@ +package mage.cards; + +import java.awt.*; + +/** + * Card's location with real component coords and sizes (can be related to screen or related to parent panel) + * + * @author JayDi85 + */ +public class MageCardLocation { + + final private Rectangle componentRect; + final private Rectangle cardRect; + + public MageCardLocation(Point componentLocation, MageCardSpace cardOuterSpace, Rectangle componentBounds) { + this.componentRect = new Rectangle(componentLocation.x, componentLocation.y, componentBounds.width, componentBounds.height); + this.cardRect = new Rectangle( + this.componentRect.x + cardOuterSpace.left, + this.componentRect.y + cardOuterSpace.top, + this.componentRect.width - cardOuterSpace.getWidth(), + this.componentRect.height - cardOuterSpace.getHeight() + ); + } + + public int getCardX() { + return cardRect.x; + } + + public int getCardY() { + return cardRect.y; + } + + /** + * Card's coord for relative calcs (0,0 is component's top left corner) + * + * @return + */ + public int getCardRelativeX() { + return cardRect.x - componentRect.x; + } + + /** + * Card's coord for relative calcs (0,0 is component's top left corner) + * + * @return + */ + public int getCardRelativeY() { + return cardRect.y - componentRect.y; + } + + public int getCardWidth() { + return cardRect.width; + } + + public int getCardHeight() { + return cardRect.height; + } + + public Point getCardPoint() { + return new Point(this.cardRect.x, this.cardRect.y); + } + + public int getComponentX() { + return componentRect.x; + } + + public int getComponentY() { + return componentRect.y; + } + + public int getComponentWidth() { + return componentRect.width; + } + + public int getComponentHeight() { + return componentRect.height; + } + + public Rectangle getCardBounds() { + // must be copy + return new Rectangle(this.cardRect); + } + + public Rectangle getComponentBounds() { + // must be copy + return new Rectangle(this.componentRect); + } +} diff --git a/Mage.Common/src/main/java/mage/cards/MageCardSpace.java b/Mage.Common/src/main/java/mage/cards/MageCardSpace.java new file mode 100644 index 00000000000..6fd4be85727 --- /dev/null +++ b/Mage.Common/src/main/java/mage/cards/MageCardSpace.java @@ -0,0 +1,77 @@ +package mage.cards; + +import java.awt.*; + +/** + * Inner our outer/draw spaces in cards (example: if you want add free space for animation for icons drawing) + * + * @author JayDi85 + */ +public class MageCardSpace { + + public static final MageCardSpace empty = new MageCardSpace(0, 0, 0, 0); + + int left; + int right; + int top; + int bottom; + + Color debugColor = null; // add colored border for draw debug + + public MageCardSpace(int left, int right, int top, int bottom) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + } + + public MageCardSpace withDebugColor(Color color) { + this.debugColor = color; + return this; + } + + public int getLeft() { + return left; + } + + public int getRight() { + return right; + } + + public int getTop() { + return top; + } + + public int getBottom() { + return bottom; + } + + public int getWidth() { + return this.left + this.right; + } + + public int getHeight() { + return this.top + this.bottom; + } + + public Color getDebugColor() { + return debugColor; + } + + /** + * Creates combined space (sums all values from each space and clear a debug color) + * + * @param spaces list of spaces to combine + * @return + */ + public static MageCardSpace combine(MageCardSpace... spaces) { + MageCardSpace res = new MageCardSpace(0, 0, 0, 0); + for (MageCardSpace space : spaces) { + res.left += space.left; + res.right += space.right; + res.top += space.top; + res.bottom += space.bottom; + } + return res; + } +} diff --git a/Mage.Common/src/main/java/mage/cards/MagePermanent.java b/Mage.Common/src/main/java/mage/cards/MagePermanent.java index a8320821b5d..7042b74b9d3 100644 --- a/Mage.Common/src/main/java/mage/cards/MagePermanent.java +++ b/Mage.Common/src/main/java/mage/cards/MagePermanent.java @@ -2,19 +2,19 @@ package mage.cards; import mage.view.PermanentView; -import java.util.List; - public abstract class MagePermanent extends MageCard { + private static final long serialVersionUID = -3469258620601702171L; - public abstract List getLinks(); + public abstract void update(PermanentView card); + public abstract PermanentView getOriginalPermanent(); - public boolean isCreature(){ + public boolean isCreature() { return getOriginal().isCreature(); } - public boolean isLand(){ + public boolean isLand() { return getOriginal().isLand(); } diff --git a/Mage.Common/src/main/java/mage/cards/TextPopup.java b/Mage.Common/src/main/java/mage/cards/TextPopup.java index 9596fe91ea2..03afd6bd673 100644 --- a/Mage.Common/src/main/java/mage/cards/TextPopup.java +++ b/Mage.Common/src/main/java/mage/cards/TextPopup.java @@ -1,11 +1,3 @@ - - -/* - * TextPopup.java - * - * Created on Apr 6, 2010, 9:36:13 AM - */ - package mage.cards; /** diff --git a/Mage.Common/src/main/java/mage/cards/action/ActionCallback.java b/Mage.Common/src/main/java/mage/cards/action/ActionCallback.java index 0fa19c5d0d4..99ebf1dd06a 100644 --- a/Mage.Common/src/main/java/mage/cards/action/ActionCallback.java +++ b/Mage.Common/src/main/java/mage/cards/action/ActionCallback.java @@ -1,11 +1,12 @@ package mage.cards.action; +import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; public interface ActionCallback { - void mouseClicked(MouseEvent e, TransferData data); + void mouseClicked(MouseEvent e, TransferData data, boolean doubleClick); void mousePressed(MouseEvent e, TransferData data); @@ -23,4 +24,13 @@ public interface ActionCallback { void hideOpenComponents(); + void popupMenuCard(MouseEvent e, TransferData data); + + /** + * Show popup menu + * + * @param e + * @param sourceComponent custom source component for the event, must support CardEventProducer + */ + void popupMenuPanel(MouseEvent e, Component sourceComponent); } diff --git a/Mage.Common/src/main/java/mage/cards/action/TransferData.java b/Mage.Common/src/main/java/mage/cards/action/TransferData.java index 815adb8893f..ac1f6c7d99f 100644 --- a/Mage.Common/src/main/java/mage/cards/action/TransferData.java +++ b/Mage.Common/src/main/java/mage/cards/action/TransferData.java @@ -1,25 +1,35 @@ package mage.cards.action; -import java.awt.Component; -import java.awt.Point; -import java.util.UUID; +import mage.cards.MageCard; import mage.cards.TextPopup; import mage.view.CardView; +import java.awt.*; +import java.util.UUID; + +/** + * Data for main card panel events like mouse moves or clicks + */ public class TransferData { - private Component component; + + private MageCard component; // real card panel (it may lie under multiple layer panels, so use getTopPanelRef for top) private TextPopup popupText; - private Point locationOnScreen; + private Point locationOnScreen; // must contains REAL card location (e.g. without outer/draw spaces), so use getCardLocationOnScreen to update it private int popupOffsetX; private int popupOffsetY; private UUID gameId; private CardView card; - public Component getComponent() { + /** + * If you use it with cards then call top layer panel like data.getComponent().getTopPanelRef() + * + * @return + */ + public MageCard getComponent() { return component; } - public void setComponent(Component component) { + public void setComponent(MageCard component) { this.component = component; } diff --git a/Mage.Common/src/main/java/mage/cards/action/impl/EmptyCallback.java b/Mage.Common/src/main/java/mage/cards/action/impl/EmptyCallback.java index c6ac2e1e19b..1c07e2aea6c 100644 --- a/Mage.Common/src/main/java/mage/cards/action/impl/EmptyCallback.java +++ b/Mage.Common/src/main/java/mage/cards/action/impl/EmptyCallback.java @@ -1,10 +1,12 @@ package mage.cards.action.impl; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; import mage.cards.action.ActionCallback; import mage.cards.action.TransferData; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + /** * Callback that does nothing on any action * @@ -38,7 +40,7 @@ public class EmptyCallback implements ActionCallback { } @Override - public void mouseClicked(MouseEvent e, TransferData data) { + public void mouseClicked(MouseEvent e, TransferData data, boolean doubleClick) { } @Override @@ -49,4 +51,13 @@ public class EmptyCallback implements ActionCallback { public void mouseReleased(MouseEvent e, TransferData data) { } + @Override + public void popupMenuCard(MouseEvent e, TransferData data) { + + } + + @Override + public void popupMenuPanel(MouseEvent e, Component sourceComponent) { + + } } diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java index cf9e43df398..95f21ad8376 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java @@ -28,6 +28,7 @@ public enum ClientCallbackMethod { GAME_INFORM_PERSONAL("gameInformPersonal"), GAME_ERROR("gameError"), GAME_UPDATE("gameUpdate"), + GAME_REDRAW_GUI("gameRedrawGUI", true), DRAFT_OVER("draftOver"), REPLAY_DONE("replayDone"), USER_REQUEST_DIALOG("userRequestDialog"), @@ -44,13 +45,22 @@ public enum ClientCallbackMethod { GAME_PLAY_XMANA("gamePlayXMana"), GAME_GET_AMOUNT("gameSelectAmount"), DRAFT_INIT("draftInit"), - // DRAFT_INFORM("draftInform"), DRAFT_PICK("draftPick"), DRAFT_UPDATE("draftUpdate"); - String value; + String code; + boolean isClientSideMessage; - ClientCallbackMethod(String value) { - this.value = value; + ClientCallbackMethod(String code) { + this(code, false); + } + + ClientCallbackMethod(String code, boolean isClientSideMessage) { + this.code = code; + this.isClientSideMessage = isClientSideMessage; + } + + public boolean isClientSideMessage() { + return this.isClientSideMessage; } } diff --git a/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java index 5478cbd2654..150a2278b85 100644 --- a/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java +++ b/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java @@ -1,6 +1,6 @@ package mage.interfaces.plugin; -import mage.cards.MagePermanent; +import mage.cards.MageCard; import mage.cards.action.ActionCallback; import mage.view.CardView; import mage.view.PermanentView; @@ -16,17 +16,16 @@ import java.util.UUID; * Interface for card plugins * * @author nantuko - * @version 0.1 31.10.2010 #getMagePermanent, #sortPermanents */ public interface CardPlugin extends Plugin { - MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, - boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender); - - MagePermanent getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, + MageCard getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender); - int sortPermanents(Map ui, Map cards, boolean nonPermanentsOwnRow, boolean topPanel); + MageCard getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, + boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender); + + int sortPermanents(Map ui, Map cards, boolean nonPermanentsOwnRow, boolean topPanel); /** * Download various symbols (mana, tap, set). @@ -35,9 +34,21 @@ public interface CardPlugin extends Plugin { */ void downloadSymbols(String imagesDir); - void onAddCard(MagePermanent card, int count); + /** + * Uses for show/hide animation on the battlefield + * + * @param card + * @param count + */ + void onAddCard(MageCard card, int count); - void onRemoveCard(MagePermanent card, int count); + /** + * Uses for show/hide animation on the battlefield + * + * @param card + * @param count + */ + void onRemoveCard(MageCard card, int count); JComponent getCardInfoPane(); diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 541f985bd30..a2b3b45c7fe 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -116,6 +116,9 @@ public class SessionImpl implements Session { } private void showMessageToUser(String message) { + if (message.contains("free port for use")) { + message += " (try to close and restart a client app)"; + } client.showMessage("Remote task error. " + message); } @@ -558,15 +561,6 @@ public class SessionImpl implements Session { @Override public void handleCallback(Callback callback) throws HandleCallbackException { try { -// Object object = callback.getCallbackObject(); -// if (((ClientCallback) object).getMethod().equals(ClientCallbackMethod.GAME_TARGET)) { -// Object data = ((ClientCallback) object).getData(); -// if (data instanceof GameClientMessage) { -// GameClientMessage message = (GameClientMessage) ((ClientCallback) object).getData(); -// logger.info("Client Session Event->" + ((ClientCallback) object).getMethod() + " (id:" + ((ClientCallback) object).getMessageId() + ") " + message.getMessage() -// ); -// } -// } client.processCallback((ClientCallback) callback.getCallbackObject()); } catch (Exception ex) { logger.error("handleCallback error", ex); diff --git a/Mage.Common/src/main/java/mage/utils/CardColorUtil.java b/Mage.Common/src/main/java/mage/utils/CardColorUtil.java deleted file mode 100644 index 2d601e4a1de..00000000000 --- a/Mage.Common/src/main/java/mage/utils/CardColorUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -package mage.utils; - -import mage.ObjectColor; -import mage.view.CardView; - -import java.util.List; - -/** - * Utility class for {@link CardView} - * - * @version 0.1 02.11.2010 - * @author nantuko - */ -public final class CardColorUtil { - - private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*"; - private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*"; - private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*"; - private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*"; - private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; - - public static int getColorIdentitySortValue(List manaCost, ObjectColor originalColor, List rules) { - ObjectColor color = new ObjectColor(originalColor); - for (String rule : rules) { - rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic - if (rule.matches(regexBlack)) { - color.setBlack(true); - } - if (rule.matches(regexBlue)) { - color.setBlue(true); - } - if (rule.matches(regexGreen)) { - color.setGreen(true); - } - if (rule.matches(regexRed)) { - color.setRed(true); - } - if (rule.matches(regexWhite)) { - color.setWhite(true); - } - } - - int hash = 3; - hash = 23 * hash + (color.isWhite() || manaCost.contains("{W}") ? 1 : 0); - hash = 23 * hash + (color.isBlue() || manaCost.contains("{U}") ? 1 : 0); - hash = 23 * hash + (color.isBlack() || manaCost.contains("{B}") ? 1 : 0); - hash = 23 * hash + (color.isRed() || manaCost.contains("{R}") ? 1 : 0); - hash = 23 * hash + (color.isGreen() || manaCost.contains("{G}") ? 1 : 0); - return hash; - } -} diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index d0bee10ac8c..4469f2df7f8 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -10,6 +10,7 @@ import mage.abilities.SpellAbility; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; +import mage.abilities.icon.CardIcon; import mage.abilities.keyword.AftermathAbility; import mage.cards.*; import mage.cards.mock.MockCard; @@ -118,6 +119,7 @@ public class CardView extends SimpleCardView { protected boolean canAttack; protected boolean canBlock; protected boolean inViewerOnly; + protected List cardIcons = new ArrayList<>(); // additional icons to render protected Card originalCard = null; @@ -138,7 +140,7 @@ public class CardView extends SimpleCardView { this(card, null, false); this.id = simpleCardView.getId(); - this.isPlayable = simpleCardView.isPlayable; + this.playableStats = simpleCardView.playableStats.copy(); this.isChoosable = simpleCardView.isChoosable; this.isSelected = simpleCardView.isSelected; } @@ -148,7 +150,7 @@ public class CardView extends SimpleCardView { this.id = cardId; } - public CardView(CardView cardView) { + public CardView(final CardView cardView) { super(cardView); // generetate new ID (TODO: why new ID?) @@ -222,6 +224,9 @@ public class CardView extends SimpleCardView { this.canBlock = cardView.canBlock; this.inViewerOnly = cardView.inViewerOnly; this.originalCard = cardView.originalCard == null ? null : cardView.originalCard.copy(); + if (cardView.cardIcons != null) { + cardView.cardIcons.forEach(icon -> this.cardIcons.add(icon.copy())); + } } /** @@ -408,6 +413,11 @@ public class CardView extends SimpleCardView { controlledByOwner = false; } } + + // card icons for permanents on battlefield + permanent.getAbilities(game).forEach(ability -> { + this.cardIcons.addAll(ability.getIcons()); + }); } else { if (card.isCopy()) { this.mageObjectType = MageObjectType.COPY_CARD; @@ -1134,4 +1144,8 @@ public class CardView extends SimpleCardView { public Card getOriginalCard() { return this.originalCard; } + + public List getCardIcons() { + return this.cardIcons; + } } diff --git a/Mage.Common/src/main/java/mage/view/EmblemView.java b/Mage.Common/src/main/java/mage/view/EmblemView.java index 759050d58a2..fe232761b21 100644 --- a/Mage.Common/src/main/java/mage/view/EmblemView.java +++ b/Mage.Common/src/main/java/mage/view/EmblemView.java @@ -2,6 +2,7 @@ package mage.view; import mage.cards.Card; import mage.game.command.Emblem; +import mage.players.PlayableObjectStats; import java.io.Serializable; import java.util.List; @@ -16,8 +17,7 @@ public class EmblemView implements CommandObjectView, Serializable { protected String name; protected String expansionSetCode; protected List rules; - protected boolean isPlayable = false; - protected int playableAmount = 0; + protected PlayableObjectStats playableStats = new PlayableObjectStats(); public EmblemView(Emblem emblem, Card sourceCard) { this.id = emblem.getId(); @@ -57,25 +57,19 @@ public class EmblemView implements CommandObjectView, Serializable { return rules; } - @Override public boolean isPlayable() { - return isPlayable; + return this.playableStats.getPlayableAmount() > 0; } @Override - public void setPlayable(boolean isPlayable) { - this.isPlayable = isPlayable; + public void setPlayableStats(PlayableObjectStats playableStats) { + this.playableStats = playableStats; } @Override - public void setPlayableAmount(int playableAmount) { - this.playableAmount = playableAmount; - } - - @Override - public int getPlayableAmount() { - return playableAmount; + public PlayableObjectStats getPlayableStats() { + return this.playableStats; } @Override diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 14bd2c34056..3fb7f7e80bf 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -21,6 +21,7 @@ import mage.game.permanent.PermanentToken; import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.game.stack.StackObject; +import mage.players.PlayableObjectsList; import mage.players.Player; import mage.watchers.common.CastSpellLastTurnWatcher; import org.apache.log4j.Logger; @@ -42,7 +43,7 @@ public class GameView implements Serializable { private final int priorityTime; private final List players = new ArrayList<>(); private CardsView hand; - private Map canPlayObjects; + private PlayableObjectsList canPlayObjects; private Map opponentHands; private Map watchedHands; private final CardsView stack = new CardsView(); @@ -313,11 +314,11 @@ public class GameView implements Serializable { return isPlayer; } - public Map getCanPlayObjects() { + public PlayableObjectsList getCanPlayObjects() { return canPlayObjects; } - public void setCanPlayObjects(Map canPlayObjects) { + public void setCanPlayObjects(PlayableObjectsList canPlayObjects) { this.canPlayObjects = canPlayObjects; } diff --git a/Mage.Common/src/main/java/mage/view/PlaneView.java b/Mage.Common/src/main/java/mage/view/PlaneView.java index 9f4ea2040c4..5dd32260e91 100644 --- a/Mage.Common/src/main/java/mage/view/PlaneView.java +++ b/Mage.Common/src/main/java/mage/view/PlaneView.java @@ -2,6 +2,7 @@ package mage.view; import mage.cards.Card; import mage.game.command.Plane; +import mage.players.PlayableObjectStats; import java.io.Serializable; import java.util.List; @@ -16,9 +17,7 @@ public class PlaneView implements CommandObjectView, Serializable { protected String name; protected String expansionSetCode; protected List rules; - - protected boolean isPlayable = false; - protected int playableAmount = 0; + protected PlayableObjectStats playableStats = new PlayableObjectStats(); public PlaneView(Plane plane, Card sourceCard) { this.id = plane.getId(); @@ -60,22 +59,17 @@ public class PlaneView implements CommandObjectView, Serializable { @Override public boolean isPlayable() { - return isPlayable; + return this.playableStats.getPlayableAmount() > 0; } @Override - public void setPlayable(boolean isPlayable) { - this.isPlayable = isPlayable; + public void setPlayableStats(PlayableObjectStats playableStats) { + this.playableStats = playableStats; } @Override - public void setPlayableAmount(int playableAmount) { - this.playableAmount = playableAmount; - } - - @Override - public int getPlayableAmount() { - return playableAmount; + public PlayableObjectStats getPlayableStats() { + return this.playableStats; } @Override diff --git a/Mage.Common/src/main/java/mage/view/SelectableObjectView.java b/Mage.Common/src/main/java/mage/view/SelectableObjectView.java index 57004aec101..38dd33a143a 100644 --- a/Mage.Common/src/main/java/mage/view/SelectableObjectView.java +++ b/Mage.Common/src/main/java/mage/view/SelectableObjectView.java @@ -1,5 +1,7 @@ package mage.view; +import mage.players.PlayableObjectStats; + /** * @author JayDi85 */ @@ -7,11 +9,9 @@ public interface SelectableObjectView { boolean isPlayable(); - void setPlayable(boolean isPlayable); + void setPlayableStats(PlayableObjectStats playableStats); - void setPlayableAmount(int playableAmount); - - int getPlayableAmount(); + PlayableObjectStats getPlayableStats(); boolean isChoosable(); diff --git a/Mage.Common/src/main/java/mage/view/SimpleCardView.java b/Mage.Common/src/main/java/mage/view/SimpleCardView.java index 06660d2ae02..4fc6cf2063d 100644 --- a/Mage.Common/src/main/java/mage/view/SimpleCardView.java +++ b/Mage.Common/src/main/java/mage/view/SimpleCardView.java @@ -1,6 +1,7 @@ package mage.view; import com.google.gson.annotations.Expose; +import mage.players.PlayableObjectStats; import java.io.Serializable; import java.util.UUID; @@ -18,10 +19,9 @@ public class SimpleCardView implements Serializable, SelectableObjectView { protected boolean usesVariousArt; protected boolean gameObject; - protected boolean isPlayable; protected boolean isChoosable; protected boolean isSelected; - protected int playableAmount; // playable abilities count on object + protected PlayableObjectStats playableStats = new PlayableObjectStats(); public SimpleCardView(final SimpleCardView view) { this.id = view.id; @@ -32,10 +32,9 @@ public class SimpleCardView implements Serializable, SelectableObjectView { this.usesVariousArt = view.usesVariousArt; this.gameObject = view.gameObject; - this.isPlayable = view.isPlayable; this.isChoosable = view.isChoosable; this.isSelected = view.isSelected; - this.playableAmount = view.playableAmount; + this.playableStats = view.playableStats.copy(); } public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, String tokenSetCode, String tokenDescriptor) { @@ -82,22 +81,17 @@ public class SimpleCardView implements Serializable, SelectableObjectView { @Override public boolean isPlayable() { - return isPlayable; + return this.playableStats.getPlayableAmount() > 0; } @Override - public void setPlayable(boolean isPlayable) { - this.isPlayable = isPlayable; + public void setPlayableStats(PlayableObjectStats playableStats) { + this.playableStats = playableStats; } @Override - public void setPlayableAmount(int playableAmount) { - this.playableAmount = playableAmount; - } - - @Override - public int getPlayableAmount() { - return playableAmount; + public PlayableObjectStats getPlayableStats() { + return this.playableStats; } @Override diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java b/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java index 4fa5dfdc655..2b2c2b1a0d9 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java @@ -1,10 +1,3 @@ - - - /* - * ConnectDialog.java - * - * Created on 20-Jan-2010, 9:37:07 PM - */ package mage.server.console; import java.awt.*; diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java index b6b5526d7a2..e177a1f2b7a 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java @@ -246,7 +246,7 @@ .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 459, Short.MAX_VALUE) ); - btnRemoveTable.setLabel("Remove Table"); + btnRemoveTable.setText("Remove Table"); btnRemoveTable.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnRemoveTableActionPerformed(evt); diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 61d36241d52..237a27ec62b 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -19,6 +19,7 @@ import org.jboss.remoting.callback.InvokerCallbackHandler; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,7 +39,7 @@ public class Session { private final String sessionId; private UUID userId; private String host; - private int messageId = 0; + private final AtomicInteger messageId = new AtomicInteger(0); private final Date timeConnected; private boolean isAdmin = false; private final AsynchInvokerCallbackHandler callbackHandler; @@ -357,17 +358,9 @@ public class Session { boolean lockSet = false; try { if (valid && callBackLock.tryLock(50, TimeUnit.MILLISECONDS)) { - call.setMessageId(messageId++); + call.setMessageId(messageId.incrementAndGet()); lockSet = true; Callback callback = new Callback(call); -// if (call.getMethod().equals(ClientCallbackMethod.GAME_TARGET)) { -// Object object = call.getData(); -// if (object instanceof GameClientMessage) { -// String message = ((GameClientMessage) object).getMessage(); -// logger.info("Server Session Event->" + call.getMethod() + " (id:" + call.getMessageId() + ") " + message); -// logger.info(callback.toString()); -// } -// } callbackHandler.handleCallbackOneway(callback); } } catch (InterruptedException ex) { diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java index 8f6da19db6c..5ce07c2f02f 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -17,6 +17,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Planes; import mage.constants.Zone; +import mage.counters.Counter; import mage.counters.CounterType; import mage.game.Game; import mage.game.GameCommanderImpl; @@ -395,13 +396,13 @@ public final class SystemUtil { break; case COMMAND_SHOW_MY_HAND: - info = getCardsListForSpecialInform(game, feedbackPlayer.getHand(), runGroup.commands); - game.informPlayer(feedbackPlayer, info); + info = getCardsListForSpecialInform(game, feedbackPlayer.getHand(), runGroup.commands); + game.informPlayer(feedbackPlayer, info); break; case COMMAND_SHOW_MY_LIBRARY: - info = getCardsListForSpecialInform(game, feedbackPlayer.getLibrary().getCardList(), runGroup.commands); - game.informPlayer(feedbackPlayer, info); + info = getCardsListForSpecialInform(game, feedbackPlayer.getLibrary().getCardList(), runGroup.commands); + game.informPlayer(feedbackPlayer, info); break; case COMMAND_ACTIVATE_OPPONENT_ABILITY: diff --git a/Mage.Sets/src/mage/cards/s/SealAway.java b/Mage.Sets/src/mage/cards/s/SealAway.java index 5f3a3661282..830d01edf27 100644 --- a/Mage.Sets/src/mage/cards/s/SealAway.java +++ b/Mage.Sets/src/mage/cards/s/SealAway.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/w/WordOfCommand.java b/Mage.Sets/src/mage/cards/w/WordOfCommand.java index 95860d66f57..8838df44014 100644 --- a/Mage.Sets/src/mage/cards/w/WordOfCommand.java +++ b/Mage.Sets/src/mage/cards/w/WordOfCommand.java @@ -176,7 +176,7 @@ class WordOfCommandEffect extends OneShotEffect { } else { // Word of Command allows the chosen card to be played "as if it had flash" so we need to invoke such effect to bypass the check AsThoughEffectImpl effect2 = new WordOfCommandTestFlashEffect(); game.addEffect(effect2, source); - if (targetPlayer.getPlayableObjects(game, Zone.HAND).containsKey(card.getId())) { + if (targetPlayer.getPlayableObjects(game, Zone.HAND).containsObject(card.getId())) { canPlay = true; } for (AsThoughEffect eff : game.getContinuousEffects().getApplicableAsThoughEffects(AsThoughEffectType.CAST_AS_INSTANT, game)) { diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 8a31e592922..17c3b0553ba 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -46,10 +46,7 @@ import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.game.tournament.Tournament; import mage.player.ai.ComputerPlayer; -import mage.players.Library; -import mage.players.ManaPool; -import mage.players.Player; -import mage.players.PlayerImpl; +import mage.players.*; import mage.players.net.UserData; import mage.target.*; import mage.target.common.*; @@ -3420,7 +3417,7 @@ public class TestPlayer implements Player { } @Override - public Map getPlayableObjects(Game game, Zone zone) { + public PlayableObjectsList getPlayableObjects(Game game, Zone zone) { return computerPlayer.getPlayableObjects(game, zone); } diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index 041bea1810e..78fd6b98591 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -34,6 +34,7 @@ import mage.game.permanent.Permanent; import mage.game.tournament.Tournament; import mage.players.Library; import mage.players.ManaPool; +import mage.players.PlayableObjectsList; import mage.players.Player; import mage.players.net.UserData; import mage.target.Target; @@ -1060,7 +1061,7 @@ public class PlayerStub implements Player { } @Override - public Map getPlayableObjects(Game game, Zone zone) { + public PlayableObjectsList getPlayableObjects(Game game, Zone zone) { return null; } diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index e2c0bffdf73..adecc774384 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -9,6 +9,7 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.hint.Hint; +import mage.abilities.icon.CardIcon; import mage.constants.*; import mage.game.Controllable; import mage.game.Game; @@ -523,6 +524,10 @@ public interface Ability extends Controllable, Serializable { Ability addHint(Hint hint); + List getIcons(); + + Ability addIcon(CardIcon cardIcon); + Ability addCustomOutcome(Outcome customOutcome); Outcome getCustomOutcome(); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index bc4976e4a91..ebf5bd8031f 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -15,6 +15,7 @@ import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.mana.ManaEffect; import mage.abilities.hint.Hint; +import mage.abilities.icon.CardIcon; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.Card; import mage.cards.SplitCard; @@ -70,6 +71,7 @@ public abstract class AbilityImpl implements Ability { protected TargetAdjuster targetAdjuster = null; protected CostAdjuster costAdjuster = null; protected List hints = new ArrayList<>(); + protected List icons = new ArrayList<>(); protected Outcome customOutcome = null; // uses for AI decisions instead effects protected MageIdentifier identifier; // used to identify specific ability (e.g. to match with corresponding watcher) protected String appendToRule = null; @@ -120,6 +122,9 @@ public abstract class AbilityImpl implements Ability { for (Hint hint : ability.getHints()) { this.hints.add(hint.copy()); } + for (CardIcon icon : ability.getIcons()) { + this.icons.add(icon.copy()); + } this.customOutcome = ability.customOutcome; this.identifier = ability.identifier; this.activated = ability.activated; @@ -1294,6 +1299,17 @@ public abstract class AbilityImpl implements Ability { return this; } + @Override + public List getIcons() { + return this.icons; + } + + @Override + public Ability addIcon(CardIcon cardIcon) { + this.icons.add(cardIcon); + return this; + } + @Override public Ability addCustomOutcome(Outcome customOutcome) { this.customOutcome = customOutcome; diff --git a/Mage/src/main/java/mage/abilities/common/CrewWithToughnessAbility.java b/Mage/src/main/java/mage/abilities/common/CrewWithToughnessAbility.java index 687b952f27f..6455647f657 100644 --- a/Mage/src/main/java/mage/abilities/common/CrewWithToughnessAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CrewWithToughnessAbility.java @@ -8,12 +8,15 @@ import mage.constants.Zone; import java.io.ObjectStreamException; /** - * * @author varaghar */ public class CrewWithToughnessAbility extends StaticAbility implements MageSingleton { - private static final CrewWithToughnessAbility instance = new CrewWithToughnessAbility(); + private static final CrewWithToughnessAbility instance; + + static { + instance = new CrewWithToughnessAbility(); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/common/PlayLandFromGraveyardAbility.java b/Mage/src/main/java/mage/abilities/common/PlayLandFromGraveyardAbility.java deleted file mode 100644 index b4c2aefa592..00000000000 --- a/Mage/src/main/java/mage/abilities/common/PlayLandFromGraveyardAbility.java +++ /dev/null @@ -1,11 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.PlayLandAbility; -import mage.constants.Zone; - -public class PlayLandFromGraveyardAbility extends PlayLandAbility { - public PlayLandFromGraveyardAbility(String name){ - super(name); - zone = Zone.GRAVEYARD; - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java index 37940ff454d..f0b6a2a4ef4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java @@ -47,8 +47,7 @@ public class BecomeBlockedTargetEffect extends OneShotEffect { continue; } boolean alreadyBlocked = combatGroup.getBlocked(); - combatGroup.setBlocked(true); // non-banded creatures - combatGroup.setBlocked(true, game); // this only works for banded creatures and needs to be checked out + combatGroup.setBlocked(true, game); if (alreadyBlocked) { continue; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java index 7266e590abd..c91f0a5a96b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java @@ -2,6 +2,7 @@ package mage.abilities.effects.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.abilities.icon.abilities.CrewAbilityIcon; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -11,6 +12,7 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl { public CrewsVehicleSourceTriggeredAbility(Effect effect) { super(Zone.BATTLEFIELD, effect, false); + this.addIcon(CrewAbilityIcon.instance); } public CrewsVehicleSourceTriggeredAbility(final CrewsVehicleSourceTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/hint/HintUtils.java b/Mage/src/main/java/mage/abilities/hint/HintUtils.java index 69aade0218d..372b4208a4d 100644 --- a/Mage/src/main/java/mage/abilities/hint/HintUtils.java +++ b/Mage/src/main/java/mage/abilities/hint/HintUtils.java @@ -31,7 +31,7 @@ public class HintUtils { // text if (text != null && color != null) { - String hex = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); + String hex = colorToHtml(color); res = String.format("%s", hex, text); } else { res = text; @@ -55,4 +55,14 @@ public class HintUtils { } } } + + /** + * Conver color to html hex format like #7FFFD4 + * + * @param color + * @return + */ + public static String colorToHtml(Color color) { + return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); + } } diff --git a/Mage/src/main/java/mage/abilities/icon/CardIcon.java b/Mage/src/main/java/mage/abilities/icon/CardIcon.java new file mode 100644 index 00000000000..5278fcb4f57 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIcon.java @@ -0,0 +1,34 @@ +package mage.abilities.icon; + +import mage.util.Copyable; + +/** + * @author JayDi85 + */ +public interface CardIcon extends Copyable { + + CardIconType getIconType(); + + String getText(); + + /** + * Card hint on popup, support html and mana/restrict symbols + * + * @return + */ + String getHint(); + + /** + * Combined info (text + hint) + * + * @return + */ + default String getCombinedInfo() { + String res = getText(); + if (!getHint().isEmpty()) { + res += res.isEmpty() ? "" : " - "; + res += getHint(); + } + return res; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconCategory.java b/Mage/src/main/java/mage/abilities/icon/CardIconCategory.java new file mode 100644 index 00000000000..fa1f4ad2f52 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconCategory.java @@ -0,0 +1,11 @@ +package mage.abilities.icon; + +/** + * @author JayDi85 + */ +public enum CardIconCategory { + + ABILITY, // example: flying + PLAYABLE_COUNT, + SYSTEM +} diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconComparator.java b/Mage/src/main/java/mage/abilities/icon/CardIconComparator.java new file mode 100644 index 00000000000..6f9f59e05fe --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconComparator.java @@ -0,0 +1,23 @@ +package mage.abilities.icon; + +import java.util.Comparator; + +/** + * @author JayDi85 + */ +public enum CardIconComparator implements Comparator { + instance; + + @Override + public int compare(CardIcon a, CardIcon b) { + // by icon type + int res = Integer.compare(a.getIconType().getSortOrder(), b.getIconType().getSortOrder()); + + // by text + if (res == 0) { + res = String.CASE_INSENSITIVE_ORDER.compare(a.getCombinedInfo(), b.getCombinedInfo()); + } + + return res; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconImpl.java b/Mage/src/main/java/mage/abilities/icon/CardIconImpl.java new file mode 100644 index 00000000000..89318cb15a2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconImpl.java @@ -0,0 +1,47 @@ +package mage.abilities.icon; + +/** + * @author JayDi85 + */ +public class CardIconImpl implements CardIcon { + + private final CardIconType cardIconType; + private final String text; + private final String hint; + + public CardIconImpl(CardIconType cardIconType, String hint) { + this(cardIconType, hint, ""); + } + + public CardIconImpl(CardIconType cardIconType, String hint, String text) { + this.text = text; + this.hint = hint; + this.cardIconType = cardIconType; + } + + public CardIconImpl(final CardIconImpl icon) { + this.cardIconType = icon.cardIconType; + this.text = icon.text; + this.hint = icon.hint; + } + + @Override + public CardIconType getIconType() { + return cardIconType; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getHint() { + return hint; + } + + @Override + public CardIcon copy() { + return new CardIconImpl(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconOrder.java b/Mage/src/main/java/mage/abilities/icon/CardIconOrder.java new file mode 100644 index 00000000000..3f21afcc38f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconOrder.java @@ -0,0 +1,22 @@ +package mage.abilities.icon; + +/** + * Card icons order on the card's side + * + * @author JayDi85 + */ +public enum CardIconOrder { + + START, + CENTER, + END; + + public static CardIconOrder fromString(String value) { + for (CardIconOrder item : CardIconOrder.values()) { + if (item.toString().equals(value)) { + return item; + } + } + return null; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconPosition.java b/Mage/src/main/java/mage/abilities/icon/CardIconPosition.java new file mode 100644 index 00000000000..af38c1ed20b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconPosition.java @@ -0,0 +1,37 @@ +package mage.abilities.icon; + +/** + * Card icons position on the card + * + * @author JayDi85 + */ +public enum CardIconPosition { + + TOP(7), + LEFT(7), + BOTTOM(7), + RIGHT(7), + CORNER_TOP_LEFT(1), + CORNER_TOP_RIGHT(1), + CORNER_BOTTOM_LEFT(1), + CORNER_BOTTOM_RIGHT(1); + + private final int maxIconsAmount; // max icons for the panel + + CardIconPosition(int maxIconsAmount) { + this.maxIconsAmount = maxIconsAmount; + } + + public int getMaxIconsAmount() { + return this.maxIconsAmount; + } + + public static CardIconPosition fromString(String value) { + for (CardIconPosition item : CardIconPosition.values()) { + if (item.toString().equals(value)) { + return item; + } + } + return null; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconRenderSettings.java b/Mage/src/main/java/mage/abilities/icon/CardIconRenderSettings.java new file mode 100644 index 00000000000..19337ee7dae --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconRenderSettings.java @@ -0,0 +1,64 @@ +package mage.abilities.icon; + +/** + * Card icons drawing settings for MageCard + * + * @author JayDi85 + */ +public class CardIconRenderSettings { + + // custom settings for test render dialog + CardIconPosition customPosition = null; + CardIconOrder customOrder = null; + int customMaxVisibleCount = 0; + int customIconSizePercent = 0; + boolean debugMode = false; + + public CardIconRenderSettings() { + } + + public CardIconRenderSettings withCustomPosition(CardIconPosition customPosition) { + this.customPosition = customPosition; + return this; + } + + public CardIconRenderSettings withCustomOrder(CardIconOrder customOrder) { + this.customOrder = customOrder; + return this; + } + + public CardIconRenderSettings withCustomMaxVisibleCount(int customMaxVisibleCount) { + this.customMaxVisibleCount = customMaxVisibleCount; + return this; + } + + public CardIconRenderSettings withCustomIconSizePercent(int customIconSizePercent) { + this.customIconSizePercent = customIconSizePercent; + return this; + } + + public CardIconRenderSettings withDebugMode(boolean debugMode) { + this.debugMode = debugMode; + return this; + } + + public CardIconOrder getCustomOrder() { + return customOrder; + } + + public CardIconPosition getCustomPosition() { + return customPosition; + } + + public int getCustomIconSizePercent() { + return customIconSizePercent; + } + + public int getCustomMaxVisibleCount() { + return customMaxVisibleCount; + } + + public boolean isDebugMode() { + return debugMode; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/CardIconType.java b/Mage/src/main/java/mage/abilities/icon/CardIconType.java new file mode 100644 index 00000000000..3e8d3f783a9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/CardIconType.java @@ -0,0 +1,62 @@ +package mage.abilities.icon; + +/** + * Icons for GUI card panel + * + * @author JayDi85 + */ +public enum CardIconType { + + /* + - Use svg icon file name from folder ..\Mage.Client\src\main\resources\card\icons + - Look at readme-icons.txt file for more info about files format + - Sort order can be same for icons (it will be sorted by text) + - Sort order goes from 0 to 1000 (from first to last icons in normal order like START, + but CENTER and END order can be different) + */ + + PLAYABLE_COUNT("prepared/cog.svg", CardIconCategory.PLAYABLE_COUNT, 100), + // + ABILITY_FLYING("prepared/feather-alt.svg", CardIconCategory.ABILITY, 100), + ABILITY_DEFENDER("prepared/chess-rook.svg", CardIconCategory.ABILITY, 100), + ABILITY_DEATHTOUCH("prepared/skull-crossbones.svg", CardIconCategory.ABILITY, 100), + ABILITY_LIFELINK("prepared/link.svg", CardIconCategory.ABILITY, 100), + ABILITY_DOUBLE_STRIKE("prepared/mars-double.svg", CardIconCategory.ABILITY, 100), + ABILITY_FIRST_STRIKE("prepared/mars.svg", CardIconCategory.ABILITY, 100), + ABILITY_CREW("prepared/truck-monster.svg", CardIconCategory.ABILITY, 100), + ABILITY_TRAMPLE("prepared/grimace.svg", CardIconCategory.ABILITY, 100), + ABILITY_HEXPROOF("prepared/expand-arrows-alt.svg", CardIconCategory.ABILITY, 100), + ABILITY_INFECT("prepared/flask.svg", CardIconCategory.ABILITY, 100), + ABILITY_INDESTRUCTIBLE("prepared/ankh.svg", CardIconCategory.ABILITY, 100), + ABILITY_VIGILANCE("prepared/khanda.svg", CardIconCategory.ABILITY, 100), + // + SYSTEM_COMBINED("prepared/square-fill.svg", CardIconCategory.SYSTEM, 1000), // inner usage, must use last order + SYSTEM_DEBUG("prepared/link.svg", CardIconCategory.SYSTEM, 1000); // used for test render dialog + + private final String resourceName; + private final CardIconCategory category; + private final int sortOrder; + + CardIconType(String resourceName, CardIconCategory category, int sortOrder) { + this.resourceName = resourceName; + this.category = category; + this.sortOrder = sortOrder; + } + + public CardIconCategory getCategory() { + return category; + } + + public String getResourceName() { + return resourceName; + } + + public int getSortOrder() { + return sortOrder; + } + + @Override + public String toString() { + return resourceName; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/CrewAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/CrewAbilityIcon.java new file mode 100644 index 00000000000..0d9f21bb9d1 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/CrewAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum CrewAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_CREW; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Crew ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/DeathtouchAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/DeathtouchAbilityIcon.java new file mode 100644 index 00000000000..d87ec58c270 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/DeathtouchAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum DeathtouchAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_DEATHTOUCH; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Deathtouch ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/DefenderAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/DefenderAbilityIcon.java new file mode 100644 index 00000000000..7a4a0fac32b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/DefenderAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum DefenderAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_DEFENDER; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Defender ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/DoubleStrikeAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/DoubleStrikeAbilityIcon.java new file mode 100644 index 00000000000..9fe7396bf6a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/DoubleStrikeAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum DoubleStrikeAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_DOUBLE_STRIKE; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Double strike ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/FirstStrikeAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/FirstStrikeAbilityIcon.java new file mode 100644 index 00000000000..10aca3c142b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/FirstStrikeAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum FirstStrikeAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_FIRST_STRIKE; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "First strike ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/FlyingAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/FlyingAbilityIcon.java new file mode 100644 index 00000000000..4642f813c1d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/FlyingAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum FlyingAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_FLYING; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Flying ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/HexproofAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/HexproofAbilityIcon.java new file mode 100644 index 00000000000..1872ad3d79e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/HexproofAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum HexproofAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_HEXPROOF; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Hexproof ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/IndestructibleAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/IndestructibleAbilityIcon.java new file mode 100644 index 00000000000..c99ff69183f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/IndestructibleAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum IndestructibleAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_INDESTRUCTIBLE; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Indestructible ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/InfectAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/InfectAbilityIcon.java new file mode 100644 index 00000000000..39faf5bd8b2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/InfectAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum InfectAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_INFECT; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Infect ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/LifelinkAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/LifelinkAbilityIcon.java new file mode 100644 index 00000000000..7367130899e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/LifelinkAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum LifelinkAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_LIFELINK; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Lifelink ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/TrampleAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/TrampleAbilityIcon.java new file mode 100644 index 00000000000..2b60b10786a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/TrampleAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum TrampleAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_TRAMPLE; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Trumple ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/abilities/VigilanceAbilityIcon.java b/Mage/src/main/java/mage/abilities/icon/abilities/VigilanceAbilityIcon.java new file mode 100644 index 00000000000..b668d7a6b01 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/abilities/VigilanceAbilityIcon.java @@ -0,0 +1,31 @@ +package mage.abilities.icon.abilities; + +import mage.abilities.icon.CardIcon; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public enum VigilanceAbilityIcon implements CardIcon { + instance; + + @Override + public CardIconType getIconType() { + return CardIconType.ABILITY_VIGILANCE; + } + + @Override + public String getText() { + return ""; + } + + @Override + public String getHint() { + return "Vigilance ability"; + } + + @Override + public CardIcon copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/icon/system/CombinedCountIcon.java b/Mage/src/main/java/mage/abilities/icon/system/CombinedCountIcon.java new file mode 100644 index 00000000000..2d52c02ee37 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/system/CombinedCountIcon.java @@ -0,0 +1,15 @@ +package mage.abilities.icon.system; + +import mage.abilities.icon.CardIconImpl; +import mage.abilities.icon.CardIconType; + +/** + * @author JayDi85 + */ +public class CombinedCountIcon extends CardIconImpl { + + public CombinedCountIcon(int hiddenCount, String hint) { + super(CardIconType.SYSTEM_COMBINED, hint, "+" + hiddenCount); + } + +} diff --git a/Mage/src/main/java/mage/abilities/icon/system/PlayableCountIcon.java b/Mage/src/main/java/mage/abilities/icon/system/PlayableCountIcon.java new file mode 100644 index 00000000000..e7a9f783b2c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/icon/system/PlayableCountIcon.java @@ -0,0 +1,42 @@ +package mage.abilities.icon.system; + +import mage.abilities.icon.CardIconImpl; +import mage.abilities.icon.CardIconType; +import mage.players.PlayableObjectStats; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * GUI: playable icon, shown if card have an improtant playable abilities + * + * @author JayDi85 + */ +public final class PlayableCountIcon extends CardIconImpl { + + public PlayableCountIcon(PlayableObjectStats objectStats) { + // show full stats, so users can see normal and important abilities + super(CardIconType.PLAYABLE_COUNT, getHint(objectStats), getAmount(objectStats)); + } + + static private String getAmount(PlayableObjectStats objectStats) { + return String.valueOf(objectStats.getPlayableAmount()); + } + + static private String getHint(PlayableObjectStats objectStats) { + String res = "Playable abilities: " + objectStats.getPlayableAmount(); + // abilities list already sorted + List list = objectStats.getPlayableAbilities(); + final int[] counter = {0}; + if (list.size() > 0) { + res += "
" + list + .stream() + .map(s -> { + counter[0]++; + return " " + counter[0] + ". " + s; + }) + .collect(Collectors.joining("
")) + ""; + } + return res; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java b/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java index cd32a02e4d4..04133a0ddb1 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.MageInt; @@ -9,6 +8,7 @@ import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; import mage.abilities.hint.HintUtils; +import mage.abilities.icon.abilities.CrewAbilityIcon; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -36,6 +36,7 @@ public class CrewAbility extends SimpleActivatedAbility { public CrewAbility(int value) { super(Zone.BATTLEFIELD, new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT), new CrewCost(value)); this.addEffect(new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE)); + this.addIcon(CrewAbilityIcon.instance); this.value = value; } diff --git a/Mage/src/main/java/mage/abilities/keyword/DeathtouchAbility.java b/Mage/src/main/java/mage/abilities/keyword/DeathtouchAbility.java index bbca80fd97d..3392586663b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DeathtouchAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DeathtouchAbility.java @@ -1,19 +1,23 @@ - package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.DeathtouchAbilityIcon; +import mage.constants.Zone; import java.io.ObjectStreamException; /** - * * @author BetaSteward_at_googlemail.com */ public class DeathtouchAbility extends StaticAbility implements MageSingleton { - private static final DeathtouchAbility instance = new DeathtouchAbility(); + private static final DeathtouchAbility instance; + + static { + instance = new DeathtouchAbility(); + instance.addIcon(DeathtouchAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java index 57905a581db..5da4dc094fd 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java @@ -1,20 +1,23 @@ - - package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.DefenderAbilityIcon; +import mage.constants.Zone; import java.io.ObjectStreamException; /** - * * @author BetaSteward_at_googlemail.com */ public class DefenderAbility extends StaticAbility implements MageSingleton { - private static final DefenderAbility instance = new DefenderAbility(); + private static final DefenderAbility instance; + + static { + instance = new DefenderAbility(); + instance.addIcon(DefenderAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/DoubleStrikeAbility.java b/Mage/src/main/java/mage/abilities/keyword/DoubleStrikeAbility.java index b87d3a45678..71e3e3edcd7 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DoubleStrikeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DoubleStrikeAbility.java @@ -1,18 +1,23 @@ - package mage.abilities.keyword; -import java.io.ObjectStreamException; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.DoubleStrikeAbilityIcon; import mage.constants.Zone; +import java.io.ObjectStreamException; + /** - * * @author BetaSteward_at_googlemail.com */ public class DoubleStrikeAbility extends StaticAbility implements MageSingleton { - private static final DoubleStrikeAbility instance = new DoubleStrikeAbility(); + private static final DoubleStrikeAbility instance; + + static { + instance = new DoubleStrikeAbility(); + instance.addIcon(DoubleStrikeAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/FirstStrikeAbility.java b/Mage/src/main/java/mage/abilities/keyword/FirstStrikeAbility.java index 3c467bb9dc6..67c9a52f148 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FirstStrikeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FirstStrikeAbility.java @@ -1,18 +1,23 @@ - package mage.abilities.keyword; -import java.io.ObjectStreamException; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.FirstStrikeAbilityIcon; import mage.constants.Zone; +import java.io.ObjectStreamException; + /** - * * @author BetaSteward_at_googlemail.com */ public class FirstStrikeAbility extends StaticAbility implements MageSingleton { - private static final FirstStrikeAbility instance = new FirstStrikeAbility(); + private static final FirstStrikeAbility instance; + + static { + instance = new FirstStrikeAbility(); + instance.addIcon(FirstStrikeAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/FlyingAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlyingAbility.java index 3550730f0b8..dc682908efa 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlyingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlyingAbility.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import mage.abilities.EvasionAbility; import mage.abilities.MageSingleton; import mage.abilities.effects.RestrictionEffect; +import mage.abilities.icon.abilities.FlyingAbilityIcon; import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.SubType; @@ -17,7 +18,12 @@ import java.io.ObjectStreamException; */ public class FlyingAbility extends EvasionAbility implements MageSingleton { - private static final FlyingAbility instance = new FlyingAbility(); + private static final FlyingAbility instance; + + static { + instance = new FlyingAbility(); + instance.addIcon(FlyingAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java index 539ca5d5135..dea7eea8176 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java @@ -3,6 +3,7 @@ package mage.abilities.keyword; import mage.MageObject; import mage.abilities.MageSingleton; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.icon.abilities.HexproofAbilityIcon; import mage.constants.Zone; import mage.game.Game; @@ -15,6 +16,7 @@ public abstract class HexproofBaseAbility extends SimpleStaticAbility implements HexproofBaseAbility() { super(Zone.BATTLEFIELD, null); + this.addIcon(HexproofAbilityIcon.instance); } public abstract boolean checkObject(MageObject source, Game game); diff --git a/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java b/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java index 0200258fd19..2286fc15c26 100644 --- a/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/IndestructibleAbility.java @@ -1,9 +1,10 @@ - package mage.abilities.keyword; -import java.io.ObjectStreamException; -import mage.constants.Zone; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.IndestructibleAbilityIcon; +import mage.constants.Zone; + +import java.io.ObjectStreamException; /** * OLD RULES: 700.4. If a permanent is indestructible, rules and effects can't @@ -11,18 +12,13 @@ import mage.abilities.StaticAbility; * lethal damage, and they ignore the lethal-damage state-based action (see rule * 704.5g). Rules or effects may cause an indestructible permanent to be * sacrificed, put into a graveyard, or exiled. # - * + *

* 700.4a Although the text "[This permanent] is indestructible" is an ability, * actually being indestructible is neither an ability nor a characteristic. * It's just something that's true about a permanent. - * + *

* NEW RULES * - * - * - * - * - * * @author BetaSteward_at_googlemail.com */ public class IndestructibleAbility extends StaticAbility { @@ -31,6 +27,7 @@ public class IndestructibleAbility extends StaticAbility { static { instance = new IndestructibleAbility(); + instance.addIcon(IndestructibleAbilityIcon.instance); } private Object readResolve() throws ObjectStreamException { diff --git a/Mage/src/main/java/mage/abilities/keyword/InfectAbility.java b/Mage/src/main/java/mage/abilities/keyword/InfectAbility.java index dd003f9e18a..6482a1d971f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/InfectAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/InfectAbility.java @@ -1,39 +1,43 @@ - - package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.InfectAbilityIcon; +import mage.constants.Zone; import java.io.ObjectStreamException; /** - * 702.87. Infect + * 702.87. Infect + *

+ * 702.87a. Infect is a static ability. + *

+ * 702.87b. Damage dealt to a player by a source with infect doesn't cause that player to lose life. Rather, it causes the player to get that many poison counters. See rule 119.3. + *

+ * 702.87c. Damage dealt to a creature by a source with infect isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3. + *

+ * 702.87d. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information + * (Last Known Information: Information about an object that's no longer in the zone it's expected to be in, or information about a player that's no longer in the game. This information captures that object's last existence in that zone or that player's last existence in the game....) + * 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage... + * 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its... + * 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a... + * 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if they are still in the game; otherwise, the effect uses the last known information about that player before they left the game. + * is used to determine whether it had infect. + *

+ * 702.87e. The infect rules function no matter what zone an object with infect deals damage from. + *

+ * 702.87f. Multiple instances of infect on the same object are redundant. * - * 702.87a. Infect is a static ability. - * - * 702.87b. Damage dealt to a player by a source with infect doesn't cause that player to lose life. Rather, it causes the player to get that many poison counters. See rule 119.3. - * - * 702.87c. Damage dealt to a creature by a source with infect isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3. - * - * 702.87d. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information - * (Last Known Information: Information about an object that's no longer in the zone it's expected to be in, or information about a player that's no longer in the game. This information captures that object's last existence in that zone or that player's last existence in the game....) - * 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage... - * 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its... - * 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a... - * 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if they are still in the game; otherwise, the effect uses the last known information about that player before they left the game. - * is used to determine whether it had infect. - * - * 702.87e. The infect rules function no matter what zone an object with infect deals damage from. - * - * 702.87f. Multiple instances of infect on the same object are redundant. - * - * @author nantuko + * @author nantuko */ public class InfectAbility extends StaticAbility implements MageSingleton { - private static final InfectAbility instance = new InfectAbility(); + private static final InfectAbility instance; + + static { + instance = new InfectAbility(); + instance.addIcon(InfectAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java index 388ad91711b..d406593a10b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java @@ -1,20 +1,23 @@ - - package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.LifelinkAbilityIcon; +import mage.constants.Zone; import java.io.ObjectStreamException; /** - * * @author BetaSteward_at_googlemail.com */ public class LifelinkAbility extends StaticAbility implements MageSingleton { - private static final LifelinkAbility instance = new LifelinkAbility(); + private static final LifelinkAbility instance; + + static { + instance = new LifelinkAbility(); + instance.addIcon(LifelinkAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/TrampleAbility.java b/Mage/src/main/java/mage/abilities/keyword/TrampleAbility.java index a4b14d88a5b..6497f0a8e0a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TrampleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TrampleAbility.java @@ -1,18 +1,23 @@ - package mage.abilities.keyword; -import java.io.ObjectStreamException; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.TrampleAbilityIcon; import mage.constants.Zone; +import java.io.ObjectStreamException; + /** - * * @author BetaSteward_at_googlemail.com */ public class TrampleAbility extends StaticAbility implements MageSingleton { - private static final TrampleAbility instance = new TrampleAbility(); + private static final TrampleAbility instance; + + static { + instance = new TrampleAbility(); + instance.addIcon(TrampleAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/abilities/keyword/VigilanceAbility.java b/Mage/src/main/java/mage/abilities/keyword/VigilanceAbility.java index f5d4e4e8eaa..2d873a0379f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/VigilanceAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/VigilanceAbility.java @@ -1,19 +1,23 @@ - package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; +import mage.abilities.icon.abilities.VigilanceAbilityIcon; +import mage.constants.Zone; import java.io.ObjectStreamException; /** - * * @author BetaSteward_at_googlemail.com */ public class VigilanceAbility extends StaticAbility implements MageSingleton { - private static final VigilanceAbility instance = new VigilanceAbility(); + private static final VigilanceAbility instance; + + static { + instance = new VigilanceAbility(); + instance.addIcon(VigilanceAbilityIcon.instance); + } private Object readResolve() throws ObjectStreamException { return instance; diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 2fcded0f292..8b769adeacc 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -21,6 +21,7 @@ import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.util.CardUtil; import mage.util.GameLog; +import mage.util.ManaUtil; import mage.watchers.Watcher; import org.apache.log4j.Logger; @@ -34,12 +35,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { private static final Logger logger = Logger.getLogger(CardImpl.class); - private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*"; - private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*"; - private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*"; - private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*"; - private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; - protected UUID ownerId; protected String cardNumber; protected String expansionSetCode; @@ -807,60 +802,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public FilterMana getColorIdentity() { - FilterMana mana = new FilterMana(); - mana.setBlack(getManaCost().getText().matches(regexBlack)); - mana.setBlue(getManaCost().getText().matches(regexBlue)); - mana.setGreen(getManaCost().getText().matches(regexGreen)); - mana.setRed(getManaCost().getText().matches(regexRed)); - mana.setWhite(getManaCost().getText().matches(regexWhite)); - - for (String rule : getRules()) { - rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic - if (!mana.isBlack() && (rule.matches(regexBlack) || this.color.isBlack())) { - mana.setBlack(true); - } - if (!mana.isBlue() && (rule.matches(regexBlue) || this.color.isBlue())) { - mana.setBlue(true); - } - if (!mana.isGreen() && (rule.matches(regexGreen) || this.color.isGreen())) { - mana.setGreen(true); - } - if (!mana.isRed() && (rule.matches(regexRed) || this.color.isRed())) { - mana.setRed(true); - } - if (!mana.isWhite() && (rule.matches(regexWhite) || this.color.isWhite())) { - mana.setWhite(true); - } - } - if (isTransformable()) { - Card secondCard = getSecondCardFace(); - ObjectColor color = secondCard.getColor(null); - mana.setBlack(mana.isBlack() || color.isBlack()); - mana.setGreen(mana.isGreen() || color.isGreen()); - mana.setRed(mana.isRed() || color.isRed()); - mana.setBlue(mana.isBlue() || color.isBlue()); - mana.setWhite(mana.isWhite() || color.isWhite()); - for (String rule : secondCard.getRules()) { - rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic - if (!mana.isBlack() && rule.matches(regexBlack)) { - mana.setBlack(true); - } - if (!mana.isBlue() && rule.matches(regexBlue)) { - mana.setBlue(true); - } - if (!mana.isGreen() && rule.matches(regexGreen)) { - mana.setGreen(true); - } - if (!mana.isRed() && rule.matches(regexRed)) { - mana.setRed(true); - } - if (!mana.isWhite() && rule.matches(regexWhite)) { - mana.setWhite(true); - } - } - } - - return mana; + return ManaUtil.getColorIdentity(this); } @Override diff --git a/Mage/src/main/java/mage/cards/decks/Deck.java b/Mage/src/main/java/mage/cards/decks/Deck.java index 7c80c0de462..d99c97f6488 100644 --- a/Mage/src/main/java/mage/cards/decks/Deck.java +++ b/Mage/src/main/java/mage/cards/decks/Deck.java @@ -1,12 +1,5 @@ - package mage.cards.decks; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; import mage.cards.Card; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -14,6 +7,9 @@ import mage.game.GameException; import mage.util.DeckUtil; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; + public class Deck implements Serializable { private String name; @@ -59,8 +55,8 @@ public class Deck implements Serializable { * Warning, AI can't play Mock cards, so call it with extra params in real games or tests * * @param deckCardLists cards to load - * @param ignoreErrors - do not raise exception error on wrong deck - * @param mockCards - use it for GUI only code, real game cards must be real + * @param ignoreErrors - do not raise exception error on wrong deck + * @param mockCards - use it for GUI only code, real game cards must be real * @return * @throws GameException */ @@ -182,6 +178,14 @@ public class Deck implements Serializable { return cards; } + public Card findCard(UUID cardId) { + return cards + .stream() + .filter(card -> card.getId().equals(cardId)) + .findFirst() + .orElse(null); + } + public DeckCardLayout getCardsLayout() { return cardsLayout; } @@ -190,6 +194,14 @@ public class Deck implements Serializable { return sideboard; } + public Card findSideboardCard(UUID cardId) { + return sideboard + .stream() + .filter(card -> card.getId().equals(cardId)) + .findFirst() + .orElse(null); + } + public DeckCardLayout getSideboardLayout() { return sideboardLayout; } diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 7f95c77a3a0..3d5d81ce473 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -781,20 +781,6 @@ public class CombatGroup implements Serializable, Copyable { } } - /** - * There are effects, that set an attacker to be blocked. Therefore this - * setter can be used. - *

- * This method lacks a band check, use setBlocked(blocked, game) instead. - * - * @param blocked - * @deprecated - */ - @Deprecated - public void setBlocked(boolean blocked) { - this.blocked = blocked; - } - public void setBlocked(boolean blocked, Game game) { this.blocked = blocked; for (UUID attackerId : attackers) { diff --git a/Mage/src/main/java/mage/game/draft/DraftCube.java b/Mage/src/main/java/mage/game/draft/DraftCube.java index 7854940ccbd..5c4e63cc99a 100644 --- a/Mage/src/main/java/mage/game/draft/DraftCube.java +++ b/Mage/src/main/java/mage/game/draft/DraftCube.java @@ -1,4 +1,3 @@ - package mage.game.draft; import java.util.ArrayList; diff --git a/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java b/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java index 508ce78bf05..4aaf42d0338 100644 --- a/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java +++ b/Mage/src/main/java/mage/game/events/PlayerQueryEvent.java @@ -25,7 +25,20 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri public enum QueryType { - ASK, CHOOSE_CHOICE, CHOOSE_ABILITY, CHOOSE_MODE, PICK_TARGET, PICK_ABILITY, SELECT, PLAY_MANA, PLAY_X_MANA, AMOUNT, PICK_CARD, CONSTRUCT, CHOOSE_PILE, PERSONAL_MESSAGE + ASK, + CHOOSE_CHOICE, + CHOOSE_ABILITY, + CHOOSE_MODE, + PICK_TARGET, + PICK_ABILITY, + SELECT, + PLAY_MANA, + PLAY_X_MANA, + AMOUNT, + PICK_CARD, + CONSTRUCT, + CHOOSE_PILE, + PERSONAL_MESSAGE } private String message; diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index b563538431d..a8a645f5d24 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -33,6 +33,7 @@ import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.util.CardUtil; import mage.util.GameLog; +import mage.util.ManaUtil; import mage.util.SubTypes; import java.util.*; @@ -45,12 +46,6 @@ public class Spell extends StackObjImpl implements Card { private final List spellAbilities = new ArrayList<>(); private final List spellCards = new ArrayList<>(); - private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*"; - private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*"; - private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*"; - private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*"; - private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; - private final Card card; private final ObjectColor color; private final ObjectColor frameColor; @@ -1000,60 +995,7 @@ public class Spell extends StackObjImpl implements Card { @Override public FilterMana getColorIdentity() { - FilterMana mana = new FilterMana(); - mana.setBlack(getManaCost().getText().matches(regexBlack)); - mana.setBlue(getManaCost().getText().matches(regexBlue)); - mana.setGreen(getManaCost().getText().matches(regexGreen)); - mana.setRed(getManaCost().getText().matches(regexRed)); - mana.setWhite(getManaCost().getText().matches(regexWhite)); - - for (String rule : getRules()) { - rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic - if (!mana.isBlack() && (rule.matches(regexBlack) || this.color.isBlack())) { - mana.setBlack(true); - } - if (!mana.isBlue() && (rule.matches(regexBlue) || this.color.isBlue())) { - mana.setBlue(true); - } - if (!mana.isGreen() && (rule.matches(regexGreen) || this.color.isGreen())) { - mana.setGreen(true); - } - if (!mana.isRed() && (rule.matches(regexRed) || this.color.isRed())) { - mana.setRed(true); - } - if (!mana.isWhite() && (rule.matches(regexWhite) || this.color.isWhite())) { - mana.setWhite(true); - } - } - if (isTransformable()) { - Card secondCard = getSecondCardFace(); - ObjectColor objectColor = secondCard.getColor(null); - mana.setBlack(mana.isBlack() || objectColor.isBlack()); - mana.setGreen(mana.isGreen() || objectColor.isGreen()); - mana.setRed(mana.isRed() || objectColor.isRed()); - mana.setBlue(mana.isBlue() || objectColor.isBlue()); - mana.setWhite(mana.isWhite() || objectColor.isWhite()); - for (String rule : secondCard.getRules()) { - rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic - if (!mana.isBlack() && rule.matches(regexBlack)) { - mana.setBlack(true); - } - if (!mana.isBlue() && rule.matches(regexBlue)) { - mana.setBlue(true); - } - if (!mana.isGreen() && rule.matches(regexGreen)) { - mana.setGreen(true); - } - if (!mana.isRed() && rule.matches(regexRed)) { - mana.setRed(true); - } - if (!mana.isWhite() && rule.matches(regexWhite)) { - mana.setWhite(true); - } - } - } - - return mana; + return ManaUtil.getColorIdentity(this); } @Override diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index e98c852fb33..615e70ee8f7 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -15,6 +15,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.hint.Hint; +import mage.abilities.icon.CardIcon; import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; @@ -680,6 +681,16 @@ public class StackAbility extends StackObjImpl implements Ability { throw new IllegalArgumentException("Stack ability is not supports hint adding"); } + @Override + public List getIcons() { + return this.ability.getIcons(); + } + + @Override + public Ability addIcon(CardIcon cardIcon) { + throw new IllegalArgumentException("Stack ability is not supports icon adding"); + } + @Override public Ability addCustomOutcome(Outcome customOutcome) { throw new IllegalArgumentException("Stack ability is not supports custom outcome adding"); diff --git a/Mage/src/main/java/mage/players/PlayableObjectStats.java b/Mage/src/main/java/mage/players/PlayableObjectStats.java new file mode 100644 index 00000000000..1a847c82ef8 --- /dev/null +++ b/Mage/src/main/java/mage/players/PlayableObjectStats.java @@ -0,0 +1,136 @@ +package mage.players; + +import mage.abilities.ActivatedAbility; +import mage.abilities.PlayLandAbility; +import mage.abilities.SpellAbility; +import mage.abilities.mana.BasicManaAbility; +import mage.constants.SpellAbilityType; +import mage.util.Copyable; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * Contains stats with playable abilities for one object + * + * @author JayDi85 + */ +public class PlayableObjectStats implements Serializable, Copyable { + + // map: ability.id -> ability.rules + List basicManaAbilities = new ArrayList<>(); + List basicPlayAbilities = new ArrayList<>(); + List basicCastAbilities = new ArrayList<>(); + List other = new ArrayList<>(); + + public PlayableObjectStats() { + } + + public PlayableObjectStats(List activatedAbilities) { + load(activatedAbilities); + } + + public void load(List activatedAbilities) { + this.basicManaAbilities.clear(); + this.basicPlayAbilities.clear(); + this.basicCastAbilities.clear(); + this.other.clear(); + + // split abilities to types (it allows to enable or disable playable icon) + for (ActivatedAbility ability : activatedAbilities) { + List dest; + if (ability instanceof BasicManaAbility) { + dest = this.basicManaAbilities; + } else if (ability instanceof PlayLandAbility) { + dest = this.basicPlayAbilities; + } else if (ability instanceof SpellAbility && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.BASE)) { + dest = this.basicCastAbilities; + } else { + dest = this.other; + } + + // collect info about abilities for card icons popup, must be simple online text (html symbols are possible) + // some long html tags can be miss (example: ability extra hint) -- that's ok + String shortInfo = ability.toString(); + if (shortInfo.length() > 50) { + shortInfo = shortInfo.substring(0, 50 - 1) + "..."; + } + shortInfo = shortInfo.replace("
", " "); + shortInfo = shortInfo.replace("\n", " "); + dest.add(new PlayableObjectRecord(ability.getId(), shortInfo)); + } + } + + public PlayableObjectStats(final PlayableObjectStats source) { + for (PlayableObjectRecord rec : source.basicManaAbilities) { + this.basicManaAbilities.add(rec.copy()); + } + for (PlayableObjectRecord rec : source.basicPlayAbilities) { + this.basicPlayAbilities.add(rec.copy()); + } + for (PlayableObjectRecord rec : source.basicCastAbilities) { + this.basicCastAbilities.add(rec.copy()); + } + for (PlayableObjectRecord rec : source.other) { + this.other.add(rec.copy()); + } + } + + @Override + public PlayableObjectStats copy() { + return new PlayableObjectStats(this); + } + + public int getPlayableAmount() { + return this.basicManaAbilities.size() + + this.basicPlayAbilities.size() + + this.basicCastAbilities.size() + + this.other.size(); + } + + public List getPlayableAbilities() { + List all = new ArrayList<>(); + all.addAll(this.basicManaAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList())); + all.addAll(this.basicPlayAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList())); + all.addAll(this.basicCastAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList())); + all.addAll(this.other.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList())); + return all; + } + + public int getPlayableImportantAmount() { + // return only important abilities (e.g. show it as card icons) + return this.other.size(); + } +} + +class PlayableObjectRecord implements Serializable, Copyable { + + UUID id; + String value; + + public PlayableObjectRecord(UUID id, String value) { + this.id = id; + this.value = value; + } + + private PlayableObjectRecord(final PlayableObjectRecord record) { + this.id = record.id; + this.value = record.value; + } + + public UUID getId() { + return id; + } + + public String getValue() { + return value; + } + + @Override + public PlayableObjectRecord copy() { + return new PlayableObjectRecord(this); + } +} diff --git a/Mage/src/main/java/mage/players/PlayableObjectsList.java b/Mage/src/main/java/mage/players/PlayableObjectsList.java new file mode 100644 index 00000000000..e5b5d9f56e4 --- /dev/null +++ b/Mage/src/main/java/mage/players/PlayableObjectsList.java @@ -0,0 +1,69 @@ +package mage.players; + +import mage.abilities.ActivatedAbility; +import mage.util.Copyable; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * Contains stats with all playable cards for the player + * + * @author JayDi85 + */ +public class PlayableObjectsList implements Serializable, Copyable { + + Map objects = new HashMap<>(); + + public PlayableObjectsList() { + } + + public PlayableObjectsList(Map> playableObjects) { + load(playableObjects); + } + + public PlayableObjectsList(final PlayableObjectsList source) { + source.objects.entrySet().forEach(entry -> { + this.objects.put(entry.getKey(), entry.getValue().copy()); + }); + } + + @Override + public PlayableObjectsList copy() { + return new PlayableObjectsList(this); + } + + public void load(Map> playableObjects) { + objects.clear(); + playableObjects.forEach((objectId, list) -> { + objects.put(objectId, new PlayableObjectStats(list)); + }); + } + + public boolean containsObject(UUID objectId) { + return objects.containsKey(objectId); + } + + public boolean isEmpty() { + return objects.isEmpty(); + } + + public PlayableObjectStats getStats(UUID objectId) { + if (objects.containsKey(objectId)) { + return objects.get(objectId).copy(); + } else { + return new PlayableObjectStats(); + } + } + + public int getPlayableAmount(UUID objectId) { + if (objects.containsKey(objectId)) { + return objects.get(objectId).getPlayableAmount(); + } else { + return 0; + } + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index a8882302f09..f9723f918de 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -82,10 +82,10 @@ public interface Player extends MageItem, Copyable { void setLife(int life, Game game, Ability source); /** - * @param amount amount of life loss + * @param amount amount of life loss * @param game - * @param source can be null for default game events like mana burn - * @param atCombat was the source combat damage + * @param source can be null for default game events like mana burn + * @param atCombat was the source combat damage * @param attackerId id of the attacker for combat events (can be null) * @return */ @@ -94,7 +94,6 @@ public interface Player extends MageItem, Copyable { int loseLife(int amount, Game game, Ability source, boolean atCombat); /** - * * @param amount * @param game * @param source can be null for default game events life lifelink damage @@ -338,7 +337,6 @@ public interface Player extends MageItem, Copyable { void reset(); /** - * * @param source can be null for game default shuffle (non effects, example: mulligans) * @param game */ @@ -360,7 +358,7 @@ public interface Player extends MageItem, Copyable { * @param num * @param source can be null for game default draws (non effects, example: start of the turn) * @param game - * @param event original draw event in replacement code + * @param event original draw event in replacement code * @return */ int drawCards(int num, Ability source, Game game, GameEvent event); @@ -609,7 +607,7 @@ public interface Player extends MageItem, Copyable { * @param game * @param source * @param xFromTheTop - * @param withName - show card name in game logs for all players + * @param withName - show card name in game logs for all players * @return */ boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop, boolean withName); @@ -705,7 +703,7 @@ public interface Player extends MageItem, Copyable { List getPlayableOptions(Ability ability, Game game); - Map getPlayableObjects(Game game, Zone zone); + PlayableObjectsList getPlayableObjects(Game game, Zone zone); LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game); @@ -846,7 +844,8 @@ public interface Player extends MageItem, Copyable { * @param withName * @return */ - @Deprecated // if you want to use it in replaceEvent, then use ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + @Deprecated + // if you want to use it in replaceEvent, then use ((ZoneChangeEvent) event).setToZone(Zone.EXILED); boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, Ability source, Game game, Zone fromZone, boolean withName); /** diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 68ad47375c0..9ae4ef3e07f 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3710,32 +3710,45 @@ public abstract class PlayerImpl implements Player, Serializable { * abilities */ @Override - public Map getPlayableObjects(Game game, Zone zone) { + public PlayableObjectsList getPlayableObjects(Game game, Zone zone) { + // collect abilities per object List playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards - Map playableObjects = new HashMap<>(); - for (Ability ability : playableAbilities) { + Map> playableObjects = new HashMap<>(); + for (ActivatedAbility ability : playableAbilities) { if (ability.getSourceId() != null) { - playableObjects.put(ability.getSourceId(), playableObjects.getOrDefault(ability.getSourceId(), 0) + 1); - // main card must be marked playable in GUI + // normal card + putToPlayableObjects(playableObjects, ability.getSourceId(), ability); + + // main card - must be marked playable in GUI Card card = game.getCard(ability.getSourceId()); if (card != null && card.getMainCard().getId() != card.getId()) { - UUID mainCardId = card.getMainCard().getId(); - playableObjects.put(mainCardId, playableObjects.getOrDefault(mainCardId, 0) + 1); + putToPlayableObjects(playableObjects, card.getMainCard().getId(), ability); } - // spell on stack can have activated abilities, + // spell on stack - can have activated abilities, // so mark it as playable too (users must able to clicks on stack objects) // example: Lightning Storm Spell spell = game.getSpell(ability.getSourceId()); if (spell != null) { - playableObjects.put(spell.getId(), playableObjects.getOrDefault(spell.getId(), 0) + 1); + putToPlayableObjects(playableObjects, spell.getId(), ability); } } } - return playableObjects; + + // collect stats + PlayableObjectsList playableObjectsList = new PlayableObjectsList(playableObjects); + return playableObjectsList; } + private void putToPlayableObjects(Map> playableObjects, UUID objectId, ActivatedAbility ability) { + if (!playableObjects.containsKey(objectId)) { + playableObjects.put(objectId, new ArrayList<>()); + } + playableObjects.get(objectId).add(ability); + } + + /** * Skip "silent" phase step when players are not allowed to cast anything. * E.g. players can't play or cast anything during declaring attackers. diff --git a/Mage/src/main/java/mage/util/DebugUtil.java b/Mage/src/main/java/mage/util/DebugUtil.java new file mode 100644 index 00000000000..634d46ade46 --- /dev/null +++ b/Mage/src/main/java/mage/util/DebugUtil.java @@ -0,0 +1,31 @@ +package mage.util; + +/** + * Devs only: enable or disable debug features + *

+ * WARNING, don't forget to disable it before release/commit + * + * @author JayDi85 + */ +public class DebugUtil { + + // cards basic (card panels) + public static boolean GUI_CARD_DRAW_OUTER_BORDER = false; + public static boolean GUI_CARD_DRAW_INNER_BORDER = false; + public static boolean GUI_CARD_ICONS_DRAW_PANEL_BORDER = false; + public static boolean GUI_CARD_ICONS_DRAW_ICON_BORDER = false; + public static boolean GUI_CARD_DRAW_MOUSE_CONTAINS_BOUNDS = false; + + // cards renders (inner card panel) + public static boolean GUI_RENDER_IMAGE_DRAW_IMAGE_BORDER = false; + public static boolean GUI_RENDER_CENTERED_TEXT_DRAW_DEBUG_LINES = false; + + // deck editor + public static boolean GUI_DECK_EDITOR_DRAW_DRAGGING_PANE_BORDER = false; + public static boolean GUI_DECK_EDITOR_DRAW_COUNT_LABEL_BORDER = false; + + // game + public static boolean GUI_GAME_DRAW_BATTLEFIELD_BORDER = false; + public static boolean GUI_GAME_DRAW_HAND_AND_STACK_BORDER = false; + public static boolean GUI_GAME_DIALOGS_DRAW_CARDS_AREA_BORDER = false; +} diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index 4beb1f8aca1..6533e8d6eac 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -3,6 +3,7 @@ package mage.util; import mage.MageObject; import mage.Mana; import mage.ManaSymbol; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; @@ -21,10 +22,16 @@ import mage.players.Player; import java.util.*; /** - * @author noxx + * @author noxx, JayDi85 */ public final class ManaUtil { + private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*"; + private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*"; + private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*"; + private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*"; + private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; + private ManaUtil() { } @@ -504,6 +511,121 @@ public final class ManaUtil { } } + /** + * Find full card's color identity (from mana cost and rules) + * + * @param cardColor color indicator + * @param cardManaSymbols mana cost + * @param cardRules rules list + * @param secondSideCard second side of double faces card + * @return + */ + public static FilterMana getColorIdentity(ObjectColor cardColor, List cardManaSymbols, List cardRules, Card secondSideCard) { + // 20210121 + // 903.4 + // The Commander variant uses color identity to determine what cards can be in a deck with a certain + // commander. The color identity of a card is the color or colors of any mana symbols in that card’s mana + // cost or rules text, plus any colors defined by its characteristic-defining abilities (see rule 604.3) + // or color indicator (see rule 204). + // 903.4d + // The back face of a double-faced card (see rule 711) is included when determining a card’s color identity. + // This is an exception to rule 711.4a. + FilterMana res = new FilterMana(); + + // from object (color indicator) + ObjectColor color = (cardColor != null ? new ObjectColor(cardColor) : new ObjectColor()); + + // from mana + res.setWhite(color.isWhite() || containsManaSymbol(cardManaSymbols, "W")); + res.setBlue(color.isBlue() || containsManaSymbol(cardManaSymbols, "U")); + res.setBlack(color.isBlack() || containsManaSymbol(cardManaSymbols, "B")); + res.setRed(color.isRed() || containsManaSymbol(cardManaSymbols, "R")); + res.setGreen(color.isGreen() || containsManaSymbol(cardManaSymbols, "G")); + + // from rules + for (String rule : cardRules) { + rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic + if (!res.isWhite() && (rule.matches(regexWhite))) { + res.setWhite(true); + } + if (!res.isBlue() && (rule.matches(regexBlue))) { + res.setBlue(true); + } + if (!res.isBlack() && rule.matches(regexBlack)) { + res.setBlack(true); + } + if (!res.isRed() && (rule.matches(regexRed))) { + res.setRed(true); + } + if (!res.isGreen() && (rule.matches(regexGreen))) { + res.setGreen(true); + } + } + + // SECOND SIDE CARD + if (secondSideCard != null) { + // from object (color indicator) + ObjectColor secondColor = secondSideCard.getColor(null); + res.setBlack(res.isBlack() || secondColor.isBlack()); + res.setGreen(res.isGreen() || secondColor.isGreen()); + res.setRed(res.isRed() || secondColor.isRed()); + res.setBlue(res.isBlue() || secondColor.isBlue()); + res.setWhite(res.isWhite() || secondColor.isWhite()); + + // from mana + List secondManaSymbols = secondSideCard.getManaCost().getSymbols(); + res.setWhite(res.isWhite() || containsManaSymbol(secondManaSymbols, "W")); + res.setBlue(res.isBlue() || containsManaSymbol(secondManaSymbols, "U")); + res.setBlack(res.isBlack() || containsManaSymbol(secondManaSymbols, "B")); + res.setRed(res.isRed() || containsManaSymbol(secondManaSymbols, "R")); + res.setGreen(res.isGreen() || containsManaSymbol(secondManaSymbols, "G")); + + // from rules + for (String rule : secondSideCard.getRules()) { + rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic + if (!res.isWhite() && rule.matches(regexWhite)) { + res.setWhite(true); + } + if (!res.isBlue() && rule.matches(regexBlue)) { + res.setBlue(true); + } + if (!res.isBlack() && rule.matches(regexBlack)) { + res.setBlack(true); + } + if (!res.isRed() && rule.matches(regexRed)) { + res.setRed(true); + } + if (!res.isGreen() && rule.matches(regexGreen)) { + res.setGreen(true); + } + } + } + + return res; + } + + private static boolean containsManaSymbol(List cardManaSymbols, String needSymbol) { + // search R in {R/B} + return cardManaSymbols.stream().anyMatch(s -> s.contains(needSymbol)); + } + + public static FilterMana getColorIdentity(Card card) { + // TODO: is it support mdf cards? + // TODO: is it support adventure cards? + Card secondSide = card.getSecondCardFace(); + return getColorIdentity(card.getColor(null), card.getManaCost().getSymbols(), card.getRules(), secondSide); + } + + public static int getColorIdentityHash(FilterMana colorIdentity) { + int hash = 3; + hash = 23 * hash + (colorIdentity.isWhite() ? 1 : 0); + hash = 23 * hash + (colorIdentity.isBlue() ? 1 : 0); + hash = 23 * hash + (colorIdentity.isBlack() ? 1 : 0); + hash = 23 * hash + (colorIdentity.isRed() ? 1 : 0); + hash = 23 * hash + (colorIdentity.isGreen() ? 1 : 0); + return hash; + } + /** * all ability/effect code with "= new GenericManaCost" must be replaced by * createManaCost call