mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
Moved card plugin to client. Fixed Issue 143.
This commit is contained in:
parent
0892489229
commit
14bebea64d
55 changed files with 7 additions and 90 deletions
|
|
@ -22,6 +22,7 @@ import mage.view.PermanentView;
|
|||
import net.xeoh.plugins.base.PluginManager;
|
||||
import net.xeoh.plugins.base.impl.PluginManagerFactory;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.CardPluginImpl;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
|
@ -51,7 +52,7 @@ public class Plugins implements MagePlugins {
|
|||
logger.info("Loading plugins...");
|
||||
pm = PluginManagerFactory.createPluginManager();
|
||||
pm.addPluginsFrom(new File(Constants.PLUGINS_DIRECTORY).toURI());
|
||||
this.cardPlugin = pm.getPlugin(CardPlugin.class);
|
||||
this.cardPlugin = new CardPluginImpl();
|
||||
this.counterPlugin = pm.getPlugin(CounterPlugin.class);
|
||||
this.themePlugin = pm.getPlugin(ThemePlugin.class);
|
||||
logger.info("Done.");
|
||||
|
|
|
|||
336
Mage.Client/src/main/java/org/mage/card/arcane/Animation.java
Normal file
336
Mage.Client/src/main/java/org/mage/card/arcane/Animation.java
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
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 mage.cards.MagePermanent;
|
||||
|
||||
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 MagePermanent parent, final boolean tapped, final boolean flipped) {
|
||||
new Animation(300) {
|
||||
protected void start () {
|
||||
parent.onBeginAnimation();
|
||||
}
|
||||
|
||||
protected void update (float percentage) {
|
||||
if (tapped) {
|
||||
panel.tappedAngle = CardPanel.TAPPED_ANGLE * percentage;
|
||||
// reverse movement if untapping
|
||||
if (!panel.isTapped()) panel.tappedAngle = CardPanel.TAPPED_ANGLE - panel.tappedAngle;
|
||||
}
|
||||
if (flipped) {
|
||||
panel.flippedAngle = CardPanel.FLIPPED_ANGLE * percentage;
|
||||
if (!panel.isFlipped()) panel.flippedAngle = CardPanel.FLIPPED_ANGLE - panel.flippedAngle;
|
||||
}
|
||||
panel.repaint();
|
||||
}
|
||||
|
||||
protected void end () {
|
||||
if (tapped) panel.tappedAngle = panel.isTapped() ? CardPanel.TAPPED_ANGLE : 0;
|
||||
if (flipped) panel.flippedAngle = panel.isFlipped() ? CardPanel.FLIPPED_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 MagePermanent card, int count) {
|
||||
if (count == 0) {
|
||||
count = 1;
|
||||
}
|
||||
new Animation(600 / count) {
|
||||
protected void start () {
|
||||
}
|
||||
|
||||
protected void update (float percentage) {
|
||||
float alpha = percentage;
|
||||
card.setAlpha(alpha);
|
||||
card.repaint();
|
||||
}
|
||||
|
||||
protected void end () {
|
||||
card.setAlpha(1.f);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static public void hideCard(final MagePermanent card, int count) {
|
||||
if (count == 0) {
|
||||
count = 1;
|
||||
}
|
||||
new Animation(600 / count) {
|
||||
protected void start () {
|
||||
}
|
||||
|
||||
protected void update (float percentage) {
|
||||
float alpha = 1 - percentage;
|
||||
card.setAlpha(alpha);
|
||||
card.repaint();
|
||||
}
|
||||
|
||||
protected void end () {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
764
Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java
Normal file
764
Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
package org.mage.card.arcane;
|
||||
|
||||
import mage.Constants.CardType;
|
||||
import mage.cards.MagePermanent;
|
||||
import mage.cards.TextPopup;
|
||||
import mage.cards.action.ActionCallback;
|
||||
import mage.cards.action.TransferData;
|
||||
import mage.components.ImagePanel;
|
||||
import mage.utils.CardUtil;
|
||||
import mage.view.AbilityView;
|
||||
import mage.view.CardView;
|
||||
import mage.view.PermanentView;
|
||||
import mage.view.StackAbilityView;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.card.arcane.ScaledImagePanel.MultipassType;
|
||||
import org.mage.card.arcane.ScaledImagePanel.ScalingType;
|
||||
import org.mage.plugins.card.images.ImageCache;
|
||||
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Main class for drawing Mage card object.
|
||||
*
|
||||
* @author arcane, nantuko
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class CardPanel extends MagePermanent implements MouseListener, MouseMotionListener {
|
||||
private static final long serialVersionUID = -3272134219262184410L;
|
||||
|
||||
private static final Logger log = Logger.getLogger(CardPanel.class);
|
||||
|
||||
static public final double TAPPED_ANGLE = Math.PI / 2;
|
||||
static public final double FLIPPED_ANGLE = Math.PI;
|
||||
static public final float ASPECT_RATIO = 3.5f / 2.5f;
|
||||
static public final int POPUP_X_GAP = 1; // prevent popup window from blinking
|
||||
//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;
|
||||
|
||||
static private final int DEFAULT_DELAY_PERIOD = 300;
|
||||
|
||||
public CardView gameCard;
|
||||
//public List<CardPanel> attachedPanels = new ArrayList();
|
||||
private List<MagePermanent> links = new ArrayList<MagePermanent>();
|
||||
public double tappedAngle = 0;
|
||||
public double flippedAngle = 0;
|
||||
public ScaledImagePanel imagePanel;
|
||||
public ImagePanel overlayPanel;
|
||||
|
||||
private GlowText titleText;
|
||||
private GlowText ptText;
|
||||
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;
|
||||
|
||||
private ActionCallback callback;
|
||||
|
||||
protected boolean popupShowing;
|
||||
protected TextPopup popupText = new TextPopup();
|
||||
protected UUID gameId;
|
||||
private TransferData data = new TransferData();
|
||||
|
||||
private boolean isPermanent;
|
||||
private boolean hasSickness;
|
||||
private boolean isFoil;
|
||||
private String zone;
|
||||
|
||||
public CardPanel(CardView newGameCard, UUID gameId, boolean loadImage, ActionCallback callback, final boolean foil) {
|
||||
this.gameCard = newGameCard;
|
||||
this.callback = callback;
|
||||
this.gameId = gameId;
|
||||
this.isPermanent = this.gameCard instanceof PermanentView;
|
||||
|
||||
if (isPermanent) {
|
||||
this.hasSickness = ((PermanentView) this.gameCard).hasSummoningSickness();
|
||||
}
|
||||
|
||||
//for container debug (don't remove)
|
||||
//setBorder(BorderFactory.createLineBorder(Color.green));
|
||||
|
||||
setBackground(Color.black);
|
||||
setOpaque(false);
|
||||
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
|
||||
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.getLoyalty());
|
||||
}
|
||||
ptText.setFont(getFont().deriveFont(Font.BOLD, 13f));
|
||||
ptText.setForeground(Color.white);
|
||||
ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY);
|
||||
add(ptText);
|
||||
|
||||
BufferedImage sickness = ImageManagerImpl.getInstance().getSicknessImage();
|
||||
overlayPanel = new ImagePanel(sickness, ImagePanel.SCALED);
|
||||
overlayPanel.setOpaque(false);
|
||||
add(overlayPanel);
|
||||
|
||||
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);
|
||||
|
||||
String cardType = getType(newGameCard);
|
||||
popupText.setText(getText(cardType, newGameCard));
|
||||
|
||||
if (!loadImage) return;
|
||||
|
||||
Util.threadPool.submit(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
|
||||
flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
|
||||
if (gameCard.isFaceDown()) return;
|
||||
BufferedImage srcImage = ImageCache.getThumbnail(gameCard);
|
||||
if (srcImage != null) {
|
||||
hasImage = true;
|
||||
setText(gameCard);
|
||||
setImage(srcImage);
|
||||
setFoil(foil);
|
||||
} else {
|
||||
//log.warn("image wasn't found, card=" + gameCard.getName() + ", set=" + gameCard.getExpansionSetCode() + ", cid=" + gameCard.getCardNumber());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Error err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setText(CardView card) {
|
||||
if (hasImage) {
|
||||
titleText.setText("");
|
||||
} else {
|
||||
titleText.setText(card.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void setImage(Image srcImage) {
|
||||
synchronized (imagePanel) {
|
||||
imagePanel.setImage(srcImage);
|
||||
repaint();
|
||||
}
|
||||
layout();
|
||||
}
|
||||
|
||||
public void setImage(final CardPanel panel) {
|
||||
synchronized (panel.imagePanel) {
|
||||
if (panel.imagePanel.hasImage()) {
|
||||
setImage(panel.imagePanel.srcImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFoil() {
|
||||
return this.isFoil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(String zone) {
|
||||
this.zone = zone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getZone() {
|
||||
return zone;
|
||||
}
|
||||
|
||||
public void setFoil(boolean foil) {
|
||||
this.isFoil = foil;
|
||||
if (foil) {
|
||||
/*BufferedImage source = BufferedImageBuilder.bufferImage(imagePanel.getSrcImage());
|
||||
HueFilter filter = FilterFactory.getHueFilter();
|
||||
filter.setHue(0.1f);
|
||||
BufferedImage dest = filter.filter(source, null);
|
||||
imagePanel.setImage(dest);
|
||||
imagePanel.repaint();*/
|
||||
|
||||
/*
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (imagePanel.getSrcImage() == null) {
|
||||
return;
|
||||
}
|
||||
BufferedImage source = BufferedImageBuilder.bufferImage(imagePanel.getSrcImage());
|
||||
HueFilter filter = FilterFactory.getHueFilter();
|
||||
float hue = 0.005f;
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(DEFAULT_DELAY_PERIOD);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
hue += 0.015F;
|
||||
if (hue >= 1.0D) {
|
||||
hue = 0.005F;
|
||||
}
|
||||
filter.setHue(hue);
|
||||
final BufferedImage dest = filter.filter(source, null);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
imagePanel.setImage(dest);
|
||||
imagePanel.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(false);
|
||||
thread.start();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
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 + flippedAngle > 0) {
|
||||
g2d = (Graphics2D) g2d.create();
|
||||
float edgeOffset = cardWidth / 2f;
|
||||
g2d.rotate(tappedAngle+flippedAngle, 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));
|
||||
}
|
||||
|
||||
//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);
|
||||
}*/
|
||||
|
||||
|
||||
//for debugging
|
||||
// REMOVEME
|
||||
/*
|
||||
Point component = getLocation();
|
||||
|
||||
int cx = getCardX() + component.x;
|
||||
int cy = getCardY() + component.y;
|
||||
int cw = getCardWidth();
|
||||
int ch = getCardHeight();
|
||||
|
||||
g2d.setColor(Color.white);
|
||||
g2d.drawRect(getCardX() - component.x, getCardY() - component.y, cw, ch);
|
||||
*/
|
||||
}
|
||||
|
||||
protected void paintChildren(Graphics g) {
|
||||
super.paintChildren(g);
|
||||
|
||||
if (showCastingCost && !isAnimationPanel && cardWidth < 200 && cardWidth > 60) {
|
||||
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
|
||||
int width = ManaSymbols.getWidth(manaCost);
|
||||
if (hasImage) {
|
||||
ManaSymbols.draw(g, manaCost, cardXOffset + cardWidth - width - 5, cardYOffset + 5);
|
||||
} else {
|
||||
ManaSymbols.draw(g, manaCost, 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);
|
||||
|
||||
if (hasSickness && CardUtil.isCreature(gameCard) && isPermanent) {
|
||||
overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
|
||||
overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
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 CardView getCard() {
|
||||
return this.gameCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(float alpha) {
|
||||
this.alpha = alpha;
|
||||
if (alpha == 0) {
|
||||
this.ptText.setVisible(false);
|
||||
this.titleText.setVisible(false);
|
||||
} else if (alpha == 1.0f) {
|
||||
this.ptText.setVisible(true);
|
||||
this.titleText.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
public int getCardXOffset() {
|
||||
return cardXOffset;
|
||||
}
|
||||
|
||||
public int getCardYOffset() {
|
||||
return cardYOffset;
|
||||
}
|
||||
|
||||
public void updateImage() {
|
||||
if (!hasImage) {
|
||||
throw new IllegalStateException("Not implemented");
|
||||
//TODO:
|
||||
/*Util.threadPool.submit(new Runnable() {
|
||||
public void run () {
|
||||
//BufferedImage srcImage = ImageCache.getImageOriginal(gameCard);
|
||||
//BufferedImage srcImage = null;
|
||||
//tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
|
||||
if (srcImage != null) {
|
||||
hasImage = true;
|
||||
setText(gameCard);
|
||||
setImage(srcImage, srcImage);
|
||||
}
|
||||
}
|
||||
});*/
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MagePermanent> getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTapped() {
|
||||
if (isPermanent) {
|
||||
return ((PermanentView) gameCard).isTapped();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlipped() {
|
||||
if (isPermanent) {
|
||||
return ((PermanentView) gameCard).isFlipped();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginAnimation() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndAnimation() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(CardView card) {
|
||||
if (isPermanent) {
|
||||
boolean needsTapping = isTapped() != ((PermanentView) card).isTapped();
|
||||
boolean needsFlipping = isFlipped() != ((PermanentView) card).isFlipped();
|
||||
if (needsTapping || needsFlipping) {
|
||||
Animation.tapCardToggle(this, this, needsTapping, needsFlipping);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
String cardType = getType(card);
|
||||
popupText.setText(getText(cardType, card));
|
||||
|
||||
if (hasSickness && CardUtil.isCreature(gameCard) && isPermanent) {
|
||||
overlayPanel.setVisible(true);
|
||||
} else {
|
||||
overlayPanel.setVisible(false);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(int x, int y) {
|
||||
if (containsThis(x, y, true)) return true;
|
||||
|
||||
/*
|
||||
* if (attachedCount > 0) { for (MWCardImpl card :
|
||||
* mwAttachedCards.keySet()) { if (card.contains(x, y)) return true; } }
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsThis(int x, int y, boolean root) {
|
||||
//log.info("x="+x+", y="+y);
|
||||
Point component = getLocation();
|
||||
|
||||
//int dy = component.y;
|
||||
//if (root) dy = 0;
|
||||
|
||||
int cx = getCardX() - component.x;
|
||||
int cy = getCardY() - component.y;
|
||||
int cw = getCardWidth();
|
||||
int ch = getCardHeight();
|
||||
if (isTapped()) {
|
||||
cy = ch - cw + cx /*+ attachedDy*attachedCount*/;
|
||||
ch = cw;
|
||||
cw = getCardHeight();
|
||||
}
|
||||
//int dx = drawIcons ? 19 : 0;
|
||||
//int dx = 0;
|
||||
|
||||
if (x >= cx && x <= cx + cw && y >= cy && y <= cy + ch) {
|
||||
//log.info("!cx="+cx+", cy="+cy+", dx="+cw +", ch="+ch);
|
||||
//log.info(getOriginal().getId());
|
||||
return true;
|
||||
} else {
|
||||
//log.info("cx="+cx+", cy="+cy+", dx="+cw +", ch="+ch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardView getOriginal() {
|
||||
return this.gameCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage() {
|
||||
if (this.hasImage)
|
||||
return ImageCache.getImageOriginal(gameCard);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
if (gameCard.isFaceDown()) return;
|
||||
if (!popupShowing) {
|
||||
synchronized (this) {
|
||||
if (!popupShowing) {
|
||||
popupShowing = true;
|
||||
callback.mouseEntered(e, getTransferDataForMouseEntered());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
if (gameCard.isFaceDown()) return;
|
||||
data.component = this;
|
||||
callback.mouseMoved(e, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
if (gameCard.isFaceDown()) return;
|
||||
if (getMousePosition(true) != null) return;
|
||||
if (popupShowing) {
|
||||
synchronized (this) {
|
||||
if (popupShowing) {
|
||||
popupShowing = false;
|
||||
data.component = this;
|
||||
data.card = this.gameCard;
|
||||
data.popupText = popupText;
|
||||
callback.mouseExited(e, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (gameCard.isFaceDown()) return;
|
||||
data.component = this;
|
||||
data.card = this.gameCard;
|
||||
data.gameId = this.gameId;
|
||||
callback.mousePressed(e, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares data to be sent to action callback on client side.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private TransferData getTransferDataForMouseEntered() {
|
||||
data.component = this;
|
||||
data.card = this.gameCard;
|
||||
data.popupText = popupText;
|
||||
data.gameId = this.gameId;
|
||||
data.popupOffsetX = isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP;
|
||||
data.popupOffsetY = 40;
|
||||
data.locationOnScreen = this.getLocationOnScreen();
|
||||
return data;
|
||||
}
|
||||
|
||||
protected String getType(CardView card) {
|
||||
StringBuilder sbType = new StringBuilder();
|
||||
|
||||
for (String superType : card.getSuperTypes()) {
|
||||
sbType.append(superType).append(" ");
|
||||
}
|
||||
|
||||
for (mage.Constants.CardType cardType : card.getCardTypes()) {
|
||||
sbType.append(cardType.toString()).append(" ");
|
||||
}
|
||||
|
||||
if (card.getSubTypes().size() > 0) {
|
||||
sbType.append("- ");
|
||||
for (String subType : card.getSubTypes()) {
|
||||
sbType.append(subType).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
return sbType.toString();
|
||||
}
|
||||
|
||||
protected String getText(String cardType, CardView card) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (card instanceof StackAbilityView || card instanceof AbilityView) {
|
||||
for (String rule : card.getRules()) {
|
||||
sb.append("\n").append(rule);
|
||||
}
|
||||
} else {
|
||||
sb.append(card.getName());
|
||||
if (card.getManaCost().size() > 0) {
|
||||
sb.append("\n").append(card.getManaCost());
|
||||
}
|
||||
sb.append("\n").append(cardType);
|
||||
if (card.getColor().hasColor()) {
|
||||
sb.append("\n").append(card.getColor().toString());
|
||||
}
|
||||
if (card.getCardTypes().contains(CardType.CREATURE)) {
|
||||
sb.append("\n").append(card.getPower()).append("/").append(card.getToughness());
|
||||
} else if (card.getCardTypes().contains(CardType.PLANESWALKER)) {
|
||||
sb.append("\n").append(card.getLoyalty());
|
||||
}
|
||||
for (String rule : card.getRules()) {
|
||||
sb.append("\n").append(rule);
|
||||
}
|
||||
if (card.getExpansionSetCode() != null && card.getExpansionSetCode().length() > 0) {
|
||||
sb.append("\n").append(card.getCardNumber()).append(" - ");
|
||||
//sb.append(Sets.getInstance().get(card.getExpansionSetCode()).getName()).append(" - ");
|
||||
sb.append(card.getExpansionSetCode()).append(" - ");
|
||||
sb.append(card.getRarity().toString());
|
||||
}
|
||||
}
|
||||
// sb.append("\n").append(card.getId());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(PermanentView card) {
|
||||
update((CardView) card);
|
||||
this.hasSickness = card.hasSummoningSickness();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermanentView getOriginalPermanent() {
|
||||
throw new IllegalStateException("Is not permanent.");
|
||||
}
|
||||
}
|
||||
102
Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java
Normal file
102
Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
129
Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
Normal file
129
Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package org.mage.card.arcane;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.images.ImageCache;
|
||||
import org.mage.plugins.card.utils.BufferedImageBuilder;
|
||||
|
||||
|
||||
public class ManaSymbols {
|
||||
private static final Logger log = Logger.getLogger(ManaSymbols.class);
|
||||
static private final Map<String, Image> manaImages = new HashMap<String, Image>();
|
||||
static private final Map<String, Image> manaImagesOriginal = new HashMap<String, Image>();
|
||||
static private final Map<String, Dimension> setImagesExist = new HashMap<String, Dimension>();
|
||||
static private Pattern replaceSymbolsPattern = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
|
||||
|
||||
private static final String[] sets = {"DIS", "GPT", "RAV", "MRD",
|
||||
"10E", "HOP", "ALA", "CFX", "ARB", "ZEN", "WWK", "ROE", "SOM", "M10", "M11", "M12",
|
||||
"MBS", "DDF", "DST", "SHM", "EVE", "APC", "NPH", "TMP", "CHK"};
|
||||
|
||||
|
||||
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",
|
||||
"WP", "UP", "BP", "RP", "GP", "X", "Y", "Z", "slash"};
|
||||
for (String symbol : symbols) {
|
||||
File file = new File(Constants.RESOURCE_PATH_MANA_MEDIUM + "/" + symbol + ".jpg");
|
||||
Rectangle r = new Rectangle(11, 11);
|
||||
try {
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
BufferedImage resized = ImageCache.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||
manaImages.put(symbol, resized);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
file = new File(Constants.RESOURCE_PATH_MANA_MEDIUM + "/" + symbol + ".jpg");
|
||||
try {
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
manaImagesOriginal.put(symbol, image);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
File file;
|
||||
for (String set : sets) {
|
||||
file = new File(Constants.RESOURCE_PATH_SET_SMALL);
|
||||
if (!file.exists()) {
|
||||
break;
|
||||
}
|
||||
file = new File(Constants.RESOURCE_PATH_SET_SMALL + set + "-C.png");
|
||||
try {
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
int width = image.getWidth(null);
|
||||
int height = image.getHeight(null);
|
||||
setImagesExist.put(set, new Dimension(width, height));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public Image getManaSymbolImage(String symbol) {
|
||||
return manaImagesOriginal.get(symbol);
|
||||
}
|
||||
|
||||
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 String getStringManaCost(List<String> manaCost) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : manaCost) {
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString().replace("{", "").replace("}", " ").trim();
|
||||
}
|
||||
|
||||
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("<img src='file:plugins/images/symbols/small/$1$2.jpg' alt='$1$2' width=11 height=11>");
|
||||
else {
|
||||
value = value.replace("{slash}", "<img src='file:plugins/images/symbols/medium/slash.jpg' alt='slash' width=10 height=13>");
|
||||
return replaceSymbolsPattern.matcher(value).replaceAll("<img src='file:plugins/images/symbols/medium/$1$2.jpg' alt='$1$2' width=13 height=13>");
|
||||
}
|
||||
}
|
||||
|
||||
static public String replaceSetCodeWithHTML(String set, String rarity) {
|
||||
String _set = set;
|
||||
if (_set.equals("CON")) {
|
||||
_set = "CFX";
|
||||
}
|
||||
if (setImagesExist.containsKey(_set)) {
|
||||
Integer width = setImagesExist.get(_set).width;
|
||||
Integer height = setImagesExist.get(_set).height;
|
||||
return "<img src='file:plugins/images/sets/small/" + _set + "-" + rarity + ".png' alt='" + rarity + " ' width=" + width + " height=" + height + ">";
|
||||
} else {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
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) {
|
||||
this.srcImage = srcImage;
|
||||
}
|
||||
|
||||
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) {
|
||||
return srcImage;
|
||||
//if (!blur || srcImageBlurred == null) return srcImage;
|
||||
//if (info.srcWidth / 2 < info.targetWidth || info.srcHeight / 2 < info.targetHeight) return srcImage;
|
||||
//return srcImageBlurred;
|
||||
}
|
||||
|
||||
public Image getSrcImage() {
|
||||
return srcImage;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
182
Mage.Client/src/main/java/org/mage/card/arcane/UI.java
Normal file
182
Mage.Client/src/main/java/org/mage/card/arcane/UI.java
Normal file
|
|
@ -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<URL, Image> imageCache = new Hashtable<URL, Image>();
|
||||
|
||||
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("/", "");
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Mage.Client/src/main/java/org/mage/card/arcane/Util.java
Normal file
100
Mage.Client/src/main/java/org/mage/card/arcane/Util.java
Normal file
|
|
@ -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<NetworkInterface> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,510 @@
|
|||
package org.mage.plugins.card;
|
||||
|
||||
import mage.cards.Card;
|
||||
import mage.cards.MagePermanent;
|
||||
import mage.cards.action.ActionCallback;
|
||||
import mage.interfaces.plugin.CardPlugin;
|
||||
import mage.utils.CardUtil;
|
||||
import mage.view.CardView;
|
||||
import mage.view.PermanentView;
|
||||
import net.xeoh.plugins.base.annotations.PluginImplementation;
|
||||
import net.xeoh.plugins.base.annotations.events.Init;
|
||||
import net.xeoh.plugins.base.annotations.events.PluginLoaded;
|
||||
import net.xeoh.plugins.base.annotations.meta.Author;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.card.arcane.Animation;
|
||||
import org.mage.card.arcane.CardPanel;
|
||||
import org.mage.card.arcane.ManaSymbols;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.dl.DownloadGui;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import org.mage.plugins.card.dl.Downloader;
|
||||
import org.mage.plugins.card.dl.sources.GathererSets;
|
||||
import org.mage.plugins.card.dl.sources.GathererSymbols;
|
||||
import org.mage.plugins.card.images.DownloadPictures;
|
||||
import org.mage.plugins.card.images.ImageCache;
|
||||
import org.mage.plugins.card.info.CardInfoPaneImpl;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link CardPlugin} implementation.
|
||||
*
|
||||
* @author nantuko
|
||||
* @version 0.1 01.11.2010 Mage permanents. Sorting card layout.
|
||||
* @version 0.6 17.07.2011 #sortPermanents got option to display non-land permanents in one pile
|
||||
* @version 0.7 29.07.2011 face down cards support
|
||||
*/
|
||||
@PluginImplementation
|
||||
@Author(name = "nantuko")
|
||||
public class CardPluginImpl implements CardPlugin {
|
||||
|
||||
private final static Logger log = Logger.getLogger(CardPluginImpl.class);
|
||||
|
||||
static private final int GUTTER_Y = 15;
|
||||
static private final int GUTTER_X = 5;
|
||||
static final float EXTRA_CARD_SPACING_X = 0.04f;
|
||||
static private final float CARD_SPACING_Y = 0.03f;
|
||||
static private final float STACK_SPACING_X = 0.07f;
|
||||
static private final float STACK_SPACING_Y = 0.13f;
|
||||
//static private final int MW_GUIDE_HEIGHT = 30;
|
||||
|
||||
private int landStackMax = 5;
|
||||
private int cardWidthMin = 50, cardWidthMax = Constants.CARD_SIZE_FULL.width;
|
||||
private boolean stackVertical = false;
|
||||
|
||||
private int playAreaWidth, playAreaHeight;
|
||||
private int cardWidth, cardHeight;
|
||||
private int extraCardSpacingX, cardSpacingX, cardSpacingY;
|
||||
private int stackSpacingX, stackSpacingY;
|
||||
private List<Row> rows = new ArrayList<Row>();
|
||||
|
||||
@Init
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@PluginLoaded
|
||||
public void newPlugin(CardPlugin plugin) {
|
||||
ManaSymbols.loadImages();
|
||||
log.info(plugin.toString() + " has been loaded.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Card plugin, version 0.7]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) {
|
||||
boolean foil = canBeFoil && (new Random()).nextInt(5) == 0;
|
||||
CardPanel cardPanel = new CardPanel(permanent, gameId, loadImage, callback, foil);
|
||||
cardPanel.setCardBounds(0, 0, dimension.width, dimension.height);
|
||||
boolean implemented = !permanent.getRarity().equals(mage.Constants.Rarity.NA);
|
||||
cardPanel.setShowCastingCost(implemented);
|
||||
return cardPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagePermanent getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) {
|
||||
boolean foil = canBeFoil && (new Random()).nextInt(5) == 0;
|
||||
CardPanel cardPanel = new CardPanel(permanent, gameId, loadImage, callback, foil);
|
||||
cardPanel.setCardBounds(0, 0, dimension.width, dimension.height);
|
||||
boolean implemented = !permanent.getRarity().equals(mage.Constants.Rarity.NA);
|
||||
cardPanel.setShowCastingCost(implemented);
|
||||
return cardPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents, Map<String, String> options) {
|
||||
//TODO: add caching
|
||||
//requires to find out is position have been changed that includes:
|
||||
//adding/removing permanents, type change
|
||||
|
||||
if (ui == null)
|
||||
throw new RuntimeException("Error: no components");
|
||||
JComponent component = ui.get("jScrollPane");
|
||||
JComponent component2 = ui.get("battlefieldPanel");
|
||||
if (component == null)
|
||||
throw new RuntimeException("Error: jScrollPane is missing");
|
||||
if (component2 == null)
|
||||
throw new RuntimeException("Error: battlefieldPanel is missing");
|
||||
if (!(component instanceof JScrollPane))
|
||||
throw new RuntimeException("Error: jScrollPane has wrong type.");
|
||||
if (!(component instanceof JScrollPane))
|
||||
throw new RuntimeException("Error: battlefieldPanel is missing");
|
||||
|
||||
JScrollPane jScrollPane = (JScrollPane) component;
|
||||
JLayeredPane battlefieldPanel = (JLayeredPane) component2;
|
||||
|
||||
Row allLands = new Row();
|
||||
|
||||
outerLoop:
|
||||
//
|
||||
for (MagePermanent permanent : permanents) {
|
||||
if (!CardUtil.isLand(permanent) || CardUtil.isCreature(permanent))
|
||||
continue;
|
||||
|
||||
int insertIndex = -1;
|
||||
|
||||
// Find lands with the same name.
|
||||
for (int i = 0, n = allLands.size(); i < n; i++) {
|
||||
Stack stack = allLands.get(i);
|
||||
MagePermanent firstPanel = stack.get(0);
|
||||
if (firstPanel.getOriginal().getName().equals(permanent.getOriginal().getName())) {
|
||||
if (!empty(firstPanel.getLinks())) {
|
||||
// Put this land to the left of lands with the same name and attachments.
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
if (!empty(permanent.getLinks()) || stack.size() == landStackMax) {
|
||||
// If this land has attachments or the stack is full, put it to the right.
|
||||
insertIndex = i + 1;
|
||||
continue;
|
||||
}
|
||||
// Add to stack.
|
||||
stack.add(0, permanent);
|
||||
continue outerLoop;
|
||||
}
|
||||
if (insertIndex != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
Stack stack = new Stack();
|
||||
stack.add(permanent);
|
||||
allLands.add(insertIndex == -1 ? allLands.size() : insertIndex, stack);
|
||||
}
|
||||
|
||||
Row allCreatures = new Row(permanents, RowType.creature);
|
||||
Row allOthers = new Row(permanents, RowType.other);
|
||||
|
||||
boolean othersOnTheRight = true;
|
||||
if (options != null && options.containsKey("nonLandPermanentsInOnePile")) {
|
||||
if (options.get("nonLandPermanentsInOnePile").equals("true")) {
|
||||
System.out.println("in one pile");
|
||||
othersOnTheRight = false;
|
||||
allCreatures.addAll(allOthers);
|
||||
allOthers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
cardWidth = cardWidthMax;
|
||||
Rectangle rect = jScrollPane.getVisibleRect();
|
||||
playAreaWidth = rect.width;
|
||||
playAreaHeight = rect.height;
|
||||
while (true) {
|
||||
rows.clear();
|
||||
cardHeight = Math.round(cardWidth * CardPanel.ASPECT_RATIO);
|
||||
extraCardSpacingX = (int) Math.round(cardWidth * EXTRA_CARD_SPACING_X);
|
||||
cardSpacingX = cardHeight - cardWidth + extraCardSpacingX;
|
||||
cardSpacingY = (int) Math.round(cardHeight * CARD_SPACING_Y);
|
||||
stackSpacingX = stackVertical ? 0 : (int) Math.round(cardWidth * STACK_SPACING_X);
|
||||
stackSpacingY = (int) Math.round(cardHeight * STACK_SPACING_Y);
|
||||
Row creatures = (Row) allCreatures.clone();
|
||||
Row lands = (Row) allLands.clone();
|
||||
Row others = (Row) allOthers.clone();
|
||||
// Wrap all creatures and lands.
|
||||
wrap(creatures, rows, -1);
|
||||
int afterCreaturesIndex = rows.size();
|
||||
wrap(lands, rows, afterCreaturesIndex);
|
||||
// Store the current rows and others.
|
||||
List<Row> storedRows = new ArrayList<Row>(rows.size());
|
||||
for (Row row : rows)
|
||||
storedRows.add((Row) row.clone());
|
||||
Row storedOthers = (Row) others.clone();
|
||||
// Fill in all rows with others.
|
||||
for (Row row : rows)
|
||||
fillRow(others, rows, row);
|
||||
|
||||
// Stop if everything fits, otherwise revert back to the stored values.
|
||||
if (creatures.isEmpty() && lands.isEmpty() && others.isEmpty())
|
||||
break;
|
||||
rows = storedRows;
|
||||
others = storedOthers;
|
||||
// Try to put others on their own row(s) and fill in the rest.
|
||||
wrap(others, rows, afterCreaturesIndex);
|
||||
for (Row row : rows)
|
||||
fillRow(others, rows, row);
|
||||
// If that still doesn't fit, scale down.
|
||||
if (creatures.isEmpty() && lands.isEmpty() && others.isEmpty())
|
||||
break;
|
||||
//cardWidth = (int)(cardWidth / 1.2);
|
||||
//FIXME: -1 is too slow. why not binary search?
|
||||
cardWidth--;
|
||||
}
|
||||
|
||||
// Get size of all the rows.
|
||||
int x, y = GUTTER_Y;
|
||||
int maxRowWidth = 0;
|
||||
for (Row row : rows) {
|
||||
int rowBottom = 0;
|
||||
x = GUTTER_X;
|
||||
for (int stackIndex = 0, stackCount = row.size(); stackIndex < stackCount; stackIndex++) {
|
||||
Stack stack = row.get(stackIndex);
|
||||
rowBottom = Math.max(rowBottom, y + stack.getHeight());
|
||||
x += stack.getWidth();
|
||||
}
|
||||
y = rowBottom;
|
||||
maxRowWidth = Math.max(maxRowWidth, x);
|
||||
}
|
||||
//setPreferredSize(new Dimension(maxRowWidth - cardSpacingX, y - cardSpacingY));
|
||||
//revalidate();
|
||||
|
||||
// Position all card panels.
|
||||
x = 0;
|
||||
y = GUTTER_Y;
|
||||
for (Row row : rows) {
|
||||
int rowBottom = 0;
|
||||
x = GUTTER_X;
|
||||
for (int stackIndex = 0, stackCount = row.size(); stackIndex < stackCount; stackIndex++) {
|
||||
Stack stack = row.get(stackIndex);
|
||||
// Align others to the right.
|
||||
if (othersOnTheRight && RowType.other.isType(stack.get(0))) {
|
||||
x = playAreaWidth - GUTTER_X + extraCardSpacingX;
|
||||
for (int i = stackIndex, n = row.size(); i < n; i++)
|
||||
x -= row.get(i).getWidth();
|
||||
}
|
||||
for (int panelIndex = 0, panelCount = stack.size(); panelIndex < panelCount; panelIndex++) {
|
||||
MagePermanent panel = stack.get(panelIndex);
|
||||
int stackPosition = panelCount - panelIndex - 1;
|
||||
///setComponentZOrder((Component)panel, panelIndex);
|
||||
int panelX = x + (stackPosition * stackSpacingX);
|
||||
int panelY = y + (stackPosition * stackSpacingY);
|
||||
//panel.setLocation(panelX, panelY);
|
||||
try {
|
||||
// may cause:
|
||||
// java.lang.IllegalArgumentException: illegal component position 26 should be less then 26
|
||||
battlefieldPanel.moveToBack(panel);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
panel.setCardBounds(panelX, panelY, cardWidth, cardHeight);
|
||||
}
|
||||
rowBottom = Math.max(rowBottom, y + stack.getHeight());
|
||||
x += stack.getWidth();
|
||||
}
|
||||
y = rowBottom;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean empty(List<?> list) {
|
||||
return list == null || list.isEmpty();
|
||||
}
|
||||
|
||||
private int wrap(Row sourceRow, List<Row> rows, int insertIndex) {
|
||||
// The cards are sure to fit (with vertical scrolling) at the minimum card width.
|
||||
boolean allowHeightOverflow = cardWidth == cardWidthMin;
|
||||
|
||||
Row currentRow = new Row();
|
||||
for (int i = 0, n = sourceRow.size() - 1; i <= n; i++) {
|
||||
Stack stack = sourceRow.get(i);
|
||||
// If the row is not empty and this stack doesn't fit, add the row.
|
||||
int rowWidth = currentRow.getWidth();
|
||||
if (!currentRow.isEmpty() && rowWidth + stack.getWidth() > playAreaWidth) {
|
||||
// Stop processing if the row is too wide or tall.
|
||||
if (!allowHeightOverflow && rowWidth > playAreaWidth)
|
||||
break;
|
||||
if (!allowHeightOverflow && getRowsHeight(rows) + sourceRow.getHeight() > playAreaHeight)
|
||||
break;
|
||||
rows.add(insertIndex == -1 ? rows.size() : insertIndex, currentRow);
|
||||
currentRow = new Row();
|
||||
}
|
||||
currentRow.add(stack);
|
||||
}
|
||||
// Add the last row if it is not empty and it fits.
|
||||
if (!currentRow.isEmpty()) {
|
||||
int rowWidth = currentRow.getWidth();
|
||||
if (allowHeightOverflow || rowWidth <= playAreaWidth) {
|
||||
if (allowHeightOverflow || getRowsHeight(rows) + sourceRow.getHeight() <= playAreaHeight) {
|
||||
rows.add(insertIndex == -1 ? rows.size() : insertIndex, currentRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove the wrapped stacks from the source row.
|
||||
for (Row row : rows)
|
||||
for (Stack stack : row)
|
||||
sourceRow.remove(stack);
|
||||
return insertIndex;
|
||||
}
|
||||
|
||||
private void fillRow(Row sourceRow, List<Row> rows, Row row) {
|
||||
int rowWidth = row.getWidth();
|
||||
while (!sourceRow.isEmpty()) {
|
||||
Stack stack = sourceRow.get(0);
|
||||
rowWidth += stack.getWidth();
|
||||
if (rowWidth > playAreaWidth)
|
||||
break;
|
||||
if (stack.getHeight() > row.getHeight()) {
|
||||
if (getRowsHeight(rows) - row.getHeight() + stack.getHeight() > playAreaHeight)
|
||||
break;
|
||||
}
|
||||
row.add(sourceRow.remove(0));
|
||||
}
|
||||
}
|
||||
|
||||
private int getRowsHeight(List<Row> rows) {
|
||||
int height = 0;
|
||||
for (Row row : rows)
|
||||
height += row.getHeight();
|
||||
return height - cardSpacingY + GUTTER_Y * 2;
|
||||
}
|
||||
|
||||
static private enum RowType {
|
||||
land, creature, other;
|
||||
|
||||
public boolean isType(MagePermanent card) {
|
||||
switch (this) {
|
||||
case land:
|
||||
return CardUtil.isLand(card);
|
||||
case creature:
|
||||
return CardUtil.isCreature(card);
|
||||
case other:
|
||||
return !CardUtil.isLand(card) && !CardUtil.isCreature(card);
|
||||
default:
|
||||
throw new RuntimeException("Unhandled type: " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Row extends ArrayList<Stack> {
|
||||
public Row() {
|
||||
super(16);
|
||||
}
|
||||
|
||||
public Row(Collection<MagePermanent> permanents, RowType type) {
|
||||
this();
|
||||
addAll(permanents, type);
|
||||
}
|
||||
|
||||
private void addAll(Collection<MagePermanent> permanents, RowType type) {
|
||||
for (MagePermanent panel : permanents) {
|
||||
if (!type.isType(panel)) {
|
||||
continue;
|
||||
}
|
||||
Stack stack = new Stack();
|
||||
stack.add(panel);
|
||||
add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Stack> c) {
|
||||
boolean changed = super.addAll(c);
|
||||
c.clear();
|
||||
return changed;
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
if (isEmpty())
|
||||
return 0;
|
||||
int width = 0;
|
||||
for (Stack stack : this)
|
||||
width += stack.getWidth();
|
||||
return width + GUTTER_X * 2 - extraCardSpacingX;
|
||||
}
|
||||
|
||||
private int getHeight() {
|
||||
if (isEmpty())
|
||||
return 0;
|
||||
int height = 0;
|
||||
for (Stack stack : this)
|
||||
height = Math.max(height, stack.getHeight());
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
private class Stack extends ArrayList<MagePermanent> {
|
||||
public Stack() {
|
||||
super(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(MagePermanent panel) {
|
||||
boolean appended = super.add(panel);
|
||||
//for (CardPanel attachedPanel : panel.attachedPanels)
|
||||
//add(attachedPanel);
|
||||
return appended;
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return cardWidth + (size() - 1) * stackSpacingX + cardSpacingX;
|
||||
}
|
||||
|
||||
private int getHeight() {
|
||||
return cardHeight + (size() - 1) * stackSpacingY + cardSpacingY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download images.
|
||||
*
|
||||
* @param allCards Set of cards to download images for.
|
||||
* @param imagesPath Path to check in and store images to. Can be null, in such case default path should be used.
|
||||
*/
|
||||
@Override
|
||||
public void downloadImages(Set<Card> allCards, String imagesPath) {
|
||||
DownloadPictures.startDownload(null, allCards, imagesPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download various symbols (mana, tap, set).
|
||||
*
|
||||
* @param imagesPath Path to check in and store symbols to. Can be null, in such case default path should be used.
|
||||
*/
|
||||
@Override
|
||||
public void downloadSymbols(String imagesPath) {
|
||||
final DownloadGui g = new DownloadGui(new Downloader());
|
||||
|
||||
Iterable<DownloadJob> it = new GathererSymbols(imagesPath);
|
||||
|
||||
for (DownloadJob job : it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
|
||||
it = new GathererSets(imagesPath);
|
||||
for(DownloadJob job:it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
|
||||
JDialog d = new JDialog((Frame) null, "Download pictures", false);
|
||||
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
d.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
g.getDownloader().dispose();
|
||||
}
|
||||
});
|
||||
d.setLayout(new BorderLayout());
|
||||
d.add(g);
|
||||
d.pack();
|
||||
d.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getManaSymbolImage(String symbol) {
|
||||
return ManaSymbols.getManaSymbolImage(symbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddCard(MagePermanent card, int count) {
|
||||
if (card != null) {
|
||||
Animation.showCard((CardPanel) card, count > 0 ? count : 1);
|
||||
try {
|
||||
while ((card).getAlpha() + 0.05f < 1) {
|
||||
Thread.sleep(30);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveCard(MagePermanent card, int count) {
|
||||
if (card != null) {
|
||||
Animation.hideCard(card, count > 0 ? count : 1);
|
||||
try {
|
||||
while ((card).getAlpha() - 0.05f > 0) {
|
||||
Thread.sleep(30);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getCardInfoPane() {
|
||||
return new CardInfoPaneImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage getOriginalImage(CardView card) {
|
||||
return ImageCache.getImageOriginal(card);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.mage.plugins.card.constants;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.io.File;
|
||||
|
||||
public class Constants {
|
||||
public static final String RESOURCE_PATH_MANA_LARGE = IO.imageBaseDir + File.separator + "symbols" + File.separator + "large";
|
||||
public static final String RESOURCE_PATH_MANA_MEDIUM = IO.imageBaseDir + File.separator + "symbols" + File.separator + "medium";
|
||||
|
||||
public static final String RESOURCE_PATH_SET = IO.imageBaseDir + File.separator + "sets" + File.separator;
|
||||
public static final String RESOURCE_PATH_SET_SMALL = RESOURCE_PATH_SET + File.separator + "small" + File.separator;
|
||||
|
||||
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
|
||||
public static final Rectangle THUMBNAIL_SIZE_FULL = new Rectangle(102, 146);
|
||||
|
||||
public interface IO {
|
||||
public static final String imageBaseDir = "plugins" + File.separator + "images";
|
||||
public static final String IMAGE_PROPERTIES_FILE = "image.url.properties";
|
||||
}
|
||||
|
||||
public static final String CARD_IMAGE_PATH_TEMPLATE = "." + File.separator + "plugins" + File.separator + "images/{set}/{name}.{collector}.full.jpg";
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* DownloadGui.java
|
||||
*
|
||||
* Created on 25.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl;
|
||||
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.IndexedPropertyChangeEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoundedRangeModel;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultBoundedRangeModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import org.mage.plugins.card.dl.DownloadJob.State;
|
||||
|
||||
|
||||
/**
|
||||
* The class DownloadGui.
|
||||
*
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class DownloadGui extends JPanel {
|
||||
private static final long serialVersionUID = -7346572382493844327L;
|
||||
|
||||
private final Downloader d;
|
||||
private final DownloadListener l = new DownloadListener();
|
||||
private final BoundedRangeModel model = new DefaultBoundedRangeModel(0, 0, 0, 0);
|
||||
private final JProgressBar progress = new JProgressBar(model);
|
||||
|
||||
private final Map<DownloadJob, DownloadPanel> progresses = new HashMap<DownloadJob, DownloadPanel>();
|
||||
private final JPanel panel = new JPanel();
|
||||
|
||||
public DownloadGui(Downloader d) {
|
||||
super(new BorderLayout());
|
||||
this.d = d;
|
||||
d.addPropertyChangeListener(l);
|
||||
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
p.setBorder(BorderFactory.createTitledBorder("Progress:"));
|
||||
p.add(progress);
|
||||
JButton b = new JButton("X");
|
||||
b.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getDownloader().dispose();
|
||||
}
|
||||
});
|
||||
p.add(b, BorderLayout.EAST);
|
||||
add(p, BorderLayout.NORTH);
|
||||
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
JScrollPane pane = new JScrollPane(panel);
|
||||
pane.setPreferredSize(new Dimension(500, 300));
|
||||
add(pane);
|
||||
for(int i = 0; i < d.getJobs().size(); i++)
|
||||
addJob(i, d.getJobs().get(i));
|
||||
}
|
||||
|
||||
public Downloader getDownloader() {
|
||||
return d;
|
||||
}
|
||||
|
||||
private class DownloadListener implements PropertyChangeListener {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String name = evt.getPropertyName();
|
||||
if(evt.getSource() instanceof DownloadJob) {
|
||||
DownloadPanel p = progresses.get(evt.getSource());
|
||||
if("state".equals(name)) {
|
||||
if(evt.getOldValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
|
||||
changeProgress(-1, 0);
|
||||
}
|
||||
if(evt.getNewValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
|
||||
changeProgress(+1, 0);
|
||||
}
|
||||
if(p != null) {
|
||||
p.setVisible(p.getJob().getState() != State.FINISHED);
|
||||
p.revalidate();
|
||||
}
|
||||
} else if("message".equals(name)) {
|
||||
if(p != null) {
|
||||
JProgressBar bar = p.getBar();
|
||||
String message = p.getJob().getMessage();
|
||||
bar.setStringPainted(message != null);
|
||||
bar.setString(message);
|
||||
}
|
||||
}
|
||||
} else if(evt.getSource() == d) {
|
||||
if("jobs".equals(name)) {
|
||||
IndexedPropertyChangeEvent ev = (IndexedPropertyChangeEvent) evt;
|
||||
int index = ev.getIndex();
|
||||
|
||||
DownloadJob oldValue = (DownloadJob) ev.getOldValue();
|
||||
if(oldValue != null) removeJob(index, oldValue);
|
||||
|
||||
DownloadJob newValue = (DownloadJob) ev.getNewValue();
|
||||
if(newValue != null) addJob(index, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addJob(int index, DownloadJob job) {
|
||||
job.addPropertyChangeListener(l);
|
||||
changeProgress(0, +1);
|
||||
DownloadPanel p = new DownloadPanel(job);
|
||||
progresses.put(job, p);
|
||||
panel.add(p, index);
|
||||
panel.revalidate();
|
||||
}
|
||||
|
||||
private synchronized void removeJob(int index, DownloadJob job) {
|
||||
assert progresses.get(job) == panel.getComponent(index);
|
||||
job.removePropertyChangeListener(l);
|
||||
changeProgress(0, -1);
|
||||
progresses.remove(job);
|
||||
panel.remove(index);
|
||||
panel.revalidate();
|
||||
}
|
||||
|
||||
private synchronized void changeProgress(int progress, int total) {
|
||||
progress += model.getValue();
|
||||
total += model.getMaximum();
|
||||
model.setMaximum(total);
|
||||
model.setValue(progress);
|
||||
this.progress.setStringPainted(true);
|
||||
this.progress.setString(progress + "/" + total);
|
||||
}
|
||||
|
||||
private class DownloadPanel extends JPanel {
|
||||
private static final long serialVersionUID = 1187986738303477168L;
|
||||
|
||||
private DownloadJob job;
|
||||
private JProgressBar bar;
|
||||
|
||||
public DownloadPanel(DownloadJob job) {
|
||||
super(new BorderLayout());
|
||||
this.job = job;
|
||||
|
||||
setBorder(BorderFactory.createTitledBorder(job.getName()));
|
||||
add(bar = new JProgressBar(job.getProgress()));
|
||||
JButton b = new JButton("X");
|
||||
b.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
switch(getJob().getState()) {
|
||||
case NEW:
|
||||
case WORKING:
|
||||
getJob().setState(State.ABORTED);
|
||||
}
|
||||
}
|
||||
});
|
||||
add(b, BorderLayout.EAST);
|
||||
|
||||
if(job.getState() == State.FINISHED | job.getState() == State.ABORTED) {
|
||||
changeProgress(+1, 0);
|
||||
}
|
||||
setVisible(job.getState() != State.FINISHED);
|
||||
|
||||
String message = job.getMessage();
|
||||
bar.setStringPainted(message != null);
|
||||
bar.setString(message);
|
||||
|
||||
Dimension d = getPreferredSize();
|
||||
d.width = Integer.MAX_VALUE;
|
||||
setMaximumSize(d);
|
||||
// d.width = 500;
|
||||
// setMinimumSize(d);
|
||||
}
|
||||
|
||||
public DownloadJob getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public JProgressBar getBar() {
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
/**
|
||||
* DownloadJob.java
|
||||
*
|
||||
* Created on 25.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import javax.swing.BoundedRangeModel;
|
||||
import javax.swing.DefaultBoundedRangeModel;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||
|
||||
|
||||
/**
|
||||
* The class DownloadJob.
|
||||
*
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class DownloadJob extends AbstractLaternaBean {
|
||||
public static enum State {
|
||||
NEW, WORKING, FINISHED, ABORTED;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final Source source;
|
||||
private final Destination destination;
|
||||
private final Property<State> state = properties.property("state", State.NEW);
|
||||
private final Property<String> message = properties.property("message");
|
||||
private final Property<Exception> error = properties.property("error");
|
||||
private final BoundedRangeModel progress = new DefaultBoundedRangeModel();
|
||||
|
||||
public DownloadJob(String name, Source source, Destination destination) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's state. If the state is {@link State#ABORTED}, it instead sets the error to "ABORTED"
|
||||
*/
|
||||
public void setState(State state) {
|
||||
if(state == State.ABORTED) setError("ABORTED");
|
||||
else this.state.setValue(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's state to {@link State#ABORTED} and the error message to the given message. Logs a warning
|
||||
* with the given message.
|
||||
*/
|
||||
public void setError(String message) {
|
||||
setError(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the
|
||||
* given exception.
|
||||
*/
|
||||
public void setError(Exception error) {
|
||||
setError(null, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the
|
||||
* given message and exception.
|
||||
*/
|
||||
public void setError(String message, Exception error) {
|
||||
if(message == null) message = error.toString();
|
||||
log.warn(message, error);
|
||||
this.state.setValue(State.ABORTED);
|
||||
this.error.setValue(error);
|
||||
this.message.setValue(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's message.
|
||||
*/
|
||||
public void setMessage(String message) {
|
||||
this.message.setValue(message);
|
||||
}
|
||||
|
||||
public BoundedRangeModel getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state.getValue();
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return error.getValue();
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message.getValue();
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Source getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public Destination getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public static Source fromURL(final String url) {
|
||||
return fromURL(null, url);
|
||||
}
|
||||
|
||||
public static Source fromURL(final URL url) {
|
||||
return fromURL(null, url);
|
||||
}
|
||||
|
||||
public static Source fromURL(final Proxy proxy, final String url) {
|
||||
return new Source() {
|
||||
private URLConnection c;
|
||||
|
||||
public URLConnection getConnection() throws IOException {
|
||||
if(c == null) c = proxy == null? new URL(url).openConnection():new URL(url).openConnection(proxy);
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream open() throws IOException {
|
||||
return getConnection().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() throws IOException {
|
||||
return getConnection().getContentLength();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Source fromURL(final Proxy proxy, final URL url) {
|
||||
return new Source() {
|
||||
private URLConnection c;
|
||||
|
||||
public URLConnection getConnection() throws IOException {
|
||||
if(c == null) c = proxy == null? url.openConnection():url.openConnection(proxy);
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream open() throws IOException {
|
||||
return getConnection().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() throws IOException {
|
||||
return getConnection().getContentLength();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Destination toFile(final String file) {
|
||||
return toFile(new File(file));
|
||||
}
|
||||
|
||||
public static Destination toFile(final File file) {
|
||||
return new Destination() {
|
||||
@Override
|
||||
public OutputStream open() throws IOException {
|
||||
File parent = file.getAbsoluteFile().getParentFile();
|
||||
//Trying to create the directory before checking it exists makes it threadsafe
|
||||
if(!parent.mkdirs() && !parent.exists()) throw new IOException(parent
|
||||
+ ": directory could not be created");
|
||||
return new FileOutputStream(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return file.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() throws IOException {
|
||||
if(file.exists() && !file.delete()) throw new IOException(file + " couldn't be deleted");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static interface Source {
|
||||
public InputStream open() throws IOException;
|
||||
|
||||
public int length() throws IOException;
|
||||
}
|
||||
|
||||
public static interface Destination {
|
||||
public OutputStream open() throws IOException;
|
||||
|
||||
public boolean exists() throws IOException;
|
||||
|
||||
public void delete() throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* Downloader.java
|
||||
*
|
||||
* Created on 25.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl;
|
||||
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.BoundedRangeModel;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jetlang.channels.Channel;
|
||||
import org.jetlang.channels.MemoryChannel;
|
||||
import org.jetlang.core.Callback;
|
||||
import org.jetlang.core.Disposable;
|
||||
import org.jetlang.fibers.Fiber;
|
||||
import org.jetlang.fibers.PoolFiberFactory;
|
||||
import org.mage.plugins.card.dl.DownloadJob.Destination;
|
||||
import org.mage.plugins.card.dl.DownloadJob.Source;
|
||||
import org.mage.plugins.card.dl.DownloadJob.State;
|
||||
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The class Downloader.
|
||||
*
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class Downloader extends AbstractLaternaBean implements Disposable {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Downloader.class);
|
||||
|
||||
private final List<DownloadJob> jobs = properties.list("jobs");
|
||||
private final Channel<DownloadJob> channel = new MemoryChannel<DownloadJob>();
|
||||
|
||||
private final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
private final List<Fiber> fibers = new ArrayList<Fiber>();
|
||||
|
||||
public Downloader() {
|
||||
PoolFiberFactory f = new PoolFiberFactory(pool);
|
||||
//subscribe multiple fibers for parallel execution
|
||||
for(int i = 0, numThreads = 10; i < numThreads; i++) {
|
||||
Fiber fiber = f.create();
|
||||
fiber.start();
|
||||
fibers.add(fiber);
|
||||
channel.subscribe(fiber, new DownloadCallback());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
for(DownloadJob j:getJobs()) {
|
||||
switch(j.getState()) {
|
||||
case NEW:
|
||||
case WORKING:
|
||||
j.setState(State.ABORTED);
|
||||
}
|
||||
}
|
||||
|
||||
for(Fiber f:fibers)
|
||||
f.dispose();
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
dispose();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public void add(DownloadJob job) {
|
||||
if(job.getState() == State.WORKING) throw new IllegalArgumentException("Job already running");
|
||||
if(job.getState() == State.FINISHED) throw new IllegalArgumentException("Job already finished");
|
||||
job.setState(State.NEW);
|
||||
jobs.add(job);
|
||||
channel.publish(job);
|
||||
}
|
||||
|
||||
public List<DownloadJob> getJobs() {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the download job: Transfers data from {@link Source} to {@link Destination} and updates the
|
||||
* download job's state to reflect the progress.
|
||||
*/
|
||||
private class DownloadCallback implements Callback<DownloadJob> {
|
||||
@Override
|
||||
public void onMessage(DownloadJob job) {
|
||||
//the job won't be processed by multiple threads
|
||||
synchronized(job) {
|
||||
if(job.getState() != State.NEW) return;
|
||||
job.setState(State.WORKING);
|
||||
}
|
||||
try {
|
||||
Source src = job.getSource();
|
||||
Destination dst = job.getDestination();
|
||||
BoundedRangeModel progress = job.getProgress();
|
||||
|
||||
if(dst.exists()) {
|
||||
progress.setMaximum(1);
|
||||
progress.setValue(1);
|
||||
} else {
|
||||
progress.setMaximum(src.length());
|
||||
InputStream is = new BufferedInputStream(src.open());
|
||||
try {
|
||||
OutputStream os = new BufferedOutputStream(dst.open());
|
||||
try {
|
||||
byte[] buf = new byte[8 * 1024];
|
||||
int total = 0;
|
||||
for(int len; (len = is.read(buf)) != -1;) {
|
||||
if(job.getState() == State.ABORTED) throw new IOException("Job was aborted");
|
||||
progress.setValue(total += len);
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
} catch(IOException ex) {
|
||||
try {
|
||||
dst.delete();
|
||||
} catch(IOException ex1) {
|
||||
log.warn("While deleting", ex1);
|
||||
}
|
||||
throw ex;
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
} catch(IOException ex) {
|
||||
log.warn("While closing", ex);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch(IOException ex) {
|
||||
log.warn("While closing", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
job.setState(State.FINISHED);
|
||||
} catch(IOException ex) {
|
||||
job.setError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* AbstractBoundBean.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
|
||||
/**
|
||||
* The class AbstractBoundBean.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public abstract class AbstractBoundBean implements BoundBean {
|
||||
protected PropertyChangeSupport s = new PropertyChangeSupport(this);
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
s.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||
s.addPropertyChangeListener(propertyName, listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
s.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||
s.removePropertyChangeListener(propertyName, listener);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* BoundBean.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
|
||||
/**
|
||||
* The class BoundBean.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public interface BoundBean {
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener);
|
||||
|
||||
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener);
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener);
|
||||
|
||||
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* EventListenerList.java
|
||||
*
|
||||
* Created on 08.04.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans;
|
||||
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
|
||||
/**
|
||||
* The class EventListenerList.
|
||||
*
|
||||
* @version V0.0 08.04.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class EventListenerList extends javax.swing.event.EventListenerList {
|
||||
private static final long serialVersionUID = -7545754245081842909L;
|
||||
|
||||
/**
|
||||
* Returns an iterable over all listeners for the specified classes. the listener classes are in the specified
|
||||
* order. for every class, listeners are in the reverse order of registering. A listener contained multiple
|
||||
* times (for a single or multiple classes) is only returned the first time it occurs.
|
||||
*/
|
||||
public <T extends EventListener> Iterable<T> getIterable(final Class<? extends T>... listenerClass) {
|
||||
//transform class -> iterable
|
||||
List<Iterable<T>> l = Lists.transform(asList(listenerClass), new ClassToIterableFunction<T>());
|
||||
|
||||
//compose iterable (use an arraylist to memoize the function's results)
|
||||
final Iterable<T> it = Iterables.concat(new ArrayList<Iterable<T>>(l));
|
||||
|
||||
//transform to singleton iterators
|
||||
return new Iterable<T>() {
|
||||
public Iterator<T> iterator() {
|
||||
return new SingletonIterator<T>(it.iterator());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over all listeners for the specified classes. the listener classes are in the specified
|
||||
* order. for every class, listeners are in the reverse order of registering. A listener contained multiple
|
||||
* times (for a single or multiple classes) is only returned the first time it occurs.
|
||||
*/
|
||||
public <T extends EventListener> Iterator<T> getIterator(Class<? extends T>... listenerClass) {
|
||||
return getIterable(listenerClass).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates backwards over the listeners registered for a class by using the original array. The Listener runs
|
||||
* backwards, just as listener notification usually works.
|
||||
*/
|
||||
private class ListenerIterator<T> extends AbstractIterator<T> {
|
||||
private final Class<? extends T> listenerClass;
|
||||
private Object[] listeners = listenerList;
|
||||
private int index = listeners.length;
|
||||
|
||||
private ListenerIterator(Class<? extends T> listenerClass) {
|
||||
this.listenerClass = listenerClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T computeNext() {
|
||||
for(index -= 2; index >= 0; index -= 2) {
|
||||
if(listenerClass == listeners[index]) return (T) listeners[index + 1];
|
||||
}
|
||||
return endOfData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a class to the associated listener iterator
|
||||
*/
|
||||
private class ClassToIterableFunction<T> implements Function<Class<? extends T>, Iterable<T>> {
|
||||
|
||||
public Iterable<T> apply(final Class<? extends T> from) {
|
||||
return new Iterable<T>() {
|
||||
|
||||
public Iterator<T> iterator() {
|
||||
return new ListenerIterator<T>(from);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the delegate iterator so that every but the first occurrence of every element is ignored.
|
||||
*/
|
||||
private static class SingletonIterator<T> extends AbstractIterator<T> {
|
||||
private Iterator<T> it;
|
||||
private HashSet<T> previous = new HashSet<T>();
|
||||
|
||||
public SingletonIterator(Iterator<T> it) {
|
||||
this.it = it;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while(it.hasNext()) {
|
||||
T next = it.next();
|
||||
if(previous.add(next)) return next;
|
||||
}
|
||||
return endOfData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* PropertyChangeSupport.java
|
||||
*
|
||||
* Created on 16.07.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The class PropertyChangeSupport.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class PropertyChangeSupport extends java.beans.PropertyChangeSupport {
|
||||
private static final long serialVersionUID = -4241465377828790076L;
|
||||
|
||||
private Object sourceBean;
|
||||
|
||||
public PropertyChangeSupport(Object sourceBean) {
|
||||
super(sourceBean);
|
||||
this.sourceBean = sourceBean;
|
||||
}
|
||||
|
||||
public Object getSourceBean() {
|
||||
return sourceBean;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/**
|
||||
* ListenableCollections.java
|
||||
*
|
||||
* Created on 25.04.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.collections;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSequentialList;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The class ListenableCollections supports applications that need to listen for modifications on different
|
||||
* collections. Unlike most listener models, a listenable collection does not have public methods for adding or
|
||||
* removing listeners. Instead, the wrapping methods take exactly one listener, because listening to collections is
|
||||
* low-level. That single listener will usually manage multiple high-level listeners.
|
||||
*
|
||||
* @version V0.0 25.04.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public final class ListenableCollections {
|
||||
private ListenableCollections() {}
|
||||
|
||||
public static <E> List<E> listenableList(List<E> list, ListListener<E> listener) {
|
||||
if(list instanceof RandomAccess) return new ListenableList<E>(list, listener);
|
||||
else return new ListenableSequentialList<E>(list, listener);
|
||||
}
|
||||
|
||||
public static <E> Set<E> listenableSet(Set<E> set, SetListener<E> listener) {
|
||||
return new ListenableSet<E>(set, listener);
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> listenableMap(Map<K, V> map, MapListener<K, V> listener) {
|
||||
return new ListenableMap<K, V>(map, listener);
|
||||
}
|
||||
|
||||
public static interface ListListener<E> extends Serializable {
|
||||
/**
|
||||
* Notified after a value was added to the list.
|
||||
*/
|
||||
public void add(int index, E newValue);
|
||||
|
||||
/**
|
||||
* Notified after a value in the list was changed.
|
||||
*/
|
||||
public void set(int index, E oldValue, E newValue);
|
||||
|
||||
/**
|
||||
* Notified after a value was removed from the list.
|
||||
*/
|
||||
public void remove(int index, E oldValue);
|
||||
}
|
||||
|
||||
private static class ListenableList<E> extends AbstractList<E> implements RandomAccess, Serializable {
|
||||
private static final long serialVersionUID = 8622608480525537692L;
|
||||
|
||||
private List<E> delegate;
|
||||
private ListListener<E> listener;
|
||||
|
||||
public ListenableList(List<E> delegate, ListListener<E> listener) {
|
||||
this.delegate = delegate;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E e) {
|
||||
delegate.add(index, e);
|
||||
listener.add(index, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
E e = delegate.set(index, element);
|
||||
listener.set(index, e, element);
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int index) {
|
||||
E e = delegate.remove(index);
|
||||
listener.remove(index, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
return delegate.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ListenableSequentialList<E> extends AbstractSequentialList<E> implements Serializable {
|
||||
private static final long serialVersionUID = 3630474556578001885L;
|
||||
|
||||
private List<E> delegate;
|
||||
private ListListener<E> listener;
|
||||
|
||||
public ListenableSequentialList(List<E> delegate, ListListener<E> listener) {
|
||||
this.delegate = delegate;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(final int index) {
|
||||
return new ListIterator<E>() {
|
||||
private final ListIterator<E> it = delegate.listIterator(index);
|
||||
private int lastIndex;
|
||||
private E lastValue;
|
||||
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
public boolean hasPrevious() {
|
||||
return it.hasPrevious();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
lastIndex = it.nextIndex();
|
||||
lastValue = it.next();
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
public int nextIndex() {
|
||||
return it.nextIndex();
|
||||
}
|
||||
|
||||
public E previous() {
|
||||
lastIndex = it.previousIndex();
|
||||
lastValue = it.previous();
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
public int previousIndex() {
|
||||
return it.previousIndex();
|
||||
}
|
||||
|
||||
public void add(E o) {
|
||||
it.add(o);
|
||||
listener.add(previousIndex(), o);
|
||||
}
|
||||
|
||||
public void set(E o) {
|
||||
it.set(o);
|
||||
listener.set(lastIndex, lastValue, o);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
it.remove();
|
||||
listener.remove(lastIndex, lastValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface SetListener<E> extends Serializable {
|
||||
/**
|
||||
* Notified after a value was added to the set.
|
||||
*/
|
||||
public void add(E newValue);
|
||||
|
||||
/**
|
||||
* Notified after a value was removed from the set.
|
||||
*/
|
||||
public void remove(E oldValue);
|
||||
}
|
||||
|
||||
private static class ListenableSet<E> extends AbstractSet<E> implements Serializable {
|
||||
private static final long serialVersionUID = 7728087988927063221L;
|
||||
|
||||
private Set<E> delegate;
|
||||
private SetListener<E> listener;
|
||||
|
||||
public ListenableSet(Set<E> set, SetListener<E> listener) {
|
||||
delegate = set;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return delegate.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
boolean b = delegate.add(o);
|
||||
if(b) listener.add(o);
|
||||
return b;
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
boolean b = delegate.remove(o);
|
||||
if(b) listener.remove((E) o);
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
private final Iterator<E> it = delegate.iterator();
|
||||
private boolean hasLast;
|
||||
private E last;
|
||||
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
last = it.next();
|
||||
hasLast = true;
|
||||
return last;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if(!hasLast) throw new IllegalStateException();
|
||||
it.remove();
|
||||
listener.remove(last);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface MapListener<K, V> extends Serializable {
|
||||
/**
|
||||
* Notified after a value was put into the map.
|
||||
*/
|
||||
public void put(K key, V newValue);
|
||||
|
||||
/**
|
||||
* Notified after a value in the map was changed.
|
||||
*/
|
||||
public void set(K key, V oldValue, V newValue);
|
||||
|
||||
/**
|
||||
* Notified after a value was removed from the map.
|
||||
*/
|
||||
public void remove(K key, V oldValue);
|
||||
}
|
||||
|
||||
private static class ListenableMap<K, V> extends AbstractMap<K, V> implements Serializable {
|
||||
private static final long serialVersionUID = 4032087477448965103L;
|
||||
|
||||
private Map<K, V> delegate;
|
||||
private MapListener<K, V> listener;
|
||||
private Set<Entry<K, V>> entrySet;
|
||||
|
||||
public ListenableMap(Map<K, V> map, MapListener<K, V> listener) {
|
||||
this.listener = listener;
|
||||
delegate = map;
|
||||
entrySet = new EntrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
if(delegate.containsKey(key)) {
|
||||
V old = delegate.put(key, value);
|
||||
listener.set(key, old, value);
|
||||
return old;
|
||||
} else {
|
||||
delegate.put(key, value);
|
||||
listener.put(key, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
if(delegate.containsKey(key)) {
|
||||
V old = delegate.remove(key);
|
||||
listener.remove((K) key, old);
|
||||
return old;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<java.util.Map.Entry<K, V>> entrySet() {
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
private final class EntrySet extends AbstractSet<Entry<K, V>> implements Serializable {
|
||||
private static final long serialVersionUID = -780485106953107075L;
|
||||
private final Set<Entry<K, V>> delegate = ListenableMap.this.delegate.entrySet();
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return new Iterator<Entry<K, V>>() {
|
||||
private final Iterator<Entry<K, V>> it = delegate.iterator();
|
||||
private boolean hasLast;
|
||||
private Entry<K, V> last;
|
||||
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
public Entry<K, V> next() {
|
||||
last = it.next();
|
||||
hasLast = true;
|
||||
return last;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if(!hasLast) throw new IllegalStateException();
|
||||
hasLast = false;
|
||||
it.remove();
|
||||
listener.remove(last.getKey(), last.getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* This package contains bean-related utilities, most importantly
|
||||
* <ul>
|
||||
* <li>{@link net.slightlymagic.beans.BoundBean} and {@link net.slightlymagic.beans.AbstractBoundBean}, an interface and an
|
||||
* abstract class for easier property change support for bean classes</li>
|
||||
* <li>{@link net.slightlymagic.beans.properties.Properties} and its implementations. These make it easy for beans
|
||||
* to use delegates, by providing a centralized configuration for bean properties. For example, a {@link
|
||||
* net.slightlymagic.beans.properties.bound.BoundProperties} object creates properties that do automatic property
|
||||
* change notifications. What exact configuration is used is thus hidden from the delegate.</li>
|
||||
* <li>The {@link net.slightlymagic.beans.relational.Relations} class provides relational properties that model
|
||||
* bidirectional 1:1, 1:n and m:n relationships.</li>
|
||||
* </ul>
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans;
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* AbstractProperties.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.basic.BasicProperty;
|
||||
|
||||
|
||||
/**
|
||||
* The class AbstractProperties.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public abstract class AbstractProperties implements Properties {
|
||||
public <T> Property<T> property(String name, T value) {
|
||||
return property(name, new BasicProperty<T>(value));
|
||||
}
|
||||
|
||||
public <T> Property<T> property(String name) {
|
||||
return property(name, new BasicProperty<T>());
|
||||
}
|
||||
|
||||
public <E> List<E> list(String name) {
|
||||
return list(name, new ArrayList<E>());
|
||||
}
|
||||
|
||||
public <E> Set<E> set(String name) {
|
||||
return set(name, new HashSet<E>());
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> map(String name) {
|
||||
return map(name, new HashMap<K, V>());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* AbstractProperty.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties;
|
||||
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
|
||||
/**
|
||||
* The class AbstractProperty.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public abstract class AbstractProperty<T> implements Property<T> {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
T value = getValue();
|
||||
return value == null? 0:value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof Property<?>)) return false;
|
||||
Object value = getValue();
|
||||
Object other = ((Property<?>) obj).getValue();
|
||||
return value == other || (value != null && value.equals(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return valueOf(getValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* CompoundProperties.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties;
|
||||
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The class CompoundProperties.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class CompoundProperties extends AbstractProperties {
|
||||
private List<Properties> delegates;
|
||||
|
||||
public CompoundProperties(Properties... delegates) {
|
||||
this.delegates = asList(delegates);
|
||||
Collections.reverse(this.delegates);
|
||||
}
|
||||
|
||||
public CompoundProperties(List<Properties> delegates) {
|
||||
this.delegates = new ArrayList<Properties>(delegates);
|
||||
Collections.reverse(this.delegates);
|
||||
}
|
||||
|
||||
public <T> Property<T> property(String name, Property<T> property) {
|
||||
for(Properties p:delegates)
|
||||
property = p.property(name, property);
|
||||
return property;
|
||||
}
|
||||
|
||||
public <E> List<E> list(String name, List<E> list) {
|
||||
for(Properties p:delegates)
|
||||
list = p.list(name, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public <E> Set<E> set(String name, Set<E> set) {
|
||||
for(Properties p:delegates)
|
||||
set = p.set(name, set);
|
||||
return set;
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||
for(Properties p:delegates)
|
||||
map = p.map(name, map);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Properties.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.bound.BoundProperties;
|
||||
|
||||
|
||||
/**
|
||||
* The class Properties. A Properties object is a factory for bean properties. For example, the
|
||||
* {@link BoundProperties} class produces properties that can be observed using {@link PropertyChangeListener}s.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public interface Properties {
|
||||
public <T> Property<T> property(String name, Property<T> property);
|
||||
|
||||
public <E> List<E> list(String name, List<E> list);
|
||||
|
||||
public <E> Set<E> set(String name, Set<E> set);
|
||||
|
||||
public <K, V> Map<K, V> map(String name, Map<K, V> map);
|
||||
|
||||
|
||||
public <T> Property<T> property(String name, T value);
|
||||
|
||||
public <T> Property<T> property(String name);
|
||||
|
||||
public <E> List<E> list(String name);
|
||||
|
||||
public <E> Set<E> set(String name);
|
||||
|
||||
public <K, V> Map<K, V> map(String name);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Property.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties;
|
||||
|
||||
|
||||
/**
|
||||
* The class Property.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public interface Property<T> {
|
||||
public void setValue(T value);
|
||||
|
||||
public T getValue();
|
||||
|
||||
/**
|
||||
* A property's hash code is its value's hashCode, or {@code null} if the value is null.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode();
|
||||
|
||||
/**
|
||||
* Two properties are equal if their values are equal.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* BoundProperties.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.basic;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.AbstractProperties;
|
||||
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||
|
||||
|
||||
/**
|
||||
* The class BoundProperties.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class BasicProperties extends AbstractProperties {
|
||||
public <T> Property<T> property(String name, Property<T> value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public <E> List<E> list(String name, List<E> list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
public <E> Set<E> set(String name, Set<E> set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* BasicProperty.java
|
||||
*
|
||||
* Created on 16.07.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.basic;
|
||||
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.AbstractProperty;
|
||||
|
||||
|
||||
/**
|
||||
* The class BasicProperty.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class BasicProperty<T> extends AbstractProperty<T> {
|
||||
private T value;
|
||||
|
||||
public BasicProperty() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public BasicProperty(T initialValue) {
|
||||
value = initialValue;
|
||||
}
|
||||
|
||||
public void setValue(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* BoundProperties.java
|
||||
*
|
||||
* Created on 24.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||
|
||||
|
||||
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableList;
|
||||
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableMap;
|
||||
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableSet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||
import org.mage.plugins.card.dl.beans.properties.AbstractProperties;
|
||||
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||
|
||||
|
||||
/**
|
||||
* The class BoundProperties.
|
||||
*
|
||||
* @version V0.0 24.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class BoundProperties extends AbstractProperties {
|
||||
public final PropertyChangeSupport s;
|
||||
|
||||
public BoundProperties(Object sourceBean) {
|
||||
this(new PropertyChangeSupport(sourceBean));
|
||||
}
|
||||
|
||||
public BoundProperties(PropertyChangeSupport s) {
|
||||
if(s == null) throw new IllegalArgumentException("s == null");
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public <T> Property<T> property(String name, Property<T> property) {
|
||||
return new BoundProperty<T>(s, name, property);
|
||||
}
|
||||
|
||||
public <E> List<E> list(String name, List<E> list) {
|
||||
return listenableList(list, new PropertyChangeListListener<E>(s, name));
|
||||
}
|
||||
|
||||
public <E> Set<E> set(String name, Set<E> set) {
|
||||
return listenableSet(set, new PropertyChangeSetListener<E>(s, set, name));
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||
return listenableMap(map, new PropertyChangeMapListener<K, V>(s, map, name));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* BasicProperty.java
|
||||
*
|
||||
* Created on 16.07.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeSupport;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.properties.AbstractProperty;
|
||||
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||
|
||||
|
||||
/**
|
||||
* The class BasicProperty.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class BoundProperty<T> extends AbstractProperty<T> {
|
||||
private PropertyChangeSupport s;
|
||||
private String name;
|
||||
private Property<T> property;
|
||||
|
||||
public BoundProperty(PropertyChangeSupport s, String name, Property<T> property) {
|
||||
if(property == null) throw new IllegalArgumentException();
|
||||
this.s = s;
|
||||
this.name = name;
|
||||
this.property = property;
|
||||
|
||||
}
|
||||
|
||||
public void setValue(T value) {
|
||||
T old = getValue();
|
||||
property.setValue(value);
|
||||
s.firePropertyChange(name, old, getValue());
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return property.getValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class PropertyChangeListListener<E> implements ListListener<E> {
|
||||
private static final long serialVersionUID = 625853864429729560L;
|
||||
|
||||
private PropertyChangeSupport s;
|
||||
private String propertyName;
|
||||
|
||||
public PropertyChangeListListener(PropertyChangeSupport s, String propertyName) {
|
||||
this.s = s;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public void add(int index, E newValue) {
|
||||
s.fireIndexedPropertyChange(propertyName, index, null, newValue);
|
||||
}
|
||||
|
||||
public void set(int index, E oldValue, E newValue) {
|
||||
s.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
|
||||
}
|
||||
|
||||
public void remove(int index, E oldValue) {
|
||||
s.fireIndexedPropertyChange(propertyName, index, oldValue, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* PropertyChangeMapListener.java
|
||||
*
|
||||
* Created on 16.07.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||
import org.mage.plugins.card.dl.beans.collections.ListenableCollections.MapListener;
|
||||
|
||||
|
||||
/**
|
||||
* The class PropertyChangeMapListener. This listener alway fires events with {@link Map} -> {@link Map} as the
|
||||
* value parameters, as nonindexed collection properties. The events will be {@link MapEvent} instances.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class PropertyChangeMapListener<K, V> implements MapListener<K, V> {
|
||||
private static final long serialVersionUID = 625853864429729560L;
|
||||
|
||||
private PropertyChangeSupport s;
|
||||
private Map<K, V> map;
|
||||
private String propertyName;
|
||||
|
||||
public PropertyChangeMapListener(PropertyChangeSupport s, Map<K, V> map, String propertyName) {
|
||||
this.s = s;
|
||||
this.map = map;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public void put(K key, V newValue) {
|
||||
s.firePropertyChange(new MapPutEvent<K, V>(s.getSourceBean(), propertyName, map, key, newValue));
|
||||
}
|
||||
|
||||
public void set(K key, V oldValue, V newValue) {
|
||||
s.firePropertyChange(new MapSetEvent<K, V>(s.getSourceBean(), propertyName, map, key, oldValue, newValue));
|
||||
}
|
||||
|
||||
public void remove(K key, V oldValue) {
|
||||
s.firePropertyChange(new MapRemoveEvent<K, V>(s.getSourceBean(), propertyName, map, key, oldValue));
|
||||
}
|
||||
|
||||
public static abstract class MapEvent<K, V> extends PropertyChangeEvent {
|
||||
private static final long serialVersionUID = -651568020675693544L;
|
||||
|
||||
private Map<K, V> map;
|
||||
private K key;
|
||||
|
||||
public MapEvent(Object source, String propertyName, Map<K, V> map, K key) {
|
||||
super(source, propertyName, null, null);
|
||||
this.map = map;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getOldValue() {
|
||||
//old and new value must not be equal
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getNewValue() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapPutEvent<K, V> extends MapEvent<K, V> {
|
||||
private static final long serialVersionUID = 6006291474676939650L;
|
||||
|
||||
private V newElement;
|
||||
|
||||
public MapPutEvent(Object source, String propertyName, Map<K, V> map, K key, V newElement) {
|
||||
super(source, propertyName, map, key);
|
||||
this.newElement = newElement;
|
||||
}
|
||||
|
||||
public V getNewElement() {
|
||||
return newElement;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapSetEvent<K, V> extends MapEvent<K, V> {
|
||||
private static final long serialVersionUID = -2419438379909500079L;
|
||||
|
||||
private V oldElement, newElement;
|
||||
|
||||
public MapSetEvent(Object source, String propertyName, Map<K, V> map, K key, V oldElement, V newElement) {
|
||||
super(source, propertyName, map, key);
|
||||
this.oldElement = oldElement;
|
||||
this.newElement = newElement;
|
||||
}
|
||||
|
||||
public V getOldElement() {
|
||||
return oldElement;
|
||||
}
|
||||
|
||||
public V getNewElement() {
|
||||
return newElement;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapRemoveEvent<K, V> extends MapEvent<K, V> {
|
||||
private static final long serialVersionUID = -2644879706878221895L;
|
||||
|
||||
private V oldElement;
|
||||
|
||||
public MapRemoveEvent(Object source, String propertyName, Map<K, V> map, K key, V oldElement) {
|
||||
super(source, propertyName, map, key);
|
||||
this.oldElement = oldElement;
|
||||
}
|
||||
|
||||
public V getOldElement() {
|
||||
return oldElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* PropertyChangeSetListener.java
|
||||
*
|
||||
* Created on 16.07.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||
import org.mage.plugins.card.dl.beans.collections.ListenableCollections.SetListener;
|
||||
|
||||
|
||||
/**
|
||||
* The class PropertyChangeSetListener. This listener always fires events with {@link Set} -> {@link Set} as the
|
||||
* value parameters, as nonindexed collection properties. The events will be {@link SetEvent} instances.
|
||||
*
|
||||
* @version V0.0 16.07.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class PropertyChangeSetListener<E> implements SetListener<E> {
|
||||
private static final long serialVersionUID = 625853864429729560L;
|
||||
|
||||
private PropertyChangeSupport s;
|
||||
private Set<E> set;
|
||||
private String propertyName;
|
||||
|
||||
public PropertyChangeSetListener(PropertyChangeSupport s, Set<E> set, String propertyName) {
|
||||
this.s = s;
|
||||
this.set = set;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public void add(E newValue) {
|
||||
s.firePropertyChange(new SetAddEvent<E>(s.getSourceBean(), propertyName, set, newValue));
|
||||
}
|
||||
|
||||
public void remove(E oldValue) {
|
||||
s.firePropertyChange(new SetRemoveEvent<E>(s.getSourceBean(), propertyName, set, oldValue));
|
||||
}
|
||||
|
||||
public static abstract class SetEvent<E> extends PropertyChangeEvent {
|
||||
private static final long serialVersionUID = -651568020675693544L;
|
||||
|
||||
private Set<E> set;
|
||||
|
||||
public SetEvent(Object source, String propertyName, Set<E> set) {
|
||||
super(source, propertyName, null, null);
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<E> getOldValue() {
|
||||
//old and new value must not be equal
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<E> getNewValue() {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetAddEvent<E> extends SetEvent<E> {
|
||||
private static final long serialVersionUID = 9041766866796759871L;
|
||||
|
||||
private E newElement;
|
||||
|
||||
public SetAddEvent(Object source, String propertyName, Set<E> set, E newElement) {
|
||||
super(source, propertyName, set);
|
||||
this.newElement = newElement;
|
||||
}
|
||||
|
||||
public E getNewElement() {
|
||||
return newElement;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetRemoveEvent<E> extends SetEvent<E> {
|
||||
private static final long serialVersionUID = -1315342339926392385L;
|
||||
|
||||
private E oldElement;
|
||||
|
||||
public SetRemoveEvent(Object source, String propertyName, Set<E> set, E oldElement) {
|
||||
super(source, propertyName, set);
|
||||
this.oldElement = oldElement;
|
||||
}
|
||||
|
||||
public E getOldElement() {
|
||||
return oldElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class AbstractLaternaBean extends AbstractBoundBean {
|
||||
protected static final Logger log = Logger.getLogger(AbstractLaternaBean.class);
|
||||
protected Properties properties = new BoundProperties(s);
|
||||
protected EventListenerList listeners = new EventListenerList();
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public interface CardImageSource {
|
||||
|
||||
String generateURL(Integer collectorId, String cardSet) throws Exception;
|
||||
String generateTokenUrl(String name, String set);
|
||||
Float getAverageSize();
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
|
||||
public class GathererSets implements Iterable<DownloadJob> {
|
||||
|
||||
private final static String SETS_PATH = File.separator + "sets";
|
||||
private final static File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SETS_PATH);
|
||||
private static File outDir = DEFAULT_OUT_DIR;
|
||||
|
||||
private static final String[] symbols = {"DIS", "DST", "GPT", "RAV", "MRD", "10E", "HOP", "SHM", "EVE", "APC", "TMP", "CHK"};
|
||||
private static final String[] withMythics = {"ALA", "CFX", "ARB", "ZEN", "WWK", "ROE", "SOM", "M10", "M11", "M12", "DDF", "MBS", "NPH"};
|
||||
private static final HashMap<String, String> symbolsReplacements = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
symbolsReplacements.put("CFX", "CON");
|
||||
symbolsReplacements.put("APC", "AP");
|
||||
symbolsReplacements.put("TMP", "TE");
|
||||
}
|
||||
|
||||
public GathererSets(String path) {
|
||||
if (path == null) {
|
||||
useDefaultDir();
|
||||
} else {
|
||||
changeOutDir(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<DownloadJob> iterator() {
|
||||
ArrayList<DownloadJob> jobs = new ArrayList<DownloadJob>();
|
||||
for (String symbol : symbols) {
|
||||
jobs.add(generateDownloadJob(symbol, "C"));
|
||||
jobs.add(generateDownloadJob(symbol, "U"));
|
||||
jobs.add(generateDownloadJob(symbol, "R"));
|
||||
}
|
||||
for (String symbol : withMythics) {
|
||||
jobs.add(generateDownloadJob(symbol, "C"));
|
||||
jobs.add(generateDownloadJob(symbol, "U"));
|
||||
jobs.add(generateDownloadJob(symbol, "R"));
|
||||
jobs.add(generateDownloadJob(symbol, "M"));
|
||||
}
|
||||
return jobs.iterator();
|
||||
}
|
||||
|
||||
private DownloadJob generateDownloadJob(String set, String rarity) {
|
||||
File dst = new File(outDir, set + "-" + rarity + ".jpg");
|
||||
if (symbolsReplacements.containsKey(set)) {
|
||||
set = symbolsReplacements.get(set);
|
||||
}
|
||||
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + rarity;
|
||||
return new DownloadJob(set + "-" + rarity, fromURL(url), toFile(dst));
|
||||
}
|
||||
|
||||
private void changeOutDir(String path) {
|
||||
File file = new File(path + SETS_PATH);
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
} else {
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useDefaultDir() {
|
||||
outDir = DEFAULT_OUT_DIR;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* GathererSymbols.java
|
||||
*
|
||||
* Created on 25.08.2010
|
||||
*/
|
||||
|
||||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
|
||||
|
||||
/**
|
||||
* The class GathererSymbols.
|
||||
*
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza
|
||||
*/
|
||||
public class GathererSymbols implements Iterable<DownloadJob> {
|
||||
//TODO chaos and planeswalker symbol
|
||||
//chaos: http://gatherer.wizards.com/Images/Symbols/chaos.gif
|
||||
|
||||
private final static String SYMBOLS_PATH = File.separator + "symbols";
|
||||
private final static File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SYMBOLS_PATH);
|
||||
private static File outDir = DEFAULT_OUT_DIR;
|
||||
|
||||
private static final String urlFmt = "http://gatherer.wizards.com/handlers/image.ashx?size=%1$s&name=%2$s&type=symbol";
|
||||
|
||||
private static final String[] sizes = {"small", "medium", "large"};
|
||||
|
||||
private static final String[] symbols = {"W", "U", "B", "R", "G",
|
||||
|
||||
"W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U",
|
||||
|
||||
"2/W", "2/U", "2/B", "2/R", "2/G",
|
||||
|
||||
"WP", "UP", "BP", "RP", "GP",
|
||||
|
||||
"X", "S", "T", "Q"};
|
||||
private static final int minNumeric = 0, maxNumeric = 16;
|
||||
|
||||
public GathererSymbols(String path) {
|
||||
if (path == null) {
|
||||
useDefaultDir();
|
||||
} else {
|
||||
changeOutDir(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<DownloadJob> iterator() {
|
||||
return new AbstractIterator<DownloadJob>() {
|
||||
private int sizeIndex, symIndex, numeric = minNumeric;
|
||||
private File dir = new File(outDir, sizes[sizeIndex]);
|
||||
|
||||
@Override
|
||||
protected DownloadJob computeNext() {
|
||||
String sym;
|
||||
if(symIndex < symbols.length) {
|
||||
sym = symbols[symIndex++];
|
||||
} else if(numeric <= maxNumeric) {
|
||||
sym = "" + (numeric++);
|
||||
} else {
|
||||
sizeIndex++;
|
||||
if(sizeIndex == sizes.length) return endOfData();
|
||||
|
||||
symIndex = 0;
|
||||
numeric = 0;
|
||||
dir = new File(outDir, sizes[sizeIndex]);
|
||||
return computeNext();
|
||||
}
|
||||
String symbol = sym.replaceAll("/", "");
|
||||
File dst = new File(dir, symbol + ".jpg");
|
||||
|
||||
if(symbol.equals("T")) symbol = "tap";
|
||||
else if(symbol.equals("Q")) symbol = "untap";
|
||||
else if(symbol.equals("S")) symbol = "snow";
|
||||
|
||||
String url = format(urlFmt, sizes[sizeIndex], symbol);
|
||||
|
||||
return new DownloadJob(sym, fromURL(url), toFile(dst));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void changeOutDir(String path) {
|
||||
File file = new File(path + SYMBOLS_PATH);
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
} else {
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useDefaultDir() {
|
||||
outDir = DEFAULT_OUT_DIR;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class MagicCardsImageSource implements CardImageSource {
|
||||
|
||||
private static CardImageSource instance = new MagicCardsImageSource();
|
||||
private static final Map<String, String> setNameReplacement = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
put("NPH", "new-phyrexia");
|
||||
put("MBS", "mirrodin-besieged");
|
||||
put("SOM", "scars-of-mirrodin");
|
||||
put("M11", "magic-2011");
|
||||
put("ROE", "rise-of-the-eldrazi");
|
||||
put("PVC", "duel-decks-phyrexia-vs-the-coalition");
|
||||
put("WWK", "worldwake");
|
||||
put("ZEN", "zendikar");
|
||||
put("HOP", "planechase");
|
||||
put("M10", "magic-2010");
|
||||
put("GVL", "duel-decks-garruk-vs-liliana");
|
||||
put("ARB", "alara-reborn");
|
||||
put("DVD", "duel-decks-divine-vs-demonic");
|
||||
put("CON", "conflux");
|
||||
put("JVC", "duel-decks-jace-vs-chandra");
|
||||
put("ALA", "shards-of-alara");
|
||||
put("EVE", "eventide");
|
||||
put("SHM", "shadowmoor");
|
||||
put("EVG", "duel-decks-elves-vs-goblins");
|
||||
put("MOR", "morningtide");
|
||||
put("LRW", "lorwyn");
|
||||
put("10E", "tenth-edition");
|
||||
put("CSP", "coldsnap");
|
||||
put("CHK", "player-rewards-2004");
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
|
||||
public static CardImageSource getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new MagicCardsImageSource();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateURL(Integer collectorId, String cardSet) throws Exception {
|
||||
if (collectorId == null || cardSet == null) {
|
||||
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
|
||||
}
|
||||
String set = CardImageUtils.updateSet(cardSet, true);
|
||||
String url = "http://magiccards.info/scans/en/";
|
||||
url += set.toLowerCase() + "/" + collectorId + ".jpg";
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTokenUrl(String name, String set) {
|
||||
String _name = name.replaceAll(" ", "-").toLowerCase();
|
||||
String _set = "not-supported-set";
|
||||
if (setNameReplacement.containsKey(set)) {
|
||||
_set = setNameReplacement.get(set);
|
||||
} else {
|
||||
_set += "-" + set;
|
||||
}
|
||||
String url = "http://magiccards.info/extras/token/" + _set + "/" + _name + ".jpg";
|
||||
return url;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getAverageSize() {
|
||||
return 70.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class MtgatheringRuImageSource implements CardImageSource {
|
||||
|
||||
private static CardImageSource hqInstance;
|
||||
private static CardImageSource mqInstance;
|
||||
private static CardImageSource lqInstance;
|
||||
private static final Map setsAliases;
|
||||
|
||||
static {
|
||||
setsAliases = new HashMap();
|
||||
setsAliases.put("M11", "magic2011");
|
||||
}
|
||||
private String quality;
|
||||
|
||||
public static CardImageSource getHqInstance() {
|
||||
if (hqInstance == null) {
|
||||
hqInstance = new MtgatheringRuImageSource("hq");
|
||||
}
|
||||
return hqInstance;
|
||||
}
|
||||
|
||||
public static CardImageSource getMqInstance() {
|
||||
if (mqInstance == null) {
|
||||
mqInstance = new MtgatheringRuImageSource("md");
|
||||
}
|
||||
return mqInstance;
|
||||
}
|
||||
|
||||
public static CardImageSource getLqInstance() {
|
||||
if (lqInstance == null) {
|
||||
lqInstance = new MtgatheringRuImageSource("lq");
|
||||
}
|
||||
return lqInstance;
|
||||
}
|
||||
|
||||
public MtgatheringRuImageSource(String quality) {
|
||||
this.quality = quality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateURL(Integer collectorId, String cardSet) throws Exception {
|
||||
if (collectorId == null || cardSet == null) {
|
||||
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
|
||||
}
|
||||
if (setsAliases.get(cardSet) != null) {
|
||||
String set = CardImageUtils.updateSet(cardSet, true);
|
||||
String url = "http://mtgathering.ru/scans/en/";
|
||||
url += set.toLowerCase() + "/" + quality + "/" + collectorId + ".jpg";
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTokenUrl(String name, String set) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getAverageSize() {
|
||||
if(quality.equalsIgnoreCase("hq"))
|
||||
return 80.0f;
|
||||
if(quality.equalsIgnoreCase("md"))
|
||||
return 30.0f;
|
||||
if(quality.equalsIgnoreCase("lq"))
|
||||
return 9.0f;
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class WizardCardsImageSource implements CardImageSource {
|
||||
|
||||
private static CardImageSource instance;
|
||||
private static Map setsAliases;
|
||||
private Map sets;
|
||||
|
||||
public static CardImageSource getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WizardCardsImageSource();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public WizardCardsImageSource() {
|
||||
sets = new HashMap();
|
||||
setsAliases = new HashMap();
|
||||
setsAliases.put("M12", "magic2012/cig");
|
||||
setsAliases.put("NPH", "newphyrexia/spoiler");
|
||||
setsAliases.put("MBS", "mirrodinbesieged/spoiler");
|
||||
setsAliases.put("SOM", "scarsofmirrodin/spoiler");
|
||||
setsAliases.put("M11", "magic2011/spoiler");
|
||||
setsAliases.put("ROE", "riseoftheeldrazi/spoiler");
|
||||
setsAliases.put("WWK", "worldwake/spoiler");
|
||||
setsAliases.put("ZEN", "zendikar/spoiler");
|
||||
setsAliases.put("M10", "magic2010/spoiler");
|
||||
setsAliases.put("ARB", "alarareborn/spoiler");
|
||||
setsAliases.put("CON", "conflux/spoiler");
|
||||
setsAliases.put("ALA", "shardsofalara/spoiler");
|
||||
}
|
||||
|
||||
private List<String> getSetLinks(String cardSet) {
|
||||
List<String> setLinks = new ArrayList<String>();
|
||||
try {
|
||||
Document doc = Jsoup.connect("http://www.wizards.com/magic/tcg/article.aspx?x=mtg/tcg/" + (String) setsAliases.get(cardSet)).get();
|
||||
Elements cardsImages = doc.select("img[height$=370]");
|
||||
for (int i = 0; i < cardsImages.size(); i++) {
|
||||
setLinks.add(cardsImages.get(i).attr("src"));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Exception when parsing the wizards page: " + ex.getMessage());
|
||||
}
|
||||
return setLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateURL(Integer collectorId, String cardSet) throws Exception {
|
||||
if (collectorId == null || cardSet == null) {
|
||||
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
|
||||
}
|
||||
if (setsAliases.get(cardSet) != null) {
|
||||
List<String> setLinks = (List<String>) sets.get(cardSet);
|
||||
if (setLinks == null) {
|
||||
setLinks = getSetLinks(cardSet);
|
||||
sets.put(cardSet, setLinks);
|
||||
}
|
||||
String link;
|
||||
if (setLinks.size() >= collectorId) {
|
||||
link = setLinks.get(collectorId - 1);
|
||||
} else {
|
||||
link = setLinks.get(collectorId - 21);
|
||||
link = link.replace(Integer.toString(collectorId - 20), (Integer.toString(collectorId - 20) + "a"));
|
||||
}
|
||||
if (!link.startsWith("http://")) {
|
||||
link = "http://www.wizards.com" + link;
|
||||
}
|
||||
return link;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTokenUrl(String name, String set) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getAverageSize() {
|
||||
return 60.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package org.mage.plugins.card.images;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class CardInfo {
|
||||
|
||||
private String name;
|
||||
private String set;
|
||||
private Integer collectorId;
|
||||
private boolean token;
|
||||
|
||||
public CardInfo(String name, String set, Integer collectorId) {
|
||||
this.name = name;
|
||||
this.set = set;
|
||||
this.collectorId = collectorId;
|
||||
token = false;
|
||||
}
|
||||
|
||||
public CardInfo(String name, String set, Integer collectorId, boolean token) {
|
||||
this.name = name;
|
||||
this.set = set;
|
||||
this.collectorId = collectorId;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public CardInfo(final CardInfo card) {
|
||||
this.name = card.name;
|
||||
this.set = card.set;
|
||||
this.collectorId = card.collectorId;
|
||||
this.token = card.token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final CardInfo other = (CardInfo) obj;
|
||||
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
if ((this.set == null) ? (other.set != null) : !this.set.equals(other.set)) {
|
||||
return false;
|
||||
}
|
||||
if (this.collectorId != other.collectorId && (this.collectorId == null || !this.collectorId.equals(other.collectorId))) {
|
||||
return false;
|
||||
}
|
||||
if (this.token != other.token) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 47 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
hash = 47 * hash + (this.set != null ? this.set.hashCode() : 0);
|
||||
hash = 47 * hash + (this.collectorId != null ? this.collectorId.hashCode() : 0);
|
||||
hash = 47 * hash + (this.token ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Integer getCollectorId() {
|
||||
return collectorId;
|
||||
}
|
||||
|
||||
public void setCollectorId(Integer collectorId) {
|
||||
this.collectorId = collectorId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
public void setSet(String set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public boolean isToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(boolean token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,538 @@
|
|||
package org.mage.plugins.card.images;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.management.ImmutableDescriptor;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.DefaultBoundedRangeModel;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import mage.cards.Card;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.dl.sources.CardImageSource;
|
||||
import org.mage.plugins.card.dl.sources.MagicCardsImageSource;
|
||||
import org.mage.plugins.card.dl.sources.MtgatheringRuImageSource;
|
||||
import org.mage.plugins.card.dl.sources.WizardCardsImageSource;
|
||||
import org.mage.plugins.card.properties.SettingsManager;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
|
||||
|
||||
private int type;
|
||||
private JTextField addr, port;
|
||||
private JProgressBar bar;
|
||||
private JOptionPane dlg;
|
||||
private boolean cancel;
|
||||
private JButton closeButton;
|
||||
private JButton startDownloadButton;
|
||||
private int cardIndex;
|
||||
private ArrayList<CardInfo> cards;
|
||||
private JComboBox jComboBox1;
|
||||
private JLabel jLabel1;
|
||||
private static boolean offlineMode = false;
|
||||
private JCheckBox checkBox;
|
||||
private final Object sync = new Object();
|
||||
private String imagesPath;
|
||||
|
||||
private static CardImageSource cardImageSource;
|
||||
|
||||
private Proxy p;
|
||||
|
||||
private ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||
|
||||
public static final Proxy.Type[] types = Proxy.Type.values();
|
||||
|
||||
public static void main(String[] args) {
|
||||
startDownload(null, null, null);
|
||||
}
|
||||
|
||||
public static void startDownload(JFrame frame, Set<Card> allCards, String imagesPath) {
|
||||
ArrayList<CardInfo> cards = getNeededCards(allCards, imagesPath);
|
||||
|
||||
/*
|
||||
* if (cards == null || cards.size() == 0) {
|
||||
* JOptionPane.showMessageDialog(null,
|
||||
* "All card pictures have been downloaded."); return; }
|
||||
*/
|
||||
|
||||
DownloadPictures download = new DownloadPictures(cards, imagesPath);
|
||||
JDialog dlg = download.getDlg(frame);
|
||||
dlg.setVisible(true);
|
||||
dlg.dispose();
|
||||
download.setCancel(true);
|
||||
}
|
||||
|
||||
public JDialog getDlg(JFrame frame) {
|
||||
String title = "Downloading";
|
||||
|
||||
final JDialog dialog = this.dlg.createDialog(frame, title);
|
||||
closeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void setCancel(boolean cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
public DownloadPictures(ArrayList<CardInfo> cards, String imagesPath) {
|
||||
this.cards = cards;
|
||||
this.imagesPath = imagesPath;
|
||||
|
||||
addr = new JTextField("Proxy Address");
|
||||
port = new JTextField("Proxy Port");
|
||||
bar = new JProgressBar(this);
|
||||
|
||||
JPanel p0 = new JPanel();
|
||||
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
|
||||
|
||||
// Proxy Choice
|
||||
ButtonGroup bg = new ButtonGroup();
|
||||
String[] labels = { "No Proxy", "HTTP Proxy", "SOCKS Proxy" };
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
JRadioButton rb = new JRadioButton(labels[i]);
|
||||
rb.addChangeListener(new ProxyHandler(i));
|
||||
bg.add(rb);
|
||||
p0.add(rb);
|
||||
if (i == 0)
|
||||
rb.setSelected(true);
|
||||
}
|
||||
|
||||
// Proxy config
|
||||
p0.add(addr);
|
||||
p0.add(port);
|
||||
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
jLabel1 = new JLabel();
|
||||
jLabel1.setText("Please select server:");
|
||||
|
||||
jLabel1.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
|
||||
p0.add(jLabel1);
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
ComboBoxModel jComboBox1Model = new DefaultComboBoxModel(new String[] { "magiccards.info", "wizards.com", "mtgathering.ru HQ", "mtgathering.ru MQ", "mtgathering.ru LQ"});
|
||||
jComboBox1 = new JComboBox();
|
||||
|
||||
cardImageSource = MagicCardsImageSource.getInstance();
|
||||
|
||||
jComboBox1.setModel(jComboBox1Model);
|
||||
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
jComboBox1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JComboBox cb = (JComboBox) e.getSource();
|
||||
switch (cb.getSelectedIndex()) {
|
||||
case 0:
|
||||
cardImageSource = MagicCardsImageSource.getInstance();
|
||||
break;
|
||||
case 1:
|
||||
cardImageSource = WizardCardsImageSource.getInstance();
|
||||
break;
|
||||
case 2:
|
||||
cardImageSource = MtgatheringRuImageSource.getHqInstance();
|
||||
break;
|
||||
case 3:
|
||||
cardImageSource = MtgatheringRuImageSource.getMqInstance();
|
||||
break;
|
||||
case 4:
|
||||
cardImageSource = MtgatheringRuImageSource.getLqInstance();
|
||||
break;
|
||||
}
|
||||
int count = DownloadPictures.this.cards.size();
|
||||
float mb = (count * cardImageSource.getAverageSize()) / 1024;
|
||||
bar.setString(String.format(cardIndex == count ? "%d of %d cards finished! Please close!"
|
||||
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, count, mb));
|
||||
}
|
||||
});
|
||||
p0.add(jComboBox1);
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
|
||||
// Start
|
||||
startDownloadButton = new JButton("Start download");
|
||||
startDownloadButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
new Thread(DownloadPictures.this).start();
|
||||
startDownloadButton.setEnabled(false);
|
||||
checkBox.setEnabled(false);
|
||||
}
|
||||
});
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
|
||||
// Progress
|
||||
p0.add(bar);
|
||||
bar.setStringPainted(true);
|
||||
int count = cards.size();
|
||||
float mb = (count * cardImageSource.getAverageSize()) / 1024;
|
||||
bar.setString(String.format(cardIndex == cards.size() ? "%d of %d cards finished! Please close!"
|
||||
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cards.size(), mb));
|
||||
Dimension d = bar.getPreferredSize();
|
||||
d.width = 300;
|
||||
bar.setPreferredSize(d);
|
||||
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
checkBox = new JCheckBox("Download for current game only.");
|
||||
p0.add(checkBox);
|
||||
p0.add(Box.createVerticalStrut(5));
|
||||
checkBox.setEnabled(!offlineMode);
|
||||
|
||||
checkBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int count = DownloadPictures.this.cards.size();
|
||||
float mb = (count * cardImageSource.getAverageSize()) / 1024;
|
||||
bar.setString(String.format(cardIndex == count ? "%d of %d cards finished! Please close!"
|
||||
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, count, mb));
|
||||
}
|
||||
});
|
||||
|
||||
// JOptionPane
|
||||
Object[] options = { startDownloadButton, closeButton = new JButton("Cancel") };
|
||||
dlg = new JOptionPane(p0, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
|
||||
}
|
||||
|
||||
private static ArrayList<CardInfo> getNeededCards(Set<Card> allCards, String imagesPath) {
|
||||
|
||||
ArrayList<CardInfo> cardsToDownload = new ArrayList<CardInfo>();
|
||||
|
||||
/**
|
||||
* read all card names and urls
|
||||
*/
|
||||
ArrayList<CardInfo> allCardsUrls = new ArrayList<CardInfo>();
|
||||
|
||||
try {
|
||||
offlineMode = true;
|
||||
|
||||
for (Card card : allCards) {
|
||||
if (card.getCardNumber() > 0 && !card.getExpansionSetCode().isEmpty()) {
|
||||
CardInfo url = new CardInfo(card.getName(), card.getExpansionSetCode(), card.getCardNumber(), false);
|
||||
allCardsUrls.add(url);
|
||||
} else {
|
||||
if (card.getCardNumber() < 1) {
|
||||
System.err.println("There was a critical error!");
|
||||
log.error("Card has no collector ID and won't be sent to client: " + card);
|
||||
} else if (card.getExpansionSetCode().isEmpty()) {
|
||||
System.err.println("There was a critical error!");
|
||||
log.error("Card has no set name and won't be sent to client:" + card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allCardsUrls.addAll(getTokenCardUrls());
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
File file;
|
||||
|
||||
/**
|
||||
* check to see which cards we already have
|
||||
*/
|
||||
for (CardInfo card : allCardsUrls) {
|
||||
boolean withCollectorId = false;
|
||||
if (card.getName().equals("Forest") || card.getName().equals("Mountain") || card.getName().equals("Swamp") || card.getName().equals("Island")
|
||||
|| card.getName().equals("Plains")) {
|
||||
withCollectorId = true;
|
||||
}
|
||||
file = new File(CardImageUtils.getImagePath(card, withCollectorId, imagesPath));
|
||||
if (!file.exists()) {
|
||||
cardsToDownload.add(card);
|
||||
}
|
||||
}
|
||||
|
||||
for (CardInfo card : cardsToDownload) {
|
||||
if (card.isToken()) {
|
||||
log.info("Card to download: " + card.getName() + " (Token) ");
|
||||
} else {
|
||||
try {
|
||||
log.info("Card to download: " + card.getName() + " (" + card.getSet() + ")");
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cardsToDownload;
|
||||
}
|
||||
|
||||
private static ArrayList<CardInfo> getTokenCardUrls() throws RuntimeException {
|
||||
ArrayList<CardInfo> list = new ArrayList<CardInfo>();
|
||||
HashSet<String> filter = new HashSet<String>();
|
||||
InputStream in = DownloadPictures.class.getClassLoader().getResourceAsStream("card-pictures-tok.txt");
|
||||
readImageURLsFromFile(in, list, filter);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void readImageURLsFromFile(InputStream in, ArrayList<CardInfo> list, Set<String> filter) throws RuntimeException {
|
||||
if (in == null) {
|
||||
log.error("resources input stream is null");
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedReader reader = null;
|
||||
InputStreamReader input = null;
|
||||
try {
|
||||
input = new InputStreamReader(in);
|
||||
reader = new BufferedReader(input);
|
||||
String line;
|
||||
|
||||
line = reader.readLine();
|
||||
while (line != null) {
|
||||
line = line.trim();
|
||||
if (line.startsWith("|")) { // new format
|
||||
String[] params = line.split("\\|");
|
||||
if (params.length >= 4) {
|
||||
if (params[1].toLowerCase().equals("generate") && params[2].startsWith("TOK:")) {
|
||||
String set = params[2].substring(4);
|
||||
CardInfo card = new CardInfo(params[3], set, 0, true);
|
||||
list.add(card);
|
||||
}
|
||||
} else {
|
||||
log.error("wrong format for image urls: " + line);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error(ex);
|
||||
throw new RuntimeException("DownloadPictures : readFile() error");
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyHandler implements ChangeListener {
|
||||
private int type;
|
||||
|
||||
public ProxyHandler(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (((AbstractButton) e.getSource()).isSelected()) {
|
||||
DownloadPictures.this.type = type;
|
||||
addr.setEnabled(type != 0);
|
||||
port.setEnabled(type != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
File base = new File(this.imagesPath != null ? imagesPath : Constants.IO.imageBaseDir);
|
||||
if (!base.exists()) {
|
||||
base.mkdir();
|
||||
}
|
||||
|
||||
if (type == 0)
|
||||
p = Proxy.NO_PROXY;
|
||||
else
|
||||
try {
|
||||
p = new Proxy(types[type], new InetSocketAddress(addr.getText(), Integer.parseInt(port.getText())));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Gui_DownloadPictures : error 1 - " + ex);
|
||||
}
|
||||
|
||||
if (p != null) {
|
||||
HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
|
||||
|
||||
update(0);
|
||||
for (int i = 0; i < cards.size() && !cancel; i++) {
|
||||
try {
|
||||
|
||||
CardInfo card = cards.get(i);
|
||||
|
||||
log.info("Downloading card: " + card.getName() + " (" + card.getSet() + ")");
|
||||
|
||||
String url;
|
||||
if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
|
||||
if (card.getCollectorId() != 0) {
|
||||
continue;
|
||||
}
|
||||
url = cardImageSource.generateTokenUrl(card.getName(), card.getSet());
|
||||
} else {
|
||||
url = cardImageSource.generateURL(card.getCollectorId(), card.getSet());
|
||||
}
|
||||
|
||||
if (url != null) {
|
||||
Runnable task = new DownloadTask(card, new URL(url), imagesPath);
|
||||
executor.execute(task);
|
||||
} else {
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error(ex, ex);
|
||||
}
|
||||
}
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
closeButton.setText("Close");
|
||||
}
|
||||
|
||||
private final class DownloadTask implements Runnable {
|
||||
private CardInfo card;
|
||||
private URL url;
|
||||
private String imagesPath;
|
||||
|
||||
public DownloadTask(CardInfo card, URL url, String imagesPath) {
|
||||
this.card = card;
|
||||
this.url = url;
|
||||
this.imagesPath = imagesPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
BufferedInputStream in = new BufferedInputStream(url.openConnection(p).getInputStream());
|
||||
|
||||
createDirForCard(card, imagesPath);
|
||||
|
||||
boolean withCollectorId = false;
|
||||
if (card.getName().equals("Forest") || card.getName().equals("Mountain") || card.getName().equals("Swamp")
|
||||
|| card.getName().equals("Island") || card.getName().equals("Plains")) {
|
||||
withCollectorId = true;
|
||||
}
|
||||
File fileOut = new File(CardImageUtils.getImagePath(card, withCollectorId));
|
||||
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileOut));
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int len = 0;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
// user cancelled
|
||||
if (cancel) {
|
||||
in.close();
|
||||
out.flush();
|
||||
out.close();
|
||||
// delete what was written so far
|
||||
fileOut.delete();
|
||||
}
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static File createDirForCard(CardInfo card, String imagesPath) throws Exception {
|
||||
File setDir = new File(CardImageUtils.getImageDir(card, imagesPath));
|
||||
if (!setDir.exists()) {
|
||||
setDir.mkdirs();
|
||||
}
|
||||
return setDir;
|
||||
}
|
||||
|
||||
private void update(int card) {
|
||||
this.cardIndex = card;
|
||||
int count = DownloadPictures.this.cards.size();
|
||||
|
||||
if (cardIndex < count) {
|
||||
float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024;
|
||||
bar.setString(String.format("%d of %d cards finished! Please wait! [%.1f Mb]",
|
||||
card, count, mb));
|
||||
} else {
|
||||
Iterator<CardInfo> cardsIterator = DownloadPictures.this.cards.iterator();
|
||||
while (cardsIterator.hasNext()) {
|
||||
CardInfo cardInfo = cardsIterator.next();
|
||||
boolean withCollectorId = false;
|
||||
if (cardInfo.getName().equals("Forest") || cardInfo.getName().equals("Mountain") || cardInfo.getName().equals("Swamp") || cardInfo.getName().equals("Island")
|
||||
|| cardInfo.getName().equals("Plains")) {
|
||||
withCollectorId = true;
|
||||
}
|
||||
File file = new File(CardImageUtils.getImagePath(cardInfo, withCollectorId));
|
||||
if (file.exists()) {
|
||||
cardsIterator.remove();
|
||||
}
|
||||
}
|
||||
count = DownloadPictures.this.cards.size();
|
||||
|
||||
if (count == 0) {
|
||||
bar.setString(String.format("0 cards remaining! Please close!", count));
|
||||
} else {
|
||||
bar.setString(String.format("%d cards remaining! Please choose another source!", count));
|
||||
executor = Executors.newFixedThreadPool(10);
|
||||
startDownloadButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger log = Logger.getLogger(DownloadPictures.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
package org.mage.plugins.card.images;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import mage.view.CardView;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ComputationException;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.mortennobel.imagescaling.ResampleOp;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
/**
|
||||
* This class stores ALL card images in a cache with soft values. this means
|
||||
* that the images may be collected when they are not needed any more, but will
|
||||
* be kept as long as possible.
|
||||
*
|
||||
* Key format: "<cardname>#<setname>#<collectorID>#<param>"
|
||||
*
|
||||
* where param is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>#Normal: request for unrotated image</li>
|
||||
* <li>#Tapped: request for rotated image</li>
|
||||
* <li>#Cropped: request for cropped image that is used for Shandalar like card
|
||||
* look</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ImageCache {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ImageCache.class);
|
||||
|
||||
private static final Map<String, BufferedImage> imageCache;
|
||||
|
||||
/**
|
||||
* Common pattern for keys.
|
||||
* Format: "<cardname>#<setname>#<collectorID>"
|
||||
*/
|
||||
private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)");
|
||||
|
||||
static {
|
||||
imageCache = new MapMaker().softValues().makeComputingMap(new Function<String, BufferedImage>() {
|
||||
@Override
|
||||
public BufferedImage apply(String key) {
|
||||
try {
|
||||
boolean thumbnail = false;
|
||||
if (key.endsWith("#thumb")) {
|
||||
thumbnail = true;
|
||||
key = key.replace("#thumb", "");
|
||||
}
|
||||
Matcher m = KEY_PATTERN.matcher(key);
|
||||
|
||||
if (m.matches()) {
|
||||
String name = m.group(1);
|
||||
String set = m.group(2);
|
||||
Integer collectorId = Integer.parseInt(m.group(3));
|
||||
|
||||
CardInfo info = new CardInfo(name, set, collectorId);
|
||||
|
||||
if (collectorId == 0) info.setToken(true);
|
||||
String path = CardImageUtils.getImagePath(info);
|
||||
if (path == null) return null;
|
||||
File file = new File(path);
|
||||
|
||||
if (thumbnail && path.endsWith(".jpg")) {
|
||||
String thumbnailPath = path.replace(".jpg", ".thumb.jpg");
|
||||
File thumbnailFile = new File(thumbnailPath);
|
||||
if (thumbnailFile.exists()) {
|
||||
//log.debug("loading thumbnail for " + key + ", path="+thumbnailPath);
|
||||
return loadImage(thumbnailFile);
|
||||
} else {
|
||||
BufferedImage image = loadImage(file);
|
||||
image = getWizardsCard(image);
|
||||
if (image == null) return null;
|
||||
//log.debug("creating thumbnail for " + key);
|
||||
return makeThumbnail(image, thumbnailPath);
|
||||
}
|
||||
} else {
|
||||
return getWizardsCard(loadImage(file));
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof ComputationException)
|
||||
throw (ComputationException) ex;
|
||||
else
|
||||
throw new ComputationException(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static BufferedImage getWizardsCard(BufferedImage image) {
|
||||
if (image.getWidth() == 265 && image.getHeight() == 370) {
|
||||
BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics2D = crop.createGraphics();
|
||||
graphics2D.drawImage(image, 0, 0, 255, 360, 5, 5, 261, 365, null);
|
||||
graphics2D.dispose();
|
||||
return crop;
|
||||
} else {
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedImage getThumbnail(CardView card) {
|
||||
String key = getKey(card) + "#thumb";
|
||||
//log.debug("#key: " + key);
|
||||
return getImage(key);
|
||||
}
|
||||
|
||||
public static BufferedImage getImageOriginal(CardView card) {
|
||||
String key = getKey(card);
|
||||
//log.debug("#key: " + key);
|
||||
return getImage(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Image corresponding to the key
|
||||
*/
|
||||
private static BufferedImage getImage(String key) {
|
||||
try {
|
||||
BufferedImage image = imageCache.get(key);
|
||||
return image;
|
||||
} catch (NullPointerException ex) {
|
||||
// unfortunately NullOutputException, thrown when apply() returns
|
||||
// null, is not public
|
||||
// NullOutputException is a subclass of NullPointerException
|
||||
// legitimate, happens when a card has no image
|
||||
return null;
|
||||
} catch (ComputationException ex) {
|
||||
if (ex.getCause() instanceof NullPointerException)
|
||||
return null;
|
||||
log.error(ex,ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map key for a card, without any suffixes for the image size.
|
||||
*/
|
||||
private static String getKey(CardView card) {
|
||||
String set = card.getExpansionSetCode();
|
||||
String key = card.getName() + "#" + set + "#" + String.valueOf(card.getCardNumber());
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load image from file
|
||||
*
|
||||
* @param file
|
||||
* file to load image from
|
||||
* @return {@link BufferedImage}
|
||||
*/
|
||||
public static BufferedImage loadImage(File file) {
|
||||
BufferedImage image = null;
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
image = ImageIO.read(file);
|
||||
} catch (Exception e) {
|
||||
log.error(e, e);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public static BufferedImage makeThumbnail(BufferedImage original, String path) {
|
||||
BufferedImage image = getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL);
|
||||
File imagePath = new File(path);
|
||||
try {
|
||||
//log.debug("thumbnail path:"+path);
|
||||
ImageIO.write(image, "jpg", imagePath);
|
||||
} catch (Exception e) {
|
||||
log.error(e,e);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image scaled to the size given
|
||||
*/
|
||||
public static BufferedImage getNormalSizeImage(BufferedImage original) {
|
||||
if (original == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int srcWidth = original.getWidth();
|
||||
int srcHeight = original.getHeight();
|
||||
|
||||
int tgtWidth = Constants.CARD_SIZE_FULL.width;
|
||||
int tgtHeight = Constants.CARD_SIZE_FULL.height;
|
||||
|
||||
if (srcWidth == tgtWidth && srcHeight == tgtHeight)
|
||||
return original;
|
||||
|
||||
ResampleOp resampleOp = new ResampleOp(tgtWidth, tgtHeight);
|
||||
BufferedImage image = resampleOp.filter(original, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image scaled to the size appropriate for the card picture
|
||||
* panel For future use.
|
||||
*/
|
||||
private static BufferedImage getFullSizeImage(BufferedImage original, double scale) {
|
||||
if (scale == 1)
|
||||
return original;
|
||||
ResampleOp resampleOp = new ResampleOp((int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
|
||||
BufferedImage image = resampleOp.filter(original, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image scaled to the size appropriate for the card picture
|
||||
* panel
|
||||
*/
|
||||
public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) {
|
||||
ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height);
|
||||
BufferedImage image = resampleOp.filter(original, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image appropriate to display the card in the picture panel
|
||||
*/
|
||||
public static BufferedImage getImage(CardView card, int width, int height) {
|
||||
String key = getKey(card);
|
||||
BufferedImage original = getImage(key);
|
||||
if (original == null)
|
||||
return null;
|
||||
|
||||
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight());
|
||||
if (scale > 1)
|
||||
scale = 1;
|
||||
|
||||
return getFullSizeImage(original, scale);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
package org.mage.plugins.card.info;
|
||||
|
||||
import mage.Constants;
|
||||
import mage.components.CardInfoPane;
|
||||
import mage.utils.CardUtil;
|
||||
import mage.utils.ThreadUtils;
|
||||
import mage.view.CardView;
|
||||
import mage.view.CounterView;
|
||||
import mage.view.PermanentView;
|
||||
import org.mage.card.arcane.ManaSymbols;
|
||||
import org.mage.card.arcane.UI;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Card info pane for displaying card rules.
|
||||
* Supports drawing mana symbols.
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane {
|
||||
|
||||
private CardView currentCard;
|
||||
|
||||
public CardInfoPaneImpl() {
|
||||
UI.setHTMLEditorKit(this);
|
||||
setEditable(false);
|
||||
setBackground(Color.white);
|
||||
}
|
||||
|
||||
public void setCard(final CardView card) {
|
||||
if (card == null) return;
|
||||
if (isCurrentCard(card)) return;
|
||||
currentCard = card;
|
||||
|
||||
ThreadUtils.threadPool.submit(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
if (!card.equals(currentCard)) return;
|
||||
|
||||
String manaCost = "";
|
||||
for (String m : card.getManaCost()) {
|
||||
manaCost += m;
|
||||
}
|
||||
String castingCost = UI.getDisplayManaCost(manaCost);
|
||||
castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, false);
|
||||
|
||||
int symbolCount = 0;
|
||||
int offset = 0;
|
||||
while ((offset = castingCost.indexOf("<img", offset) + 1) != 0)
|
||||
symbolCount++;
|
||||
|
||||
List<String> rules = card.getRules();
|
||||
List<String> rulings = new ArrayList<String>(rules);
|
||||
|
||||
if (card instanceof PermanentView) {
|
||||
List<CounterView> counters = ((PermanentView) card).getCounters();
|
||||
int count = counters != null ? counters.size() : 0;
|
||||
if (count > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int index = 0;
|
||||
for (CounterView counter : ((PermanentView) card).getCounters()) {
|
||||
if (counter.getCount() > 0) {
|
||||
if (index == 0) {
|
||||
sb.append("<b>Counters:</b> ");
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(counter.getCount() + "x<i>" + counter.getName() + "</i>");
|
||||
index++;
|
||||
}
|
||||
}
|
||||
rulings.add(sb.toString());
|
||||
}
|
||||
int damage = ((PermanentView)card).getDamage();
|
||||
if (damage > 0) {
|
||||
rulings.add("<span color='red'><b>Damage dealt:</b> " + damage + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
boolean smallImages = true;
|
||||
int fontSize = 11;
|
||||
|
||||
String fontFamily = "tahoma";
|
||||
/*if (prefs.fontFamily == CardFontFamily.arial)
|
||||
fontFamily = "arial";
|
||||
else if (prefs.fontFamily == CardFontFamily.verdana) {
|
||||
fontFamily = "verdana";
|
||||
}*/
|
||||
|
||||
final StringBuffer buffer = new StringBuffer(512);
|
||||
buffer.append("<html><body style='font-family:");
|
||||
buffer.append(fontFamily);
|
||||
buffer.append(";font-size:");
|
||||
buffer.append(fontSize);
|
||||
buffer.append("pt;margin:0px 1px 0px 1px'>");
|
||||
buffer.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'>");
|
||||
buffer.append("<tr><td valign='top'><b>");
|
||||
buffer.append(card.getName());
|
||||
buffer.append("</b></td><td align='right' valign='top' style='width:");
|
||||
buffer.append(symbolCount * 11 + 1);
|
||||
buffer.append("px'>");
|
||||
buffer.append(castingCost);
|
||||
buffer.append("</td></tr></table>");
|
||||
buffer.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'><tr><td style='margin-left: 1px'>");
|
||||
buffer.append(getTypes(card));
|
||||
buffer.append("</td><td align='right'>");
|
||||
switch (card.getRarity()) {
|
||||
case RARE:
|
||||
buffer.append("<b color='#FFBF00'>");
|
||||
break;
|
||||
case UNCOMMON:
|
||||
buffer.append("<b color='silver'>");
|
||||
break;
|
||||
case COMMON:
|
||||
buffer.append("<b color='black'>");
|
||||
break;
|
||||
case MYTHIC:
|
||||
buffer.append("<b color='#D5330B'>");
|
||||
break;
|
||||
}
|
||||
String rarity = card.getRarity().getCode();
|
||||
if (card.getExpansionSetCode() != null) {
|
||||
buffer.append(ManaSymbols.replaceSetCodeWithHTML(card.getExpansionSetCode().toUpperCase(), rarity));
|
||||
}
|
||||
buffer.append("</td></tr></table>");
|
||||
|
||||
String pt = "";
|
||||
if (CardUtil.isCreature(card)) {
|
||||
pt = card.getPower() + "/" + card.getToughness();
|
||||
} else if (CardUtil.isPlaneswalker(card)) {
|
||||
pt = card.getLoyalty().toString();
|
||||
}
|
||||
if (pt.length() > 0) {
|
||||
buffer.append("<table cellspacing=0 cellpadding=0 border=0 width='100%' valign='bottom'><tr><td>");
|
||||
buffer.append("<b>");
|
||||
buffer.append(pt);
|
||||
buffer.append("</b>");
|
||||
buffer.append("</td></tr></table>");
|
||||
}
|
||||
|
||||
String legal = "";
|
||||
if (rulings.size() > 0) {
|
||||
legal = legal.replaceAll("#([^#]+)#", "<i>$1</i>");
|
||||
legal = legal.replaceAll("\\s*//\\s*", "<hr width='50%'>");
|
||||
legal = legal.replace("\r\n", "<div style='font-size:5pt'></div>");
|
||||
legal += "<br>";
|
||||
for (String ruling : rulings) {
|
||||
if (!ruling.replace(".", "").trim().isEmpty()) {
|
||||
legal += "<p style='margin: 2px'>";
|
||||
legal += ruling;
|
||||
legal += "</p>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (legal.length() > 0) {
|
||||
//buffer.append("<br>");
|
||||
legal = legal.replaceAll("\\{this\\}", card.getName());
|
||||
legal = legal.replaceAll("\\{source\\}", card.getName());
|
||||
buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, smallImages));
|
||||
}
|
||||
|
||||
buffer.append("<br></body></html>");
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (!card.equals(currentCard)) return;
|
||||
setText(buffer.toString());
|
||||
//System.out.println(buffer.toString());
|
||||
setCaretPosition(0);
|
||||
//ThreadUtils.sleep(300);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getTypes(CardView card) {
|
||||
String types = "";
|
||||
for (String superType : card.getSuperTypes()) {
|
||||
types += superType + " ";
|
||||
}
|
||||
for (Constants.CardType cardType : card.getCardTypes()) {
|
||||
types += cardType.toString() + " ";
|
||||
}
|
||||
if (card.getSubTypes().size() > 0) {
|
||||
types += "- ";
|
||||
}
|
||||
for (String subType : card.getSubTypes()) {
|
||||
types += subType + " ";
|
||||
}
|
||||
return types.trim();
|
||||
}
|
||||
|
||||
public boolean isCurrentCard(CardView card) {
|
||||
return currentCard != null && card.equals(currentCard);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package org.mage.plugins.card.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
|
||||
public class SettingsManager {
|
||||
|
||||
private static SettingsManager settingsManager = null;
|
||||
|
||||
public static SettingsManager getIntance() {
|
||||
if (settingsManager == null) {
|
||||
synchronized (SettingsManager.class) {
|
||||
if (settingsManager == null) settingsManager = new SettingsManager();
|
||||
}
|
||||
}
|
||||
return settingsManager;
|
||||
}
|
||||
|
||||
private SettingsManager() {
|
||||
loadImageProperties();
|
||||
}
|
||||
|
||||
public void reloadImageProperties() {
|
||||
loadImageProperties();
|
||||
}
|
||||
|
||||
private void loadImageProperties() {
|
||||
imageUrlProperties = new Properties();
|
||||
try {
|
||||
InputStream is = SettingsManager.class.getClassLoader().getResourceAsStream(Constants.IO.IMAGE_PROPERTIES_FILE);
|
||||
if (is == null)
|
||||
throw new RuntimeException("Couldn't load " + Constants.IO.IMAGE_PROPERTIES_FILE);
|
||||
imageUrlProperties.load(is);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSetNameReplacement(String setName) {
|
||||
String result = setName;
|
||||
if (imageUrlProperties != null) {
|
||||
result = imageUrlProperties.getProperty(setName, setName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public HashSet<String> getIgnoreUrls() {
|
||||
HashSet<String> ignoreUrls = new HashSet<String>();
|
||||
if (imageUrlProperties != null) {
|
||||
String result = imageUrlProperties.getProperty("ignore.urls");
|
||||
if (result != null) {
|
||||
String[] ignore = result.split(",");
|
||||
ignoreUrls.addAll(Arrays.asList(ignore));
|
||||
}
|
||||
}
|
||||
return ignoreUrls;
|
||||
}
|
||||
|
||||
public ArrayList<String> getTokenLookupOrder() {
|
||||
ArrayList<String> order = new ArrayList<String>();
|
||||
if (imageUrlProperties != null) {
|
||||
String result = imageUrlProperties.getProperty("token.lookup.order");
|
||||
if (result != null) {
|
||||
String[] sets = result.split(",");
|
||||
order.addAll(Arrays.asList(sets));
|
||||
}
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
private Properties imageUrlProperties;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package org.mage.plugins.card.utils;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageObserver;
|
||||
|
||||
/**
|
||||
* Utility class for creating BufferedImage object from Image instance.
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
public class BufferedImageBuilder {
|
||||
|
||||
private static final int DEFAULT_IMAGE_TYPE = BufferedImage.TYPE_INT_RGB;
|
||||
|
||||
/**
|
||||
* Hide constructor
|
||||
*/
|
||||
private BufferedImageBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public static BufferedImage bufferImage(Image image) {
|
||||
return bufferImage(image, DEFAULT_IMAGE_TYPE);
|
||||
}
|
||||
|
||||
public static BufferedImage bufferImage(Image image, int type) {
|
||||
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
|
||||
Graphics2D g = bufferedImage.createGraphics();
|
||||
g.drawImage(image, null, null);
|
||||
//waitForImage(bufferedImage);
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
private void waitForImage(BufferedImage bufferedImage) {
|
||||
final ImageLoadStatus imageLoadStatus = new ImageLoadStatus();
|
||||
bufferedImage.getHeight(new ImageObserver() {
|
||||
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||
if (infoflags == ALLBITS) {
|
||||
imageLoadStatus.heightDone = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
bufferedImage.getWidth(new ImageObserver() {
|
||||
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||
if (infoflags == ALLBITS) {
|
||||
imageLoadStatus.widthDone = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
while (!imageLoadStatus.widthDone && !imageLoadStatus.heightDone) {
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImageLoadStatus {
|
||||
|
||||
public boolean widthDone = false;
|
||||
public boolean heightDone = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package org.mage.plugins.card.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.images.CardInfo;
|
||||
import org.mage.plugins.card.properties.SettingsManager;
|
||||
|
||||
public class CardImageUtils {
|
||||
|
||||
private static HashMap<CardInfo, String> pathCache = new HashMap<CardInfo, String>();
|
||||
|
||||
/**
|
||||
* Get path to image for specific card.
|
||||
*
|
||||
* @param card
|
||||
* card to get path for
|
||||
* @return String if image exists, else null
|
||||
*/
|
||||
public static String getImagePath(CardInfo card) {
|
||||
String filePath;
|
||||
String suffix = ".jpg";
|
||||
|
||||
File file = null;
|
||||
if (card.isToken()) {
|
||||
if (pathCache.containsKey(card)) {
|
||||
return pathCache.get(card);
|
||||
}
|
||||
filePath = getTokenImagePath(card);
|
||||
file = new File(filePath);
|
||||
|
||||
if (!file.exists()) {
|
||||
filePath = searchForCardImage(card);
|
||||
file = new File(filePath);
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
pathCache.put(card, filePath);
|
||||
}
|
||||
} else {
|
||||
filePath = getImagePath(card, false);
|
||||
file = new File(filePath);
|
||||
|
||||
if (!file.exists()) {
|
||||
filePath = getImagePath(card, true);
|
||||
file = new File(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* try current directory
|
||||
*/
|
||||
if (file == null || !file.exists()) {
|
||||
filePath = cleanString(card.getName()) + suffix;
|
||||
file = new File(filePath);
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
return filePath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTokenImagePath(CardInfo card) {
|
||||
String filename = getImagePath(card, false);
|
||||
|
||||
File file = new File(filename);
|
||||
if (!file.exists()) {
|
||||
CardInfo updated = new CardInfo(card);
|
||||
updated.setName(card.getName() + " 1");
|
||||
filename = getImagePath(updated, false);
|
||||
file = new File(filename);
|
||||
if (!file.exists()) {
|
||||
updated = new CardInfo(card);
|
||||
updated.setName(card.getName() + " 2");
|
||||
filename = getImagePath(updated, false);
|
||||
file = new File(filename);
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
private static String searchForCardImage(CardInfo card) {
|
||||
File file = null;
|
||||
String path = "";
|
||||
CardInfo c = new CardInfo(card);
|
||||
|
||||
for (String set : SettingsManager.getIntance().getTokenLookupOrder()) {
|
||||
c.setSet(set);
|
||||
path = getTokenImagePath(c);
|
||||
file = new File(path);
|
||||
if (file.exists()) {
|
||||
pathCache.put(card, path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String cleanString(String in) {
|
||||
in = in.trim();
|
||||
StringBuilder out = new StringBuilder();
|
||||
char c;
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
c = in.charAt(i);
|
||||
if (c == ' ' || c == '-')
|
||||
out.append('_');
|
||||
else if (Character.isLetterOrDigit(c)) {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public static String updateSet(String cardSet, boolean forUrl) {
|
||||
String set = cardSet.toLowerCase();
|
||||
if (set.equals("con")) {
|
||||
set = "cfx";
|
||||
}
|
||||
if (forUrl) {
|
||||
set = SettingsManager.getIntance().getSetNameReplacement(set);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public static String getImageDir(CardInfo card, String imagesPath) {
|
||||
if (card.getSet() == null) {
|
||||
return "";
|
||||
}
|
||||
String set = updateSet(card.getSet(), false).toUpperCase();
|
||||
String imagesDir = (imagesPath != null ? imagesPath : Constants.IO.imageBaseDir);
|
||||
if (card.isToken()) {
|
||||
return imagesDir + File.separator + "TOK" + File.separator + set;
|
||||
} else {
|
||||
return imagesDir + File.separator + set;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getImagePath(CardInfo card, boolean withCollector) {
|
||||
return getImagePath(card, withCollector, null);
|
||||
}
|
||||
|
||||
public static String getImagePath(CardInfo card, boolean withCollector, String imagesPath) {
|
||||
if (withCollector) {
|
||||
return getImageDir(card, imagesPath) + File.separator + card.getName() + "." + card.getCollectorId() + ".full.jpg";
|
||||
} else {
|
||||
return getImageDir(card, imagesPath) + File.separator + card.getName() + ".full.jpg";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package org.mage.plugins.card.utils;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface ImageManager {
|
||||
public Image getSicknessImage();
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.mage.plugins.card.utils;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.FilteredImageSource;
|
||||
import java.awt.image.ImageFilter;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
public class Transparency {
|
||||
public static Image makeColorTransparent(Image im, final Color color) {
|
||||
ImageFilter filter = new RGBImageFilter() {
|
||||
// the color we are looking for... Alpha bits are set to opaque
|
||||
public int markerRGB = color.getRGB() | 0xFF000000;
|
||||
|
||||
@Override
|
||||
public final int filterRGB(int x, int y, int rgb) {
|
||||
if ((rgb | 0xFF000000) == markerRGB) {
|
||||
// Mark the alpha bits as zero - transparent
|
||||
return 0x00FFFFFF & rgb;
|
||||
} else {
|
||||
// nothing to do
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
|
||||
return Toolkit.getDefaultToolkit().createImage(ip);
|
||||
}
|
||||
|
||||
public static BufferedImage makeImageTranslucent(BufferedImage source,
|
||||
double alpha) {
|
||||
BufferedImage target = new BufferedImage(source.getWidth(), source
|
||||
.getHeight(), java.awt.Transparency.TRANSLUCENT);
|
||||
// Get the images graphics
|
||||
Graphics2D g = target.createGraphics();
|
||||
// Set the Graphics composite to Alpha
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
|
||||
(float) alpha));
|
||||
// Draw the image into the prepared reciver image
|
||||
g.drawImage(source, null, 0, 0);
|
||||
// let go of all system resources in this Graphics
|
||||
g.dispose();
|
||||
// Return the image
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
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.CropImageFilter;
|
||||
import java.awt.image.FilteredImageSource;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.mage.plugins.card.utils.BufferedImageBuilder;
|
||||
import org.mage.plugins.card.utils.ImageManager;
|
||||
import org.mage.plugins.card.utils.Transparency;
|
||||
|
||||
public class ImageManagerImpl implements ImageManager {
|
||||
|
||||
private static ImageManagerImpl fInstance = new ImageManagerImpl();
|
||||
|
||||
public static ImageManagerImpl getInstance() {
|
||||
return fInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage getSicknessImage() {
|
||||
if (imageSickness == null) {
|
||||
Image image = getImageFromResourceTransparent("/sickness.png", Color.WHITE, new Rectangle(296, 265));
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
image = tk.createImage(new FilteredImageSource(image.getSource(), new CropImageFilter(0, 0, 200, 285)));
|
||||
imageSickness = BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
return imageSickness;
|
||||
}
|
||||
|
||||
protected static Image getImageFromResourceTransparent(String path, Color mask, Rectangle rec) {
|
||||
BufferedImage image = null;
|
||||
Image imageCardTransparent = null;
|
||||
Image resized = null;
|
||||
|
||||
URL imageURL = ImageManager.class.getResource(path);
|
||||
|
||||
try {
|
||||
image = ImageIO.read(imageURL);
|
||||
imageCardTransparent = Transparency.makeColorTransparent(image, mask);
|
||||
|
||||
resized = imageCardTransparent.getScaledInstance(rec.width, rec.height, java.awt.Image.SCALE_SMOOTH);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return resized;
|
||||
}
|
||||
|
||||
private static BufferedImage imageSickness = null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue