diff --git a/.gitattributes b/.gitattributes
index 93e278ddd47..e17aed17739 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6819,10 +6819,11 @@ res/lib/miglayout-3.7.3.1-swing.jar -text svneol=unset#unset
res/lib/napkinlaf-1.2.jar -text svneol=unset#unset
res/lib/nimrodlf.jar -text svneol=unset#unset
res/lib/substance.jar -text svneol=unset#unset
-res/lib/xpp3-license.txt svneol=native#text/plain
res/lib/xpp3_min-1.1.4c.jar -text svneol=unset#unset
res/lib/xstream-1.3.1.jar -text svneol=unset#unset
-res/lib/xstream-license.txt svneol=native#text/plain
+res/licenses/multiline-label-license.txt svneol=native#text/plain
+res/licenses/xpp3-license.txt svneol=native#text/plain
+res/licenses/xstream-license.txt svneol=native#text/plain
res/main.properties svneol=native#text/plain
res/mtg-data.txt svneol=native#text/plain
res/pics/BookIcon.png -text svneol=unset#image/png
@@ -7418,6 +7419,8 @@ src/forge/error/ExceptionHandler.java -text svneol=native#text/plain
src/forge/gui/ForgeAction.java svneol=native#text/plain
src/forge/gui/GuiUtils.java svneol=native#text/plain
src/forge/gui/ListChooser.java svneol=native#text/plain
+src/forge/gui/MultiLineLabel.java svneol=native#text/plain
+src/forge/gui/MultiLineLabelUI.java svneol=native#text/plain
src/forge/gui/game/CardDetailPanel.java svneol=native#text/plain
src/forge/gui/game/CardPanel.java svneol=native#text/plain
src/forge/gui/game/CardPicturePanel.java svneol=native#text/plain
diff --git a/res/licenses/multiline-label-license.txt b/res/licenses/multiline-label-license.txt
new file mode 100644
index 00000000000..23bc45e4fb1
--- /dev/null
+++ b/res/licenses/multiline-label-license.txt
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2009 Samuel Sjoberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
\ No newline at end of file
diff --git a/res/lib/xpp3-license.txt b/res/licenses/xpp3-license.txt
similarity index 100%
rename from res/lib/xpp3-license.txt
rename to res/licenses/xpp3-license.txt
diff --git a/res/lib/xstream-license.txt b/res/licenses/xstream-license.txt
similarity index 100%
rename from res/lib/xstream-license.txt
rename to res/licenses/xstream-license.txt
diff --git a/src/forge/gui/MultiLineLabel.java b/src/forge/gui/MultiLineLabel.java
new file mode 100755
index 00000000000..32736d01d40
--- /dev/null
+++ b/src/forge/gui/MultiLineLabel.java
@@ -0,0 +1,120 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package forge.gui;
+
+import javax.swing.*;
+import java.awt.Rectangle;
+
+
+/**
+ * A {@link JLabel} with support for multi-line text that wraps when the line
+ * doesn't fit in the available width. Multi-line text support is handled by the
+ * {@link MultiLineLabelUI}, the default UI delegate of this component. The text
+ * in the label can be horizontally and vertically aligned, relative to the
+ * bounds of the component.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.0.0
+ */
+public class MultiLineLabel extends JLabel {
+
+ /** Default serial version UID. */
+ private static final long serialVersionUID = 1L;
+
+ /** Horizontal text alignment. */
+ private int halign = LEFT;
+
+ /** Vertical text alignment. */
+ private int valign = CENTER;
+
+ /** Cache to save heap allocations. */
+ private Rectangle bounds;
+
+ /**
+ * Creates a new empty label.
+ */
+ public MultiLineLabel() {
+ super();
+ setUI(MultiLineLabelUI.labelUI);
+ }
+
+ /**
+ * Creates a new label with text value.
+ *
+ * @param text
+ * the value of the label
+ */
+ public MultiLineLabel(String text) {
+ this();
+ setText(text);
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle getBounds() {
+ if (bounds == null) {
+ bounds = new Rectangle();
+ }
+ return super.getBounds(bounds);
+ }
+
+ /**
+ * Set the vertical text alignment.
+ *
+ * @param alignment
+ * vertical alignment
+ */
+ public void setVerticalTextAlignment(int alignment) {
+ firePropertyChange("verticalTextAlignment", valign, alignment);
+ valign = alignment;
+ }
+
+ /**
+ * Set the horizontal text alignment.
+ *
+ * @param alignment
+ * horizontal alignment
+ */
+ public void setHorizontalTextAlignment(int alignment) {
+ firePropertyChange("horizontalTextAlignment", halign, alignment);
+ halign = alignment;
+ }
+
+ /**
+ * Get the vertical text alignment.
+ *
+ * @return vertical text alignment
+ */
+ public int getVerticalTextAlignment() {
+ return valign;
+ }
+
+ /**
+ * Get the horizontal text alignment.
+ *
+ * @return horizontal text alignment
+ */
+ public int getHorizontalTextAlignment() {
+ return halign;
+ }
+}
diff --git a/src/forge/gui/MultiLineLabelUI.java b/src/forge/gui/MultiLineLabelUI.java
new file mode 100755
index 00000000000..fb920374a74
--- /dev/null
+++ b/src/forge/gui/MultiLineLabelUI.java
@@ -0,0 +1,592 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package forge.gui;
+
+import javax.swing.*;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicLabelUI;
+import javax.swing.text.*;
+import java.awt.*;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Label UI delegate that supports multiple lines and line wrapping. Hard line
+ * breaks (\n) are preserved. If the dimensions of the label is too
+ * small to fit all content, the string will be clipped and "..." appended to
+ * the end of the visible text (similar to the default behavior of
+ * JLabel). If used in conjunction with a {@link MultiLineLabel},
+ * text alignment (horizontal and vertical) is supported. The UI delegate can be
+ * used on a regular JLabel if text alignment isn't required. The
+ * default alignment, left and vertically centered, will then be used.
+ *
+ * Example of usage: + * + *
+ * JLabel myLabel = new JLabel();
+ * myLabel.setUI(MultiLineLabelUI.labelUI);
+ * myLabel.setText("A long label that will wrap automatically.");
+ *
+ *
+ *
+ * The line and wrapping support is implemented without using a
+ * View to make it easy for subclasses to add custom text effects
+ * by overriding {@link #paintEnabledText(JLabel, Graphics, String, int, int)}
+ * and {@link #paintDisabledText(JLabel, Graphics, String, int, int)}. This
+ * class is designed to be easily extended by subclasses.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.3.0
+ */
+public class MultiLineLabelUI extends BasicLabelUI implements ComponentListener {
+
+ /** Shared instance of the UI delegate. */
+ public static LabelUI labelUI = new MultiLineLabelUI();
+
+ /**
+ * Client property key used to store the calculated wrapped lines on the
+ * JLabel.
+ */
+ public static final String PROPERTY_KEY = "WrappedText";
+
+ // Static references to avoid heap allocations.
+ protected static Rectangle paintIconR = new Rectangle();
+ protected static Rectangle paintTextR = new Rectangle();
+ protected static Rectangle paintViewR = new Rectangle();
+ protected static Insets paintViewInsets = new Insets(0, 0, 0, 0);
+
+ /** Font metrics of the JLabel being rendered. */
+ protected FontMetrics metrics;
+
+ /** Default size of the lines list. */
+ protected static int defaultSize = 4;
+
+ /**
+ * Get the shared UI instance.
+ *
+ * @param c
+ * the component about to be installed
+ * @return the shared UI delegate instance
+ */
+ public static ComponentUI createUI(JComponent c) {
+ return labelUI;
+ }
+
+ /** {@inheritDoc} */
+ protected void uninstallDefaults(JLabel c) {
+ super.uninstallDefaults(c);
+ clearCache(c);
+ }
+
+ /** {@inheritDoc} */
+ protected void installListeners(JLabel c) {
+ super.installListeners(c);
+ c.addComponentListener(this);
+ }
+
+ /** {@inheritDoc} */
+ protected void uninstallListeners(JLabel c) {
+ super.uninstallListeners(c);
+ c.removeComponentListener(this);
+ }
+
+ /**
+ * Clear the wrapped line cache.
+ *
+ * @param l
+ * the label containing a cached value
+ */
+ protected void clearCache(JLabel l) {
+ l.putClientProperty(PROPERTY_KEY, null);
+ }
+
+ /** {@inheritDoc} */
+ public void propertyChange(PropertyChangeEvent e) {
+ super.propertyChange(e);
+ final String name = e.getPropertyName();
+ if (name.equals("text") || "font".equals(name)) {
+ clearCache((JLabel) e.getSource());
+ }
+ }
+
+ /**
+ * Calculate the paint rectangles for the icon and text for the passed
+ * label.
+ *
+ * @param l
+ * a label
+ * @param fm
+ * the font metrics to use, or null to get the font
+ * metrics from the label
+ * @param width
+ * label width
+ * @param height
+ * label height
+ */
+ protected void updateLayout(JLabel l, FontMetrics fm, int width, int height) {
+ if (fm == null) {
+ fm = l.getFontMetrics(l.getFont());
+ }
+ metrics = fm;
+
+ String text = l.getText();
+ Icon icon = l.getIcon();
+ Insets insets = l.getInsets(paintViewInsets);
+
+ paintViewR.x = insets.left;
+ paintViewR.y = insets.top;
+ paintViewR.width = width - (insets.left + insets.right);
+ paintViewR.height = height - (insets.top + insets.bottom);
+
+ paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
+ paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
+
+ layoutCL(l, fm, text, icon, paintViewR, paintIconR, paintTextR);
+ }
+
+ protected void prepareGraphics(Graphics g) {
+ }
+
+ /** {@inheritDoc} */
+ public void paint(Graphics g, JComponent c) {
+
+ // parent's update method fills the background
+ prepareGraphics(g);
+
+ JLabel label = (JLabel) c;
+ String text = label.getText();
+ Icon icon = (label.isEnabled()) ? label.getIcon() : label
+ .getDisabledIcon();
+
+ if ((icon == null) && (text == null)) {
+ return;
+ }
+
+ FontMetrics fm = g.getFontMetrics();
+
+ updateLayout(label, fm, c.getWidth(), c.getHeight());
+
+ if (icon != null) {
+ icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
+ }
+
+ if (text != null) {
+ View v = (View) c.getClientProperty("html");
+ if (v != null) {
+ // HTML view disables multi-line painting.
+ v.paint(g, paintTextR);
+ } else {
+ // Paint the multi line text
+ paintTextLines(g, label, fm);
+ }
+ }
+ }
+
+ /**
+ * Paint the wrapped text lines.
+ *
+ * @param g
+ * graphics component to paint on
+ * @param label
+ * the label being painted
+ * @param fm
+ * font metrics for current font
+ */
+ protected void paintTextLines(Graphics g, JLabel label, FontMetrics fm) {
+ ListBasicHTML.isHTMLString(String) in future JDKs.
+ *
+ * @param s
+ * the string
+ * @return true if string is HTML, otherwise false
+ */
+ private static boolean isHTMLString(String s) {
+ if (s != null) {
+ if ((s.length() >= 6) && (s.charAt(0) == '<')
+ && (s.charAt(5) == '>')) {
+ String tag = s.substring(1, 5);
+ return tag.equalsIgnoreCase("html");
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public Dimension getPreferredSize(JComponent c) {
+ Dimension d = super.getPreferredSize(c);
+ JLabel label = (JLabel) c;
+
+ if (isHTMLString(label.getText())) {
+ return d; // HTML overrides everything and we don't need to process
+ }
+
+ // Width calculated by super is OK. The preferred width is the width of
+ // the unwrapped content as long as it does not exceed the width of the
+ // parent container.
+
+ if (c.getParent() != null) {
+ // Ensure that preferred width never exceeds the available width
+ // (including its border insets) of the parent container.
+ Insets insets = c.getParent().getInsets();
+ Dimension size = c.getParent().getSize();
+ if (size.width > 0) {
+ // If width isn't set component shouldn't adjust.
+ d.width = size.width - insets.left - insets.right;
+ }
+ }
+
+ updateLayout(label, null, d.width, d.height);
+
+ // The preferred height is either the preferred height of the text
+ // lines, or the height of the icon.
+ d.height = Math.max(d.height, getPreferredHeight(label));
+
+ return d;
+ }
+
+ /**
+ * The preferred height of the label is the height of the lines with added
+ * top and bottom insets.
+ *
+ * @param label
+ * the label
+ * @return the preferred height of the wrapped lines.
+ */
+ protected int getPreferredHeight(JLabel label) {
+ int numOfLines = getTextLines(label).size();
+ Insets insets = label.getInsets(paintViewInsets);
+ return numOfLines * metrics.getHeight() + insets.top + insets.bottom;
+ }
+
+ /**
+ * Get the lines of text contained in the text label. The prepared lines is
+ * cached as a client property, accessible via {@link #PROPERTY_KEY}.
+ *
+ * @param l
+ * the label
+ * @return the text lines of the label.
+ */
+ @SuppressWarnings("unchecked")
+ protected Listp1 if content does
+ * not need to wrap, otherwise it will be less than p1.
+ */
+ protected int calculateBreakPosition(Document doc, int p0, int p1) {
+ Segment segment = SegmentCache.getSegment();
+ try {
+ doc.getText(p0, p1 - p0, segment);
+ } catch (BadLocationException e) {
+ throw new Error("Can't get line text");
+ }
+
+ int width = paintTextR.width;
+ int p = p0
+ + Utilities.getBreakLocation(segment, metrics, 0, width, null,
+ p0);
+ SegmentCache.releaseSegment(segment);
+ return p;
+ }
+
+ /**
+ * Static singleton {@link Segment} cache.
+ *
+ * @see javax.swing.text.SegmentCache
+ *
+ * @author Samuel Sjoberg
+ */
+ protected static final class SegmentCache {
+
+ /** Reused segments. */
+ private ArrayListSegment. When done, the Segment
+ * should be recycled by invoking {@link #releaseSegment(Segment)}.
+ *
+ * @return a Segment.
+ */
+ public static Segment getSegment() {
+ int size = cache.segments.size();
+ if (size > 0) {
+ return cache.segments.remove(size - 1);
+ }
+ return new Segment();
+ }
+
+ /**
+ * Releases a Segment. A segment should not be used after
+ * it is released, and a segment should never be released more than
+ * once.
+ */
+ public static void releaseSegment(Segment segment) {
+ segment.array = null;
+ segment.count = 0;
+ cache.segments.add(segment);
+ }
+ }
+}
\ No newline at end of file