001 // License: GPL. For details, see LICENSE file.
002
003 package org.openstreetmap.josm.gui;
004
005 import java.awt.Dimension;
006 import java.awt.Rectangle;
007
008 import javax.swing.JLabel;
009 import javax.swing.plaf.basic.BasicHTML;
010 import javax.swing.text.View;
011
012 /**
013 * Creates a normal label that will wrap its contents if there less width than
014 * required to print it in one line. Additionally the maximum width of the text
015 * can be set using <code>setMaxWidth</code>.
016 *
017 * Note that this won't work if JMultilineLabel is put into a JScrollBox or
018 * similar as the bounds will never change. Instead scrollbars will be displayed.
019 */
020 public class JMultilineLabel extends JLabel {
021 private int maxWidth = Integer.MAX_VALUE;
022 private Dimension superPreferred = null;
023 private Rectangle oldbounds = null;
024 private Dimension oldPreferred = null;
025
026 /**
027 * Constructs a normal label but adds HTML tags if not already done so.
028 * Supports both newline characters (<code>\n</code>) as well as the HTML
029 * <code><br></code> to insert new lines.
030 *
031 * Use setMaxWidth to limit the width of the label.
032 * @param text
033 */
034 public JMultilineLabel(String text)
035 {
036 super();
037 text = text.trim().replaceAll("\n", "<br>");
038 if(!text.startsWith("<html>")) {
039 text = "<html>" + text + "</html>";
040 }
041 super.setText(text);
042 }
043
044 /**
045 * Set the maximum width. Use this method instead of setMaximumSize because
046 * this saves a little bit of overhead and is actually taken into account.
047 *
048 * @param width
049 */
050 public void setMaxWidth(int width) {
051 this.maxWidth = width;
052 }
053
054 /**
055 * Tries to determine a suitable height for the given contents and return
056 * that dimension.
057 */
058 @Override
059 public Dimension getPreferredSize()
060 {
061 // Without this check it will result in an infinite loop calling
062 // getPreferredSize. Remember the old bounds and only recalculate if
063 // the size actually changed.
064 if(this.getBounds().equals(oldbounds) && oldPreferred != null)
065 return oldPreferred;
066 oldbounds = this.getBounds();
067
068 this.superPreferred = super.getPreferredSize();
069 // Make it not larger than required
070 int width = Math.min(superPreferred.width, maxWidth);
071
072 // Calculate suitable width and height
073 final View v = (View) super.getClientProperty(BasicHTML.propertyKey);
074
075 if(v == null)
076 return superPreferred;
077
078 v.setSize(width, 0);
079 int w = (int) Math.ceil(v.getPreferredSpan(View.X_AXIS));
080 int h = (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS));
081
082 oldPreferred = new Dimension(w, h);
083 return oldPreferred;
084 }
085 }