| /******************************************************************************* |
| * Copyright (c) 2012, 2020 Original authors and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Original authors and others - initial API and implementation |
| * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 461261 |
| * Loris Securo <lorissek@gmail.com> - Bug 472668 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.painter.cell; |
| |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.CellDisplayConversionUtils; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; |
| import org.eclipse.nebula.widgets.nattable.style.IStyle; |
| import org.eclipse.nebula.widgets.nattable.style.TextDecorationEnum; |
| import org.eclipse.nebula.widgets.nattable.util.GUIHelper; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.GC; |
| |
| /** |
| * Abstract TextPainter the contains general methods for drawing text into a |
| * cell. Can handle word wrapping and/or word cutting and/or automatic |
| * calculation and resizing of the cell height or width if the text does not fit |
| * into the cell. |
| */ |
| public abstract class AbstractTextPainter extends BackgroundPainter { |
| |
| public static final String EMPTY = ""; //$NON-NLS-1$ |
| public static final String DOT = "..."; //$NON-NLS-1$ |
| |
| /** |
| * The regular expression to find predefined new lines in the text to show. |
| * Is used for word wrapping to preserve user defined new lines. To be |
| * platform independent \n and \r and the combination of both are used to |
| * find user defined new lines. |
| */ |
| public static final String NEW_LINE_REGEX = "\\n\\r|\\r\\n|\\n|\\r"; //$NON-NLS-1$ |
| |
| public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ |
| |
| /** |
| * @since 1.5 |
| */ |
| protected boolean wordWrapping = false; |
| protected boolean wrapText; |
| protected final boolean paintBg; |
| protected boolean paintFg = true; |
| protected int spacing = 5; |
| /** |
| * @since 1.5 |
| */ |
| protected int lineSpacing = 0; |
| // can only grow but will not calculate the minimal length |
| protected boolean calculateByTextLength; |
| protected boolean calculateByTextHeight; |
| private boolean underline; |
| private boolean strikethrough; |
| |
| private boolean trimText = true; |
| |
| private Color originalBackground; |
| private Color originalForeground; |
| private Font originalFont; |
| |
| private static Map<String, Integer> temporaryMap = new WeakHashMap<>(); |
| private static Map<org.eclipse.swt.graphics.Font, FontData[]> fontDataCache = new WeakHashMap<>(); |
| |
| public AbstractTextPainter() { |
| this(false, true); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg) { |
| this(wrapText, paintBg, 0); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| * @param spacing |
| * The space between text and cell border |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg, int spacing) { |
| this(wrapText, paintBg, spacing, false); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| * @param calculate |
| * tells the text painter to calculate the cell borders regarding |
| * the content |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg, |
| boolean calculate) { |
| this(wrapText, paintBg, 0, calculate); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| * @param calculateByTextLength |
| * tells the text painter to calculate the cell border by |
| * containing text length. For horizontal text rendering, this |
| * means the width of the cell is calculated by content, for |
| * vertical text rendering the height is calculated |
| * @param calculateByTextHeight |
| * tells the text painter to calculate the cell border by |
| * containing text height. For horizontal text rendering, this |
| * means the height of the cell is calculated by content, for |
| * vertical text rendering the width is calculated |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg, |
| boolean calculateByTextLength, boolean calculateByTextHeight) { |
| this(wrapText, paintBg, 0, calculateByTextLength, calculateByTextHeight); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| * @param spacing |
| * The space between text and cell border |
| * @param calculate |
| * tells the text painter to calculate the cell borders regarding |
| * the content |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg, int spacing, |
| boolean calculate) { |
| this(wrapText, paintBg, spacing, calculate, calculate); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| * @param spacing |
| * The space between text and cell border |
| * @param calculateByTextLength |
| * tells the text painter to calculate the cell border by |
| * containing text length. For horizontal text rendering, this |
| * means the width of the cell is calculated by content, for |
| * vertical text rendering the height is calculated |
| * @param calculateByTextHeight |
| * tells the text painter to calculate the cell border by |
| * containing text height. For horizontal text rendering, this |
| * means the height of the cell is calculated by content, for |
| * vertical text rendering the width is calculated |
| */ |
| public AbstractTextPainter(boolean wrapText, boolean paintBg, int spacing, |
| boolean calculateByTextLength, boolean calculateByTextHeight) { |
| this.wrapText = wrapText; |
| this.paintBg = paintBg; |
| this.spacing = spacing; |
| this.calculateByTextLength = calculateByTextLength; |
| this.calculateByTextHeight = calculateByTextHeight; |
| } |
| |
| /** |
| * Convert the data value of the cell using the {@link IDisplayConverter} |
| * from the {@link IConfigRegistry} |
| * |
| * @param cell |
| * The cell whose data value should be converted. |
| * @param configRegistry |
| * The {@link IConfigRegistry} to retrieve the converter. |
| * @return The data value converted to a String. |
| */ |
| protected String convertDataType(ILayerCell cell, IConfigRegistry configRegistry) { |
| return CellDisplayConversionUtils.convertDataType(cell, configRegistry); |
| } |
| |
| /** |
| * Setup the GC by the values defined in the given cell style. |
| * |
| * @param gc |
| * The {@link GC} that is used for rendering. |
| * @param cellStyle |
| * The {@link IStyle} to retrieve the styling options. |
| */ |
| public void setupGCFromConfig(GC gc, IStyle cellStyle) { |
| Color fg = cellStyle.getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR); |
| Color bg = cellStyle.getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR); |
| Font font = cellStyle.getAttributeValue(CellStyleAttributes.FONT); |
| |
| // remember previous settings |
| this.originalFont = gc.getFont(); |
| this.originalForeground = gc.getForeground(); |
| this.originalBackground = gc.getBackground(); |
| |
| gc.setAntialias(GUIHelper.DEFAULT_ANTIALIAS); |
| gc.setTextAntialias(GUIHelper.DEFAULT_TEXT_ANTIALIAS); |
| gc.setFont(font); |
| gc.setForeground(fg != null ? fg : GUIHelper.COLOR_LIST_FOREGROUND); |
| gc.setBackground(bg != null ? bg : GUIHelper.COLOR_LIST_BACKGROUND); |
| } |
| |
| /** |
| * Reset the GC to the original values. |
| * |
| * @param gc |
| * The {@link GC} that is used for rendering. |
| * |
| * @since 1.4 |
| */ |
| public void resetGC(GC gc) { |
| gc.setFont(this.originalFont); |
| gc.setForeground(this.originalForeground); |
| gc.setBackground(this.originalBackground); |
| } |
| |
| /** |
| * Checks if there is a underline text decoration configured within the |
| * given cell style. |
| * |
| * @param cellStyle |
| * The cell style of the current cell to check for the text |
| * decoration. |
| * @return <code>true</code> if there is a underline text decoration |
| * configured, <code>false</code> otherwise. |
| */ |
| protected boolean renderUnderlined(IStyle cellStyle) { |
| TextDecorationEnum decoration = cellStyle.getAttributeValue(CellStyleAttributes.TEXT_DECORATION); |
| if (decoration != null) { |
| return (decoration.equals(TextDecorationEnum.UNDERLINE) |
| || decoration.equals(TextDecorationEnum.UNDERLINE_STRIKETHROUGH)); |
| } |
| return this.underline; |
| } |
| |
| /** |
| * Checks if there is a strikethrough text decoration configured within the |
| * given cell style. |
| * |
| * @param cellStyle |
| * The cell style of the current cell to check for the text |
| * decoration. |
| * @return <code>true</code> if there is a strikethrough text decoration |
| * configured, <code>false</code> otherwise. |
| */ |
| protected boolean renderStrikethrough(IStyle cellStyle) { |
| TextDecorationEnum decoration = cellStyle.getAttributeValue(CellStyleAttributes.TEXT_DECORATION); |
| if (decoration != null) { |
| return (decoration.equals(TextDecorationEnum.STRIKETHROUGH) |
| || decoration.equals(TextDecorationEnum.UNDERLINE_STRIKETHROUGH)); |
| } |
| return this.strikethrough; |
| } |
| |
| /** |
| * Scans for new line characters and counts the number of lines for the |
| * given text. |
| * |
| * @param text |
| * the text to scan |
| * @return the number of lines for the given text |
| */ |
| protected int getNumberOfNewLines(String text) { |
| String[] lines = text.split(NEW_LINE_REGEX); |
| return lines.length; |
| } |
| |
| /** |
| * Calculates the length of a given text by using the GC. To minimize the |
| * count of calculations, the calculation result will be stored within a |
| * Map, so the next time the length of the same text is asked for, the |
| * result is only returned by cache and is not calculated again. |
| * |
| * @param gc |
| * the current GC |
| * @param text |
| * the text to get the length for |
| * @return the length of the text |
| */ |
| protected int getLengthFromCache(GC gc, String text) { |
| String originalString = text; |
| StringBuilder buffer = new StringBuilder(); |
| buffer.append(text); |
| if (gc.getFont() != null) { |
| FontData[] datas = fontDataCache.get(gc.getFont()); |
| if (datas == null) { |
| datas = gc.getFont().getFontData(); |
| fontDataCache.put(gc.getFont(), datas); |
| } |
| if (datas != null && datas.length > 0) { |
| buffer.append(datas[0].getName()); |
| buffer.append(","); //$NON-NLS-1$ |
| buffer.append(datas[0].getHeight()); |
| buffer.append(","); //$NON-NLS-1$ |
| buffer.append(datas[0].getStyle()); |
| } |
| } |
| text = buffer.toString(); |
| Integer width = temporaryMap.computeIfAbsent(text, t -> Integer.valueOf(gc.textExtent(originalString).x)); |
| |
| return width.intValue(); |
| } |
| |
| /** |
| * Computes dependent on the configuration of the TextPainter the text to |
| * display. If word wrapping is enabled new lines are inserted if the |
| * available space is not enough. If calculation of available space is |
| * enabled, the space is automatically widened for the text to display, and |
| * if no calculation is enabled the text is cut and modified to end with |
| * "..." to fit into the available space |
| * |
| * @param cell |
| * the current cell to paint |
| * @param gc |
| * the current GC |
| * @param availableLength |
| * the available space for the text to display |
| * @param text |
| * the text that should be modified for display |
| * @return the modified text |
| */ |
| protected String getTextToDisplay(ILayerCell cell, GC gc, int availableLength, String text) { |
| StringBuilder output = new StringBuilder(); |
| |
| if (isTrimText()) { |
| text = text.trim(); |
| } |
| |
| // take the whole width of the text |
| int textLength = getLengthFromCache(gc, text); |
| |
| if (this.wordWrapping || (!this.calculateByTextLength && this.wrapText)) { |
| String[] lines = text.split(NEW_LINE_REGEX); |
| for (String line : lines) { |
| if (output.length() > 0) { |
| output.append(LINE_SEPARATOR); |
| } |
| |
| String[] words = line.split("\\s"); //$NON-NLS-1$ |
| |
| // concat the words with spaces and newlines |
| String computedText = ""; //$NON-NLS-1$ |
| for (String word : words) { |
| computedText = computeTextToDisplay(computedText, word, gc, availableLength); |
| } |
| |
| output.append(computedText); |
| } |
| |
| } else if (this.calculateByTextLength && this.wrapText) { |
| if (availableLength < textLength) { |
| // calculate length by finding the longest word in text |
| textLength = (availableLength - (2 * this.spacing)); |
| |
| String[] lines = text.split(NEW_LINE_REGEX); |
| for (String line : lines) { |
| if (output.length() > 0) { |
| output.append(LINE_SEPARATOR); |
| } |
| |
| String[] words = line.split("\\s"); //$NON-NLS-1$ |
| for (String word : words) { |
| textLength = Math.max(textLength, getLengthFromCache(gc, word)); |
| } |
| |
| // concat the words with spaces and newlines to be always |
| // smaller then available |
| String computedText = ""; //$NON-NLS-1$ |
| for (String word : words) { |
| computedText = computeTextToDisplay(computedText, word, gc, textLength); |
| } |
| output.append(computedText); |
| } |
| } else { |
| output.append(text); |
| } |
| |
| setNewMinLength(cell, textLength + calculatePadding(cell, availableLength)); |
| } else if (this.calculateByTextLength && !this.wrapText) { |
| output.append(modifyTextToDisplay(text, gc, textLength)); |
| |
| // add padding and spacing to textLength because they are needed for |
| // correct sizing |
| // padding can occur on using decorators like the |
| // BeveledBorderDecorator or the PaddingDecorator |
| setNewMinLength(cell, textLength + calculatePadding(cell, availableLength) + (2 * this.spacing)); |
| } else if (!this.calculateByTextLength && !this.wrapText) { |
| output.append(modifyTextToDisplay(text, gc, availableLength + (2 * this.spacing))); |
| } |
| |
| return output.toString(); |
| } |
| |
| /** |
| * This method gets only called if text wrapping is enabled. Concatenates |
| * the two given words by taking the availableSpace into account. If |
| * concatenating those two words with a space as delimiter does fit into the |
| * available space the return value is exactly this. Else instead of a space |
| * there will be a new line character used as delimiter. |
| * |
| * @param one |
| * the first word or the whole text before the next word |
| * @param two |
| * the next word to add to the first parameter |
| * @param gc |
| * the current GC |
| * @param availableSpace |
| * the available space |
| * @return the concatenated String of the first two parameters |
| */ |
| private String computeTextToDisplay(String one, String two, GC gc, int availableSpace) { |
| String result = one; |
| // if one is empty or one ends with newline just add two |
| if (one == null || one.length() == 0 || one.endsWith(LINE_SEPARATOR)) { |
| result += modifyTextToDisplay(two, gc, availableSpace); |
| } else { |
| // if one contains a newline |
| if (one.indexOf(LINE_SEPARATOR) != -1) { |
| // get the end of the last part after the last newline |
| one = one.substring(one.lastIndexOf(LINE_SEPARATOR) + LINE_SEPARATOR.length()); |
| } |
| |
| if (getLengthFromCache(gc, one) == availableSpace |
| || getLengthFromCache(gc, one + " " + two) >= availableSpace) { //$NON-NLS-1$ |
| result += LINE_SEPARATOR; |
| result += modifyTextToDisplay(two, gc, availableSpace); |
| } else { |
| result += ' '; |
| result += two; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Checks if the given text is bigger than the available space. If not the |
| * given text is simply returned without modification. If the text does not |
| * fit into the available space, it will be modified by cutting and adding |
| * three dots. |
| * |
| * @param text |
| * the text to compute |
| * @param gc |
| * the current GC |
| * @param availableLength |
| * the available space |
| * @return the modified text if it is bigger than the available space or the |
| * text as it was given if it fits into the available space |
| */ |
| private String modifyTextToDisplay(String text, GC gc, int availableLength) { |
| // length of the text on GC taking new lines into account |
| // this means the textLength is the value of the longest line |
| int textLength = getLengthFromCache(gc, text); |
| if (textLength > availableLength) { |
| // as looking at the text length without taking new lines into |
| // account we have to look at every line itself |
| StringBuilder result = new StringBuilder(); |
| String[] lines = text.split(NEW_LINE_REGEX); |
| for (String line : lines) { |
| if (result.length() > 0) { |
| result.append(LINE_SEPARATOR); |
| } |
| |
| // now modify every line if it is longer than the available |
| // space this way every line will get ... if it doesn't fit |
| int lineLength = getLengthFromCache(gc, line); |
| if (lineLength > availableLength) { |
| if (!this.wordWrapping) { |
| int numExtraChars = 0; |
| |
| int newStringLength = line.length(); |
| String trialLabelText = line + DOT; |
| int newTextExtent = getLengthFromCache(gc, trialLabelText); |
| |
| while (newTextExtent > availableLength + 1 && newStringLength > 0) { |
| double avgWidthPerChar = (double) newTextExtent / trialLabelText.length(); |
| numExtraChars += 1 + (int) ((newTextExtent - availableLength) / avgWidthPerChar); |
| |
| newStringLength = line.length() - numExtraChars; |
| if (newStringLength > 0) { |
| trialLabelText = line.substring(0, newStringLength) + DOT; |
| newTextExtent = getLengthFromCache(gc, trialLabelText); |
| } |
| } |
| |
| if (numExtraChars > line.length()) { |
| numExtraChars = line.length(); |
| } |
| |
| // now we have gone too short, lets add chars one at a |
| // time to exceed the width... |
| String testString = line; |
| for (int i = 0; i < line.length(); i++) { |
| testString = line.substring(0, line.length() + i - numExtraChars) + DOT; |
| textLength = getLengthFromCache(gc, testString); |
| |
| if (textLength >= availableLength) { |
| |
| // now roll back one as this was the first |
| // number that exceeded |
| if (line.length() + i - numExtraChars < 1) { |
| line = EMPTY; |
| } else { |
| line = line.substring(0, line.length() + i - numExtraChars - 1) + DOT; |
| } |
| break; |
| } |
| } |
| } else { |
| StringBuilder output = new StringBuilder(); |
| StringBuilder wrap = new StringBuilder(); |
| for (char c : line.toCharArray()) { |
| wrap.append(c); |
| int length = getLengthFromCache(gc, wrap.toString()); |
| if (length >= availableLength) { |
| output.append(wrap.substring(0, wrap.length() - 1)).append(LINE_SEPARATOR); |
| wrap = new StringBuilder(); |
| wrap.append(c); |
| } |
| } |
| line = output.append(wrap).toString(); |
| } |
| } |
| result.append(line); |
| } |
| |
| return result.toString(); |
| } |
| return text; |
| } |
| |
| /** |
| * This method gets only called if automatic length calculation is enabled. |
| * Calculate the new cell width/height by using the given content length and |
| * the difference from current cell width/height to available length. If the |
| * calculated cell is greater than the current set contentLength, update the |
| * contentLength and execute a corresponding resize command. |
| * |
| * @param cell |
| * the current cell that is painted |
| * @param contentLength |
| * the length of the content |
| */ |
| protected abstract void setNewMinLength(ILayerCell cell, int contentLength); |
| |
| /** |
| * This method is used to determine the padding from the cell to the |
| * available length. A padding can occur for example by using a |
| * BeveledBorderDecorator or PaddingDecorator. This TextPainter is called |
| * with the available space rectangle which is calculated by the wrapping |
| * painters and decorators by subtracting paddings. As this TextPainter does |
| * not know his wrapping painters and decorators the existing padding needs |
| * to be calculated for automatic resizing. Abstract because a horizontal |
| * TextPainter uses the width while a VerticalTextPainter uses the height of |
| * the cell and the Rectangle. |
| * |
| * @param cell |
| * the current cell which should be resized |
| * @param availableLength |
| * the length value that is available and was given into |
| * paintCell() as Rectangle argument |
| * @return the padding between the current cell length - availableLength |
| */ |
| protected abstract int calculatePadding(ILayerCell cell, int availableLength); |
| |
| /** |
| * Set if the text should be rendered underlined or not. |
| * |
| * @param underline |
| * <code>true</code> if the text should be printed underlined, |
| * <code>false</code> if not |
| */ |
| public void setUnderline(boolean underline) { |
| this.underline = underline; |
| } |
| |
| /** |
| * Set if the text should be rendered strikethrough or not. |
| * |
| * @param strikethrough |
| * <code>true</code> if the text should be printed strikethrough, |
| * <code>false</code> if not |
| */ |
| public void setStrikethrough(boolean strikethrough) { |
| this.strikethrough = strikethrough; |
| } |
| |
| /** |
| * @return <code>true</code> if this text painter is calculating the cell |
| * dimensions by containing text length. For horizontal text |
| * rendering, this means the <b>width</b> of the cell is calculated |
| * by content, for vertical text rendering the <b>height</b> is |
| * calculated. |
| */ |
| public boolean isCalculateByTextLength() { |
| return this.calculateByTextLength; |
| } |
| |
| /** |
| * Configure whether the text painter should calculate the cell dimensions |
| * by containing text length. For horizontal text rendering, this means the |
| * <b>width</b> of the cell is calculated by content, for vertical text |
| * rendering the <b>height</b> is calculated. |
| * |
| * @param calculateByTextLength |
| * <code>true</code> to calculate and modify the cell dimension |
| * according to the text length, <code>false</code> to not |
| * modifying the cell dimensions. |
| */ |
| public void setCalculateByTextLength(boolean calculateByTextLength) { |
| this.calculateByTextLength = calculateByTextLength; |
| } |
| |
| /** |
| * @return <code>true</code> if this text painter is calculating the cell |
| * dimensions by containing text height. For horizontal text |
| * rendering, this means the <b>height</b> of the cell is calculated |
| * by content, for vertical text rendering the <b>width</b> is |
| * calculated. |
| */ |
| public boolean isCalculateByTextHeight() { |
| return this.calculateByTextHeight; |
| } |
| |
| /** |
| * Configure whether the text painter should calculate the cell dimensions |
| * by containing text height. For horizontal text rendering, this means the |
| * <b>height</b> of the cell is calculated by content, for vertical text |
| * rendering the <b>width</b> is calculated. |
| * |
| * @param calculateByTextHeight |
| * <code>true</code> to calculate and modify the cell dimension |
| * according to the text height, <code>false</code> to not |
| * modifying the cell dimensions. |
| */ |
| public void setCalculateByTextHeight(boolean calculateByTextHeight) { |
| this.calculateByTextHeight = calculateByTextHeight; |
| } |
| |
| /** |
| * @return <code>true</code> if the text to display should be trimmed, |
| * <code>false</code> if not. Default is <code>true</code> |
| * |
| * @since 1.3 |
| */ |
| public boolean isTrimText() { |
| return this.trimText; |
| } |
| |
| /** |
| * @param trimText |
| * <code>true</code> if the text to display should be trimmed, |
| * <code>false</code> if not. |
| * |
| * @since 1.3 |
| */ |
| public void setTrimText(boolean trimText) { |
| this.trimText = trimText; |
| } |
| |
| /** |
| * |
| * @return <code>true</code> if the text will be wrapped, <code>false</code> |
| * if not. |
| * |
| * @since 1.4 |
| */ |
| public boolean isWrapText() { |
| return this.wrapText; |
| } |
| |
| /** |
| * |
| * @param wrapText |
| * <code>true</code> if the text should be wrapped, |
| * <code>false</code> if not. |
| * |
| * @since 1.4 |
| */ |
| public void setWrapText(boolean wrapText) { |
| this.wrapText = wrapText; |
| } |
| |
| /** |
| * Render a decoration to the text, e.g. underline and/or strikethrough |
| * lines. |
| * |
| * @param cellStyle |
| * The {@link IStyle} that contains the styling information for |
| * rendering. |
| * @param gc |
| * the {@link GC} used to paint |
| * @param x |
| * start x of the text |
| * @param y |
| * start y of the text |
| * @param length |
| * length of the text |
| * @param fontHeight |
| * The height of the current font |
| * |
| * @since 1.4 |
| */ |
| protected void paintDecoration(IStyle cellStyle, GC gc, int x, int y, int length, int fontHeight) { |
| boolean ul = renderUnderlined(cellStyle); |
| boolean st = renderStrikethrough(cellStyle); |
| |
| if ((ul || st) && length > 0) { |
| // check and draw underline and strikethrough separately |
| // so it is possible to combine both |
| if (ul) { |
| // y = start y of text + font height - half of the font |
| // descent so the underline is between baseline and bottom |
| int underlineY = y + fontHeight - (gc.getFontMetrics().getDescent() / 2); |
| gc.drawLine(x, underlineY, x + length, underlineY); |
| } |
| |
| if (st) { |
| // y = start y of text + half of font height + ascent |
| // this way lower case characters are also strikethrough |
| int strikeY = y + (fontHeight / 2) + (gc.getFontMetrics().getLeading() / 2); |
| gc.drawLine(x, strikeY, x + length, strikeY); |
| } |
| } |
| } |
| |
| /** |
| * Return whether word wrapping is enabled or not. |
| * <p> |
| * Word wrapping is the wrapping behavior similar to spreadsheet |
| * applications where words are wrapped if there is not enough space. Text |
| * wrapping on the other hand only wraps whole words. |
| * </p> |
| * <p> |
| * Enabling this feature could result in slow rendering performance. It is |
| * therefore disabled by default. |
| * </p> |
| * <p> |
| * <b>Note:</b> If word wrapping is enabled, features like automatic size |
| * calculation by text length and text wrapping are ignored. |
| * </p> |
| * |
| * @return <code>true</code> if word wrapping is enabled, <code>false</code> |
| * if not. |
| * |
| * @since 1.5 |
| */ |
| public boolean isWordWrapping() { |
| return this.wordWrapping; |
| } |
| |
| /** |
| * Configure whether word wrapping should be enabled or not. |
| * <p> |
| * Word wrapping is the wrapping behavior similar to spreadsheet |
| * applications where words are wrapped if there is not enough space. Text |
| * wrapping on the other hand only wraps whole words. |
| * </p> |
| * <p> |
| * Enabling this feature could result in slow rendering performance. It is |
| * therefore disabled by default. |
| * </p> |
| * <p> |
| * <b>Note:</b> If word wrapping is enabled, features like automatic size |
| * calculation by text length and text wrapping are ignored. |
| * </p> |
| * |
| * @param wordWrapping |
| * <code>true</code> to enable word wrapping, <code>false</code> |
| * to disable it. |
| * |
| * @since 1.5 |
| */ |
| public void setWordWrapping(boolean wordWrapping) { |
| this.wordWrapping = wordWrapping; |
| } |
| |
| /** |
| * Return the number of pixels that are added between lines. Default is 0, |
| * which means that the line height is defined by the font height only. |
| * |
| * @return The number of pixels that are added between lines |
| * |
| * @since 1.5 |
| */ |
| public int getLineSpacing() { |
| return this.lineSpacing; |
| } |
| |
| /** |
| * Specify the number of pixels that should be added between lines. Default |
| * is 0, which means that the line height is defined by the font height |
| * only. |
| * |
| * @param spacing |
| * The number of pixels that should be added between lines |
| * |
| * @since 1.5 |
| */ |
| public void setLineSpacing(int spacing) { |
| this.lineSpacing = spacing; |
| } |
| } |