diff --git a/Mage.Client/plugins/mage-card-plugin.jar b/Mage.Client/plugins/mage-card-plugin.jar
index a7db7a9f45..53cd6661aa 100644
Binary files a/Mage.Client/plugins/mage-card-plugin.jar and b/Mage.Client/plugins/mage-card-plugin.jar differ
diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java
index bf4a607f9b..f141f9908d 100644
--- a/Mage.Client/src/main/java/mage/client/MageFrame.java
+++ b/Mage.Client/src/main/java/mage/client/MageFrame.java
@@ -34,6 +34,7 @@
 
 package mage.client;
 
+import mage.cards.Card;
 import mage.cards.decks.Deck;
 import mage.client.cards.CardsStorage;
 import mage.client.components.MageComponents;
@@ -41,6 +42,7 @@ import mage.client.components.MageJDesktop;
 import mage.client.components.MageRoundPane;
 import mage.client.components.arcane.ManaSymbols;
 import mage.client.constants.Constants.DeckEditorMode;
+import mage.client.deckeditor.collection.viewer.CollectionViewerPane;
 import mage.client.dialog.*;
 import mage.client.plugins.impl.Plugins;
 import mage.client.remote.Session;
@@ -61,6 +63,7 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.UUID;
 import java.util.logging.Level;
@@ -122,6 +125,7 @@ public class MageFrame extends javax.swing.JFrame {
         }
 
         Plugins.getInstance().loadPlugins();
+        ManaSymbols.loadImages();
 
         initComponents();
         setSize(1024, 768);
@@ -137,8 +141,6 @@ public class MageFrame extends javax.swing.JFrame {
         desktopPane.add(pickNumber, JLayeredPane.POPUP_LAYER);
         session.getUI().addComponent(MageComponents.DESKTOP_PANE, desktopPane);
 
-        ManaSymbols.loadImages();
-
         addTooltipContainer();
         setBackground();
         addMageLabel();
@@ -287,7 +289,7 @@ public class MageFrame extends javax.swing.JFrame {
     }
 
     private void btnImagesActionPerformed(java.awt.event.ActionEvent evt) {
-        Plugins.getInstance().downloadImage(CardsStorage.getAllCards());
+        Plugins.getInstance().downloadImage(new HashSet<Card>(CardsStorage.getAllCards()));
     }
 
     private void btnSymbolsActionPerformed(java.awt.event.ActionEvent evt) {
@@ -355,6 +357,7 @@ public class MageFrame extends javax.swing.JFrame {
         tablesPane = new mage.client.table.TablesPane();
         gamePane = new mage.client.game.GamePane();
         deckEditorPane = new mage.client.deckeditor.DeckEditorPane();
+        collectionViewerPane = new CollectionViewerPane();
         mageToolbar = new javax.swing.JToolBar();
         btnConnect = new javax.swing.JButton();
         jSeparator5 = new javax.swing.JToolBar.Separator();
@@ -368,11 +371,12 @@ public class MageFrame extends javax.swing.JFrame {
         jSeparator1 = new javax.swing.JToolBar.Separator();
         btnExit = new javax.swing.JButton();
         lblStatus = new javax.swing.JLabel();
+        jSeparator6 = new javax.swing.JToolBar.Separator();
+        btnCollectionViewer = new JButton();
 
         setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
         //setMinimumSize(new java.awt.Dimension(1024, 768));
 
-        //desktopPane.setBackground(new java.awt.Color(204, 204, 204));
         tablesPane.setBounds(20, 10, 560, 440);
         desktopPane.add(tablesPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
         try {
@@ -394,6 +398,13 @@ public class MageFrame extends javax.swing.JFrame {
         } catch (java.beans.PropertyVetoException e1) {
             e1.printStackTrace();
         }
+        collectionViewerPane.setBounds(140, 50, -1, -1);
+        desktopPane.add(collectionViewerPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+        try {
+            collectionViewerPane.setMaximum(true);
+        } catch (java.beans.PropertyVetoException e1) {
+            e1.printStackTrace();
+        }
 
         mageToolbar.setFloatable(false);
         mageToolbar.setRollover(true);
@@ -437,6 +448,19 @@ public class MageFrame extends javax.swing.JFrame {
         mageToolbar.add(btnDeckEditor);
         mageToolbar.add(jSeparator2);
 
+        btnCollectionViewer.setText("Collection Viewer");
+        btnCollectionViewer.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
+        btnCollectionViewer.setFocusable(false);
+        btnCollectionViewer.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        btnCollectionViewer.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        btnCollectionViewer.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                btnCollectionViewerActionPerformed(evt);
+            }
+        });
+        mageToolbar.add(btnCollectionViewer);
+        mageToolbar.add(jSeparator6);
+
         btnPreferences.setText("Preferences");
         btnPreferences.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
         btnPreferences.setFocusable(false);
@@ -500,9 +524,17 @@ public class MageFrame extends javax.swing.JFrame {
     private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed
         this.gamePane.setVisible(false);
         this.tablesPane.setVisible(false);
+        this.collectionViewerPane.setVisible(false);
         showDeckEditor(DeckEditorMode.Constructed, null, null);
     }//GEN-LAST:event_btnDeckEditorActionPerformed
 
+    private void btnCollectionViewerActionPerformed(java.awt.event.ActionEvent evt) {
+        this.gamePane.setVisible(false);
+        this.tablesPane.setVisible(false);
+        this.deckEditorPane.setVisible(false);
+        showCollectionViewer();
+    }
+
     private void btnPreferencesActionPerformed(java.awt.event.ActionEvent evt) {
         PhasesDialog.main(new String[]{});
     }
@@ -510,6 +542,7 @@ public class MageFrame extends javax.swing.JFrame {
     private void btnGamesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGamesActionPerformed
         this.gamePane.setVisible(false);
         this.deckEditorPane.setVisible(false);
+        this.collectionViewerPane.setVisible(false);
         this.tablesPane.setVisible(true);
         this.tablesPane.showTables();
     }//GEN-LAST:event_btnGamesActionPerformed
@@ -556,6 +589,7 @@ public class MageFrame extends javax.swing.JFrame {
         this.tablesPane.setVisible(false);
         this.gamePane.setVisible(false);
         this.deckEditorPane.setVisible(false);
+        this.collectionViewerPane.setVisible(false);
     }
 
     public void showDeckEditor(DeckEditorMode mode, Deck deck, UUID tableId) {
@@ -563,6 +597,10 @@ public class MageFrame extends javax.swing.JFrame {
         this.deckEditorPane.show(mode, deck, tableId);
     }
 
+    public void showCollectionViewer() {
+        this.collectionViewerPane.setVisible(true);
+    }
+
     public static CombatDialog getCombatDialog() {
         return combat;
     }
@@ -608,10 +646,12 @@ public class MageFrame extends javax.swing.JFrame {
     private javax.swing.JButton btnAbout;
     private javax.swing.JButton btnConnect;
     private javax.swing.JButton btnDeckEditor;
+    private javax.swing.JButton btnCollectionViewer;
     private javax.swing.JButton btnPreferences;
     private javax.swing.JButton btnExit;
     private javax.swing.JButton btnGames;
     private mage.client.deckeditor.DeckEditorPane deckEditorPane;
+    private CollectionViewerPane collectionViewerPane;
     private static MageJDesktop desktopPane;
     private mage.client.game.GamePane gamePane;
     private javax.swing.JToolBar.Separator jSeparator1;
@@ -619,6 +659,7 @@ public class MageFrame extends javax.swing.JFrame {
     private javax.swing.JToolBar.Separator jSeparator3;
     private javax.swing.JToolBar.Separator jSeparator4;
     private javax.swing.JToolBar.Separator jSeparator5;
+    private javax.swing.JToolBar.Separator jSeparator6;
     private javax.swing.JLabel lblStatus;
     private javax.swing.JToolBar mageToolbar;
     private mage.client.table.TablesPane tablesPane;
diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.java b/Mage.Client/src/main/java/mage/client/cards/BigCard.java
index 76837ee626..769d347041 100644
--- a/Mage.Client/src/main/java/mage/client/cards/BigCard.java
+++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.java
@@ -69,9 +69,9 @@ public class BigCard extends JComponent {
     protected float hue = 0.005f;
     protected float dh = 0.005f;
 
-    static private final int DEFAULT_DELAY_PERIOD = 40;
+    static private final int DEFAULT_DELAY_PERIOD = 30;
     static private final float LEFT_BOUNDARY = 0.0f;
-    static private final float RIGHT_BOUNDARY = 0.1f;
+    static private final float RIGHT_BOUNDARY = 1f;
 
     public BigCard() {
         initComponents();
@@ -247,6 +247,7 @@ public class BigCard extends JComponent {
 
         setFocusable(false);
         setMinimumSize(new Dimension(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT));
+        setMaximumSize(new Dimension(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT));
         setOpaque(false);
         setPreferredSize(getMinimumSize());
         setLayout(null);
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
index f15687451d..d82e4f72dd 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
@@ -93,7 +93,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener
 	}
 	
 	private void addCard(CardView card, BigCard bigCard, UUID gameId) {
-		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId);
+		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId, true);
 		cards.put(card.getId(), cardImg);
 		cardImg.addMouseListener(this);
 		add(cardImg);
diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java
index ee0c38dc97..f98722137b 100644
--- a/Mage.Client/src/main/java/mage/client/cards/Cards.java
+++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java
@@ -120,7 +120,7 @@ public class Cards extends javax.swing.JPanel {
 	}
 
 	private void addCard(CardView card, BigCard bigCard, UUID gameId) {
-		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId);
+		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId, true);
 		cards.put(card.getId(), cardImg);
 		cardArea.add(cardImg);
 	}
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java
index c386a18da9..6aa6b0cd43 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java
@@ -101,7 +101,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener {
 	}
 
 	private void addCard(CardView card, BigCard bigCard, UUID gameId, Rectangle rectangle) {
-		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId);
+		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, Config.dimensions, gameId, true);
 		cardImg.setBounds(rectangle);
 		cardArea.add(cardImg);
 		cardArea.moveToFront(cardImg);
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java b/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
index 036bacd350..8d5b2d5c56 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
@@ -1,93 +1,193 @@
 package mage.client.cards;
 
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-
 import mage.cards.Card;
 import mage.cards.ExpansionSet;
 import mage.sets.Sets;
 import mage.utils.CardUtil;
 
+import java.io.InputStream;
+import java.util.*;
+
+/**
+ * Stores all implemented cards on client side.
+ * Used by deck editor, deck generator, collection viewer, etc.
+ *
+ * @author nantuko
+ */
 public class CardsStorage {
-	private static Set<Card> allCards = new LinkedHashSet<Card>();
-	private static Set<Card> nonBasicLandCards = new LinkedHashSet<Card>();
-	private static Map<String, Integer> ratings;
-	private static Integer min = Integer.MAX_VALUE, max = 0;
+    private static List<Card> allCards = new ArrayList<Card>();
+    private static Set<Card> nonBasicLandCards = new LinkedHashSet<Card>();
+    private static Map<String, Integer> ratings;
+    private static Integer min = Integer.MAX_VALUE, max = 0;
+    private static int cardsCount;
+    private static List<String> setCodes = new ArrayList<String>();
 
-	static {
-		for (ExpansionSet set : Sets.getInstance().values()) {
-			Set<Card> cards = set.createCards();
-			allCards.addAll(cards);
-			for (Card card : cards) {
-				if (CardUtil.isLand(card) && !CardUtil.isBasicLand(card)) {
-					nonBasicLandCards.add(card);
-				}
-			}
-		}
-	}
+    static {
+        for (ExpansionSet set : Sets.getInstance().values()) {
+            setCodes.add(set.getCode());
+            Set<Card> cards = set.createCards();
+            allCards.addAll(cards);
+            for (Card card : cards) {
+                if (CardUtil.isLand(card) && !CardUtil.isBasicLand(card)) {
+                    nonBasicLandCards.add(card);
+                }
+            }
+        }
+        Collections.sort(allCards, new CardComparator());
+        Collections.sort(setCodes, new SetComparator());
+        cardsCount = allCards.size();
+    }
 
-	public static Set<Card> getAllCards() {
-		return allCards;
-	}
+    public static List<Card> getAllCards() {
+        return allCards;
+    }
 
-	public static Set<Card> getNonBasicLandCards() {
-		return nonBasicLandCards;
-	}
+    /**
+     * Get cards from card pool starting from start index and ending with end index.
+     * Can filter cards by set (if parameter is not null).
+     *
+     * @param start
+     * @param end
+     * @param set   Cards set code. Can be null.
+     * @return
+     */
+    public static List<Card> getAllCards(int start, int end, String set) {
+        List<Card> cards = new ArrayList<Card>();
+        List<Card> pool;
+        if (set == null) {
+            pool = allCards;
+        } else {
+            pool = new ArrayList<Card>();
+            for (Card card : allCards) {
+                if (card.getExpansionSetCode().equals(set)) {
+                    pool.add(card);
+                }
+            }
+        }
+        for (int i = start; i < Math.min(end + 1, pool.size()); i++) {
+            cards.add(pool.get(i));
+        }
+        return cards;
+    }
 
-	/**
-	 * Return rating of a card: 1-10.
-	 * 
-	 * @param card
-	 * @return
-	 */
-	public static int rateCard(Card card) {
-		if (ratings == null) {
-			readRatings();
-		}
-		if (ratings.containsKey(card.getName())) {
-			int r = ratings.get(card.getName());
-			float f = 10.0f * (r - min) / (max - min);
-			return (int)Math.round(f); // normalize to [1..10]
-		}
-		return 0;
-	}
-	
-	private synchronized static void readRatings() {
-		if (ratings == null) {
-			ratings = new HashMap<String, Integer>();
-			String filename = "/ratings.txt";
-			try {
-				InputStream is = CardsStorage.class.getResourceAsStream(filename);
-				Scanner scanner = new Scanner(is);
-				while (scanner.hasNextLine()) {
-					String line = scanner.nextLine();
-					String[] s = line.split(":");
-					if (s.length == 2) {
-						Integer rating = Integer.parseInt(s[0].trim());
-						String name = s[1].trim();
-						if (rating > max) { max = rating; }
-						if (rating < min) { min = rating; }
-						ratings.put(name, rating);
-					}
-				}
-			} catch (Exception e) {
-				e.printStackTrace();
-				ratings.clear(); // no rating available on exception
-			}
-		}
-	}
+    public static int getCardsCount() {
+        return cardsCount;
+    }
 
-	public static void main(String[] argv) {
-		for (Card card : getAllCards()) {
-			String name = card.getName();
-			if (name.equals("Baneslayer Angel") || name.equals("Lightning Bolt") || name.equals("Zombie Outlander")
-					|| name.equals("Naturalize") || name.equals("Kraken's Eye") || name.equals("Serra Angel")) {
-				System.out.println(name + " : " + rateCard(card));
-			}
-		}
-	}
+    public static List<String> getSetCodes() {
+        return setCodes;
+    }
+
+    public static Set<Card> getNonBasicLandCards() {
+        return nonBasicLandCards;
+    }
+
+    /**
+     * Return rating of a card: 1-10.
+     *
+     * @param card
+     * @return
+     */
+    public static int rateCard(Card card) {
+        if (ratings == null) {
+            readRatings();
+        }
+        if (ratings.containsKey(card.getName())) {
+            int r = ratings.get(card.getName());
+            float f = 10.0f * (r - min) / (max - min);
+            return (int) Math.round(f); // normalize to [1..10]
+        }
+        return 0;
+    }
+
+    private synchronized static void readRatings() {
+        if (ratings == null) {
+            ratings = new HashMap<String, Integer>();
+            String filename = "/ratings.txt";
+            try {
+                InputStream is = CardsStorage.class.getResourceAsStream(filename);
+                Scanner scanner = new Scanner(is);
+                while (scanner.hasNextLine()) {
+                    String line = scanner.nextLine();
+                    String[] s = line.split(":");
+                    if (s.length == 2) {
+                        Integer rating = Integer.parseInt(s[0].trim());
+                        String name = s[1].trim();
+                        if (rating > max) {
+                            max = rating;
+                        }
+                        if (rating < min) {
+                            min = rating;
+                        }
+                        ratings.put(name, rating);
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                ratings.clear(); // no rating available on exception
+            }
+        }
+    }
+
+    public static void main(String[] argv) {
+        for (Card card : getAllCards()) {
+            String name = card.getName();
+            if (name.equals("Baneslayer Angel") || name.equals("Lightning Bolt") || name.equals("Zombie Outlander")
+                    || name.equals("Naturalize") || name.equals("Kraken's Eye") || name.equals("Serra Angel")) {
+                System.out.println(name + " : " + rateCard(card));
+            }
+        }
+    }
+
+    /**
+     * Card comparator.
+     * First compares set codes, then collector ids and just then card names.
+     * <p/>
+     * Show latest set cards on top.
+     *
+     * @author nantuko
+     */
+    private static class CardComparator implements Comparator<Card> {
+        private static final String LATEST_SET_CODE = "SOM";
+
+        @Override
+        public int compare(Card o1, Card o2) {
+            String set1 = o1.getExpansionSetCode();
+            String set2 = o2.getExpansionSetCode();
+            if (set1.equals(set2)) {
+                Integer cid1 = o1.getCardNumber();
+                Integer cid2 = o2.getCardNumber();
+                if (cid1 == cid2) {
+                    return o1.getName().compareTo(o2.getName());
+                } else {
+                    return cid1.compareTo(cid2);
+                }
+            } else {
+                // put latest set on top
+                if (set1.equals(LATEST_SET_CODE)) {
+                    return -1;
+                }
+                if (set2.equals(LATEST_SET_CODE)) {
+                    return 1;
+                }
+                return set1.compareTo(set2);
+            }
+        }
+    }
+
+    private static class SetComparator implements Comparator<String> {
+        private static final String LATEST_SET_CODE = "SOM";
+
+        @Override
+        public int compare(String set1, String set2) {
+            // put latest set on top
+            if (set1.equals(LATEST_SET_CODE)) {
+                return -1;
+            }
+            if (set2.equals(LATEST_SET_CODE)) {
+                return 1;
+            }
+            return set1.compareTo(set2);
+        }
+    }
 }
diff --git a/Mage.Client/src/main/java/mage/client/components/HoverButton.java b/Mage.Client/src/main/java/mage/client/components/HoverButton.java
new file mode 100644
index 0000000000..83dab1f3a8
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/components/HoverButton.java
@@ -0,0 +1,171 @@
+package mage.client.components;
+
+import mage.client.util.Command;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.font.FontRenderContext;
+
+/**
+ * Image button with hover.
+ *
+ * @author nantuko
+ */
+public class HoverButton extends JPanel implements MouseListener {
+
+    private Image image;
+    private Image hoverImage;
+    private Image disabledImage;
+    private Image selectedImage;
+    private Image overlayImage;
+    private Rectangle imageSize;
+    private Rectangle buttonSize;
+    private String text;
+    private int textOffsetY = 0;
+    private int textOffsetX = -1;
+    private Dimension overlayImageSize;
+
+    private boolean isHovered = false;
+    private boolean isSelected = false;
+
+    private Command observer = null;
+    private Command onHover = null;
+    private Color textColor = Color.white;
+
+    final static Font textFont = new Font("Arial", Font.PLAIN, 12);
+    final static Font textFontMini = new Font("Arial", Font.PLAIN, 11);
+    private boolean useMiniFont = false;
+
+    public HoverButton(String text, Image image, Image hover, Image disabled, Rectangle size) {
+        this(text, image, hover, null, disabled, size);
+    }
+
+    public HoverButton(String text, Image image, Image hover, Image selected, Image disabled, Rectangle size) {
+        this.image = image;
+        this.hoverImage = hover;
+        this.selectedImage = selected;
+        this.disabledImage = disabled;
+        this.imageSize = size;
+        this.text = text;
+        setOpaque(false);
+        addMouseListener(this);
+    }
+
+    @Override
+    public void paintComponent(Graphics g) {
+        if (isEnabled()) {
+            if (isHovered) {
+                g.drawImage(hoverImage, 0, 0, imageSize.width, imageSize.height, this);
+                if (text != null) {
+                    Graphics2D g2d = (Graphics2D) g;
+                    if (textColor != null) g2d.setColor(textColor);
+                    if (useMiniFont) g2d.setFont(textFontMini);
+                    else g2d.setFont(textFont);
+                    if (textOffsetX == -1) { // calculate once
+                        FontRenderContext frc = g2d.getFontRenderContext();
+                        int textWidth = (int) textFont.getStringBounds(text, frc).getWidth();
+                        if (textWidth > buttonSize.width) {
+                            g2d.setFont(textFontMini);
+                            useMiniFont = true;
+                            frc = g2d.getFontRenderContext();
+                            textWidth = (int) textFontMini.getStringBounds(text, frc).getWidth();
+                        }
+                        textOffsetX = (int) ((imageSize.width - textWidth) / 2);
+                    }
+                    g2d.drawString(text, textOffsetX, textOffsetY);
+                }
+            } else {
+                g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this);
+            }
+            if (isSelected) {
+                if (selectedImage != null) {
+                    g.drawImage(selectedImage, 0, 0, imageSize.width, imageSize.height, this);
+                } else {
+                    System.err.println("No selectedImage for button.");
+                }
+            }
+        } else {
+            g.drawImage(disabledImage, 0, 0, imageSize.width, imageSize.height, this);
+        }
+        if (overlayImage != null) {
+            g.drawImage(overlayImage, (imageSize.width - overlayImageSize.width) / 2, 10, this);
+        }
+    }
+
+    public void setTextColor(Color textColor) {
+        this.textColor = textColor;
+    }
+
+    public void setOverlayImage(Image image) {
+        this.overlayImage = image;
+        this.overlayImageSize = new Dimension(image.getWidth(null), image.getHeight(null));
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+        isHovered = true;
+        this.repaint();
+        if (onHover != null) {
+            onHover.execute();
+        }
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+        isHovered = false;
+        this.repaint();
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        if (e.getButton() == MouseEvent.BUTTON1) {
+            if (isEnabled() && observer != null) {
+                observer.execute();
+            }
+        }
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+    }
+
+    public void setObserver(Command observer) {
+        this.observer = observer;
+    }
+
+    public void setOnHover(Command onHover) {
+        this.onHover = onHover;
+    }
+
+    @Override
+    public void setBounds(Rectangle r) {
+        super.setBounds(r);
+        this.textOffsetY = r.height - 2;
+        this.buttonSize = r;
+    }
+
+    @Override
+    public void setBounds(int x, int y, int width, int height) {
+        super.setBounds(x, y, width, height);
+        this.textOffsetY = height - 2;
+        this.buttonSize = new Rectangle(x, y, width, height);
+    }
+
+    public boolean isSelected() {
+        return isSelected;
+    }
+
+    public void setSelected(boolean isSelected) {
+        this.isSelected = isSelected;
+    }
+
+    public void changeSelected() {
+        this.isSelected = !this.isSelected;
+    }
+}
diff --git a/Mage.Client/src/main/java/mage/client/components/ImageButton.java b/Mage.Client/src/main/java/mage/client/components/ImageButton.java
new file mode 100644
index 0000000000..96836c32de
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/components/ImageButton.java
@@ -0,0 +1,59 @@
+package mage.client.components;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Image based button.
+ *
+ * @author nantuko
+ */
+public class ImageButton extends JButton {
+
+    private Image image;
+    private String text;
+
+    public ImageButton(String text, Image image) {
+        super(new ImageIcon(image));
+        this.image = image;
+        this.text = text;
+    }
+
+    @Override
+    public void paintComponent(Graphics g) {
+        super.paintComponent(g);
+
+        if (text != null) {
+            g.setColor(Color.GRAY);
+            int dx = 15;
+            int dy = 17;
+            g.setColor(Color.WHITE);
+            if (text.length() > 5) {
+                g.drawString(this.text, 8, dy);
+            } else {
+                g.drawString(this.text, dx, dy);
+            }
+        }
+    }
+
+    public void setImage(Image src) {
+        this.image = src;
+        repaint();
+    }
+
+    @Override
+    public Dimension getPreferredSize() {
+        return new Dimension(image.getWidth(this), image.getHeight(this));
+    }
+
+    @Override
+    public Dimension getMinimumSize() {
+        return new Dimension(image.getWidth(this), image.getHeight(this));
+    }
+
+    @Override
+    public void setText(String text) {
+        this.text = text;
+        repaint();
+    }
+}
diff --git a/Mage.Client/src/main/java/mage/client/components/arcane/GlowText.java b/Mage.Client/src/main/java/mage/client/components/arcane/GlowText.java
new file mode 100644
index 0000000000..0a92f2565f
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/components/arcane/GlowText.java
@@ -0,0 +1,96 @@
+package mage.client.components.arcane;
+
+import javax.swing.*;
+import java.awt.*;
+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;
+
+public class GlowText extends JLabel {
+	private static final long serialVersionUID = 1827677946939348001L;
+	private int glowSize;
+	@SuppressWarnings("unused")
+	private float glowIntensity;
+	private Color glowColor;
+	private boolean wrap;
+	private int lineCount = 0;
+
+	public void setGlow (Color glowColor, int size, float intensity) {
+		this.glowColor = glowColor;
+		this.glowSize = size;
+		this.glowIntensity = intensity;
+	}
+
+	public void setWrap (boolean wrap) {
+		this.wrap = wrap;
+	}
+
+	public Dimension getPreferredSize () {
+		Dimension size = super.getPreferredSize();
+		size.width += glowSize;
+		size.height += glowSize / 2;
+		return size;
+	}
+
+	public void setText (String text) {
+		super.setText(text);
+	}
+
+	public void paint (Graphics g) {
+		if (getText().length() == 0) return;
+
+		Graphics2D g2d = (Graphics2D)g;
+		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+		Dimension size = getSize();
+		int textX = 0, textY = 0;
+		int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE);
+
+		AttributedString attributedString = new AttributedString(getText());
+		attributedString.addAttribute(TextAttribute.FONT, getFont());
+		AttributedCharacterIterator charIterator = attributedString.getIterator();
+		FontRenderContext fontContext = g2d.getFontRenderContext();
+
+		LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext);
+		lineCount = 0;
+		while (measurer.getPosition() < charIterator.getEndIndex()) {
+			//TextLayout textLayout = measurer.nextLayout(wrapWidth);
+			lineCount++;
+			if (lineCount > 2) break;
+		}
+		charIterator.first();
+		// Use char wrap if word wrap would cause more than two lines of text.
+		if (lineCount > 2)
+			measurer = new LineBreakMeasurer(charIterator, BreakIterator.getCharacterInstance(Locale.ENGLISH), fontContext);
+		else
+			measurer.setPosition(0);
+		while (measurer.getPosition() < charIterator.getEndIndex()) {
+			TextLayout textLayout = measurer.nextLayout(wrapWidth);
+			float ascent = textLayout.getAscent();
+			textY += ascent; // Move down to baseline.
+
+			g2d.setColor(glowColor);
+			g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
+			textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1);
+			textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1);
+			textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1);
+			textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1);
+
+			g2d.setColor(getForeground());
+			g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
+			textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2);
+
+			textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line.
+		}
+	}
+	
+	public int getLineCount() {
+		return this.lineCount;
+	}
+}
diff --git a/Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java b/Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java
index 9526ad1e2d..fce8a1d66d 100644
--- a/Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java
+++ b/Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java
@@ -1,5 +1,6 @@
 package mage.client.components.arcane;
 
+import mage.client.cards.CardsStorage;
 import mage.client.constants.Constants;
 import mage.client.util.gui.BufferedImageBuilder;
 import mage.client.util.gui.ImageResizeUtil;
@@ -18,6 +19,7 @@ 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, Image> setImages = new HashMap<String, Image>();
 	static private Pattern replaceSymbolsPattern = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
     static private boolean noManaSymbols = false;
 
@@ -40,11 +42,33 @@ public class ManaSymbols {
 				manaImagesOriginal.put(symbol, image);
 			} catch (Exception e) {}
 		}
+        for (String set : CardsStorage.getSetCodes()) {
+            String _set = set.equals("CON") ? "CFX" : set;
+            File file = new File(Constants.RESOURCE_PATH_SET + _set + ".jpg");
+            try {
+				Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
+                int width = image.getWidth(null);
+                if (width > 21) {
+                    int h = image.getHeight(null);
+                    if (h > 0) {
+                        Rectangle r = new Rectangle(21, (int)(h * 21.0f / width));
+                        BufferedImage resized = ImageResizeUtil.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
+                        setImages.put(set, resized);
+                    }
+                } else {
+				    setImages.put(set, image);
+                }
+			} catch (Exception e) {}
+        }
 	}
 
 	static public Image getManaSymbolImage(String symbol) {
 		return manaImagesOriginal.get(symbol);
 	}
+
+    static public Image getSetSymbolImage(String set) {
+		return setImages.get(set);
+	}
 	
 	static public void draw (Graphics g, String manaCost, int x, int y) {
 		if (manaCost.length() == 0) return;
diff --git a/Mage.Client/src/main/java/mage/client/constants/Constants.java b/Mage.Client/src/main/java/mage/client/constants/Constants.java
index a91617ed20..0d583e1860 100644
--- a/Mage.Client/src/main/java/mage/client/constants/Constants.java
+++ b/Mage.Client/src/main/java/mage/client/constants/Constants.java
@@ -77,6 +77,7 @@ public final class Constants {
 
     public static final String RESOURCE_PATH_MANA_LARGE = IO.imageBaseDir + "symbols" + File.separator + "large";
 	public static final String RESOURCE_PATH_MANA_MEDIUM = IO.imageBaseDir + "symbols" + File.separator + "medium";
+    public static final String RESOURCE_PATH_SET = IO.imageBaseDir + "sets" + File.separator;
 
     public interface IO {
 		public static final String imageBaseDir = "plugins" + File.separator + "images" + File.separator;
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPane.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPane.java
new file mode 100644
index 0000000000..3f9946f323
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPane.java
@@ -0,0 +1,86 @@
+/*
+* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are
+* permitted provided that the following conditions are met:
+*
+*    1. Redistributions of source code must retain the above copyright notice, this list of
+*       conditions and the following disclaimer.
+*
+*    2. Redistributions in binary form must reproduce the above copyright notice, this list
+*       of conditions and the following disclaimer in the documentation and/or other materials
+*       provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* The views and conclusions contained in the software and documentation are those of the
+* authors and should not be interpreted as representing official policies, either expressed
+* or implied, of BetaSteward_at_googlemail.com.
+*/
+package mage.client.deckeditor.collection.viewer;
+
+import mage.client.MagePane;
+import mage.client.plugins.impl.Plugins;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Collection viewer pane.
+ * Contains background and components container.
+ *
+ * @author nantuko
+ */
+public class CollectionViewerPane extends MagePane {
+
+    public CollectionViewerPane() {
+        boolean initialized = false;
+        if (Plugins.getInstance().isThemePluginLoaded()) {
+        	Map<String, JComponent> ui = new HashMap<String, JComponent>();
+	        JComponent container = Plugins.getInstance().updateTablePanel(ui);
+			if (container != null) {
+				collectionViewerPanel = new CollectionViewerPanel();
+				initComponents(container);
+    			container.add(collectionViewerPanel);
+    			container.setOpaque(false);
+    			collectionViewerPanel.setOpaque(false);
+				initialized = true;
+			}
+        }
+        if (!initialized) {
+        	initComponents(null);
+        }
+    }
+
+    private void initComponents(Component container) {
+        Component component = container != null ? container : collectionViewerPanel;
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(component, javax.swing.GroupLayout.DEFAULT_SIZE, 885, Short.MAX_VALUE)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(component, javax.swing.GroupLayout.DEFAULT_SIZE, 626, Short.MAX_VALUE)
+        );
+
+        pack();
+    }
+
+    private CollectionViewerPanel collectionViewerPanel;
+}
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java
new file mode 100644
index 0000000000..72ad0e678c
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java
@@ -0,0 +1,103 @@
+/*
+* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are
+* permitted provided that the following conditions are met:
+*
+*    1. Redistributions of source code must retain the above copyright notice, this list of
+*       conditions and the following disclaimer.
+*
+*    2. Redistributions in binary form must reproduce the above copyright notice, this list
+*       of conditions and the following disclaimer in the documentation and/or other materials
+*       provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* The views and conclusions contained in the software and documentation are those of the
+* authors and should not be interpreted as representing official policies, either expressed
+* or implied, of BetaSteward_at_googlemail.com.
+*/
+package mage.client.deckeditor.collection.viewer;
+
+import mage.client.cards.BigCard;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+
+/**
+ * Pane with big card and mage book.
+ *
+ * @author nantuko
+ */
+public class CollectionViewerPanel extends JPanel {
+    public CollectionViewerPanel() {
+        initComponents();
+    }
+
+    public void initComponents() {
+        jPanel1 = new javax.swing.JPanel();
+        jPanel1.setOpaque(false);
+        bigCard = new BigCard();
+        BoxLayout boxlayout = new BoxLayout(jPanel1, BoxLayout.X_AXIS);
+        jPanel1.setLayout(boxlayout);
+        bigCard.setAlignmentY(Component.BOTTOM_ALIGNMENT);
+        bigCard.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+        jPanel1.add(bigCard);
+
+        jPanel2 = new MageBookContainer();
+        jPanel2.setOpaque(false);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                        .addGroup(layout.createSequentialGroup()
+                                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                .addGap(0, 0, 0)
+                                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, 604, Short.MAX_VALUE))
+        );
+        layout.setVerticalGroup(
+                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                        .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, 615, Short.MAX_VALUE)
+        );
+    }
+
+    private class MageBookContainer extends JPanel {
+        public MageBookContainer() {
+            super();
+            initComponents();
+        }
+
+        public void initComponents() {
+            jPanel = new JPanel();
+            jScrollPane1 = new JScrollPane(jPanel);
+            jScrollPane1.getViewport().setBackground(new Color(0,0,0,0));
+
+            jPanel.setLayout(new GridBagLayout()); // centers mage book
+            jPanel.setBackground(new Color(0,0,0,0));
+            jPanel.add(new MageBook(bigCard));
+
+            setLayout(new java.awt.BorderLayout());
+            add(jScrollPane1, java.awt.BorderLayout.CENTER);
+        }
+
+        private JPanel jPanel;
+        private javax.swing.JScrollPane jScrollPane1;
+    }
+
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    private mage.client.cards.BigCard bigCard;
+
+}
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
new file mode 100644
index 0000000000..a10e83fccf
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
@@ -0,0 +1,305 @@
+/*
+* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are
+* permitted provided that the following conditions are met:
+*
+*    1. Redistributions of source code must retain the above copyright notice, this list of
+*       conditions and the following disclaimer.
+*
+*    2. Redistributions in binary form must reproduce the above copyright notice, this list
+*       of conditions and the following disclaimer in the documentation and/or other materials
+*       provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* The views and conclusions contained in the software and documentation are those of the
+* authors and should not be interpreted as representing official policies, either expressed
+* or implied, of BetaSteward_at_googlemail.com.
+*/
+
+package mage.client.deckeditor.collection.viewer;
+
+import mage.cards.Card;
+import mage.cards.CardDimensions;
+import mage.cards.MageCard;
+import mage.client.cards.BigCard;
+import mage.client.cards.CardsStorage;
+import mage.client.components.HoverButton;
+import mage.client.components.ImageButton;
+import mage.client.components.arcane.GlowText;
+import mage.client.components.arcane.ManaSymbols;
+import mage.client.plugins.impl.Plugins;
+import mage.client.util.Command;
+import mage.client.util.Config;
+import mage.client.util.ImageHelper;
+import mage.components.ImagePanel;
+import mage.utils.ThreadUtils;
+import mage.view.CardView;
+import org.apache.log4j.Logger;
+
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.UUID;
+
+/**
+ * Mage book with cards and page flipping.
+ *
+ * @author nantuko
+ */
+public class MageBook extends JComponent {
+
+    public MageBook(BigCard bigCard) {
+        super();
+        this.bigCard = bigCard;
+        initComponents();
+    }
+
+    private void initComponents() {
+        setOpaque(false);
+        setSize(WIDTH, HEIGHT);
+        setPreferredSize(new Dimension(WIDTH, HEIGHT));
+        setMinimumSize(new Dimension(WIDTH, HEIGHT));
+        //setBorder(BorderFactory.createLineBorder(Color.green));
+
+        jPanelLeft = getImagePanel(LEFT_PANEL_IMAGE_PATH, ImagePanel.TILED);
+        jPanelLeft.setPreferredSize(new Dimension(LEFT_RIGHT_PAGES_WIDTH, 0));
+        jPanelLeft.setLayout(null);
+        jPanelCenter = getImagePanel(CENTER_PANEL_IMAGE_PATH, ImagePanel.SCALED);
+        jPanelCenter.setLayout(new BorderLayout());
+        jPanelRight = getImagePanel(RIGHT_PANEL_IMAGE_PATH, ImagePanel.TILED);
+        jPanelRight.setPreferredSize(new Dimension(LEFT_RIGHT_PAGES_WIDTH, 0));
+        jPanelRight.setLayout(null);
+
+        jLayeredPane = new JLayeredPane();
+        jPanelCenter.add(jLayeredPane, BorderLayout.CENTER);
+
+        Image image = ImageHelper.loadImage(LEFT_PAGE_BUTTON_IMAGE_PATH);
+        pageLeft = new HoverButton(null, image, image, image, new Rectangle(64,64));
+        pageLeft.setBounds(0,0,64,64);
+        pageLeft.setVisible(false);
+        pageLeft.setObserver(new Command() {
+            public void execute() {
+                currentPage--;
+                if (currentPage == 0) {
+                    pageLeft.setVisible(false);
+                }
+                pageRight.setVisible(true);
+                showCards();
+            }
+        });
+
+        image = ImageHelper.loadImage(RIGHT_PAGE_BUTTON_IMAGE_PATH);
+        pageRight = new HoverButton(null, image, image, image, new Rectangle(64,64));
+        pageRight.setBounds(WIDTH - 2*LEFT_RIGHT_PAGES_WIDTH - 64,0,64,64);
+        pageRight.setVisible(false);
+        pageRight.setObserver(new Command() {
+            public void execute() {
+                currentPage++;
+                pageLeft.setVisible(true);
+                pageRight.setVisible(false);
+                showCards();
+            }
+        });
+
+        addSetTabs();
+
+        setLayout(new BorderLayout());
+        add(jPanelLeft, BorderLayout.LINE_START);
+        add(jPanelCenter, BorderLayout.CENTER);
+        add(jPanelRight, BorderLayout.LINE_END);
+
+        cardDimensions = new CardDimensions(0.45d);
+
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                showCards();
+            }
+        });
+    }
+
+    private void addLeftRightPageButtons() {
+        jLayeredPane.add(pageLeft, JLayeredPane.DEFAULT_LAYER, 0);
+        jLayeredPane.add(pageRight, JLayeredPane.DEFAULT_LAYER, 1);
+    }
+
+    private void addSetTabs() {
+        jPanelLeft.removeAll();
+        jPanelRight.removeAll();
+        Image image = ImageHelper.loadImage(LEFT_TAB_IMAGE_PATH);
+        Image imageRight = ImageHelper.loadImage(RIGHT_TAB_IMAGE_PATH);
+        int y = 0;
+        int dy = 0;
+        if (CardsStorage.getSetCodes().size() > 1) {
+            dy = (HEIGHT - 120) / (CardsStorage.getSetCodes().size() - 1) + 1;
+        }
+        int count = 0;
+        JPanel currentPanel = jPanelLeft;
+        for (String set : CardsStorage.getSetCodes()) {
+            HoverButton tab = new HoverButton(null, image, image, image, new Rectangle(39,120));
+            Image setImage = ManaSymbols.getSetSymbolImage(set);
+            if (setImage != null) {
+                tab.setOverlayImage(setImage);
+            } else {
+                System.out.println("Couldn't find: " + "/plugins/images/sets/" + set + ".jpg");
+            }
+            tab.setBounds(0, y, 39, 120);
+            final String _set = set;
+            tab.setObserver(new Command() {
+                public void execute() {
+                    currentPage = 0;
+                    currentSet = _set;
+                    pageLeft.setVisible(false);
+                    pageRight.setVisible(false);
+                    addSetTabs();
+                    showCards();
+                }
+            });
+            currentPanel.add(tab, JLayeredPane.DEFAULT_LAYER + count++, 0);
+            y += dy;
+            if (set.equals(currentSet)) {
+                currentPanel = jPanelRight;
+                image = imageRight;
+            }
+        }
+        jPanelLeft.revalidate();
+        jPanelLeft.repaint();
+        jPanelRight.revalidate();
+        jPanelRight.repaint();
+    }
+
+    private void showCards() {
+        jLayeredPane.removeAll();
+        addLeftRightPageButtons();
+
+        java.util.List<Card> cards = getCards(currentPage, currentSet);
+        int size = cards.size();
+
+        Rectangle rectangle = new Rectangle();
+        rectangle.translate(OFFSET_X, OFFSET_Y);
+        for (int i = 0; i < Math.min(CARDS_PER_PAGE / 2, size); i++) {
+            addCard(new CardView(cards.get(i)), bigCard, null, rectangle);
+            rectangle = CardPosition.translatePosition(i, rectangle);
+        }
+
+        // calculate the x offset of the second (right) page
+        int second_page_x = (WIDTH - 2*LEFT_RIGHT_PAGES_WIDTH) -
+                (cardDimensions.frameWidth+ CardPosition.GAP_X)*3 + CardPosition.GAP_X - OFFSET_X;
+
+        rectangle.setLocation(second_page_x, OFFSET_Y);
+        for (int i = CARDS_PER_PAGE / 2; i < Math.min(CARDS_PER_PAGE, size); i++) {
+            addCard(new CardView(cards.get(i)), bigCard, null, rectangle);
+            rectangle = CardPosition.translatePosition(i - CARDS_PER_PAGE / 2, rectangle);
+        }
+    }
+
+    private void addCard(CardView card, BigCard bigCard, UUID gameId, Rectangle rectangle) {
+        final MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, cardDimensions, gameId, false);
+        cardImg.setBounds(rectangle);
+        jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10);
+        cardImg.update(card);
+        cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.frameWidth, cardDimensions.frameHeight);
+
+        GlowText label = new GlowText();
+        label.setGlow(Color.green, 12, 0.0f);
+        label.setText("Implemented");
+        label.setBounds(rectangle.x + 15, rectangle.y + cardDimensions.frameHeight + 7, 100, 30);
+        jLayeredPane.add(label);
+    }
+
+    private java.util.List<Card> getCards(int page, String set) {
+        int start = page * CARDS_PER_PAGE;
+        int end = (page + 1) * CARDS_PER_PAGE;
+        java.util.List<Card> cards = CardsStorage.getAllCards(start, end, currentSet);
+        if (cards.size() > CARDS_PER_PAGE) {
+            pageRight.setVisible(true);
+        }
+        return cards;
+    }
+
+    private ImagePanel getImagePanel(String filename, int type) {
+        try {
+            InputStream is = this.getClass().getResourceAsStream(filename);
+
+            if (is == null) {
+                throw new FileNotFoundException("Couldn't find " + filename + " in resources.");
+            }
+
+            BufferedImage background = ImageIO.read(is);
+
+            if (background == null) {
+                throw new FileNotFoundException("Couldn't find " + filename + " in resources.");
+            }
+
+            return new ImagePanel(background, type);
+
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Defines the position of the next card on the mage book
+     */
+    private static class CardPosition {
+        private CardPosition() {
+        }
+
+        public static Rectangle translatePosition(int index, Rectangle r) {
+            Rectangle rect = new Rectangle(r);
+            rect.translate((cardDimensions.frameWidth+GAP_X) * dx[index],
+                    (cardDimensions.frameHeight + GAP_Y) * dy[index]);
+            return rect;
+        }
+
+        private static final int[] dx = {1, 1, -2, 1, 1, -2, 1, 1,  2, 1, -2, 1, 1, -2, 1, 1};
+        private static final int[] dy = {0, 0,  1, 0, 0,  1, 0, 0, -2, 0,  1, 0, 0,  1, 0, 0};
+        public static final int GAP_X = 17;
+        public static final int GAP_Y = 45;
+        private static int cardWidth;
+        private static int cardHeight;
+    }
+
+    private JPanel jPanelLeft;
+    private ImagePanel jPanelCenter;
+    private JPanel jPanelRight;
+    private JLayeredPane jLayeredPane;
+    private BigCard bigCard;
+    private HoverButton pageLeft;
+    private HoverButton pageRight;
+
+    private int currentPage = 0;
+    private String currentSet = "M10";
+
+    private static CardDimensions cardDimensions = new CardDimensions(1.2d);
+    private static Font font = new Font("Arial", Font.PLAIN, 14);
+    private static final Logger log = Logger.getLogger(MageBook.class);
+
+    static private final String CENTER_PANEL_IMAGE_PATH = "/book_bg.jpg";
+    static private final String RIGHT_PANEL_IMAGE_PATH = "/book_right.jpg";
+    static private final String LEFT_PANEL_IMAGE_PATH = "/book_left.jpg";
+    static private final String LEFT_PAGE_BUTTON_IMAGE_PATH = "/book_pager_left.png";
+    static private final String RIGHT_PAGE_BUTTON_IMAGE_PATH = "/book_pager_right.png";
+    static private final String LEFT_TAB_IMAGE_PATH = "/tab_left.png";
+    static private final String RIGHT_TAB_IMAGE_PATH = "/tab_right.png";
+    static private final int CARDS_PER_PAGE = 18;
+    static private final int WIDTH = 950;
+    static private final int HEIGHT = 650;
+    static private final int OFFSET_X = 25;
+    static private final int OFFSET_Y = 20;
+    static private final int LEFT_RIGHT_PAGES_WIDTH = 40;
+}
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java
new file mode 100644
index 0000000000..2add19017e
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java
@@ -0,0 +1,21 @@
+package mage.client.deckeditor.collection.viewer;
+
+import mage.client.components.arcane.ManaSymbols;
+import mage.client.plugins.impl.Plugins;
+
+import javax.swing.*;
+
+/**
+ * @author nantuko
+ */
+public class TestMageBook extends JFrame {
+    public static void main(String[] args) {
+        Plugins.getInstance().loadPlugins();
+        ManaSymbols.loadImages();
+        JFrame frame = new TestMageBook();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.add(new MageBook(null));
+        frame.pack();
+        frame.setVisible(true);
+    }
+}
diff --git a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java
index 9a0d4fd06b..385ba9dc92 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java
@@ -100,7 +100,7 @@ public class ShowCardsDialog extends MageDialog implements MouseListener {
 			tmp.setAbility(card); // cross-reference, required for ability picker
 			card = tmp;
 		}
-		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, dimension, gameId);
+		MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, dimension, gameId, true);
 		cardImg.setBounds(rectangle);
 		cardArea.add(cardImg);
 		cardArea.moveToFront(cardImg);
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 349affb8d6..d892b9b29b 100644
--- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java
+++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java
@@ -156,7 +156,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
 	}
 
 	private void addPermanent(PermanentView permanent, final int count) {
-		final MagePermanent perm = Plugins.getInstance().getMagePermanent(permanent, bigCard, Config.dimensions, gameId);
+		final MagePermanent perm = Plugins.getInstance().getMagePermanent(permanent, bigCard, Config.dimensions, gameId, true);
 		if (!Plugins.getInstance().isCardPluginLoaded()) {
 			perm.setBounds(findEmptySpace(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)));
 		} else {
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 e2fb11d671..7318768608 100644
--- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java
+++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java
@@ -21,8 +21,8 @@ public interface MagePlugins {
 	void shutdown();
 	void updateGamePanel(Map<String, JComponent> ui);
 	JComponent updateTablePanel(Map<String, JComponent> ui);
-	MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, CardDimensions dimension, UUID gameId);
-	MageCard getMageCard(CardView card, BigCard bigCard, CardDimensions dimension, UUID gameId);
+	MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, CardDimensions dimension, UUID gameId, boolean canBeFoil);
+	MageCard getMageCard(CardView card, BigCard bigCard, CardDimensions dimension, UUID gameId, boolean canBeFoil);
 	boolean isThemePluginLoaded();
 	boolean isCardPluginLoaded();
 	boolean isCounterPluginLoaded();
diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java
index 3562a45824..861c4129cc 100644
--- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java
+++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java
@@ -142,6 +142,9 @@ public class MageActionCallback implements ActionCallback {
                     }
 
                     try {
+                        if (session == null) {
+                            return;
+                        }
                         final Component popupContainer = session.getUI().getComponent(MageComponents.POPUP_CONTAINER);
                         Component popup2 = session.getUI().getComponent(MageComponents.CARD_INFO_PANE);
                         ((CardInfoPane) popup2).setCard(data.card);
@@ -211,6 +214,9 @@ public class MageActionCallback implements ActionCallback {
             jPopupMenu.setVisible(false);
         }
         try {
+            if (session == null) {
+                return;
+            }
             Component popupContainer = session.getUI().getComponent(MageComponents.POPUP_CONTAINER);
             popupContainer.setVisible(false);
         } catch (Exception e2) {
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 8b999ea4a1..b8e4869957 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
@@ -80,22 +80,22 @@ public class Plugins implements MagePlugins {
 	}
 	
 	@Override
-	public MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, CardDimensions dimension, UUID gameId) {
+	public MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, CardDimensions dimension, UUID gameId, boolean canBeFoil) {
 		if (cardPlugin != null) {
 			mageActionCallback.refreshSession();
 			mageActionCallback.setCardPreviewComponent(bigCard);
-			return cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback);
+			return cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, canBeFoil);
 		} else {
 			return new Permanent(card, bigCard, Config.dimensions, gameId);
 		}
 	}
 	
 	@Override
-	public MageCard getMageCard(CardView card, BigCard bigCard, CardDimensions dimension, UUID gameId) {
+	public MageCard getMageCard(CardView card, BigCard bigCard, CardDimensions dimension, UUID gameId, boolean canBeFoil) {
 		if (cardPlugin != null) {
 			mageActionCallback.refreshSession();
 			mageActionCallback.setCardPreviewComponent(bigCard);
-			return cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback);
+			return cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, canBeFoil);
 		} else {
 			return new Card(card, bigCard, Config.dimensions, gameId);
 		}
diff --git a/Mage.Client/src/main/java/mage/client/util/Command.java b/Mage.Client/src/main/java/mage/client/util/Command.java
new file mode 100644
index 0000000000..ce62caaa7a
--- /dev/null
+++ b/Mage.Client/src/main/java/mage/client/util/Command.java
@@ -0,0 +1,8 @@
+package mage.client.util;
+
+/**
+ * @author nantuko
+ */
+public interface Command {
+    public void execute();
+}
diff --git a/Mage.Client/src/main/resources/book_bg.jpg b/Mage.Client/src/main/resources/book_bg.jpg
new file mode 100644
index 0000000000..5edc4cccc9
Binary files /dev/null and b/Mage.Client/src/main/resources/book_bg.jpg differ
diff --git a/Mage.Client/src/main/resources/book_left.jpg b/Mage.Client/src/main/resources/book_left.jpg
new file mode 100644
index 0000000000..84bb854566
Binary files /dev/null and b/Mage.Client/src/main/resources/book_left.jpg differ
diff --git a/Mage.Client/src/main/resources/book_pager_left.png b/Mage.Client/src/main/resources/book_pager_left.png
new file mode 100644
index 0000000000..c1e1e1550f
Binary files /dev/null and b/Mage.Client/src/main/resources/book_pager_left.png differ
diff --git a/Mage.Client/src/main/resources/book_pager_right.png b/Mage.Client/src/main/resources/book_pager_right.png
new file mode 100644
index 0000000000..b6b06212e3
Binary files /dev/null and b/Mage.Client/src/main/resources/book_pager_right.png differ
diff --git a/Mage.Client/src/main/resources/book_right.jpg b/Mage.Client/src/main/resources/book_right.jpg
new file mode 100644
index 0000000000..fe48ea2f78
Binary files /dev/null and b/Mage.Client/src/main/resources/book_right.jpg differ
diff --git a/Mage.Client/src/main/resources/tab_left.png b/Mage.Client/src/main/resources/tab_left.png
new file mode 100644
index 0000000000..b3ba91fc86
Binary files /dev/null and b/Mage.Client/src/main/resources/tab_left.png differ
diff --git a/Mage.Client/src/main/resources/tab_right.png b/Mage.Client/src/main/resources/tab_right.png
new file mode 100644
index 0000000000..569138b883
Binary files /dev/null and b/Mage.Client/src/main/resources/tab_right.png differ
diff --git a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
index 7d8bf3b659..05818cd70c 100644
--- a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
+++ b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
@@ -25,8 +25,8 @@ import net.xeoh.plugins.base.Plugin;
  * @author nantuko
  */
 public interface CardPlugin extends Plugin {
-	MagePermanent getMagePermanent(PermanentView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback);
-	MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback);
+	MagePermanent getMagePermanent(PermanentView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback, boolean canBeFoil);
+	MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback, boolean canBeFoil);
 	void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> cards);
 	void downloadImages(Set<Card> allCards);
 	void downloadSymbols();
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 9500ed5826..283221690f 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,23 +1,9 @@
 package org.mage.plugins.card;
 
-import java.awt.BorderLayout;
-import java.awt.Frame;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.*;
-
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-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;
-import mage.components.CardInfoPane;
 import mage.interfaces.plugin.CardPlugin;
 import mage.utils.CardUtil;
 import mage.view.CardView;
@@ -26,7 +12,6 @@ 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;
@@ -40,436 +25,440 @@ import org.mage.plugins.card.dl.sources.GathererSymbols;
 import org.mage.plugins.card.images.DownloadPictures;
 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.util.*;
+import java.util.List;
+
 /**
  * {@link CardPlugin} implementation.
  *
- * @version 0.4 05.01.2011 Added support for foil cards.
- * @version 0.3 07.11.2010 Mage cards.
- * @version 0.2 07.11.2010 Downloading images.
- * @version 0.1 01.11.2010 Mage permanents. Sorting card layout.
  * @author nantuko
+ * @version 0.1 01.11.2010 Mage permanents. Sorting card layout.
  */
 @PluginImplementation
 @Author(name = "nantuko")
 public class CardPluginImpl implements CardPlugin {
 
-	private final static Logger log = Logger.getLogger(CardPluginImpl.class);
+    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;
+    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 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>();
+    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() {
-	}
+    @Init
+    public void init() {
+    }
 
-	@PluginLoaded
-	public void newPlugin(CardPlugin plugin) {
-		ManaSymbols.loadImages();
-		log.info(plugin.toString() + " has been loaded.");
-	}
+    @PluginLoaded
+    public void newPlugin(CardPlugin plugin) {
+        ManaSymbols.loadImages();
+        log.info(plugin.toString() + " has been loaded.");
+    }
 
-	public String toString() {
-		return "[Card plugin, version 0.4]";
-	}
+    public String toString() {
+        return "[Card plugin, version 0.4]";
+    }
 
-	@Override
-	public MagePermanent getMagePermanent(PermanentView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback) {
-        boolean foil = (new Random()).nextInt(5) == 0;
- 		CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback, foil);
-		cardPanel.setShowCastingCost(true);
-		cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
-		cardPanel.setShowCastingCost(true);
-		return cardPanel;
-	}
-	
-	@Override
-	public MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback) {
-		boolean foil = (new Random()).nextInt(5) == 0;
+    @Override
+    public MagePermanent getMagePermanent(PermanentView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback, boolean canBeFoil) {
+        boolean foil = canBeFoil && (new Random()).nextInt(5) == 0;
         CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback, foil);
-		cardPanel.setShowCastingCost(true);
-		cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
-		cardPanel.setShowCastingCost(true);
+        cardPanel.setShowCastingCost(true);
+        cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
+        cardPanel.setShowCastingCost(true);
+        return cardPanel;
+    }
 
-		return cardPanel;
-	}
+    @Override
+    public MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback, boolean canBeFoil) {
+        boolean foil = canBeFoil && (new Random()).nextInt(5) == 0;
+        CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback, foil);
+        cardPanel.setShowCastingCost(true);
+        cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
+        cardPanel.setShowCastingCost(true);
 
-	@Override
-	public void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents) {
-		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();
+        return cardPanel;
+    }
 
-		outerLoop: //
-		for (MagePermanent permanent : permanents) {
-			if (!CardUtil.isLand(permanent) || CardUtil.isCreature(permanent))
-				continue;
+    @Override
+    public void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents) {
+        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");
 
-			int insertIndex = -1;
+        JScrollPane jScrollPane = (JScrollPane) component;
+        JLayeredPane battlefieldPanel = (JLayeredPane) component2;
 
-			// 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;
-			}
+        Row allLands = new Row();
 
-			Stack stack = new Stack();
-			stack.add(permanent);
-			allLands.add(insertIndex == -1 ? allLands.size() : insertIndex, stack);
-		}
+        outerLoop:
+        //
+        for (MagePermanent permanent : permanents) {
+            if (!CardUtil.isLand(permanent) || CardUtil.isCreature(permanent))
+                continue;
 
-		Row allCreatures = new Row(permanents, RowType.creature);
-		Row allOthers = new Row(permanents, RowType.other);
+            int insertIndex = -1;
 
-		cardWidth = cardWidthMax;
-		Rectangle rect = jScrollPane.getVisibleRect();
-		playAreaWidth = rect.width;
-		playAreaHeight = rect.height - MW_GUIDE_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);
+            // 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;
+            }
 
-			// 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);
-			cardWidth--;
-		}
+            Stack stack = new Stack();
+            stack.add(permanent);
+            allLands.add(insertIndex == -1 ? allLands.size() : insertIndex, stack);
+        }
 
-		// 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();
+        Row allCreatures = new Row(permanents, RowType.creature);
+        Row allOthers = new Row(permanents, RowType.other);
 
-		// 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 (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);
-					battlefieldPanel.moveToBack(panel);
-					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.size() == 0;
-	}
-	
-	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;
+        cardWidth = cardWidthMax;
+        Rectangle rect = jScrollPane.getVisibleRect();
+        playAreaWidth = rect.width;
+        playAreaHeight = rect.height - MW_GUIDE_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);
 
-		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;
-	}
+            // 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);
+            cardWidth--;
+        }
 
-	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));
-		}
-	}
+        // 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();
 
-	private int getRowsHeight(List<Row> rows) {
-		int height = 0;
-		for (Row row : rows)
-			height += row.getHeight();
-		return height - cardSpacingY + GUTTER_Y * 2;
-	}
+        // 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 (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);
+                    battlefieldPanel.moveToBack(panel);
+                    panel.setCardBounds(panelX, panelY, cardWidth, cardHeight);
+                }
+                rowBottom = Math.max(rowBottom, y + stack.getHeight());
+                x += stack.getWidth();
+            }
+            y = rowBottom;
+        }
+    }
 
-	static private enum RowType {
-		land, creature, other;
+    private boolean empty(List<?> list) {
+        return list == null || list.size() == 0;
+    }
 
-		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 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;
 
-	private class Row extends ArrayList<Stack> {
-		public Row() {
-			super(16);
-		}
+        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;
+    }
 
-		public Row(Collection<MagePermanent> permanents, RowType type) {
-			this();
-			addAll(permanents, type);
-		}
+    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 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);
-			}
-		}
+    private int getRowsHeight(List<Row> rows) {
+        int height = 0;
+        for (Row row : rows)
+            height += row.getHeight();
+        return height - cardSpacingY + GUTTER_Y * 2;
+    }
 
-		public boolean addAll(Collection<? extends Stack> c) {
-			boolean changed = super.addAll(c);
-			c.clear();
-			return changed;
-		}
+    static private enum RowType {
+        land, creature, other;
 
-		private int getWidth() {
-			if (isEmpty())
-				return 0;
-			int width = 0;
-			for (Stack stack : this)
-				width += stack.getWidth();
-			return width + GUTTER_X * 2 - extraCardSpacingX;
-		}
+        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 int getHeight() {
-			if (isEmpty())
-				return 0;
-			int height = 0;
-			for (Stack stack : this)
-				height = Math.max(height, stack.getHeight());
-			return height;
-		}
-	}
+    private class Row extends ArrayList<Stack> {
+        public Row() {
+            super(16);
+        }
 
-	private class Stack extends ArrayList<MagePermanent> {
-		public Stack() {
-			super(8);
-		}
+        public Row(Collection<MagePermanent> permanents, RowType type) {
+            this();
+            addAll(permanents, type);
+        }
 
-		public boolean add(MagePermanent panel) {
-			boolean appended = super.add(panel);
-			//for (CardPanel attachedPanel : panel.attachedPanels)
-			//add(attachedPanel);
-			return appended;
-		}
+        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);
+            }
+        }
 
-		private int getWidth() {
-			return cardWidth + (size() - 1) * stackSpacingX + cardSpacingX;
-		}
+        public boolean addAll(Collection<? extends Stack> c) {
+            boolean changed = super.addAll(c);
+            c.clear();
+            return changed;
+        }
 
-		private int getHeight() {
-			return cardHeight + (size() - 1) * stackSpacingY + cardSpacingY;
-		}
-	}
+        private int getWidth() {
+            if (isEmpty())
+                return 0;
+            int width = 0;
+            for (Stack stack : this)
+                width += stack.getWidth();
+            return width + GUTTER_X * 2 - extraCardSpacingX;
+        }
 
-	@Override
-	public void downloadImages(Set<Card> allCards) {
-		DownloadPictures.startDownload(null, allCards);
-	}
-	
-	@Override
-	public void downloadSymbols() {
-		final DownloadGui g = new DownloadGui(new Downloader());
+        private int getHeight() {
+            if (isEmpty())
+                return 0;
+            int height = 0;
+            for (Stack stack : this)
+                height = Math.max(height, stack.getHeight());
+            return height;
+        }
+    }
 
-		Iterable<DownloadJob> it = new GathererSymbols();
-        
-        for(DownloadJob job:it) {
+    private class Stack extends ArrayList<MagePermanent> {
+        public Stack() {
+            super(8);
+        }
+
+        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;
+        }
+    }
+
+    @Override
+    public void downloadImages(Set<Card> allCards) {
+        DownloadPictures.startDownload(null, allCards);
+    }
+
+    @Override
+    public void downloadSymbols() {
+        final DownloadGui g = new DownloadGui(new Downloader());
+
+        Iterable<DownloadJob> it = new GathererSymbols();
+
+        for (DownloadJob job : it) {
             g.getDownloader().add(job);
         }
 
-		it = new GathererSets();
-        for(DownloadJob job:it) {
-            g.getDownloader().add(job);
+        it = new GathererSets();
+	    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();
+            }
         }
+    }
 
-
-		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((CardPanel) card, count > 0 ? count : 1);
-			try {
-				while ((card).getAlpha() - 0.05f > 0) {
-					Thread.sleep(30);
-				}
-			} catch (Exception e) {
-				e.printStackTrace();
-			}
-		}
-	}
+    @Override
+    public void onRemoveCard(MagePermanent card, int count) {
+        if (card != null) {
+            Animation.hideCard((CardPanel) card, count > 0 ? count : 1);
+            try {
+                while ((card).getAlpha() - 0.05f > 0) {
+                    Thread.sleep(30);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
 
     @Override
     public JComponent getCardInfoPane() {
diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java
index d2c51658f6..469568161f 100644
--- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java
+++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java
@@ -12,7 +12,7 @@ import static org.mage.plugins.card.dl.DownloadJob.toFile;
 public class GathererSets implements Iterable<DownloadJob> {
     private static final File     outDir  = new File("plugins/images/sets");
     private static final String[] symbols = { "M10", "M11", "ARB", "DIS", "GPT", "RAV", "ALA",
-                                              "ZEN", "WWK", "ROE", "SOM", "10E", "CFX" };
+                                              "ZEN", "WWK", "ROE", "SOM", "10E", "CFX", "HOP" };
 
     @Override
     public Iterator<DownloadJob> iterator() {