/* * Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * $Id: JMultiLabel.java 3957 2008-05-26 07:57:51Z gbevin $ */ package com.uwyn.rife.swing; import com.uwyn.rife.tools.StringUtils; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; 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.util.ArrayList; import java.util.Collection; public class JMultiLabel extends JPanel implements ComponentListener { private static final long serialVersionUID = 4732617870135188398L; private String mMessageText = null; private Font mMessageFont = null; private Color mMessageColor = null; private int mWrapWidth = 0; private boolean mAutoWrap = false; private ArrayList<TextLayout> mTextLayouts = null; private double mTextWidth = 0; private double mTextHeight = 0; private int mCachedWidth = -1; public JMultiLabel() { this(""); } public JMultiLabel(String messageText) { this(messageText, 0); } public JMultiLabel(String messageText, int wrapWidth) { mMessageText = messageText; setOpaque(false); addComponentListener(this); setFont(UIManager.getFont("Label.font")); setWrapWidth(wrapWidth); } public void setMessageText(String messageText) { mMessageText = messageText; mCachedWidth = -1; } public void setWrapWidth(int wrapWidth) { mWrapWidth = wrapWidth; mCachedWidth = -1; } public void setAutoWrap(boolean autoWrap) { mAutoWrap = autoWrap; } public void setFont(Font font) { mCachedWidth = -1; mMessageFont = font; } public Font getFont() { return mMessageFont; } public void setColor(Color color) { mMessageColor = color; } public Dimension getPreferredSize() { Graphics2D g2 =(Graphics2D)getGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); ensureValidLayouts(g2); Dimension dimension = null; Border border = getBorder(); if (null == border) { dimension = new Dimension((int)Math.ceil(mTextWidth), (int)Math.ceil(mTextHeight)); } else { Insets border_insets = border.getBorderInsets(this); dimension = new Dimension(((int)Math.ceil(mTextWidth)) + border_insets.left + border_insets.right, ((int)Math.ceil(mTextHeight)) + border_insets.top + border_insets.bottom); } return dimension; } private int getWrappingWidth() { if (mWrapWidth <= 0) { if (!mAutoWrap) { return Integer.MAX_VALUE; } Border border = getBorder(); int border_horiz = 0; if (border != null) { Insets border_insets = border.getBorderInsets(this); border_horiz = border_insets.left + border_insets.right; } int width = super.getWidth() -border_horiz; if (width <= 0) { width = Integer.MAX_VALUE; } return width; } return mWrapWidth; } public Dimension getMinimumSize() { Dimension minimum_size = super.getMinimumSize(); Dimension preferred_size = getPreferredSize(); return new Dimension((int)Math.ceil(minimum_size.getWidth()), (int)Math.ceil(preferred_size.getHeight())); } public Dimension getMaximumSize() { return super.getMaximumSize(); } public void paint(Graphics g) { super.paint(g); paintMessage((Graphics2D)g); } private void addTextLayout(TextLayout layout) { mTextLayouts.add(layout); // update the global text dimensions if (mTextHeight != 0) { mTextHeight += layout.getLeading(); } double layout_width = layout.getBounds().getWidth()+1; if (layout_width > mTextWidth) { mTextWidth = layout_width; } mTextHeight += layout.getAscent()+layout.getDescent(); } private void ensureValidLayouts(Graphics2D g2) { int wrapping_width = getWrappingWidth(); if (mCachedWidth == wrapping_width) { return; } mTextLayouts = new ArrayList<TextLayout>(); FontRenderContext render_context = g2.getFontRenderContext(); if (null == mMessageText || 0 == mMessageText.length()) { TextLayout layout = new TextLayout(" ", mMessageFont, render_context); mTextLayouts.add(layout); // update the global text dimensions mTextWidth = 0; mTextHeight = 0; } else { // Get the newlines and create a new text that has them stripped away Collection<String> lines = StringUtils.split(mMessageText, "\n"); int newline_count = lines.size(); int character_count = 0; for (String line : lines) { character_count += line.length(); } TextLayout layout = null; // It might be possible that the string only contains linebreaks // the stripped string will then be empty and no linebreakmeasurer // can be used if (0 == character_count) { for (int i = 0; i < newline_count; i++) { layout = new TextLayout(" ", getFont(), render_context); addTextLayout(layout); } } else { mTextWidth = 0; mTextHeight = 0; for (String line : lines) { // handle empty newlines if (0 == line.length()) { layout = new TextLayout(" ", getFont(), render_context); addTextLayout(layout); } else { // Create an Attributed String AttributedString attributed_string = new AttributedString(line); attributed_string.addAttribute(TextAttribute.FONT, getFont()); // Get the iterator. AttributedCharacterIterator iterator = attributed_string.getIterator(); // Create the layouts LineBreakMeasurer measurer = new LineBreakMeasurer(iterator, render_context); while (measurer.getPosition() < iterator.getEndIndex()) { try { layout = measurer.nextLayout(wrapping_width); } // fix around jdk bug catch (ArrayIndexOutOfBoundsException e) { break; } addTextLayout(layout); } } } } } // Cache the width so that all these calculations are not needlessly // done if (Integer.MAX_VALUE != wrapping_width) { mCachedWidth = -1; } else { mCachedWidth = wrapping_width; } } public boolean paintMessage(Graphics2D g2) { Object previous_aliasing = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); Object previous_textaliasing = g2.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING); Color previous_color = g2.getColor(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setColor(mMessageColor); ensureValidLayouts(g2); Border border = getBorder(); int border_x = 0; int border_y = 0; if (border != null) { Insets border_insets = border.getBorderInsets(this); border_x = border_insets.left; border_y = border_insets.top; } boolean all_lines_shown = true; if (mTextLayouts != null && mTextLayouts.size() > 0) { float x = border_x; float y1 = border_y; float y2 = -1; for (TextLayout layout : mTextLayouts) { y1 += layout.getAscent(); y2 = y1+layout.getDescent(); // don't draw lines that can't be rendered completely int height = getHeight(); if (y2 > height) { all_lines_shown = false; break; } layout.draw(g2, x, y1); y1 = y2+layout.getLeading(); } } g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, previous_textaliasing); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, previous_aliasing); g2.setColor(previous_color); return all_lines_shown; } public void componentMoved(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void componentResized(ComponentEvent e) { if (mAutoWrap) { mWrapWidth = 0; mCachedWidth = -1; } } }