From a779289ec216522d8f5ff5fd7f9485b39b937940 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Tue, 2 Nov 2010 09:10:39 +0000 Subject: [PATCH] Preparations for new card drawing in Mage.Card.Plugin. --- Mage.Common/src/mage/utils/CardUtil.java | 25 ++ Mage.Common/src/mage/view/CardView.java | 4 + Mage.Plugins/Mage.Card.Plugin/pom.xml | 10 + .../src/main/java/org/mage/card/MageCard.java | 9 + .../java/org/mage/card/arcane/Animation.java | 322 ++++++++++++++ .../java/org/mage/card/arcane/CardPanel.java | 410 ++++++++++++++++++ .../java/org/mage/card/arcane/GlowText.java | 102 +++++ .../org/mage/card/arcane/ManaSymbols.java | 62 +++ .../mage/card/arcane/ScaledImagePanel.java | 192 ++++++++ .../main/java/org/mage/card/arcane/UI.java | 182 ++++++++ .../main/java/org/mage/card/arcane/Util.java | 100 +++++ .../org/mage/card/constants/Constants.java | 17 + 12 files changed, 1435 insertions(+) create mode 100644 Mage.Common/src/mage/utils/CardUtil.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/MageCard.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/GlowText.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ManaSymbols.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ScaledImagePanel.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/UI.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Util.java create mode 100644 Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/constants/Constants.java diff --git a/Mage.Common/src/mage/utils/CardUtil.java b/Mage.Common/src/mage/utils/CardUtil.java new file mode 100644 index 0000000000..e2251379d9 --- /dev/null +++ b/Mage.Common/src/mage/utils/CardUtil.java @@ -0,0 +1,25 @@ +package mage.utils; + +import mage.Constants.CardType; +import mage.view.CardView; + +/** + * Utility class for {@link CardView} + * + * @version 0.1 02.11.2010 + * @author nantuko + */ +public class CardUtil { + + public static boolean isCreature(CardView card) { + return is(card, CardType.CREATURE); + } + + public static boolean isPlaneswalker(CardView card) { + return is(card, CardType.PLANESWALKER); + } + + public static boolean is(CardView card, CardType type) { + return card.getCardTypes().contains(type); + } +} diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 467ef8fe13..4737a9c174 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -64,6 +64,7 @@ public class CardView implements Serializable { protected String art; protected Rarity rarity; protected String expansionSetCode; + protected boolean tapped; public List targets; @@ -96,6 +97,9 @@ public class CardView implements Serializable { this.expansionSetCode = card.getExpansionSetCode(); } + //FIXME + this.tapped = false; + if (card instanceof Spell) { Spell spell = (Spell)card; if (spell.getSpellAbility().getTargets().size() > 0) { diff --git a/Mage.Plugins/Mage.Card.Plugin/pom.xml b/Mage.Plugins/Mage.Card.Plugin/pom.xml index 6145841618..29795694f0 100644 --- a/Mage.Plugins/Mage.Card.Plugin/pom.xml +++ b/Mage.Plugins/Mage.Card.Plugin/pom.xml @@ -17,11 +17,21 @@ Plugin for drawing card + + org.mage + Mage-Common + ${mage-version} + org.plugins.jspf jspf-core ${jspf-version} + + log4j + log4j + 1.2.9 + diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/MageCard.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/MageCard.java new file mode 100644 index 0000000000..360c223b2b --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/MageCard.java @@ -0,0 +1,9 @@ +package org.mage.card; + +public abstract class MageCard { + abstract public void onBeginAnimation(); + abstract public void onEndAnimation(); + abstract public void repaint(); + abstract public boolean isTapped(); + abstract public void setTransparency(double transparency); +} \ No newline at end of file diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java new file mode 100644 index 0000000000..e92086932b --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java @@ -0,0 +1,322 @@ +package org.mage.card.arcane; + +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.Point; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.JLayeredPane; +import javax.swing.SwingUtilities; + +import org.mage.card.MageCard; + +abstract public class Animation { + static private final long TARGET_MILLIS_PER_FRAME = 30; + //static private final float HALF_PI = (float)(Math.PI / 2); + + static private Timer timer = new Timer("Animation", true); + + //static private CardPanel delayedCardPanel; + //static private long delayedTime; + static private CardPanel enlargedCardPanel; + static private CardPanel enlargedAnimationPanel; + static private Object enlargeLock = new Object(); + + private TimerTask timerTask; + private FrameTimer frameTimer; + private long elapsed; + + public Animation (final long duration) { + this(duration, 0); + } + + public Animation (final long duration, long delay) { + timerTask = new TimerTask() { + public void run () { + if (frameTimer == null) { + start(); + frameTimer = new FrameTimer(); + } + elapsed += frameTimer.getTimeSinceLastFrame(); + if (elapsed >= duration) { + cancel(); + elapsed = duration; + } + update(elapsed / (float)duration); + if (elapsed == duration) end(); + } + }; + timer.scheduleAtFixedRate(timerTask, delay, TARGET_MILLIS_PER_FRAME); + } + + abstract protected void update (float percentage); + + protected void cancel () { + timerTask.cancel(); + end(); + } + + protected void start () { + } + + protected void end () { + } + + /** + * Uses averaging of the time between the past few frames to provide smooth animation. + */ + private class FrameTimer { + static private final int SAMPLES = 6; + static private final long MAX_FRAME = 100; // Max time for one frame, to weed out spikes. + + private long samples[] = new long[SAMPLES]; + private int sampleIndex; + + public FrameTimer () { + long currentTime = System.currentTimeMillis(); + for (int i = SAMPLES - 1; i >= 0; i--) + samples[i] = currentTime - (SAMPLES - i) * TARGET_MILLIS_PER_FRAME; + } + + public long getTimeSinceLastFrame () { + long currentTime = System.currentTimeMillis(); + + int id = sampleIndex - 1; + if (id < 0) id += SAMPLES; + + long timeSinceLastSample = currentTime - samples[id]; + + // If the slice was too big, advance all the previous times by the diff. + if (timeSinceLastSample > MAX_FRAME) { + long diff = timeSinceLastSample - MAX_FRAME; + for (int i = 0; i < SAMPLES; i++) + samples[i] += diff; + } + + long timeSinceOldestSample = currentTime - samples[sampleIndex]; + samples[sampleIndex] = currentTime; + sampleIndex = (sampleIndex + 1) % SAMPLES; + + return timeSinceOldestSample / (long)SAMPLES; + } + } + + static public void tapCardToggle (final CardPanel panel, final MageCard parent) { + new Animation(300) { + protected void start () { + parent.onBeginAnimation(); + } + + protected void update (float percentage) { + panel.tappedAngle = CardPanel.TAPPED_ANGLE * percentage; + if (!panel.gameCard.isTapped()) panel.tappedAngle = CardPanel.TAPPED_ANGLE - panel.tappedAngle; + panel.repaint(); + } + + protected void end () { + panel.tappedAngle = panel.gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0; + parent.onEndAnimation(); + parent.repaint(); + } + }; + } + + // static public void moveCardToPlay (Component source, final CardPanel dest, final CardPanel animationPanel) { + static public 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) { + UI.invokeLater(new Runnable() { + public void run () { + 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(); + if (parent != layeredPane) { + layeredPane.add(animationPanel); + layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER); + } + + new Animation(700) { + protected void update (float percentage) { + if (placeholder != null && !placeholder.isShowing()) { + cancel(); + return; + } + int currentX = startX + Math.round((endX - startX + endWidth / 2f) * percentage); + int currentY = startY + Math.round((endY - startY + endHeight / 2f) * percentage); + int currentWidth, currentHeight; + int midWidth = Math.max(200, endWidth * 2); + int midHeight = Math.round(midWidth * CardPanel.ASPECT_RATIO); + if (percentage <= 0.5f) { + percentage = percentage * 2; + float pp = sqrta * (1 - percentage); + percentage = 1 - a * pp * pp; + currentWidth = startWidth + Math.round((midWidth - startWidth) * percentage); + currentHeight = startHeight + Math.round((midHeight - startHeight) * percentage); + } else { + percentage = (percentage - 0.5f) * 2; + float pp = sqrta * percentage; + percentage = a * pp * pp; + currentWidth = midWidth + Math.round((endWidth - midWidth) * percentage); + currentHeight = midHeight + Math.round((endHeight - midHeight) * percentage); + } + currentX -= Math.round(currentWidth / 2); + currentY -= Math.round(currentHeight / 2); + animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); + } + + protected void end () { + EventQueue.invokeLater(new Runnable() { + public void run () { + if (placeholder != null) { + placeholder.setDisplayEnabled(true); + placeholder.setImage(animationPanel); + } + animationPanel.setVisible(false); + animationPanel.repaint(); + layeredPane.remove(animationPanel); + } + }); + } + }; + } + }); + } + + static public 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) { + UI.invokeLater(new Runnable() { + public void run () { + 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(); + if (parent != layeredPane) { + layeredPane.add(animationPanel); + layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER); + } + + new Animation(speed) { + protected void update (float percentage) { + int currentX = startX + Math.round((endX - startX) * percentage); + 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); + } + + protected void end () { + EventQueue.invokeLater(new Runnable() { + public void run () { + if (placeholder != null) { + placeholder.setDisplayEnabled(true); + placeholder.setImage(animationPanel); + } + animationPanel.setVisible(false); + animationPanel.repaint(); + layeredPane.remove(animationPanel); + } + }); + } + }; + } + }); + } + + static public void shrinkCard () { + CardPanel enlargedCardPanel, enlargedAnimationPanel; + synchronized (enlargeLock) { + //delayedCardPanel = null; + //delayedTime = 0; + enlargedCardPanel = Animation.enlargedCardPanel; + enlargedAnimationPanel = Animation.enlargedAnimationPanel; + if (enlargedAnimationPanel == null) return; + Animation.enlargedCardPanel = null; + Animation.enlargedAnimationPanel = null; + } + + final CardPanel overPanel = enlargedCardPanel, animationPanel = enlargedAnimationPanel; + + animationPanel.setAnimationPanel(true); + final JLayeredPane layeredPane = SwingUtilities.getRootPane(overPanel).getLayeredPane(); + layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER); + + final int startWidth = animationPanel.getCardWidth(); + final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO); + final int endWidth = overPanel.getCardWidth(); + final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO); + + new Animation(200) { + 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); + 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)); + currentX = Math.min(currentX, layeredPane.getWidth() - currentWidth); + int currentY = Math.max(0, centerY - Math.round(currentHeight / 2f)); + currentY = Math.min(currentY, layeredPane.getHeight() - currentHeight); + animationPanel.tappedAngle = overPanel.tappedAngle * percentage; + animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight); + } + + protected void end () { + animationPanel.setVisible(false); + animationPanel.repaint(); + EventQueue.invokeLater(new Runnable() { + public void run () { + layeredPane.remove(animationPanel); + } + }); + } + }; + } + + static public boolean isShowingEnlargedCard () { + synchronized (enlargeLock) { + return enlargedAnimationPanel != null; + } + } + + static public void showCard(final CardPanel panel) { + new Animation(600) { + protected void start () { + } + + protected void update (float percentage) { + float alpha = percentage; + panel.setAlpha(alpha); + panel.repaint(); + } + + protected void end () { + } + }; + } + + static public void hideCard(final CardPanel panel, final MageCard card) { + new Animation(600) { + protected void start () { + } + + protected void update (float percentage) { + float alpha = 1 - percentage; + panel.setAlpha(alpha); + card.setTransparency(alpha); + card.repaint(); + } + + protected void end () { + } + }; + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java new file mode 100644 index 0000000000..fa31d2aed1 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java @@ -0,0 +1,410 @@ +package org.mage.card.arcane; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; + +import mage.utils.CardUtil; +import mage.view.PermanentView; + +import org.mage.card.arcane.ScaledImagePanel.MultipassType; +import org.mage.card.arcane.ScaledImagePanel.ScalingType; + +@SuppressWarnings({"unchecked","rawtypes"}) +public class CardPanel extends JPanel { + private static final long serialVersionUID = -3272134219262184410L; + static public final double TAPPED_ANGLE = Math.PI / 2; + static public final float ASPECT_RATIO = 3.5f / 2.5f; + //static public final float ASPECT_RATIO = 1.0f; + + static public CardPanel dragAnimationPanel; + + public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149); + + static private final float ROUNDED_CORNER_SIZE = 0.1f; + //static private final float SELECTED_BORDER_SIZE = 0.01f; + static private final float BLACK_BORDER_SIZE = 0.03f; + static private final int TEXT_GLOW_SIZE = 6; + static private final float TEXT_GLOW_INTENSITY = 3f; + static private final float rotCenterToTopCorner = 1.0295630140987000315797369464196f; + static private final float rotCenterToBottomCorner = 0.7071067811865475244008443621048f; + + public PermanentView gameCard; + public CardPanel attachedToPanel; + public List attachedPanels = new ArrayList(); + public double tappedAngle = 0; + public ScaledImagePanel imagePanel; + public ScaledImagePanel overlayPanel; + + private GlowText titleText; + private GlowText ptText; + private List imageLoadListeners = new ArrayList(2); + private boolean displayEnabled = true; + private boolean isAnimationPanel; + private int cardXOffset, cardYOffset, cardWidth, cardHeight; + + private boolean isSelected; + private boolean showCastingCost; + private boolean hasImage = false; + private float alpha = 1.0f; + + public CardPanel (PermanentView newGameCard, boolean loadImage) { + this.gameCard = newGameCard; + + //for container debug (don't remove) + //setBorder(BorderFactory.createLineBorder(Color.green)); + + setBackground(Color.black); + setOpaque(false); + + titleText = new GlowText(); + setText(gameCard); + titleText.setFont(getFont().deriveFont(Font.BOLD, 13f)); + titleText.setForeground(Color.white); + titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + titleText.setWrap(true); + add(titleText); + + ptText = new GlowText(); + if (CardUtil.isCreature(gameCard)) { + ptText.setText(gameCard.getPower() + "/" + gameCard.getToughness()); + } else if (CardUtil.isPlaneswalker(gameCard)) { + ptText.setText(gameCard.getOriginal().getLoyalty()); + } + ptText.setFont(getFont().deriveFont(Font.BOLD, 13f)); + ptText.setForeground(Color.white); + ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + add(ptText); + + overlayPanel = new ScaledImagePanel(); + overlayPanel.setBorder(BorderFactory.createLineBorder(Color.white)); + add(overlayPanel); + overlayPanel.setScaleLarger(true); + overlayPanel.setScalingType(ScalingType.nearestNeighbor); + overlayPanel.setScalingBlur(true); + overlayPanel.setScalingMultiPassType(MultipassType.none); + + //TODO: Image sickness = ImageManager.getSicknessImage(); + Image sickness = null; + overlayPanel.setImage(sickness, sickness); + + imagePanel = new ScaledImagePanel(); + imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); + add(imagePanel); + imagePanel.setScaleLarger(true); + imagePanel.setScalingType(ScalingType.nearestNeighbor); + imagePanel.setScalingBlur(true); + imagePanel.setScalingMultiPassType(MultipassType.none); + + if (!loadImage) return; + + Util.threadPool.submit(new Runnable() { + public void run () { + BufferedImage srcImage = null; //TODO: ImageCache.getImageOriginal(gameCard); + tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0; + if (srcImage != null) { + //setImage(srcImage, ImageUtil.getBlurredImage(srcImage, 3, 1.0f)); + hasImage = true; + setText(gameCard); + setImage(srcImage, srcImage); + } + } + }); + } + + private void setText(PermanentView card) { + if (hasImage) { + titleText.setText(""); + } else { + titleText.setText(card.getName()); + } + } + + private void setImage (Image srcImage, Image srcImageBlurred) { + synchronized (imagePanel) { + imagePanel.setImage(srcImage, srcImageBlurred); + repaint(); + for (CardPanel cardPanel : imageLoadListeners) { + cardPanel.setImage(srcImage, srcImageBlurred); + cardPanel.repaint(); + } + imageLoadListeners.clear(); + } + layout(); + } + + public void setImage (final CardPanel panel) { + synchronized (panel.imagePanel) { + if (panel.imagePanel.hasImage()) + setImage(panel.imagePanel.srcImage, panel.imagePanel.srcImageBlurred); + else + panel.imageLoadListeners.add(this); + } + } + + public void setScalingType (ScalingType scalingType) { + imagePanel.setScalingType(scalingType); + } + + public void setDisplayEnabled (boolean displayEnabled) { + this.displayEnabled = displayEnabled; + } + + public boolean isDisplayEnabled () { + return displayEnabled; + } + + public void setAnimationPanel (boolean isAnimationPanel) { + this.isAnimationPanel = isAnimationPanel; + } + + public void setSelected (boolean isSelected) { + this.isSelected = isSelected; + repaint(); + } + + public void setAttacking (boolean isAttacking) { + //TODO:uncomment + //this.gameCard.setAttacking(isAttacking); + repaint(); + } + + public boolean getSelected() { + return this.isSelected; + } + + public void setShowCastingCost (boolean showCastingCost) { + this.showCastingCost = showCastingCost; + } + + public void paint (Graphics g) { + if (!displayEnabled) return; + if (!isValid()) super.validate(); + Graphics2D g2d = (Graphics2D)g; + if (tappedAngle > 0) { + g2d = (Graphics2D)g2d.create(); + float edgeOffset = cardWidth / 2f; + g2d.rotate(tappedAngle, cardXOffset + edgeOffset, cardYOffset + cardHeight - edgeOffset); + } + super.paint(g2d); + } + + protected void paintComponent (Graphics g) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (alpha != 1.0f) { + AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha); + g2d.setComposite(composite); + } + + //TODO:uncomment + /* + if (!hasImage && gameCard.getTableID() > 0) { + g2d.setColor(new Color(30,200,200,120)); + } else { + g2d.setColor(new Color(0,0,0,200)); + } + */ + g2d.setColor(new Color(30,200,200,120)); + + //for debug repainting + //g2d.setColor(new Color(MyRandom.random.nextInt(255),MyRandom.random.nextInt(255),MyRandom.random.nextInt(255),150)); + int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE)); + g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); + if (isSelected) { + //g2d.setColor(new Color(0,250,0,200)); + g2d.setColor(new Color(200,120,40,200)); + g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize); + } + + //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); + }*/ + + /*if (isSelected) { + g2d.setColor(Color.green); + int offset = gameCard.isTapped() ? 1 : 0; + for (int i = 1, n = Math.max(1, Math.round(cardWidth * SELECTED_BORDER_SIZE)); i <= n; i++) + g2d.drawRoundRect(cardXOffset - i, cardYOffset - i + offset, cardWidth + i * 2 - 1, cardHeight + i * 2 - 1, + cornerSize, cornerSize); + }*/ + } + + protected void paintChildren (Graphics g) { + super.paintChildren(g); + + if (showCastingCost && !isAnimationPanel && cardWidth < 200 && cardWidth > 60) { + //TODO:uncomment + /*int width = ManaSymbols.getWidth(gameCard.getManaCost()); + ManaSymbols.draw(g, gameCard.getManaCost(), cardXOffset + 8, cardHeight - 9); + */ + } + } + + public void layout () { + int borderSize = Math.round(cardWidth * BLACK_BORDER_SIZE); + imagePanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); + imagePanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + + //TODO: uncomment + /*if (gameCard.hasSickness() && gameCard.isCreature() && gameCard.getTableID() != 0) { + overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); + overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + } else { + overlayPanel.setVisible(false); + }*/ + overlayPanel.setVisible(false); + + int fontHeight = Math.round(cardHeight * (27f / 680)); + boolean showText = (!isAnimationPanel && fontHeight < 12); + titleText.setVisible(showText); + ptText.setVisible(showText); + + int titleX = Math.round(cardWidth * (20f / 480)); + int titleY = Math.round(cardHeight * (9f / 680)); + titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight); + + Dimension ptSize = ptText.getPreferredSize(); + ptText.setSize(ptSize.width, ptSize.height); + int ptX = Math.round(cardWidth * (420f / 480)) - ptSize.width / 2; + int ptY = Math.round(cardHeight * (675f / 680)) - ptSize.height; + + int offsetX = Math.round((CARD_SIZE_FULL.width - cardWidth) / 10.0f); + + ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); + + if (isAnimationPanel || cardWidth < 200) + imagePanel.setScalingType(ScalingType.nearestNeighbor); + else + imagePanel.setScalingType(ScalingType.bilinear); + } + + public String toString () { + return gameCard.toString(); + } + + public void setCardBounds (int x, int y, int width, int height) { + cardWidth = width; + cardHeight = height; + int rotCenterX = Math.round(width / 2f); + int rotCenterY = height - rotCenterX; + int rotCenterToTopCorner = Math.round(width * CardPanel.rotCenterToTopCorner); + int rotCenterToBottomCorner = Math.round(width * CardPanel.rotCenterToBottomCorner); + int xOffset = rotCenterX - rotCenterToBottomCorner; + int yOffset = rotCenterY - rotCenterToTopCorner; + cardXOffset = -xOffset; + cardYOffset = -yOffset; + width = -xOffset + rotCenterX + rotCenterToTopCorner; + height = -yOffset + rotCenterY + rotCenterToBottomCorner; + setBounds(x + xOffset, y + yOffset, width, height); + } + + public void repaint () { + Rectangle b = getBounds(); + JRootPane rootPane = SwingUtilities.getRootPane(this); + if (rootPane == null) return; + Point p = SwingUtilities.convertPoint(getParent(), b.x, b.y, rootPane); + rootPane.repaint(p.x, p.y, b.width, b.height); + } + + public int getCardX () { + return getX() + cardXOffset; + } + + public int getCardY () { + return getY() + cardYOffset; + } + + public int getCardWidth () { + return cardWidth; + } + + public int getCardHeight () { + return cardHeight; + } + + public Point getCardLocation () { + Point p = getLocation(); + p.x += cardXOffset; + p.y += cardYOffset; + return p; + } + + public PermanentView getCard() { + return this.gameCard; + } + + public void setCard(PermanentView card) { + if (CardUtil.isCreature(card) && CardUtil.isPlaneswalker(card)) { + ptText.setText(card.getPower() + "/" + card.getToughness() + " (" + card.getLoyalty() + ")"); + } else if (CardUtil.isCreature(card)) { + ptText.setText(card.getPower() + "/" + card.getToughness()); + } else if (CardUtil.isPlaneswalker(card)) { + ptText.setText(card.getLoyalty()); + } else { + ptText.setText(""); + } + setText(card); + this.gameCard = card; + //TODO: uncomment + /*if (gameCard.hasSickness() && gameCard.isCreature() && gameCard.getTableID() != 0) { + overlayPanel.setVisible(true); + } else { + overlayPanel.setVisible(false); + }*/ + + repaint(); + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + public float getAlpha() { + return alpha; + } + + public int getCardXOffset() { + return cardXOffset; + } + + public int getCardYOffset() { + return cardYOffset; + } + + public void updateImage() { + if (!hasImage) { + Util.threadPool.submit(new Runnable() { + public void run () { + //TODO: BufferedImage srcImage = ImageCache.getImageOriginal(gameCard); + BufferedImage srcImage = null; + tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0; + if (srcImage != null) { + hasImage = true; + setText(gameCard); + setImage(srcImage, srcImage); + } + } + }); + } + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/GlowText.java new file mode 100644 index 0000000000..91fa7b994a --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/GlowText.java @@ -0,0 +1,102 @@ +package org.mage.card.arcane; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.BreakIterator; +import java.util.Locale; + +import javax.swing.JLabel; + +public class GlowText extends JLabel { + private static final long serialVersionUID = 1827677946939348001L; + private int glowSize; + @SuppressWarnings("unused") + private float glowIntensity; + private Color glowColor; + private boolean wrap; + private int lineCount = 0; + + public void setGlow (Color glowColor, int size, float intensity) { + this.glowColor = glowColor; + this.glowSize = size; + this.glowIntensity = intensity; + } + + public void setWrap (boolean wrap) { + this.wrap = wrap; + } + + public Dimension getPreferredSize () { + Dimension size = super.getPreferredSize(); + size.width += glowSize; + size.height += glowSize / 2; + return size; + } + + public void setText (String text) { + super.setText(text); + } + + public void paint (Graphics g) { + if (getText().length() == 0) return; + + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + Dimension size = getSize(); + int textX = 0, textY = 0; + int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE); + + AttributedString attributedString = new AttributedString(getText()); + attributedString.addAttribute(TextAttribute.FONT, getFont()); + AttributedCharacterIterator charIterator = attributedString.getIterator(); + FontRenderContext fontContext = g2d.getFontRenderContext(); + + LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext); + lineCount = 0; + while (measurer.getPosition() < charIterator.getEndIndex()) { + //TextLayout textLayout = measurer.nextLayout(wrapWidth); + lineCount++; + if (lineCount > 2) break; + } + charIterator.first(); + // Use char wrap if word wrap would cause more than two lines of text. + if (lineCount > 2) + measurer = new LineBreakMeasurer(charIterator, BreakIterator.getCharacterInstance(Locale.ENGLISH), fontContext); + else + measurer.setPosition(0); + while (measurer.getPosition() < charIterator.getEndIndex()) { + TextLayout textLayout = measurer.nextLayout(wrapWidth); + float ascent = textLayout.getAscent(); + textY += ascent; // Move down to baseline. + + g2d.setColor(glowColor); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1); + textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1); + textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1); + textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1); + + g2d.setColor(getForeground()); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); + textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2); + + textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line. + } + } + + public int getLineCount() { + return this.lineCount; + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ManaSymbols.java new file mode 100644 index 0000000000..e36e40ff7d --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -0,0 +1,62 @@ +package org.mage.card.arcane; + +import java.awt.Graphics; +import java.awt.Image; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; +import org.mage.card.constants.Constants; + +public class ManaSymbols { + private static final Logger log = Logger.getLogger(ManaSymbols.class); + static private final Map manaImages = new HashMap(); + static private Pattern replaceSymbolsPattern = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}"); + + static public void loadImages () { + String[] symbols = new String[] {"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG", + "BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", "X", "Y", "Z", "slash"}; + //TODO: replace by downloading + for (String symbol : symbols) + manaImages.put(symbol, UI.getImageIcon(Constants.RESOURCE_PATH_MANA + "/" + symbol + ".png").getImage()); + } + + static public void draw (Graphics g, String manaCost, int x, int y) { + if (manaCost.length() == 0) return; + manaCost = manaCost.replace("\\", ""); + manaCost = UI.getDisplayManaCost(manaCost); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + String symbol = tok.nextToken().substring(0); + Image image = manaImages.get(symbol); + if (image == null) { + log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost); + continue; + } + g.drawImage(image, x, y, null); + x += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide. + } + } + + static public int getWidth (String manaCost) { + int width = 0; + manaCost = manaCost.replace("\\", ""); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + String symbol = tok.nextToken().substring(0); + width += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide. + } + return width; + } + + static public synchronized String replaceSymbolsWithHTML (String value, boolean small) { + if (small) + return replaceSymbolsPattern.matcher(value).replaceAll(""); + else { + value = value.replace("{slash}", ""); + return replaceSymbolsPattern.matcher(value).replaceAll(""); + } + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ScaledImagePanel.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ScaledImagePanel.java new file mode 100644 index 0000000000..9045711064 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/ScaledImagePanel.java @@ -0,0 +1,192 @@ +package org.mage.card.arcane; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +import javax.swing.JPanel; + +public class ScaledImagePanel extends JPanel { + private static final long serialVersionUID = -1523279873208605664L; + public volatile Image srcImage; + public volatile Image srcImageBlurred; + + private ScalingType scalingType = ScalingType.bilinear; + private boolean scaleLarger; + private MultipassType multiPassType = MultipassType.bilinear; + private boolean blur; + + public ScaledImagePanel () { + super(false); + setOpaque(false); + } + + public void setImage (Image srcImage, Image srcImageBlurred) { + this.srcImage = srcImage; + this.srcImageBlurred = srcImageBlurred; + } + + public void clearImage () { + srcImage = null; + repaint(); + } + + public void setScalingMultiPassType (MultipassType multiPassType) { + this.multiPassType = multiPassType; + } + + public void setScalingType (ScalingType scalingType) { + this.scalingType = scalingType; + } + + public void setScalingBlur (boolean blur) { + this.blur = blur; + } + + public void setScaleLarger (boolean scaleLarger) { + this.scaleLarger = scaleLarger; + } + + public boolean hasImage () { + return srcImage != null; + } + + private ScalingInfo getScalingInfo () { + int panelWidth = getWidth(); + int panelHeight = getHeight(); + int srcWidth = srcImage.getWidth(null); + int srcHeight = srcImage.getHeight(null); + int targetWidth = srcWidth; + int targetHeight = srcHeight; + if (scaleLarger || srcWidth > panelWidth || srcHeight > panelHeight) { + targetWidth = Math.round(panelHeight * (srcWidth / (float)srcHeight)); + if (targetWidth > panelWidth) { + targetHeight = Math.round(panelWidth * (srcHeight / (float)srcWidth)); + targetWidth = panelWidth; + } else + targetHeight = panelHeight; + } + ScalingInfo info = new ScalingInfo(); + info.targetWidth = targetWidth; + info.targetHeight = targetHeight; + info.srcWidth = srcWidth; + info.srcHeight = srcHeight; + info.x = panelWidth / 2 - targetWidth / 2; + info.y = panelHeight / 2 - targetHeight / 2; + return info; + } + + public void paint (Graphics g) { + if (srcImage == null) return; + + Graphics2D g2 = (Graphics2D)g.create(); + ScalingInfo info = getScalingInfo(); + + switch (scalingType) { + case nearestNeighbor: + scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + break; + case bilinear: + scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + break; + case bicubic: + scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + break; + case areaAveraging: + scaleWithGetScaledInstance(g2, info, Image.SCALE_AREA_AVERAGING); + break; + case replicate: + scaleWithGetScaledInstance(g2, info, Image.SCALE_REPLICATE); + break; + } + } + + private void scaleWithGetScaledInstance (Graphics2D g2, ScalingInfo info, int hints) { + Image srcImage = getSourceImage(info); + Image scaledImage = srcImage.getScaledInstance(info.targetWidth, info.targetHeight, hints); + g2.drawImage(scaledImage, info.x, info.y, null); + } + + private void scaleWithDrawImage (Graphics2D g2, ScalingInfo info, Object hint) { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); + + int tempDestWidth = info.srcWidth / 2, tempDestHeight = info.srcHeight / 2; + if (tempDestWidth < info.targetWidth) tempDestWidth = info.targetWidth; + if (tempDestHeight < info.targetHeight) tempDestHeight = info.targetHeight; + + Image srcImage = getSourceImage(info); + + // If not doing multipass or multipass only needs a single pass, just scale it once directly to the panel surface. + if (multiPassType == MultipassType.none || (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight)) { + g2.drawImage(srcImage, info.x, info.y, info.targetWidth, info.targetHeight, null); + return; + } + + BufferedImage tempImage = new BufferedImage(tempDestWidth, tempDestHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2temp = tempImage.createGraphics(); + switch (multiPassType) { + case nearestNeighbor: + g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + break; + case bilinear: + g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + break; + case bicubic: + g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + break; + } + // Render first pass from image to temp. + g2temp.drawImage(srcImage, 0, 0, tempDestWidth, tempDestHeight, null); + // Render passes between the first and last pass. + int tempSrcWidth = tempDestWidth; + int tempSrcHeight = tempDestHeight; + while (true) { + if (tempDestWidth > info.targetWidth) { + tempDestWidth = tempDestWidth / 2; + if (tempDestWidth < info.targetWidth) tempDestWidth = info.targetWidth; + } + + if (tempDestHeight > info.targetHeight) { + tempDestHeight = tempDestHeight / 2; + if (tempDestHeight < info.targetHeight) tempDestHeight = info.targetHeight; + } + + if (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight) break; + + g2temp.drawImage(tempImage, 0, 0, tempDestWidth, tempDestHeight, 0, 0, tempSrcWidth, tempSrcHeight, null); + + tempSrcWidth = tempDestWidth; + tempSrcHeight = tempDestHeight; + } + g2temp.dispose(); + // Render last pass from temp to panel surface. + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); + g2.drawImage(tempImage, info.x, info.y, info.x + info.targetWidth, info.y + info.targetHeight, 0, 0, tempSrcWidth, + tempSrcHeight, null); + } + + private Image getSourceImage (ScalingInfo info) { + if (!blur || srcImageBlurred == null) return srcImage; + if (info.srcWidth / 2 < info.targetWidth || info.srcHeight / 2 < info.targetHeight) return srcImage; + return srcImageBlurred; + } + + static private class ScalingInfo { + public int targetWidth; + public int targetHeight; + public int srcWidth; + public int srcHeight; + public int x; + public int y; + } + + static public enum MultipassType { + none, nearestNeighbor, bilinear, bicubic + } + + static public enum ScalingType { + nearestNeighbor, replicate, bilinear, bicubic, areaAveraging + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/UI.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/UI.java new file mode 100644 index 0000000000..dcce2f64be --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/UI.java @@ -0,0 +1,182 @@ +package org.mage.card.arcane; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Toolkit; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Hashtable; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; +import javax.swing.JViewport; +import javax.swing.ScrollPaneConstants; +import javax.swing.UIManager; +import javax.swing.ViewportLayout; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.ImageView; + +/** + * UI utility functions. + */ +public class UI { + static private Hashtable imageCache = new Hashtable(); + + static public JToggleButton getToggleButton () { + JToggleButton button = new JToggleButton(); + button.setMargin(new Insets(2, 4, 2, 4)); + return button; + } + + static public JButton getButton () { + JButton button = new JButton(); + button.setMargin(new Insets(2, 4, 2, 4)); + return button; + } + + static public void setTitle (JPanel panel, String title) { + Border border = panel.getBorder(); + if (border instanceof TitledBorder) { + ((TitledBorder)panel.getBorder()).setTitle(title); + panel.repaint(); + } else + panel.setBorder(BorderFactory.createTitledBorder(title)); + } + + @SuppressWarnings("deprecation") + static public URL getFileURL (String path) { + File file = new File(path); + if (file.exists()) { + try { + return file.toURL(); + } catch (MalformedURLException ignored) { + } + } + return UI.class.getResource(path); + } + + static public ImageIcon getImageIcon (String path) { + try { + InputStream stream; + stream = UI.class.getResourceAsStream(path); + if (stream == null && new File(path).exists()) stream = new FileInputStream(path); + if (stream == null) throw new RuntimeException("Image not found: " + path); + byte[] data = new byte[stream.available()]; + stream.read(data); + return new ImageIcon(data); + } catch (IOException ex) { + throw new RuntimeException("Error reading image: " + path); + } + } + + static public void setHTMLEditorKit (JEditorPane editorPane) { + editorPane.getDocument().putProperty("imageCache", imageCache); // Read internally by ImageView, but never written. + // Extend all this shit to cache images. + editorPane.setEditorKit(new HTMLEditorKit() { + private static final long serialVersionUID = -54602188235105448L; + + public ViewFactory getViewFactory () { + return new HTMLFactory() { + public View create (Element elem) { + Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (o instanceof HTML.Tag) { + HTML.Tag kind = (HTML.Tag)o; + if (kind == HTML.Tag.IMG) return new ImageView(elem) { + public URL getImageURL () { + URL url = super.getImageURL(); + // Put an image into the cache to be read by other ImageView methods. + if (url != null && imageCache.get(url) == null) + imageCache.put(url, Toolkit.getDefaultToolkit().createImage(url)); + return url; + } + }; + } + return super.create(elem); + } + }; + } + }); + } + + static public void setVerticalScrollingView (JScrollPane scrollPane, final Component view) { + final JViewport viewport = new JViewport(); + viewport.setLayout(new ViewportLayout() { + private static final long serialVersionUID = 7701568740313788935L; + public void layoutContainer (Container parent) { + viewport.setViewPosition(new Point(0, 0)); + Dimension viewportSize = viewport.getSize(); + int width = viewportSize.width; + int height = Math.max(view.getPreferredSize().height, viewportSize.height); + viewport.setViewSize(new Dimension(width, height)); + } + }); + viewport.setView(view); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setViewport(viewport); + } + + static public String getDisplayManaCost (String manaCost) { + manaCost = manaCost.replace("/", "{slash}"); + // A pipe in the cost means "process left of the pipe as the card color, but display right of the pipe as the cost". + int pipePosition = manaCost.indexOf("{|}"); + if (pipePosition != -1) manaCost = manaCost.substring(pipePosition + 3); + return manaCost; + } + + static public void invokeLater (Runnable runnable) { + EventQueue.invokeLater(runnable); + } + + static public void invokeAndWait (Runnable runnable) { + if (EventQueue.isDispatchThread()) { + runnable.run(); + return; + } + try { + EventQueue.invokeAndWait(runnable); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + + static public void setSystemLookAndFeel () { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ex) { + System.err.println("Error setting look and feel:"); + ex.printStackTrace(); + } + } + + static public void setDefaultFont (Font font) { + for (Object key : Collections.list(UIManager.getDefaults().keys())) { + Object value = UIManager.get(key); + if (value instanceof javax.swing.plaf.FontUIResource) UIManager.put(key, font); + } + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Util.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Util.java new file mode 100644 index 0000000000..eea4cd9695 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Util.java @@ -0,0 +1,100 @@ +package org.mage.card.arcane; + +import java.awt.AWTException; +import java.awt.Robot; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Collections; +import java.util.Enumeration; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.swing.SwingUtilities; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class Util { + static public final boolean isMac = System.getProperty("os.name").toLowerCase().indexOf("mac") != -1; + static public final boolean isWindows = System.getProperty("os.name").toLowerCase().indexOf("windows") == -1; + + static public Robot robot; + static { + try { + new Robot(); + } catch (AWTException ex) { + throw new RuntimeException("Error creating robot.", ex); + } + } + + static public ThreadPoolExecutor threadPool; + static private int threadCount; + static { + threadPool = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() { + public Thread newThread (Runnable runnable) { + threadCount++; + Thread thread = new Thread(runnable, "Util" + threadCount); + thread.setDaemon(true); + return thread; + } + }); + threadPool.prestartAllCoreThreads(); + } + + public static void broadcast (byte[] data, int port) throws IOException { + DatagramSocket socket = new DatagramSocket(); + broadcast(socket, data, port, NetworkInterface.getNetworkInterfaces()); + socket.close(); + } + + private static void broadcast (DatagramSocket socket, byte[] data, int port, Enumeration ifaces) + throws IOException { + for (NetworkInterface iface : Collections.list(ifaces)) { + for (InetAddress address : Collections.list(iface.getInetAddresses())) { + if (!address.isSiteLocalAddress()) continue; + // Java 1.5 doesn't support getting the subnet mask, so try the two most common. + byte[] ip = address.getAddress(); + ip[3] = -1; // 255.255.255.0 + socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port)); + ip[2] = -1; // 255.255.0.0 + socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port)); + } + } + } + + static public void sleep (int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ignored) { + } + } + + static public boolean classExists (String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException ex) { + return false; + } + } + + static public void wait (Object lock) { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException ex) { + } + } + } + + public static void invokeAndWait (Runnable runnable) { + try { + SwingUtilities.invokeAndWait(runnable); + } catch (Exception ex) { + throw new RuntimeException("Error invoking runnable in UI thread.", ex); + } + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/constants/Constants.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/constants/Constants.java new file mode 100644 index 0000000000..15df8302d2 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/constants/Constants.java @@ -0,0 +1,17 @@ +package org.mage.card.constants; + +public class Constants { + public static final String RESOURCE_PATH = "/images"; + public static final String RESOURCE_PATH_MANA = resourcePath("mana"); + + + /** + * Build resource path. + * + * @param folder + * @return + */ + private static String resourcePath(String folder) { + return RESOURCE_PATH + "/" + folder; + } +}