/******************************************************************************* | |
* 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$ | |
} | |
} | |
} |