blob: f93bee83833e194827d44ba4484bfea045f45ff4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.uitools;
import java.awt.Color;
import java.awt.Dimension;
import javax.accessibility.AccessibleContext;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.BoxView;
import javax.swing.text.View;
import javax.swing.text.html.CSS;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
/**
* This <code>LabelArea</code> allows the text to be wrapped automatically using
* an HTML view. {@link #getOriginalText()} returns the text that was passed to
* {@link #setText(String)}, which is the non-modified string; {@link #getText()}
* actually returns the HTML formatted string.
* <p>
* By default, the text is not scrollable, which means it'll use the preferred
* width to calculate its size. When scrollable is <code>true</code>, then the
* text wraps based on the available width.
*
* @version 11.0.0
* @since 9.0.4
*/
public class LabelArea extends JLabel
{
/**
* Internal flag used to prevent an infinite loop between setText() and
* setDisplayMnemonicIndex().
*/
private boolean locked;
/**
* The text before it has been formatted as an HTML string.
*/
private String originalText;
/**
* The maximum width this label should have. The width is used to calculate
* the preferred height.
* @see #PREFERRED_WIDTH
*/
private int preferredWidth = PREFERRED_WIDTH;
/**
* Determines if the text shoudl wrap based on the available width. The
* default value is <code>false</code>.
*/
private boolean scrollable;
/**
* The default preferred width used to calculate the size of this label.
* The default value is 300.
*/
public static final int PREFERRED_WIDTH = 300;
/**
* Creates a new <code>LabelArea</code>.
*/
public LabelArea()
{
super();
}
/**
* Creates a new <code>LabelArea</code>.
*
* @param icon The icon displayed by this label, <code>null</code> is also
* valid
*/
public LabelArea(Icon icon)
{
super(icon);
}
/**
* Creates a new <code>LabelArea</code>.
*
* @param icon The icon displayed by this label, <code>null</code> is also
* valid
* @param horizontalAlignment One of the following constants defined in
* <code>SwingConstants</code>: <code>LEFT</code>, <code>CENTER</code>,
* <code>RIGHT</code>, <code>LEADING</code> or <code>TRAILING</code>.
*/
public LabelArea(Icon icon, int horizontalAlignment)
{
super(icon, horizontalAlignment);
}
/**
* Creates a new <code>LabelArea</code>.
*
* @param text This label's text
*/
public LabelArea(String text)
{
super(text);
}
/**
* Creates a new <code>LabelArea</code>.
*
* @param text This label's text
* @param icon The icon displayed by this label, <code>null</code> is also
* valid
* @param horizontalAlignment One of the following constants defined in
* <code>SwingConstants</code>: <code>LEFT</code>, <code>CENTER</code>,
* <code>RIGHT</code>, <code>LEADING</code> or <code>TRAILING</code>.
*/
public LabelArea(String text, Icon icon, int horizontalAlignment)
{
super(text, icon, horizontalAlignment);
}
/**
* Creates a new <code>LabelArea</code>.
*
* @param text This label's text
* @param horizontalAlignment One of the following constants defined in
* <code>SwingConstants</code>: <code>LEFT</code>, <code>CENTER</code>,
* <code>RIGHT</code>, <code>LEADING</code> or <code>TRAILING</code>.
*/
public LabelArea(String text, int horizontalAlignment)
{
super(text, horizontalAlignment);
}
/**
* Formats the given string into HTML using the given properties.
*
* @param text The actual text to be formatted into HTML
* @return The HTML formatted text using the given properties or the given
* text if is already an HTML formatted string
*/
private String convertToHTML()
{
// Nothing to format
if (StringTools.stringIsEmpty(originalText) ||
BasicHTML.isHTMLString(originalText))
{
return originalText;
}
StringBuilder sb = new StringBuilder();
sb.append("<HTML><BODY");
if (!isEnabled())
{
Class<?>[] parameterTypes = new Class<?>[] { Color.class };
Object[] parameterValues = new Object[] { getBackground().darker() };
String foregroundColor = (String) ClassTools.invokeStaticMethod(CSS.class, "colorToHex", parameterTypes, parameterValues);
sb.append(" TEXT=\"");
sb.append(foregroundColor);
sb.append("\"");
}
sb.append(">");
// If the mnemonic is set, then wraps it with <u></u>
int mnemonicIndex = getDisplayedMnemonicIndex();
if (mnemonicIndex > -1)
{
sb.append(originalText.substring(0, mnemonicIndex));
sb.append("<U>");
sb.append(originalText.charAt(mnemonicIndex));
sb.append("</U>");
sb.append(originalText.substring(mnemonicIndex + 1));
}
else
{
sb.append(originalText);
}
sb.append("</BODY></HTML>");
return sb.toString();
}
/*
* (non-Javadoc)
*/
private int findDisplayedMnemonicIndex()
{
int mnemonic = getDisplayedMnemonic();
if ((originalText == null) || (mnemonic == '\0'))
return -1;
char uc = Character.toUpperCase((char) mnemonic);
char lc = Character.toLowerCase((char) mnemonic);
int uci = originalText.indexOf(uc);
int lci = originalText.indexOf(lc);
if (uci == -1)
return lci;
if (lci == -1)
return uci;
return (lci < uci) ? lci : uci;
}
/*
* (non-Javadoc)
*/
@Override
public AccessibleContext getAccessibleContext()
{
if (accessibleContext == null)
accessibleContext = new AccessibleLabelArea();
return accessibleContext;
}
/**
* Returns the text prior to be formatted as an HTML string. If the string is
* already formatted, then this one is returned.
*
* @return The original string passed to {@link #setText(String)}
*/
public String getOriginalText()
{
return originalText;
}
/*
* (non-Javadoc)
*/
@Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
/*
* (non-Javadoc)
*/
@Override
public Dimension getPreferredSize()
{
View rootView = (View) getClientProperty(BasicHTML.propertyKey);
if ((rootView != null) && !isPreferredSizeSet())
{
BoxView view = (BoxView) rootView.getView(0);
if (scrollable)
{
int width = (int) rootView.getMinimumSpan(View.X_AXIS);
int height = (int) rootView.getMinimumSpan(View.Y_AXIS);
Dimension size = super.getPreferredSize();
size.width = Math.min(width, size.width);
size.height = Math.max(height, size.height);
return size;
}
else
{
float width = Math.min((int) view.getPreferredSpan(View.X_AXIS), preferredWidth);
view.setSize(width, 0.0f);
float height = view.getPreferredSpan(View.Y_AXIS);
rootView.setSize(width, height);
}
}
return super.getPreferredSize();
}
/**
* Returns the maximum width this label should have. The width is used to
* calculate the preferred height.
*
* @return A positive value that fixes the width of this label
* @see #PREFERRED_WIDTH
*/
public int getPreferredWidth()
{
return preferredWidth;
}
/*
* (non-Javadoc)
*/
@Override
public void setDisplayedMnemonicIndex(int index) throws IllegalArgumentException
{
if (locked)
return;
try
{
locked = true;
super.setDisplayedMnemonicIndex(index);
}
finally
{
locked = false;
}
setText(originalText);
}
/**
* Sets the maximum width this label should have. The width is used to
* calculate the preferred height. The default is value is 300.
*
* @return A positive value that fixes the width of this label
* @exception IllegalArgumentException The preferred width cannot be negative
*/
public void setPreferredWidth(int preferredWidth)
{
if (preferredWidth < 0)
{
throw new IllegalArgumentException("Preferred width cannot be negative");
}
this.preferredWidth = preferredWidth;
}
/**
* Determines if the text should wrap based on the available width. The
* default value is <code>false</code>.
*
* @param scrollabe <code>true</code> to let the text wrap automatically
* when the size changes; <code>false</code> otherwise to fix its width and
* height based on {@link #preferredWidth}
*/
public void setScrollable(boolean scrollable)
{
this.scrollable = scrollable;
}
/**
* Sets the text of this <code>LabelArea</code> to the specified value. The
* string will automatically be converted into an HTML string if it's not
* already one. The original string, not formatted, can be retrieved
* with {@link #getActualText()}.
*
* @param text The new text to be set
* @see javax.swing.JLabel#setText(String)
*/
@Override
public void setText(String text)
{
this.originalText = text;
text = convertToHTML();
try
{
locked = true;
super.setText(text);
super.setDisplayedMnemonicIndex(findDisplayedMnemonicIndex());
}
finally
{
locked = false;
}
}
/**
* This class implements accessibility support for the <code>LabelArea</code>
* class, the accessible name will be returned non-HTML formatted.
*/
protected class AccessibleLabelArea extends AccessibleJLabel
{
@Override
public String getAccessibleName()
{
if (accessibleName != null)
return accessibleName;
if (LabelArea.this.originalText == null)
return null;
// The regular expression supports: <br>, <br />, <p>, </p>
// and any cases of each letter
return LabelArea.this.originalText.replaceAll("<[Bb][Rr](\\s)?(/)?>|<(/)?[Pp]>", " "); //$NON-NLS-2$
}
}
}