| /****************************************************************************** |
| * Copyright (c) 2002, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Mariot Chauvin <mariot.chauvin@obeo.fr> - bug 272658 |
| ****************************************************************************/ |
| |
| package org.eclipse.gmf.runtime.draw2d.ui.figures; |
| |
| import org.eclipse.draw2d.FigureUtilities; |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontMetrics; |
| import org.eclipse.swt.graphics.Image; |
| |
| import com.ibm.icu.text.BreakIterator; |
| import com.ibm.icu.util.StringTokenizer; |
| |
| /** |
| * An extended label that has the following extra features: |
| * |
| * 1- It is capable of showing selection and focus feedback (primary or |
| * secondary) 2- It is capable of optionally underlining the label's text 3- It |
| * is capable of wrapping the label's text at a given width with a given |
| * alignment 4- It is capable of supporting multiple label icons (temporary |
| * feature) |
| * |
| * This class was originally deriving off Draw2d's <code>Label</code> class |
| * but with the introduction of the auto-wrapping feature, a copy had to be made |
| * overriding was not straightforward. Hopefully, this extended version can be |
| * pushed to opensource |
| * |
| * <p> |
| * Code taken from Eclipse reference bugzilla #98820 |
| * |
| * @author melaasar, crevells |
| * @deprecated This class has been deprecated and may be removed in the future. |
| * Use <code>WrappingLabel</code> instead. This class now extends |
| * from <code>WrappingLabel</code> so the behavior should be the |
| * same. If client code is calling one of the methods on |
| * <code>WrapLabel</code> that no longer exists, evaluate whether |
| * this is necessary or not. |
| */ |
| public class WrapLabel |
| extends WrappingLabel { |
| |
| /** the label's text used in painting after applying required styles */ |
| private String subStringText; |
| |
| /** |
| * Construct an empty Label. |
| * |
| * @since 2.0 |
| */ |
| public WrapLabel() { |
| super(); |
| } |
| |
| /** |
| * Construct a Label with passed String as its text. |
| * |
| * @param s |
| * the label text |
| * @since 2.0 |
| */ |
| public WrapLabel(String s) { |
| super(s); |
| |
| // Compensate for the fact that the original wraplabel never called the |
| // default constructor to set the default layout values. |
| setTextWrapAlignment(CENTER); |
| } |
| |
| /** |
| * Construct a Label with passed Image as its icon. |
| * |
| * @param i |
| * the label image |
| * @since 2.0 |
| */ |
| public WrapLabel(Image i) { |
| super(i); |
| |
| // Compensate for the fact that the original wraplabel never called the |
| // default constructor to set the default layout values. |
| setTextWrapAlignment(CENTER); |
| } |
| |
| /** |
| * Construct a Label with passed String as text and passed Image as its |
| * icon. |
| * |
| * @param s |
| * the label text |
| * @param i |
| * the label image |
| * @since 2.0 |
| */ |
| public WrapLabel(String s, Image i) { |
| super(s, i); |
| |
| // Compensate for the fact that the original wraplabel never called the |
| // default constructor to set the default layout values. |
| setTextWrapAlignment(CENTER); |
| } |
| |
| /** |
| * Calculates the size of the Label's text size. The text size calculated |
| * takes into consideration if the Label's text is currently truncated. If |
| * text size without considering current truncation is desired, use |
| * {@link #calculateTextSize(int, int)}. |
| * |
| * @return the size of the label's text, taking into account truncation |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| protected Dimension calculateSubStringTextSize() { |
| Font f = getFont(); |
| return getTextExtents(getSubStringText(), f, getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight())); |
| } |
| |
| /** |
| * Calculates and returns the size of the Label's text. Note that this |
| * Dimension is calculated using the Label's full text, regardless of |
| * whether or not its text is currently truncated. If text size considering |
| * current truncation is desired, use {@link #calculateSubStringTextSize()}. |
| * |
| * @param wHint a width hint |
| * @param hHint a height hint |
| * @return the size of the label's text, ignoring truncation |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| protected Dimension calculateTextSize(int wHint, int hHint) { |
| return getTextFlow().getPreferredSize(wHint, hHint); |
| } |
| |
| /** |
| * Returns the bounds of the Label's icon. |
| * |
| * @return the icon's bounds |
| * @since 2.0 |
| * @deprecated The icon location can be retrieved with |
| * {@link #getIconLocation()} and the icon(s) size can be |
| * retrieved with {@link #getTotalIconSize()}. |
| */ |
| public Rectangle getIconBounds() { |
| if (hasIcons()) { |
| final Point safeIconLocation = getIconLocation() != null ? getIconLocation() : new Point(0,0); |
| return new Rectangle(getBounds().getLocation().translate( |
| safeIconLocation), getTotalIconSize()); |
| } |
| return new Rectangle(0, 0, 0, 0); |
| } |
| |
| /** |
| * Calculates the amount of the Label's current text will fit in the Label, |
| * including an elipsis "..." if truncation is required. |
| * |
| * @return the substring |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| public String getSubStringText() { |
| if (subStringText != null) |
| return subStringText; |
| |
| String theText = getText(); |
| int textLen = theText.length(); |
| if (textLen == 0) { |
| return subStringText = "";//$NON-NLS-1$;; |
| } |
| Dimension size = getSize(); |
| Dimension shrink = getPreferredSize(size.width, size.height) |
| .getDifference(size); |
| Dimension effectiveSize = getTextSize().getExpanded(-shrink.width, |
| -shrink.height); |
| |
| if (effectiveSize.height == 0) { |
| return subStringText = "";//$NON-NLS-1$; |
| } |
| |
| Font f = getFont(); |
| FontMetrics metrics = FigureUtilities.getFontMetrics(f); |
| IMapMode mm = getFigureMapMode(); |
| int fontHeight = mm.DPtoLP(metrics.getHeight()); |
| int charAverageWidth = mm.DPtoLP(metrics.getAverageCharWidth()); |
| int maxLines = (int) (effectiveSize.height / (double) fontHeight); |
| if (maxLines == 0) { |
| return subStringText = "";//$NON-NLS-1$ |
| } |
| |
| StringBuffer accumlatedText = new StringBuffer(); |
| StringBuffer remainingText = new StringBuffer(theText); |
| |
| int effectiveSizeWidth = effectiveSize.width; |
| int widthHint = Math.max(effectiveSizeWidth |
| - getTruncationStringSize().width, 0); |
| int i = 0, j = 0; |
| while (remainingText.length() > 0 && j++ < maxLines) { |
| i = getLineWrapPosition(remainingText.toString(), f, |
| effectiveSizeWidth, fontHeight); |
| |
| if (accumlatedText.length() > 0) |
| accumlatedText.append('\n'); |
| |
| if (i == 0 || (remainingText.length() > i && j == maxLines)) { |
| i = getLargestSubstringConfinedTo(remainingText.toString(), f, |
| widthHint, fontHeight, charAverageWidth); |
| accumlatedText.append(remainingText.substring(0, i)); |
| accumlatedText.append(getEllipse()); |
| } else |
| accumlatedText.append(remainingText.substring(0, i)); |
| remainingText.delete(0, i); |
| } |
| return subStringText = accumlatedText.toString(); |
| } |
| |
| /** |
| * Returns the size of the Label's current text. If the text is currently |
| * truncated, the truncated text with its ellipsis is used to calculate the |
| * size. |
| * |
| * @return the size of this label's text, taking into account truncation |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| protected Dimension getSubStringTextSize() { |
| return calculateSubStringTextSize(); |
| } |
| |
| /** |
| * Returns the location of the label's text relative to the label. |
| * |
| * @return the text location |
| * @since 2.0 |
| * @deprecated Use <code>getTextBounds().getLocation()</code> instead. |
| */ |
| protected Point getTextLocation() { |
| return getTextBounds().getLocation(); |
| } |
| |
| /** |
| * Returns the size of the label's complete text. Note that the text used to |
| * make this calculation is the label's full text, regardless of whether the |
| * label's text is currently being truncated and is displaying an ellipsis. |
| * If the size considering current truncation is desired, call |
| * {@link #getSubStringTextSize()}. |
| * |
| * @param wHint |
| * a width hint |
| * @param hHint |
| * a height hint |
| * @return the size of this label's complete text |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| protected Dimension getTextSize(int wHint, int hHint) { |
| return getTextFlow().getPreferredSize(wHint, hHint); |
| } |
| |
| /** |
| * Gets the text size given the current size as a width hint |
| */ |
| private final Dimension getTextSize() { |
| Rectangle r = getBounds(); |
| return getTextSize(r.width, r.height); |
| } |
| |
| /** |
| * @see IFigure#invalidate() |
| */ |
| public void invalidate() { |
| subStringText = null; |
| super.invalidate(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the label's text is currently truncated |
| * and is displaying an ellipsis, <code>false</code> otherwise. |
| * |
| * @return <code>true</code> if the label's text is truncated |
| * @since 2.0 |
| * @deprecated If this behavior is required then a request can be made (with |
| * justification) for the WrappingLabel. |
| */ |
| public boolean isTextTruncated() { |
| return !getSubStringTextSize().equals(getTextSize()); |
| } |
| |
| /** |
| * Return the ellipse string. |
| * |
| * @return the <code>String</code> that represents the fact that the text |
| * has been truncated and that more text is available but hidden. |
| * Usually this is represented by "...". |
| * @deprecated Renamed to {@link #getTruncationString()} |
| */ |
| protected String getEllipse() { |
| return ELLIPSIS; |
| } |
| |
| protected String getTruncationString() { |
| if (getEllipse() != null) { |
| return getEllipse(); |
| } |
| return ELLIPSIS; |
| } |
| |
| /** |
| * @return whether the label text wrap is on |
| * @deprecated Use {@link #isTextWrapOn()} instead. This method was renamed |
| * because it never indicated if the text was actually wrapped, |
| * but whether text wrapping was turned on in the label. |
| */ |
| public boolean isTextWrapped() { |
| return isTextWrapOn(); |
| } |
| |
| /** |
| * Sets the wrapping width of the label text. This is only valid if text |
| * wrapping is turned on |
| * |
| * @param i |
| * The label text wrapping width |
| * @deprecated this method was empty and never called |
| */ |
| public void setTextWrapWidth(int i) { |
| // do nothing |
| } |
| |
| /** |
| * Sets the wrapping width of the label text. This is only valid if text |
| * wrapping is turned on |
| * |
| * @param i |
| * The label text wrapping width |
| * @deprecated Call {@link #setTextJustification(int)} and |
| * {@link #setAlignment(int)} instead. This method was somewhat |
| * controlling text justification and label alignment, but they |
| * are really two independent settings. Previously, |
| * setTextWrapAlignment(CENTER) would not only center-justifies |
| * the text, but also put the label in the center. Now, you need |
| * to call {@link #setTextJustification(int)} to justify the |
| * text (this only affects text when it is wrapped) and |
| * {@link #setAlignment(int)} to position the text correctly in |
| * the label. If you want the text in the center of the label |
| * than call <code>setAlignment(PositionConstants.CENTER)</code>. |
| * Look at the implementation of this method to see how your |
| * code needs to be migrated. |
| */ |
| public void setTextWrapAlignment(int i) { |
| setTextJustification(i); |
| |
| // The old WrapLabel's Text Wrap Alignment (i.e. justification) and |
| // Label Alignment did not work properly. They worked together |
| // previously so we need to compensate for this here. |
| switch (i) { |
| case LEFT: |
| setAlignment(TOP | LEFT); |
| break; |
| case CENTER: |
| setAlignment(TOP); |
| break; |
| case RIGHT: |
| setAlignment(TOP | RIGHT); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * @deprecated This never worked properly anyways. Call |
| * {@link #setAlignment(int)} instead to position the icon and |
| * text within the label. |
| */ |
| public void setLabelAlignment(int alignment) { |
| // setLabelAlignment() never worked properly instead the label alignment |
| // seemed to be based on the text justification. Therefore, if it was |
| // set it will be ignored. |
| } |
| |
| /** |
| * @return the label text wrapping width |
| * @deprecated Renamed to {@link #getTextJustification()} |
| */ |
| public int getTextWrapAlignment() { |
| return getTextJustification(); |
| } |
| |
| /** |
| * returns the position of last character within the supplied text that will |
| * fit within the supplied width. |
| * |
| * @param s |
| * a text string |
| * @param f |
| * font used to draw the text string |
| * @param w |
| * width in pixles. |
| * @param fontHeight |
| * int <b>mapped already to logical units</b>. |
| */ |
| private int getLineWrapPosition(String s, Font f, int w, int fontHeight) { |
| if (getTextExtents(s, f, fontHeight).width <= w) { |
| return s.length(); |
| } |
| // create an iterator for line breaking positions |
| BreakIterator iter = BreakIterator.getLineInstance(); |
| iter.setText(s); |
| int start = iter.first(); |
| int end = iter.next(); |
| |
| // if the first line segment does not fit in the width, |
| // determine the position within it where we need to cut |
| if (getTextExtents(s.substring(start, end), f, fontHeight).width > w) { |
| iter = BreakIterator.getCharacterInstance(); |
| iter.setText(s); |
| start = iter.first(); |
| } |
| |
| // keep iterating as long as width permits |
| do |
| end = iter.next(); |
| while (end != BreakIterator.DONE |
| && getTextExtents(s.substring(start, end), f, fontHeight).width <= w); |
| return (end == BreakIterator.DONE) ? iter.last() |
| : iter.previous(); |
| } |
| |
| /** |
| * Returns the largest substring of <i>s </i> in Font <i>f </i> that can be |
| * confined to the number of pixels in <i>availableWidth <i>. |
| * |
| * @param s |
| * the original string |
| * @param f |
| * the font |
| * @param w |
| * the available width |
| * @param fontHeight |
| * int <b>mapped already to logical units</b>. |
| * @param charAverageWidth |
| * int <b>mapped already to logical units</b>. |
| * @return the largest substring that fits in the given width |
| * @since 2.0 |
| */ |
| private int getLargestSubstringConfinedTo(String s, Font f, int w, |
| int fontHeight, int charAverageWidth) { |
| float avg = charAverageWidth; |
| int min = 0; |
| int max = s.length() + 1; |
| |
| // The size of the current guess |
| int guess = 0, guessSize = 0; |
| while ((max - min) > 1) { |
| // Pick a new guess size |
| // New guess is the last guess plus the missing width in pixels |
| // divided by the average character size in pixels |
| guess = guess + (int) ((w - guessSize) / avg); |
| |
| if (guess >= max) |
| guess = max - 1; |
| if (guess <= min) |
| guess = min + 1; |
| |
| // Measure the current guess |
| guessSize = getTextExtents(s.substring(0, guess), f, fontHeight).width; |
| |
| if (guessSize < w) |
| // We did not use the available width |
| min = guess; |
| else |
| // We exceeded the available width |
| max = guess; |
| } |
| return min; |
| } |
| |
| /** |
| * Gets the tex extent scaled to the mapping mode |
| */ |
| private Dimension getTextExtents(String s, Font f, int fontHeight) { |
| if (s.length() == 0) { |
| return getMapModeConstants().dimension_nDPtoLP_0; |
| } else { |
| // height should be set using the font height and the number of |
| // lines in the string |
| Dimension d = FigureUtilities.getTextExtents(s, f); |
| IMapMode mapMode = getFigureMapMode(); |
| d.width = mapMode.DPtoLP(d.width); |
| d.height = fontHeight * new StringTokenizer(s, "\n").countTokens();//$NON-NLS-1$ |
| return d; |
| } |
| } |
| |
| } |