package org.ovirt.engine.ui.webadmin.widget.table.column;
import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
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.text.shared.SimpleSafeHtmlRenderer;
import com.google.gwt.user.client.DOM;
/**
* A {@link Cell} used to render text, providing a tooltip in case the text does not fit within the parent element.
*
* @see TextCell
*/
public class TextCellWithTooltip extends AbstractSafeHtmlCell<String> {
interface CellTemplate extends SafeHtmlTemplates {
@Template("<div id=\"{0}\">{1}</div>")
SafeHtml textContainer(String id, String text);
}
public static final int UNLIMITED_LENGTH = -1;
private static final String TOO_LONG_TEXT_POSTFIX = "...";
// DOM element ID settings for the text container element
private String elementIdPrefix = DOM.createUniqueId();
private String columnId;
// Text longer than this value will be shortened, providing a tooltip with original text
private final int maxTextLength;
private static CellTemplate template;
public TextCellWithTooltip(int maxTextLength) {
super(SimpleSafeHtmlRenderer.getInstance(), "mouseover");
this.maxTextLength = maxTextLength;
// Delay cell template creation until the first time it's needed
if (template == null) {
template = GWT.create(CellTemplate.class);
}
}
public void setElementIdPrefix(String elementIdPrefix) {
this.elementIdPrefix = elementIdPrefix;
}
public void setColumnId(String columnId) {
this.columnId = columnId;
}
@Override
public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
String rawData = value != null ? value.asString() : "";
sb.append(template.textContainer(
getContainerElementId(context),
getRenderedValue(rawData)));
}
@Override
public void onBrowserEvent(Cell.Context context, Element parent,
String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
// Ignore events other than 'mouseover'
if (!"mouseover".equals(event.getType())) {
return;
}
// Enforce tooltip when the presented text doesn't match original value
boolean forceTooltip = (value != null && !value.equals(getRenderedValue(value)));
// If the parent element content overflows its area, provide tooltip to the element
if (forceTooltip || contentOverflows(parent)) {
parent.setTitle(value);
} else {
parent.setTitle("");
}
}
/**
* Returns the DOM element ID for the text container element.
*/
String getContainerElementId(Cell.Context context) {
StringBuilder sb = new StringBuilder(elementIdPrefix);
sb.append("_");
sb.append(columnId != null ? columnId : "col" + String.valueOf(context.getColumn()));
sb.append("_row");
sb.append(String.valueOf(context.getIndex()));
return sb.toString();
}
/**
* Returns the text value to be rendered by this cell.
*/
String getRenderedValue(final String text) {
String result = text;
// Check if the text needs to be shortened
if (maxTextLength > 0 && text.length() > maxTextLength) {
result = result.substring(0, Math.max(maxTextLength - TOO_LONG_TEXT_POSTFIX.length(), 0));
result = result + TOO_LONG_TEXT_POSTFIX;
}
return result;
}
/**
* Returns {@code true} when the content of the given element overflows the element's content area, {@code false}
* otherwise.
*/
boolean contentOverflows(Element elm) {
String overflowValue = elm.getStyle().getOverflow();
// Temporarily allow element content to overflow through scroll bars
elm.getStyle().setProperty("overflow", "auto");
boolean overflowX = elm.getScrollWidth() > elm.getClientWidth();
boolean overflowY = elm.getScrollHeight() > elm.getClientHeight();
// Revert to the original overflow value
elm.getStyle().setProperty("overflow", overflowValue);
return overflowX || overflowY;
}
}