| /******************************************************************************* |
| * Copyright (c) 2014, 2020 Dirk Fauth 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: |
| * Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.painter.cell; |
| |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayer; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.resize.command.ColumnResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil; |
| import org.eclipse.nebula.widgets.nattable.style.IStyle; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.graphics.Transform; |
| |
| /** |
| * TextPainter that draws text into a cell vertically. Can handle word wrapping |
| * and/or word cutting and/or automatic calculation and resizing of the cell |
| * width and height if the text does not fit into the cell. |
| * <p> |
| * <b>Note:</b><br> |
| * This is the old implementation that uses {@link GraphicsUtils} to create the |
| * counter-clockwise rotated vertical text. Internally it will render the text |
| * horizontal to a temporary image and rotate the image afterwards.<br> |
| * This implementation is kept in case the new {@link VerticalTextPainter} that |
| * uses {@link Transform} for rotations is not working as intended. A typical |
| * case for this could be wrong size calculations dependent on several fonts. |
| * </p> |
| */ |
| public class VerticalTextImagePainter extends AbstractTextPainter { |
| |
| public VerticalTextImagePainter() { |
| this(false, true); |
| } |
| |
| /** |
| * @param wrapText |
| * split text over multiple lines |
| * @param paintBg |
| * skips painting the background if is FALSE |
| */ |
| public VerticalTextImagePainter(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 VerticalTextImagePainter(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 VerticalTextImagePainter(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 VerticalTextImagePainter(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 VerticalTextImagePainter(boolean wrapText, boolean paintBg, int spacing, boolean calculate) { |
| super(wrapText, paintBg, spacing, 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 VerticalTextImagePainter(boolean wrapText, boolean paintBg, int spacing, |
| boolean calculateByTextLength, boolean calculateByTextHeight) { |
| super(wrapText, paintBg, spacing, calculateByTextLength, calculateByTextHeight); |
| } |
| |
| @Override |
| public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { |
| setupGCFromConfig(gc, CellStyleUtil.getCellStyle(cell, configRegistry)); |
| String value = convertDataType(cell, configRegistry); |
| return gc.textExtent(value).y + (this.spacing * 2) + (getNumberOfNewLines(value) - 1) * this.lineSpacing; |
| } |
| |
| @Override |
| public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) { |
| setupGCFromConfig(gc, CellStyleUtil.getCellStyle(cell, configRegistry)); |
| return getLengthFromCache(gc, convertDataType(cell, configRegistry)) + (this.spacing * 2) + 1; |
| } |
| |
| @Override |
| public void paintCell(ILayerCell cell, GC gc, Rectangle rectangle, IConfigRegistry configRegistry) { |
| if (this.paintBg) { |
| super.paintCell(cell, gc, rectangle, configRegistry); |
| } |
| |
| Rectangle originalClipping = gc.getClipping(); |
| gc.setClipping(rectangle.intersection(originalClipping)); |
| |
| IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry); |
| setupGCFromConfig(gc, cellStyle); |
| |
| boolean underline = renderUnderlined(cellStyle); |
| boolean strikethrough = renderStrikethrough(cellStyle); |
| |
| String text = convertDataType(cell, configRegistry); |
| |
| // calculate the text to display, adds dots if the text is longer than |
| // the available row height and adds new lines instead of spaces if word |
| // wrapping is enabled |
| text = getTextToDisplay(cell, gc, rectangle.height, text); |
| |
| int numberOfNewLines = getNumberOfNewLines(text); |
| |
| // if the content width is bigger than the available column width |
| // we're extending the column width (only if word wrapping is enabled) |
| int fontHeight = gc.getFontMetrics().getHeight(); |
| int contentWidth = (fontHeight * numberOfNewLines) + (this.lineSpacing * (numberOfNewLines - 1)) + (this.spacing * 2); |
| int contentToCellDiff = (cell.getBounds().width - rectangle.width); |
| |
| if ((contentWidth > rectangle.width) && this.calculateByTextHeight) { |
| ILayer layer = cell.getLayer(); |
| layer.doCommand( |
| new ColumnResizeCommand(layer, cell.getColumnPosition(), contentWidth + contentToCellDiff, true)); |
| } |
| |
| if (text != null && text.length() > 0) { |
| if (numberOfNewLines == 1) { |
| int contentHeight = Math.min(getLengthFromCache(gc, text), rectangle.height); |
| |
| GraphicsUtils.drawVerticalText( |
| text, |
| rectangle.x |
| + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, contentWidth) |
| + this.spacing, |
| rectangle.y |
| + CellStyleUtil.getVerticalAlignmentPadding(cellStyle, rectangle, contentHeight + this.spacing), |
| underline, |
| strikethrough, this.paintBg, gc, SWT.UP); |
| } else { |
| // draw every line by itself because of the alignment, otherwise |
| // the whole text is always aligned right |
| int xStartPos = rectangle.x |
| + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, contentWidth); |
| String[] lines = text.split("\n"); //$NON-NLS-1$ |
| for (String line : lines) { |
| int lineContentWidth = Math.min(getLengthFromCache(gc, line), rectangle.height); |
| |
| GraphicsUtils.drawVerticalText( |
| line, |
| xStartPos + this.spacing, |
| rectangle.y + CellStyleUtil.getVerticalAlignmentPadding( |
| cellStyle, |
| rectangle, |
| lineContentWidth + this.spacing), |
| underline, strikethrough, this.paintBg, gc, |
| SWT.UP); |
| |
| // after every line calculate the x start pos new |
| xStartPos += fontHeight; |
| xStartPos += this.lineSpacing; |
| } |
| } |
| } |
| |
| gc.setClipping(originalClipping); |
| } |
| |
| @Override |
| protected void setNewMinLength(ILayerCell cell, int contentHeight) { |
| int cellLength = cell.getBounds().height; |
| if (cellLength < contentHeight) { |
| |
| ILayer layer = cell.getLayer(); |
| layer.doCommand(new RowResizeCommand(layer, cell.getRowPosition(), contentHeight, true)); |
| } |
| } |
| |
| @Override |
| protected int calculatePadding(ILayerCell cell, int availableLength) { |
| return cell.getBounds().height - availableLength; |
| } |
| } |