From b9eba43ccf5e537bcb8ca79df629c13bb7ecc629 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Sat, 25 Dec 2010 22:52:16 +0300 Subject: [PATCH] New feedback panel (supports mana symbols). --- .../src/main/java/mage/client/MageFrame.java | 3 + .../mage/client/components/MageTextArea.java | 68 ++++++++ .../client/components/arcane/ManaSymbols.java | 98 +++++++++++ .../mage/client/components/arcane/UI.java | 163 ++++++++++++++++++ .../client/{util => constants}/Constants.java | 11 +- .../java/mage/client/game/FeedbackPanel.java | 18 +- .../client/util/gui/BufferedImageBuilder.java | 58 +++++++ .../mage/client/util/gui/ColorsChooser.java | 4 +- .../mage/client/util/gui/ImageResizeUtil.java | 18 ++ 9 files changed, 429 insertions(+), 12 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/components/MageTextArea.java create mode 100644 Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java create mode 100644 Mage.Client/src/main/java/mage/client/components/arcane/UI.java rename Mage.Client/src/main/java/mage/client/{util => constants}/Constants.java (87%) create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/BufferedImageBuilder.java create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/ImageResizeUtil.java diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 075793e4433..c8bc92ee21c 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -66,6 +66,7 @@ import javax.swing.UIManager; import mage.client.cards.CardsStorage; import mage.client.components.MageComponents; +import mage.client.components.arcane.ManaSymbols; import mage.client.dialog.*; import mage.client.plugins.impl.Plugins; import mage.client.remote.Session; @@ -151,6 +152,8 @@ public class MageFrame extends javax.swing.JFrame { session.getUI().addComponent(MageComponents.CARD_INFO_PANE, cardInfoPane); desktopPane.add(cardInfoPane, JLayeredPane.POPUP_LAYER); + ManaSymbols.loadImages(); + String filename = "/background.jpg"; try { if (Plugins.getInstance().isThemePluginLoaded()) { diff --git a/Mage.Client/src/main/java/mage/client/components/MageTextArea.java b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java new file mode 100644 index 00000000000..b59c21e0561 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java @@ -0,0 +1,68 @@ +package mage.client.components; + +import mage.client.components.arcane.ManaSymbols; +import mage.client.components.arcane.UI; +import mage.view.CardView; + +import javax.swing.*; +import java.awt.*; + +/** + * Component for displaying text in mage. + * Supports drawing mana symbols. + * + * @author nantuko + */ +public class MageTextArea extends JEditorPane { + + public MageTextArea() { + UI.setHTMLEditorKit(this); + setEditable(false); + setBackground(new Color(0, 0, 0, 0)); // transparent background + setFocusable(false); + setBorder(BorderFactory.createLineBorder(Color.red)); + //setSelectionColor(new Color(0, 0, 0, 0)); + } + + public void setText(String text) { + setText(text, null); + } + + public void setText(String text, CardView source) { + if (text == null) return; + + boolean smallImages = false; + int fontSize = 12; + + String fontFamily = "arial"; + + final StringBuffer buffer = new StringBuffer(512); + buffer.append(""); + + text = text.replaceAll("#([^#]+)#", "$1"); + text = text.replaceAll("\\s*//\\s*", "
"); + text = text.replace("\r\n", "
"); + text += "
"; + + if (text.length() > 0) { + //buffer.append("
"); + //text = text.replaceAll("\\{this\\}", card.getName()); + //text = text.replaceAll("\\{source\\}", card.getName()); + buffer.append(ManaSymbols.replaceSymbolsWithHTML(text, smallImages)); + } + + buffer.append(""); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + MageTextArea.super.setText(buffer.toString()); + //System.out.println(buffer.toString()); + setCaretPosition(0); + } + }); + } +} 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 new file mode 100644 index 00000000000..80dff70037a --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/arcane/ManaSymbols.java @@ -0,0 +1,98 @@ +package mage.client.components.arcane; + +import mage.client.constants.Constants; +import mage.client.util.gui.BufferedImageBuilder; +import mage.client.util.gui.ImageResizeUtil; +import org.apache.log4j.Logger; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +public class ManaSymbols { + private static final Logger log = Logger.getLogger(ManaSymbols.class); + static private final Map manaImages = new HashMap(); + static private final Map manaImagesOriginal = new HashMap(); + static private Pattern replaceSymbolsPattern = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}"); + static private boolean noManaSymbols = false; + + static public void loadImages () { + String[] symbols = new String[] {"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG", + "BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", "X" /*, "Y", "Z", "slash"*/}; + for (String symbol : symbols) { + File file = new File(Constants.RESOURCE_PATH_MANA_LARGE + "/" + symbol + ".jpg"); + BufferedImageBuilder builder = new BufferedImageBuilder(); + Rectangle r = new Rectangle(11, 11); + try { + Image image = UI.getImageIcon(file.getAbsolutePath()).getImage(); + BufferedImage resized = ImageResizeUtil.getResizedImage(builder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r); + manaImages.put(symbol, resized); + } catch (Exception e) { + noManaSymbols = true; + } + file = new File(Constants.RESOURCE_PATH_MANA_MEDIUM + "/" + symbol + ".jpg"); + try { + Image image = UI.getImageIcon(file.getAbsolutePath()).getImage(); + manaImagesOriginal.put(symbol, image); + } catch (Exception e) {} + } + } + + static public Image getManaSymbolImage(String symbol) { + return manaImagesOriginal.get(symbol); + } + + static public void draw (Graphics g, String manaCost, int x, int y) { + if (manaCost.length() == 0) return; + manaCost = manaCost.replace("\\", ""); + manaCost = UI.getDisplayManaCost(manaCost); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + String symbol = tok.nextToken().substring(0); + Image image = manaImages.get(symbol); + if (image == null) { + //log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost); + continue; + } + g.drawImage(image, x, y, null); + x += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide. + } + } + + static public String getStringManaCost(List manaCost) { + StringBuilder sb = new StringBuilder(); + for (String s : manaCost) { + sb.append(s); + } + return sb.toString().replace("{", "").replace("}", " ").trim(); + } + + static public int getWidth(String manaCost) { + int width = 0; + manaCost = manaCost.replace("\\", ""); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + String symbol = tok.nextToken().substring(0); + width += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide. + } + return width; + } + + static public synchronized String replaceSymbolsWithHTML (String value, boolean small) { + if (noManaSymbols) { + return value; + } else { + if (small) + return replaceSymbolsPattern.matcher(value).replaceAll("$1$2"); + else { + value = value.replace("{slash}", "slash"); + return replaceSymbolsPattern.matcher(value).replaceAll("$1$2"); + } + } + } +} diff --git a/Mage.Client/src/main/java/mage/client/components/arcane/UI.java b/Mage.Client/src/main/java/mage/client/components/arcane/UI.java new file mode 100644 index 00000000000..88f2479a9ce --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/arcane/UI.java @@ -0,0 +1,163 @@ +package mage.client.components.arcane; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.ImageView; +import java.awt.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Hashtable; + +/** + * UI utility functions. + */ +public class UI { + static private Hashtable imageCache = new Hashtable(); + + static public JToggleButton getToggleButton () { + JToggleButton button = new JToggleButton(); + button.setMargin(new Insets(2, 4, 2, 4)); + return button; + } + + static public JButton getButton () { + JButton button = new JButton(); + button.setMargin(new Insets(2, 4, 2, 4)); + return button; + } + + static public void setTitle (JPanel panel, String title) { + Border border = panel.getBorder(); + if (border instanceof TitledBorder) { + ((TitledBorder)panel.getBorder()).setTitle(title); + panel.repaint(); + } else + panel.setBorder(BorderFactory.createTitledBorder(title)); + } + + @SuppressWarnings("deprecation") + static public URL getFileURL (String path) { + File file = new File(path); + if (file.exists()) { + try { + return file.toURL(); + } catch (MalformedURLException ignored) { + } + } + return UI.class.getResource(path); + } + + static public ImageIcon getImageIcon (String path) { + try { + InputStream stream; + stream = UI.class.getResourceAsStream(path); + if (stream == null && new File(path).exists()) stream = new FileInputStream(path); + if (stream == null) throw new RuntimeException("Image not found: " + path); + byte[] data = new byte[stream.available()]; + stream.read(data); + return new ImageIcon(data); + } catch (IOException ex) { + throw new RuntimeException("Error reading image: " + path); + } + } + + static public void setHTMLEditorKit (JEditorPane editorPane) { + editorPane.getDocument().putProperty("imageCache", imageCache); // Read internally by ImageView, but never written. + // Extend all this shit to cache images. + editorPane.setEditorKit(new HTMLEditorKit() { + private static final long serialVersionUID = -54602188235105448L; + + public ViewFactory getViewFactory () { + return new HTMLFactory() { + public View create (Element elem) { + Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (o instanceof HTML.Tag) { + HTML.Tag kind = (HTML.Tag)o; + if (kind == HTML.Tag.IMG) return new ImageView(elem) { + public URL getImageURL () { + URL url = super.getImageURL(); + // Put an image into the cache to be read by other ImageView methods. + if (url != null && imageCache.get(url) == null) + imageCache.put(url, Toolkit.getDefaultToolkit().createImage(url)); + return url; + } + }; + } + return super.create(elem); + } + }; + } + }); + } + + static public void setVerticalScrollingView (JScrollPane scrollPane, final Component view) { + final JViewport viewport = new JViewport(); + viewport.setLayout(new ViewportLayout() { + private static final long serialVersionUID = 7701568740313788935L; + public void layoutContainer (Container parent) { + viewport.setViewPosition(new Point(0, 0)); + Dimension viewportSize = viewport.getSize(); + int width = viewportSize.width; + int height = Math.max(view.getPreferredSize().height, viewportSize.height); + viewport.setViewSize(new Dimension(width, height)); + } + }); + viewport.setView(view); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setViewport(viewport); + } + + static public String getDisplayManaCost (String manaCost) { + manaCost = manaCost.replace("/", "{slash}"); + // A pipe in the cost means "process left of the pipe as the card color, but display right of the pipe as the cost". + int pipePosition = manaCost.indexOf("{|}"); + if (pipePosition != -1) manaCost = manaCost.substring(pipePosition + 3); + return manaCost; + } + + static public void invokeLater (Runnable runnable) { + EventQueue.invokeLater(runnable); + } + + static public void invokeAndWait (Runnable runnable) { + if (EventQueue.isDispatchThread()) { + runnable.run(); + return; + } + try { + EventQueue.invokeAndWait(runnable); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + + static public void setSystemLookAndFeel () { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ex) { + System.err.println("Error setting look and feel:"); + ex.printStackTrace(); + } + } + + static public void setDefaultFont (Font font) { + for (Object key : Collections.list(UIManager.getDefaults().keys())) { + Object value = UIManager.get(key); + if (value instanceof javax.swing.plaf.FontUIResource) UIManager.put(key, font); + } + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/Constants.java b/Mage.Client/src/main/java/mage/client/constants/Constants.java similarity index 87% rename from Mage.Client/src/main/java/mage/client/util/Constants.java rename to Mage.Client/src/main/java/mage/client/constants/Constants.java index 05435cc70eb..2804f8d751b 100644 --- a/Mage.Client/src/main/java/mage/client/util/Constants.java +++ b/Mage.Client/src/main/java/mage/client/constants/Constants.java @@ -26,10 +26,11 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.client.util; +package mage.client.constants; import javax.swing.BorderFactory; import javax.swing.border.Border; +import java.io.File; /** * @@ -73,4 +74,12 @@ public final class Constants { public static final double SCALE_FACTOR = 0.5; public static final String PLUGINS_DIRECTORY = "plugins/"; + + 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 interface IO { + public static final String imageBaseDir = "plugins" + File.separator + "images" + File.separator; + public static final String IMAGE_PROPERTIES_FILE = "image.url.properties"; + } } diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 676212c45a2..6301ec81bc5 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -45,6 +45,7 @@ import java.util.UUID; import java.util.logging.Logger; import javax.swing.SwingUtilities; import mage.client.MageFrame; +import mage.client.components.MageTextArea; import mage.client.remote.Session; import mage.util.Logging; @@ -182,7 +183,8 @@ public class FeedbackPanel extends javax.swing.JPanel { btnRight = new javax.swing.JButton(); btnLeft = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); - lblMessage = new javax.swing.JTextArea(); + //lblMessage = new javax.swing.JTextArea(); + lblMessage = new MageTextArea(); btnSpecial = new javax.swing.JButton(); setBackground(new java.awt.Color(204, 204, 204)); @@ -204,14 +206,16 @@ public class FeedbackPanel extends javax.swing.JPanel { jScrollPane1.setBorder(javax.swing.BorderFactory.createEtchedBorder()); jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - lblMessage.setBackground(new java.awt.Color(204, 204, 204)); - lblMessage.setColumns(20); + //lblMessage.setBackground(new java.awt.Color(204, 204, 204)); + /*lblMessage.setColumns(20); lblMessage.setEditable(false); lblMessage.setLineWrap(true); lblMessage.setRows(2); - lblMessage.setWrapStyleWord(true); + lblMessage.setWrapStyleWord(true);*/ + lblMessage.setBorder(null); jScrollPane1.setViewportView(lblMessage); + jScrollPane1.setBorder(null); btnSpecial.setText("Special"); btnSpecial.addActionListener(new java.awt.event.ActionListener() { @@ -265,12 +269,10 @@ public class FeedbackPanel extends javax.swing.JPanel { }//GEN-LAST:event_btnSpecialActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnLeft; private javax.swing.JButton btnRight; private javax.swing.JButton btnSpecial; private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JTextArea lblMessage; - // End of variables declaration//GEN-END:variables - + //private javax.swing.JTextArea lblMessage; + private MageTextArea lblMessage; } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/BufferedImageBuilder.java b/Mage.Client/src/main/java/mage/client/util/gui/BufferedImageBuilder.java new file mode 100644 index 00000000000..f10dea97ac7 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/BufferedImageBuilder.java @@ -0,0 +1,58 @@ +package mage.client.util.gui; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; + +public class BufferedImageBuilder { + + private static final int DEFAULT_IMAGE_TYPE = BufferedImage.TYPE_INT_RGB; + + public BufferedImage bufferImage(Image image) { + return bufferImage(image, DEFAULT_IMAGE_TYPE); + } + + public BufferedImage bufferImage(Image image, int type) { + BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); + Graphics2D g = bufferedImage.createGraphics(); + g.drawImage(image, null, null); + //waitForImage(bufferedImage); + return bufferedImage; + } + + private void waitForImage(BufferedImage bufferedImage) { + final ImageLoadStatus imageLoadStatus = new ImageLoadStatus(); + bufferedImage.getHeight(new ImageObserver() { + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + if (infoflags == ALLBITS) { + imageLoadStatus.heightDone = true; + return true; + } + return false; + } + }); + bufferedImage.getWidth(new ImageObserver() { + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + if (infoflags == ALLBITS) { + imageLoadStatus.widthDone = true; + return true; + } + return false; + } + }); + while (!imageLoadStatus.widthDone && !imageLoadStatus.heightDone) { + try { + Thread.sleep(300); + } catch (InterruptedException e) { + + } + } + } + + class ImageLoadStatus { + + public boolean widthDone = false; + public boolean heightDone = false; + } + +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java index ac82f4d7dbe..001388f7941 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java @@ -1,10 +1,8 @@ package mage.client.util.gui; -import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.FlowLayout; -import java.awt.GridLayout; import java.awt.Image; import java.util.ArrayList; import java.util.List; @@ -17,8 +15,8 @@ import javax.swing.JList; import javax.swing.JPanel; import javax.swing.ListCellRenderer; +import mage.client.constants.Constants; import mage.client.plugins.impl.Plugins; -import mage.client.util.Constants; public class ColorsChooser extends JComboBox implements ListCellRenderer { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ImageResizeUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/ImageResizeUtil.java new file mode 100644 index 00000000000..d12e5278aba --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/ImageResizeUtil.java @@ -0,0 +1,18 @@ +package mage.client.util.gui; + +import com.mortennobel.imagescaling.ResampleOp; + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * @author nantuko + */ +public class ImageResizeUtil { + + public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) { + ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height); + BufferedImage image = resampleOp.filter(original, null); + return image; + } +}