diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index 76158ee4df4..9d8aed08b81 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -47,6 +47,12 @@ java-image-scaling 0.8.4 + + com.google.collections + google-collections + 1.0 + provided + diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 67687103ba2..ca3e208f597 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -37,19 +37,24 @@ package mage.client; import java.awt.Cursor; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.io.File; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; import javax.swing.Box; +import javax.swing.JButton; import javax.swing.JDesktopPane; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; +import javax.swing.JToolBar.Separator; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import mage.cards.Card; +import mage.cards.ExpansionSet; import mage.client.dialog.AboutDialog; import mage.client.dialog.CombatDialog; import mage.client.dialog.ConnectDialog; @@ -57,11 +62,8 @@ import mage.client.dialog.PickNumberDialog; import mage.client.plugins.impl.Plugins; import mage.client.remote.Session; import mage.client.util.EDTExceptionHandler; -import mage.interfaces.plugin.ThemePlugin; +import mage.sets.Sets; import mage.util.Logging; -import net.xeoh.plugins.base.PluginManager; -import net.xeoh.plugins.base.impl.PluginManagerFactory; -import net.xeoh.plugins.base.util.PluginManagerUtil; /** * @@ -127,7 +129,31 @@ public class MageFrame extends javax.swing.JFrame { enableButtons(); else disableButtons(); + + //TODO: + Separator separator = new javax.swing.JToolBar.Separator(); + mageToolbar.add(separator); + + JButton btnDownload = new JButton("Images"); + btnDownload.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); + btnDownload.setFocusable(false); + btnDownload.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnDownload.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnDownload.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnImagesActionPerformed(evt); + } + }); + mageToolbar.add(btnDownload); } + + private void btnImagesActionPerformed(java.awt.event.ActionEvent evt) { + Set allCards = new LinkedHashSet(); + for (ExpansionSet set: Sets.getInstance().values()) { + allCards.addAll(set.createCards()); + } + Plugins.getInstance().downloadImage(allCards); + } public void showGame(UUID gameId, UUID playerId) { this.tablesPane.hideTables(); diff --git a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java index aa2b2fed676..b972e588765 100644 --- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java @@ -84,9 +84,6 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon ui.put("jScrollPane", jScrollPane); ui.put("battlefieldPanel", this); initComponents(); - - addMouseListener(new MageMouseAdapter(this, gameId)); - addMouseMotionListener(new MageMouseMotionAdapter(this, bigCard)); } public void init(UUID gameId, BigCard bigCard) { @@ -95,13 +92,13 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon if (Plugins.getInstance().isCardPluginLoaded()) { bigCard.removeTextComponent(); } + addMouseListener(new MageMouseAdapter(this, gameId)); + addMouseMotionListener(new MageMouseMotionAdapter(this, bigCard)); } public void update(Map battlefield) { for (PermanentView permanent: battlefield.values()) { if (!permanents.containsKey(permanent.getId())) { - //TODO: remove me - //System.out.println("Add permanent: " + permanent.getCardNumber()); addPermanent(permanent); } else { @@ -133,8 +130,10 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon } permanents.put(permanent.getId(), perm); this.add(perm, 10); - moveToFront(perm); - perm.update(permanent); + if (!Plugins.getInstance().isCardPluginLoaded()) { + moveToFront(perm); + perm.update(permanent); + } } private void groupAttachments(PermanentView permanent) { @@ -228,7 +227,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon } private void resizeBattlefield() { - Dimension area = new Dimension(0, 0); + /*Dimension area = new Dimension(0, 0); Dimension size = getPreferredSize(); for (Component comp: getComponents()) { @@ -244,7 +243,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon setPreferredSize(area); revalidate(); repaint(); - } + }*/ } diff --git a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java index d4fce6bc6c7..b19e253236a 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java @@ -2,10 +2,12 @@ package mage.client.plugins; import java.util.Collection; import java.util.Map; +import java.util.Set; import java.util.UUID; import javax.swing.JComponent; +import mage.cards.Card; import mage.cards.CardDimensions; import mage.cards.MagePermanent; import mage.client.cards.BigCard; @@ -18,4 +20,5 @@ public interface MagePlugins { MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, CardDimensions dimension, UUID gameId); boolean isCardPluginLoaded(); void sortPermanents(Map ui, Collection permanents); + void downloadImage(Set allCards); } diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageMouseMotionAdapter.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageMouseMotionAdapter.java index efa2d8b942b..8f2b5bb1ab3 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageMouseMotionAdapter.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageMouseMotionAdapter.java @@ -24,6 +24,7 @@ public class MageMouseMotionAdapter extends MouseMotionAdapter { @Override public void mouseMoved(MouseEvent e) { if (!Plugins.getInstance().isCardPluginLoaded()) return; + if (bigCard == null) return; Object o = parent.getComponentAt(e.getPoint()); if (o instanceof MagePermanent) { MagePermanent card = (MagePermanent) o; diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index 00d4c594bf7..ef786e7562b 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -2,14 +2,18 @@ package mage.client.plugins.impl; import java.io.File; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JComponent; +import mage.cards.Card; import mage.cards.CardDimensions; +import mage.cards.ExpansionSet; import mage.cards.MagePermanent; import mage.cards.action.impl.EmptyCallback; import mage.client.cards.BigCard; @@ -20,6 +24,7 @@ import mage.client.util.DefaultActionCallback; import mage.constants.Constants; import mage.interfaces.plugin.CardPlugin; import mage.interfaces.plugin.ThemePlugin; +import mage.sets.Sets; import mage.util.Logging; import mage.view.PermanentView; import net.xeoh.plugins.base.PluginManager; @@ -81,4 +86,9 @@ public class Plugins implements MagePlugins { public void sortPermanents(Map ui, Collection permanents) { if (this.cardPlugin != null) this.cardPlugin.sortPermanents(ui, permanents); } + + @Override + public void downloadImage(Set allCards) { + if (this.cardPlugin != null) this.cardPlugin.downloadImages(allCards); + } } diff --git a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java index 584f611ffd9..91f2f110ae6 100644 --- a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java +++ b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java @@ -2,10 +2,12 @@ package mage.interfaces.plugin; import java.util.Collection; import java.util.Map; +import java.util.Set; import java.util.UUID; import javax.swing.JComponent; +import mage.cards.Card; import mage.cards.CardDimensions; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; @@ -21,4 +23,5 @@ import net.xeoh.plugins.base.Plugin; public interface CardPlugin extends Plugin { MagePermanent getMagePermanent(PermanentView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback); void sortPermanents(Map ui, Collection cards); + void downloadImages(Set allCards); } diff --git a/Mage.Plugins/Mage.Card.Plugin/pom.xml b/Mage.Plugins/Mage.Card.Plugin/pom.xml index 5a674465c5f..2e0ea047534 100644 --- a/Mage.Plugins/Mage.Card.Plugin/pom.xml +++ b/Mage.Plugins/Mage.Card.Plugin/pom.xml @@ -18,8 +18,8 @@ - org.mage - Mage-Common + org.mage + Mage-Common ${mage-version} @@ -33,6 +33,18 @@ 1.2.9 provided + + com.google.collections + google-collections + 1.0 + provided + + + com.mortennobel + java-image-scaling + 0.8.4 + provided + diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java index 606b3ae73ca..989e7776124 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.JRootPane; import javax.swing.SwingUtilities; @@ -28,6 +27,8 @@ import mage.view.PermanentView; 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; + @SuppressWarnings({"unchecked","rawtypes"}) public class CardPanel extends MagePermanent { @@ -125,21 +126,15 @@ public class CardPanel extends MagePermanent { Util.threadPool.submit(new Runnable() { public void run () { - //BufferedImage srcImage = null; //TODO: ImageCache.getImageOriginal(gameCard); - tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0; - - try { - log.info(gameCard.getCardNumber() + " " + gameCard.getName() + " " + gameCard.getExpansionSetCode()); - BufferedImage srcImage = ImageIO.read(CardPanel.class.getClassLoader().getResourceAsStream("Mountain.40.full.jpg")); + tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0; + BufferedImage srcImage = ImageCache.getImageOriginal(gameCard); if (srcImage != null) { //setImage(srcImage, ImageUtil.getBlurredImage(srcImage, 3, 1.0f)); hasImage = true; setText(gameCard); setImage(srcImage, srcImage); } - } catch (IOException io) { - io.printStackTrace(); - } + } }); } @@ -424,7 +419,7 @@ public class CardPanel extends MagePermanent { @Override public boolean isTapped() { - return false; + return gameCard.isTapped(); } @Override diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 1fc33d21f2e..a4c5a4a6084 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -1,18 +1,18 @@ package org.mage.plugins.card; -import java.awt.Color; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; -import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JLayeredPane; import javax.swing.JScrollPane; +import mage.cards.Card; import mage.cards.CardDimensions; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; @@ -27,7 +27,14 @@ import net.xeoh.plugins.base.annotations.meta.Author; import org.apache.log4j.Logger; import org.mage.card.arcane.CardPanel; import org.mage.plugins.card.constants.Constants; +import org.mage.plugins.card.images.DownloadPictures; +/** + * {@link CardPlugin} implementation. + * + * @version 0.1 01.11.2010 + * @author nantuko + */ @PluginImplementation @Author(name = "nantuko") public class CardPluginImpl implements CardPlugin { @@ -62,7 +69,7 @@ public class CardPluginImpl implements CardPlugin { } public String toString() { - return "[Card plugin, version 0.1]"; + return "[Card plugin, version 0.2]"; } @Override @@ -216,7 +223,7 @@ public class CardPluginImpl implements CardPlugin { int panelY = y + (stackPosition * stackSpacingY); //panel.setLocation(panelX, panelY); battlefieldPanel.moveToBack(panel); - panel.setCardBounds(panelX + 100, panelY+70, cardWidth, cardHeight); + panel.setCardBounds(panelX, panelY, cardWidth, cardHeight); } rowBottom = Math.max(rowBottom, y + stack.getHeight()); x += stack.getWidth(); @@ -370,5 +377,8 @@ public class CardPluginImpl implements CardPlugin { } } - + @Override + public void downloadImages(Set allCards) { + DownloadPictures.startDownload(null, allCards); + } } diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardUrl.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardUrl.java new file mode 100644 index 00000000000..f318d5a12e0 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardUrl.java @@ -0,0 +1,55 @@ +package org.mage.plugins.card; + +import java.io.Serializable; + +/** + * Contains card data and image url. + * + * @author nantuko + */ +public class CardUrl implements Serializable { + + public String name; + public String set; + public boolean token = false; + public Integer collector; + public String url = ""; + public boolean existsInTheGame = false; + + public CardUrl(String cardName, String cardSet, Integer collectorId, boolean isToken) { + name = cardName; + set = cardSet; + collector = collectorId; + token = isToken; + } + + @Override + public boolean equals(Object other) { + if (other == null) { return false; } + if (other instanceof CardUrl) { + return name.equals(((CardUrl) other).name) && set.equals(((CardUrl) other).set) + && collector.equals(((CardUrl) other).collector) && token==((CardUrl)other).token; + } + return false; + } + + @Override + public int hashCode() { + int hash = 0; + hash = name.hashCode(); + hash = 31*hash + set.hashCode(); + hash = 31*hash + collector.hashCode(); + hash = 31*hash + (token ? 1 : 0); + return hash; + } + + public boolean isExistsInTheGame() { + return existsInTheGame; + } + + public void setExistsInTheGame(boolean existsInTheGame) { + this.existsInTheGame = existsInTheGame; + } + + private static final long serialVersionUID = 2L; +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/constants/Constants.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/constants/Constants.java index 632578704cc..0713b66a342 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/constants/Constants.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/constants/Constants.java @@ -1,13 +1,20 @@ package org.mage.plugins.card.constants; import java.awt.Rectangle; +import java.io.File; public class Constants { public static final String RESOURCE_PATH = "/images"; public static final String RESOURCE_PATH_MANA = resourcePath("mana"); - public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149); + public interface IO { + public static final String imageBaseDir = "." + File.separator + "plugins" + File.separator + "images" + File.separator; + 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"; + /** * Build resource path. * diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/CardInfo.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/CardInfo.java new file mode 100644 index 00000000000..66c6b808e3a --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/CardInfo.java @@ -0,0 +1,13 @@ +package org.mage.plugins.card.images; + +public class CardInfo { + public String name; + public String set; + public Integer collectorId; + public boolean isToken = false; + public CardInfo(String name, String set, Integer collectorId) { + this.name = name; + this.set = set; + this.collectorId = collectorId; + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/DownloadPictures.java new file mode 100644 index 00000000000..8a448fe06ae --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/DownloadPictures.java @@ -0,0 +1,409 @@ +package org.mage.plugins.card.images; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +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.CardUrl; +import org.mage.plugins.card.constants.Constants; +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 close; + private int cardIndex; + private ArrayList cards; + private ArrayList cardsInGame; + private JComboBox jComboBox1; + private JLabel jLabel1; + private static boolean offlineMode = false; + private JCheckBox checkBox; + + public static final Proxy.Type[] types = Proxy.Type.values(); + + public static void main(String[] args) { + startDownload(null, null); + } + + public static void startDownload(JFrame frame, Set allCards) { + ArrayList cards = getNeededCards(allCards); + + if (cards == null || cards.size() == 0) { + JOptionPane.showMessageDialog(null, "All card pictures have been downloaded."); + return; + } + + DownloadPictures download = new DownloadPictures(cards); + JDialog dlg = download.getDlg(frame); + dlg.setVisible(true); + dlg.dispose(); + download.setCancel(true); + } + + public JDialog getDlg(JFrame frame) { + String title = "Downloading"; + if (offlineMode) { + title += " (using local card db)"; + } + final JDialog dlg = this.dlg.createDialog(frame, title); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dlg.setVisible(false); + } + }); + return dlg; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public DownloadPictures(ArrayList cards) { + this.cards = cards; + + this.cardsInGame = new ArrayList(); + for (CardUrl url : cards) { + if (url.isExistsInTheGame()) + cardsInGame.add(url); + } + + 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" }); + jComboBox1 = new JComboBox(); + + jComboBox1.setModel(jComboBox1Model); + jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT); + p0.add(jComboBox1); + p0.add(Box.createVerticalStrut(5)); + + // Start + final JButton b = new JButton("Start download"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + new Thread(DownloadPictures.this).start(); + b.setEnabled(false); + checkBox.setEnabled(false); + } + }); + p0.add(Box.createVerticalStrut(5)); + + // Progress + p0.add(bar); + bar.setStringPainted(true); + int count = cards.size(); + float mb = (count * 70.0f) / 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() { + public void actionPerformed(ActionEvent e) { + if (checkBox.isSelected()) { + int count = DownloadPictures.this.cardsInGame.size(); + float mb = (count * 70.0f) / 1024; + bar.setString(String.format(count == 0 ? "No images to download!" : "%d of %d cards finished! Please wait! [%.1f Mb]", + 0, DownloadPictures.this.cardsInGame.size(), mb)); + } else { + int count = DownloadPictures.this.cards.size(); + float mb = (count * 70.0f) / 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 = { b, close = new JButton("Cancel") }; + dlg = new JOptionPane(p0, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]); + } + + private static ArrayList getNeededCards(Set allCards) { + + ArrayList cardsToDownload = new ArrayList(); + + /** + * read all card names and urls + */ + ArrayList allcards = new ArrayList(); + + try { + offlineMode = true; + + for (Card card : allCards) { + if (card.getCardNumber() > 0 && !card.getExpansionSetCode().isEmpty()) { + CardUrl url = new CardUrl(card.getName(), card.getExpansionSetCode(), card.getCardNumber(), false); + allcards.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); + } + } + } + } catch (Exception e) { + log.error(e); + } + + File file; + + /** + * check to see which cards we already have + */ + for (CardUrl card : allcards) { + boolean withCollectorId = false; + if (card.name.equals("Forest") || card.name.equals("Mountain") || card.name.equals("Swamp") || card.name.equals("Island") + || card.name.equals("Plains")) { + withCollectorId = true; + } + file = new File(CardImageUtils.getImagePath(card, withCollectorId)); + if (!file.exists()) { + cardsToDownload.add(card); + } + } + + for (CardUrl card : cardsToDownload) { + if (card.token) { + log.info("Card to download: " + card.name + " (Token) " + card.url); + } else { + try { + log.info("Card to download: " + card.name + " (" + card.set + ") " + + CardImageUtils.generateURL(card.collector, card.set)); + } catch (Exception e) { + log.error(e); + } + } + } + + return cardsToDownload; + } + + private class ProxyHandler implements ChangeListener { + private int type; + + public ProxyHandler(int type) { + this.type = type; + } + + public void stateChanged(ChangeEvent e) { + if (((AbstractButton) e.getSource()).isSelected()) { + DownloadPictures.this.type = type; + addr.setEnabled(type != 0); + port.setEnabled(type != 0); + } + } + } + + public void run() { + BufferedInputStream in; + BufferedOutputStream out; + + File base = new File(Constants.IO.imageBaseDir); + if (!base.exists()) { + base.mkdir(); + } + + Proxy p = null; + 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) { + byte[] buf = new byte[1024]; + int len; + HashSet ignoreUrls = SettingsManager.getIntance().getIgnoreUrls(); + + for (update(0); (checkBox.isSelected() ? cardIndex < cardsInGame.size() : cardIndex < cards.size()) && !cancel; update(cardIndex + 1)) { + try { + + CardUrl card = checkBox.isSelected() ? cardsInGame.get(cardIndex) : cards.get(cardIndex); + + log.info("Downloading card: " + card.name + " (" + card.set + ")"); + + URL url = new URL(CardImageUtils.generateURL(card.collector, card.set)); + if (ignoreUrls.contains(card.set) || card.token) { + // we have card in scripts, but we should ignore + // downloading image for it + // (e.g. for cards from custom or not released yet sets + // such urls should come from card-pictures files + // instead + if (card.collector != 0) { + continue; + } + url = new URL(card.url); + } + in = new BufferedInputStream(url.openConnection(p).getInputStream()); + + createDirForCard(card); + + boolean withCollectorId = false; + if (card.name.equals("Forest") || card.name.equals("Mountain") || card.name.equals("Swamp") + || card.name.equals("Island") || card.name.equals("Plains")) { + withCollectorId = true; + } + File fileOut = new File(CardImageUtils.getImagePath(card, withCollectorId)); + + out = new BufferedOutputStream(new FileOutputStream(fileOut)); + + while ((len = in.read(buf)) != -1) { + // user cancelled + if (cancel) { + in.close(); + out.flush(); + out.close(); + + // delete what was written so far + fileOut.delete(); + + return; + } + + out.write(buf, 0, len); + } + + in.close(); + out.flush(); + out.close(); + } catch (Exception ex) { + log.error(ex, ex); + /* + * int more = JOptionPane.showConfirmDialog(null, + * "Some error occured. Continue downloading pictures?", + * "Error", JOptionPane.YES_NO_OPTION); if (more == + * JOptionPane.NO_OPTION) { break; } + */ + } + } + } + close.setText("Close"); + } + + private static File createDirForCard(CardUrl card) throws Exception { + File setDir = new File(CardImageUtils.getImageDir(card)); + if (!setDir.exists()) { + setDir.mkdirs(); + } + return setDir; + } + + private void update(int card) { + this.cardIndex = card; + final class Worker implements Runnable { + private int card; + + Worker(int card) { + this.card = card; + } + + public void run() { + fireStateChanged(); + if (checkBox.isSelected()) { + int count = DownloadPictures.this.cardsInGame.size(); + int countLeft = count - card; + float mb = (countLeft * 70.0f) / 1024; + bar.setString(String.format(this.card == count ? "%d of %d cards finished! Please close!" + : "%d of %d cards finished! Please wait! [%.1f Mb]", this.card, count, mb)); + } else { + int count = DownloadPictures.this.cards.size(); + int countLeft = count - card; + float mb = (countLeft * 70.0f) / 1024; + bar.setString(String.format(cardIndex == count ? "%d of %d cards finished! Please close!" + : "%d of %d cards finished! Please wait! [%.1f Mb]", this.card, count, mb)); + } + } + } + + EventQueue.invokeLater(new Worker(card)); + } + + private static final Logger log = Logger.getLogger(DownloadPictures.class); + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/ImageCache.java new file mode 100644 index 00000000000..53217e2c2f5 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -0,0 +1,197 @@ +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.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; + +/** + * 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: "###" + * + * where param is: + * + *
    + *
  • #Normal: request for unrotated image
  • + *
  • #Tapped: request for rotated image
  • + *
  • #Cropped: request for cropped image that is used for Shandalar like card + * look
  • + *
+ */ +public class ImageCache { + + private static final Logger log = Logger.getLogger(ImageCache.class); + + private static final Map imageCache; + + /** + * Common pattern for keys. + * Format: "##" + */ + private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)"); + + static { + imageCache = new MapMaker().softValues().makeComputingMap(new Function() { + public BufferedImage apply(String key) { + try { + 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.isToken = true; + String path = CardImageUtils.getImagePath(info); + if (path == null) return null; + File file = new File(path); + + BufferedImage image = loadImage(file); + return image; + } else { + throw new RuntimeException( + "Requested image doesn't fit the requirement for key (##): " + key); + } + } catch (Exception ex) { + if (ex instanceof ComputationException) + throw (ComputationException) ex; + else + throw new ComputationException(ex); + } + } + }); + } + + 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; + } + + /** + * Returns an image scaled to the size given + */ + /*private static BufferedImage getNormalSizeImage(BufferedImage original) { + int srcWidth = original.getWidth(); + int srcHeight = original.getHeight(); + + int tgtWidth = SettingsManager.getManager().getCardSize().width; + int tgtHeight = SettingsManager.getManager().getCardSize().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 + */ + private 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); + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/properties/SettingsManager.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/properties/SettingsManager.java new file mode 100644 index 00000000000..a5d6bf2091b --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/properties/SettingsManager.java @@ -0,0 +1,81 @@ +package org.mage.plugins.card.properties; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +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 getIgnoreUrls() { + HashSet ignoreUrls = new HashSet(); + if (imageUrlProperties != null) { + String result = imageUrlProperties.getProperty("ignore.urls"); + if (result != null) { + String[] ignore = result.split(","); + for (String i : ignore) { + ignoreUrls.add(i); + } + } + } + return ignoreUrls; + } + + public ArrayList getTokenLookupOrder() { + ArrayList order = new ArrayList(); + if (imageUrlProperties != null) { + String result = imageUrlProperties.getProperty("token.lookup.order"); + if (result != null) { + String[] sets = result.split(","); + for (String s : sets) { + order.add(s); + } + } + } + return order; + } + + private Properties imageUrlProperties; +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java new file mode 100644 index 00000000000..55aea6d3997 --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -0,0 +1,174 @@ +package org.mage.plugins.card.utils; + +import java.io.File; +import java.util.HashMap; + +import mage.cards.Card; +import mage.game.permanent.PermanentToken; + +import org.mage.plugins.card.CardUrl; +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 pathCache = new HashMap(); + + /** + * Get path to image for specific card. + * + * @param c + * card to get path for + * @return String if image exists, else null + */ + public static String getImagePath(CardInfo c) { + String filePath; + String suffix = ".jpg"; + String cardname = c.name; + String set = c.set; + + CardUrl card = new CardUrl(cardname, set, c.collectorId, c.isToken); + + File file = null; + if (c.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(c.name) + suffix; + file = new File(filePath); + } + + if (file.exists()) { + return filePath; + } else { + return null; + } + } + + private static boolean isToken(Card c) { + return c != null && c instanceof PermanentToken; + } + + private static String getTokenImagePath(CardUrl card) { + String filename = getImagePath(card, false); + CardUrl c = new CardUrl(card.name, card.set, 0, card.token); + + File file = new File(filename); + if (!file.exists()) { + c.name = card.name + " 1"; + filename = getImagePath(c, false); + file = new File(filename); + if (!file.exists()) { + c.name = card.name + " 2"; + filename = getImagePath(c, false); + file = new File(filename); + } + } + + return filename; + } + + private static String searchForCardImage(CardUrl card) { + File file = null; + String path = ""; + CardUrl c = new CardUrl(card.name, card.set, 0, card.token); + boolean found = false; // search only in older sets + for (String set : SettingsManager.getIntance().getTokenLookupOrder()) { + if (found) { // start looking for image only if we have found card.set in the list (as this list is ordered) + c.set = set; + path = getTokenImagePath(c); + file = new File(path); + if (file.exists()) { + pathCache.put(card, path); + return path; + } + } + if (set.equals(card.set)) found = true; + } + return ""; + } + + public static String cleanString(String in) { + in = in.trim(); + StringBuffer out = new StringBuffer(); + 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 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 = updateSet(cardSet,true); + String url = "http://magiccards.info/scans/en/"; + url += set.toLowerCase() + "/" + collectorId + ".jpg"; + + return url; + } + + private 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(CardUrl card) { + if (card.set == null) { + return ""; + } + String set = updateSet(card.set,false).toUpperCase(); + if (card.token) { + return Constants.IO.imageBaseDir + File.separator + "TOK" + File.separator + set; + } else { + return Constants.IO.imageBaseDir + set; + } + } + + public static String getImagePath(CardUrl card, boolean withCollector) { + if (withCollector) { + return getImageDir(card) + File.separator + card.name + "." + card.collector + ".full.jpg"; + } else { + return getImageDir(card) + File.separator + card.name + ".full.jpg"; + } + } +} diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/resources/image.url.properties b/Mage.Plugins/Mage.Card.Plugin/src/main/resources/image.url.properties new file mode 100644 index 00000000000..6f4bbe654fb --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/resources/image.url.properties @@ -0,0 +1,59 @@ +tsp=ts +tor=tr +mor=mt +ody=od +lrw=lw +plc=pc +gpt=gp +inv=in +ons=on +scg=sc +jud=ju +mmq=mm +pls=ps +mrd=mi +mir=mr +tst=ts +usg=us +apc=ap +nms=ne +dis=di +vis=vi +9ed=9e +8ed=8e +7ed=7e +4ed=4e +tsb=tsts +ulg=ul +5ed=5e +6ed=6e +btd=bd +sth=sh +nem=ne +por=po +s99=st +lgn=le +ice=ia +csp=cs +tmp=tp +s00=st2k +dst=ds +pcy=pr +uds=ud +exo=ex +lea=al +hop=pch +chr=ch +arn=an +wth=wl +leb=be +2ed=un +3ed=rv +brb=br +atq=aq +fem=fe +leg=lg +ptk=p3k +ignore.urls=TOK +# sets ordered by release time (newest goes first) +token.lookup.order=ROE,PVC,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS \ No newline at end of file