package org.ovirt.engine.ui.common.widget.table.cell; import org.ovirt.engine.ui.common.utils.ElementIdUtils; import org.ovirt.engine.ui.common.utils.ElementUtils; import org.ovirt.engine.ui.common.widget.table.HasStyleClass; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.safehtml.shared.SafeHtmlUtils; /** * A Cell used to render text. Supports tooltips. Supports wrapping with a css style. Supports truncation. * * If truncation is enabled, and if the text doesn't fit in the parent element, it is truncated. * * There are two types of truncation. You can specify a length in characters, or if you don't, overflow * will be detected and truncated via CSS 'text-overflow: ellipsis'. * * Truncation can also be disabled. * * When text is truncated, the full text will be rendered in a tooltip, unless a different tooltip is manually * configured. Configure a manual tooltip by overriding getTooltip() in the Column using this Cell. */ public class TextCell extends AbstractCell<String> implements HasStyleClass { interface CellTemplate extends SafeHtmlTemplates { @Template("<div class=\"{0}\" style='overflow: hidden; text-overflow: ellipsis; white-space: nowrap;' id=\"{1}\">{2}</div>") SafeHtml textContainerWithDetection(String style, String id, SafeHtml text); @Template("<div class=\"{0}\" id=\"{1}\">{2}</div>") SafeHtml textContainer(String style, String id, SafeHtml text); } private String styleClass = ""; //$NON-NLS-1$ public static final int UNLIMITED_LENGTH = -1; private static final String ELLIPSE = "..."; //$NON-NLS-1$ // Text longer than this value will be shortened private final int maxTextLength; // by default, detect overflow and truncate with an ellipse boolean useOverflowTruncation = true; private static CellTemplate template = GWT.create(CellTemplate.class); public TextCell() { this(UNLIMITED_LENGTH, true); } public TextCell(boolean useOverflowTruncation) { this(UNLIMITED_LENGTH, useOverflowTruncation); } public TextCell(int maxTextLength) { this(maxTextLength, false); } public TextCell(int maxTextLength, boolean useOverflowTruncation) { super(); this.maxTextLength = maxTextLength; this.useOverflowTruncation = useOverflowTruncation; } public void setStyleClass(String styleClass) { this.styleClass = styleClass == null ? "" : styleClass; //$NON-NLS-1$ } public String getStyleClass() { return styleClass; } @Override public void render(Context context, String value, SafeHtmlBuilder sb, String id) { if (value != null) { SafeHtml safeHtmlValue = SafeHtmlUtils.fromString(value); if (maxTextLength >= 0) { // using manual truncation SafeHtml renderedValue = getRenderedValue(safeHtmlValue); sb.append(template.textContainer(getStyleClass(), ElementIdUtils.createTableCellElementId(getElementIdPrefix(), getColumnId(), context), renderedValue)); } else if (useOverflowTruncation) { // using overflow truncation sb.append(template.textContainerWithDetection(getStyleClass(), ElementIdUtils.createTableCellElementId(getElementIdPrefix(), getColumnId(), context), safeHtmlValue)); } else { // no truncation at all sb.append(template.textContainer(getStyleClass(), ElementIdUtils.createTableCellElementId(getElementIdPrefix(), getColumnId(), context), SafeHtmlUtils.fromString(value))); } } } @Override public SafeHtml getTooltip(String value, Element parent) { if (value != null) { SafeHtml safeHtmlValue = SafeHtmlUtils.fromString(value); if (maxTextLength >= 0) { SafeHtml renderedValue = getRenderedValue(safeHtmlValue); // only render a tooltip if the text actually got truncated if (!safeHtmlValue.equals(renderedValue)) { return safeHtmlValue; } } // render a value if there was overflow detected else if (contentOverflows(parent.getFirstChildElement())) { return safeHtmlValue; } } return null; } /** * Returns the (possibly truncated) value rendered by this cell. */ private SafeHtml getRenderedValue(SafeHtml value) { String result = value.asString(); // Check if the text needs to be shortened if (maxTextLength > 0 && result.length() > maxTextLength) { result = result.substring(0, Math.max(maxTextLength - ELLIPSE.length(), 0)); result = result + ELLIPSE; } return SafeHtmlUtils.fromTrustedString(result); } /** * Returns {@code true} when the content of the given {@code parent} element overflows its area. */ protected boolean contentOverflows(Element parent) { return parent != null && (ElementUtils.detectHorizontalOverflow(parent) || ElementUtils.detectVerticalOverflow(parent)); } }