/******************************************************************************* * Copyright (c) 2012, 2016 Original authors 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: * Original authors and others - initial API and implementation * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 454506 ******************************************************************************/ 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; /** * TextPainter that draws text into a cell horizontally. 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. */ public class TextPainter extends AbstractTextPainter { public TextPainter() { this(false, true); } /** * @param wrapText * split text over multiple lines * @param paintBg * skips painting the background if is FALSE */ public TextPainter(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 TextPainter(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 TextPainter(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 TextPainter(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 TextPainter(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 TextPainter(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)); return getLengthFromCache(gc, convertDataType(cell, configRegistry)) + (this.spacing * 2) + 1; } @Override public int getPreferredHeight(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) + 1 + (getNumberOfNewLines(value) - 1) * this.lineSpacing; } @Override public void paintCell(ILayerCell cell, GC gc, Rectangle rectangle, IConfigRegistry configRegistry) { if (this.paintBg) { super.paintCell(cell, gc, rectangle, configRegistry); } if (this.paintFg) { Rectangle originalClipping = gc.getClipping(); gc.setClipping(rectangle.intersection(originalClipping)); IStyle cellStyle = CellStyleUtil.getCellStyle(cell, configRegistry); setupGCFromConfig(gc, cellStyle); int fontHeight = gc.getFontMetrics().getHeight(); String text = convertDataType(cell, configRegistry); // Draw Text text = getTextToDisplay(cell, gc, rectangle.width, text); int numberOfNewLines = getNumberOfNewLines(text); // if the content height is bigger than the available row height // we're extending the row height (only if word wrapping is enabled) int contentHeight = (fontHeight * numberOfNewLines) + (this.lineSpacing * (numberOfNewLines - 1)) + (this.spacing * 2); int contentToCellDiff = (cell.getBounds().height - rectangle.height); if (performRowResize(contentHeight, rectangle)) { ILayer layer = cell.getLayer(); layer.doCommand( new RowResizeCommand(layer, cell.getRowPosition(), contentHeight + contentToCellDiff)); } if (numberOfNewLines == 1) { int contentWidth = Math.min(getLengthFromCache(gc, text), rectangle.width); gc.drawText( text, rectangle.x + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, contentWidth) + this.spacing, rectangle.y + CellStyleUtil.getVerticalAlignmentPadding(cellStyle, rectangle, contentHeight) + this.spacing, SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER); // start x of line = start x of text int x = rectangle.x + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, contentWidth) + this.spacing; // y = start y of text int y = rectangle.y + CellStyleUtil.getVerticalAlignmentPadding(cellStyle, rectangle, contentHeight) + this.spacing; int length = gc.textExtent(text).x; paintDecoration(cellStyle, gc, x, y, length, fontHeight); } else { // draw every line by itself because of the alignment, otherwise // the whole text is always aligned right int yStartPos = rectangle.y + CellStyleUtil.getVerticalAlignmentPadding(cellStyle, rectangle, contentHeight); String[] lines = text.split("\n"); //$NON-NLS-1$ for (String line : lines) { int lineContentWidth = Math.min(getLengthFromCache(gc, line), rectangle.width); gc.drawText( line, rectangle.x + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, lineContentWidth) + this.spacing, yStartPos + this.spacing, SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER); // start x of line = start x of text int x = rectangle.x + CellStyleUtil.getHorizontalAlignmentPadding(cellStyle, rectangle, lineContentWidth) + this.spacing; // y = start y of text int y = yStartPos + this.spacing; int length = gc.textExtent(line).x; paintDecoration(cellStyle, gc, x, y, length, fontHeight); // after every line calculate the y start pos new yStartPos += fontHeight; yStartPos += this.lineSpacing; } } gc.setClipping(originalClipping); resetGC(gc); } } @Override protected void setNewMinLength(ILayerCell cell, int contentWidth) { int cellLength = cell.getBounds().width; if (cellLength < contentWidth) { // execute ColumnResizeCommand ILayer layer = cell.getLayer(); layer.doCommand( new ColumnResizeCommand(layer, cell.getColumnPosition(), contentWidth)); } } @Override protected int calculatePadding(ILayerCell cell, int availableLength) { return cell.getBounds().width - availableLength; } /** * Checks if a row resize needs to be triggered. * * @param contentHeight * The necessary height to show the content completely * @param rectangle * The available rectangle to render to * @return <code>true</code> if a row resize needs to be performed, * <code>false</code> if not */ protected boolean performRowResize(int contentHeight, Rectangle rectangle) { return ((contentHeight > rectangle.height) && this.calculateByTextHeight); } }