Greg Sheremeta has uploaded a new change for review.

Change subject: userportal, webadmin: WIP - dont merge - new tooltip 
infrastructure
......................................................................

userportal, webadmin: WIP - dont merge - new tooltip infrastructure

New tooltip infrastructure that works with Elements / Cells and uses
PatternFly tooltips.

Change-Id: Ie60ebe16e2d8830498fd819ffdb39aa043daadd2
Signed-off-by: Greg Sheremeta <gsher...@redhat.com>
---
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractCell.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractColumn.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/ImageResourceCell.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/TooltipCell.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/tooltip/ElementTooltip.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabClusterView.java
A 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/widget/table/column/CommentColumn2.java
7 files changed, 803 insertions(+), 3 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/97/36597/1

diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractCell.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractCell.java
new file mode 100644
index 0000000..eee9491
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractCell.java
@@ -0,0 +1,160 @@
+package org.ovirt.engine.ui.common.widget.table.column;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.ovirt.engine.ui.common.widget.tooltip.ElementTooltip;
+
+import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.dom.client.BrowserEvents;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Timer;
+
+/**
+ * Cell that displays a tooltip when hovered over. Uses the jQuery-based 
Bootstrap / PatternFly Tooltip
+ * widget, {@link ElementTooltip}.
+ *
+ * @param <T>
+ */
+public abstract class AbstractCell<C> extends 
com.google.gwt.cell.client.AbstractCell<C> implements TooltipCell<C> {
+
+    private static final Logger logger = 
Logger.getLogger(AbstractCell.class.getName());
+
+    private static Set<String> tooltipCellRegistry = new HashSet<String>();
+
+    private Timer initialShowTimer = null;
+
+    private Timer debounceTimer = null;
+    private Element debouncingParent = null;
+
+    /**
+     * Events to sink. By default, we only sink events that tooltips need -- 
'mouseover' and 'mouseout'.
+     * Override this (and include addAll(super.getConsumedEvents())'s events!) 
if your cell needs to
+     * respond to additional events.
+     */
+    @Override
+    public Set<String> getConsumedEvents() {
+        Set<String> set = new HashSet<String>();
+        set.add(BrowserEvents.MOUSEOVER);
+        set.add(BrowserEvents.MOUSEOUT);
+        return set;
+    }
+
+    public void onBrowserEvent(Context context, final Element parent, C value, 
final SafeHtml tooltipContent,
+            final NativeEvent event, ValueUpdater<C> valueUpdater) {
+
+        if (BrowserEvents.MOUSEOVER.equals(event.getType())) {
+
+            // debounce events
+
+            if (debounceTimer != null && debounceTimer.isRunning()) {
+                if (debouncingParent.equals(parent)) {
+                    logger.info("MOUSEOVER debounced"); //$NON-NLS-1$
+                    return;
+                }
+                else {
+                    logger.info("MOUSEOVER canceled because MOUSEOVER someone 
else"); //$NON-NLS-1$
+                    // debouncing someone else. cancel
+                    debouncingParent = null;
+                    debounceTimer.cancel();
+                    debounceTimer = null;
+                }
+            }
+
+            debouncingParent = parent;
+            debounceTimer = new Timer() {
+                @Override
+                public void run() {
+                    logger.info("MOUSEOVER!!"); //$NON-NLS-1$
+                    handleMouseover(event.getType(), parent, tooltipContent);
+                    debouncingParent = null;
+                    debounceTimer = null;
+                }
+            };
+            debounceTimer.schedule(200);
+            logger.info("MOUSEOVER timer"); //$NON-NLS-1$
+        }
+
+        if (BrowserEvents.MOUSEOUT.equals(event.getType())) {
+            if (debounceTimer != null && debounceTimer.isRunning()) {
+                logger.info("MOUSEOUT ignored"); //$NON-NLS-1$
+            }
+            else {
+                logger.info("MOUSEOUT"); //$NON-NLS-1$
+                handleMouseout(event.getType(), parent);
+            }
+        }
+    }
+
+    private void handleMouseover(String eventType, Element parent, SafeHtml 
tooltipContent) {
+        logger.info("handleMouseover"); //$NON-NLS-1$
+        if (tooltipContent == null || 
tooltipContent.asString().trim().isEmpty()) {
+            // there is no tooltip for this render. no-op.
+            logger.info("null or empty tooltip content"); //$NON-NLS-1$
+            return;
+        }
+
+        if (isTooltipConfigured(parent)) {
+            logger.info("tooltip already configured -- no-op"); //$NON-NLS-1$
+        }
+        else {
+
+            logger.info("tooltip not configured yet -- adding"); //$NON-NLS-1$
+            final ElementTooltip tooltip = addTooltipToCell(tooltipContent, 
parent);
+
+            // manually call show the first time, since we just added the 
tooltip
+            // in the handler. jQuery will handle it from here on out.
+            initialShowTimer = new Timer() {
+                @Override
+                public void run() {
+                    logger.info("calling tooltip force show"); //$NON-NLS-1$
+                    tooltip.show();
+                }
+            };
+
+            initialShowTimer.schedule(tooltip.getShowDelayMs());
+            logger.info("scheduled a show"); //$NON-NLS-1$
+        }
+    }
+
+    private void handleMouseout(String eventType, Element parent) {
+        if (initialShowTimer != null && initialShowTimer.isRunning()) {
+            logger.info("**** CANCELING INITIAL SHOW TIMER ****"); 
//$NON-NLS-1$
+            initialShowTimer.cancel();
+        }
+    }
+
+    private ElementTooltip addTooltipToCell(SafeHtml tooltipContent, Element 
parent) {
+        ElementTooltip tooltip = new ElementTooltip(parent);
+        tooltip.setContainer("body"); //$NON-NLS-1$
+
+        tooltip.setContent(tooltipContent);
+        tooltip.reconfigure();
+
+        String cellId = parent.getId();
+        if (cellId == null || cellId.isEmpty()) {
+            cellId = DOM.createUniqueId();
+            parent.setId(cellId);
+        }
+
+        tooltipCellRegistry.add(cellId);
+        logger.finer("*** " + cellId); //$NON-NLS-1$
+
+        return tooltip;
+    }
+
+    private boolean isTooltipConfigured(Element parent) {
+        String cellId = parent.getId();
+        logger.finer("checking tooltip registry  for " + cellId); //$NON-NLS-1$
+
+        if (cellId == null || cellId.isEmpty()) {
+            return false;
+        }
+        return tooltipCellRegistry.contains(cellId);
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractColumn.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractColumn.java
new file mode 100644
index 0000000..3c8a9d8
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/AbstractColumn.java
@@ -0,0 +1,59 @@
+package org.ovirt.engine.ui.common.widget.table.column;
+
+import com.google.gwt.cell.client.Cell.Context;
+import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.user.cellview.client.Column;
+
+/**
+ * A {@link Column} that allows setting options for server-side and 
client-side sorting.
+ * Supports tooltips. Supports setting ids on its cells.
+ *
+ * @param <T>
+ *            Table row data type.
+ * @param <C>
+ *            Cell data type.
+ */
+public abstract class AbstractColumn<T, C> extends Column<T, C> {
+
+    public AbstractColumn(TooltipCell<C> cell) {
+        super(cell);
+    }
+
+    public TooltipCell<C> getCell() {
+        return (TooltipCell<C>) super.getCell();
+    }
+
+    /**
+     * <p>
+     * Implement this to return tooltip content for T object. You'll likely 
use some member(s)
+     * of T to build a tooltip. You could also use a constant if the tooltip 
is always the same
+     * for this column.
+     * </p>
+     * <p>
+     * The tooltip cell will then use this value when rendering the cell.
+     * </p>
+     *
+     * @param object
+     * @return tooltip content
+     */
+    public abstract SafeHtml getTooltip(T object);
+
+    /**
+     * This is copied from GWT's Column, but we also inject the tooltip 
content into the cell.
+     * TODO-GWT: make sure that this method is in sync with 
Column::onBroswerEvent.
+     */
+    public void onBrowserEvent(Context context, Element elem, final T object, 
NativeEvent event) {
+        final int index = context.getIndex();
+        ValueUpdater<C> valueUpdater = (getFieldUpdater() == null) ? null : 
new ValueUpdater<C>() {
+            @Override
+            public void update(C value) {
+                getFieldUpdater().update(index, object, value);
+            }
+        };
+        getCell().onBrowserEvent(context, elem, getValue(object), /***/ 
getTooltip(object) /***/, event, valueUpdater);
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/ImageResourceCell.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/ImageResourceCell.java
new file mode 100644
index 0000000..5776c4a
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/ImageResourceCell.java
@@ -0,0 +1,55 @@
+package org.ovirt.engine.ui.common.widget.table.column;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ImageResource;
+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;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+
+/**
+ * Cell that renders and ImageResource. Supports setting a style / class. 
Supports tooltips.
+ * TODO: this will replace StyledImageResourceCell. Delete it and change 
references.
+ */
+public class ImageResourceCell extends AbstractCell<ImageResource> implements 
HasStyleClass {
+
+    interface CellTemplate extends SafeHtmlTemplates {
+        @Template("<div style=\"{0}\" class=\"{1}\">{2}</div>")
+        SafeHtml imageContainerWithStyleClass(String style, String styleClass, 
SafeHtml imageHtml);
+    }
+
+    private String style = "line-height: 100%; text-align: center; 
vertical-align: middle;"; //$NON-NLS-1$
+    private String styleClass = ""; //$NON-NLS-1$
+
+    private static CellTemplate template;
+
+    public ImageResourceCell() {
+        super();
+
+        // Delay cell template creation until the first time it's needed
+        if (template == null) {
+            template = GWT.create(CellTemplate.class);
+        }
+    }
+
+    @Override
+    public void render(Context context, ImageResource value, SafeHtmlBuilder 
sb) {
+        if (value != null) {
+            sb.append(template.imageContainerWithStyleClass(
+                    style,
+                    styleClass,
+                    
SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(value).getHTML())));
+        }
+    }
+
+    public void setStyle(String style) {
+        this.style = style;
+    }
+
+    @Override
+    public void setStyleClass(String styleClass) {
+        this.styleClass = styleClass == null ? "" : styleClass; //$NON-NLS-1$
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/TooltipCell.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/TooltipCell.java
new file mode 100644
index 0000000..43e8fdd
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/column/TooltipCell.java
@@ -0,0 +1,23 @@
+package org.ovirt.engine.ui.common.widget.table.column;
+
+import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.safehtml.shared.SafeHtml;
+
+/**
+ * A Cell with a tooltip. All Cells should implement this by extending ovirt's 
AbstractCell.
+ *
+ * @param <C> cell render type
+ */
+public interface TooltipCell<C> extends Cell<C> {
+
+    /**
+     * Called by AbstractColumn when an event occurs in a Cell. The only 
difference from GWT's native
+     * Column is that here we ask the column to provide us a tooltip value in 
addition to the cell's
+     * C value.
+     */
+    public void onBrowserEvent(Context context, final Element parent, C value, 
final SafeHtml tooltipContent,
+            final NativeEvent event, ValueUpdater<C> valueUpdater);
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/tooltip/ElementTooltip.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/tooltip/ElementTooltip.java
new file mode 100644
index 0000000..318d462
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/tooltip/ElementTooltip.java
@@ -0,0 +1,445 @@
+package org.ovirt.engine.ui.common.widget.tooltip;
+
+import java.util.logging.Logger;
+
+import org.gwtbootstrap3.client.ui.base.HasHover;
+import org.gwtbootstrap3.client.ui.base.HasId;
+import org.gwtbootstrap3.client.ui.constants.Placement;
+import org.gwtbootstrap3.client.ui.constants.Trigger;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Timer;
+
+/**
+ * <p>
+ * Implementation of Bootstrap tooltips that are capable of wrapping non-GWT 
elements.
+ * This is designed primarily for use in grids, but could be used in any Cell. 
Since
+ * Cells don't support Widget's event system, we must use a reaper timer to 
check for
+ * when a tooltip's Element has been detached from the document.
+ * </p>
+ * <p>
+ * Bootstrap tooltips use jQuery under the hood. jQuery must be present for 
this widget
+ * to function.
+ * </p>
+ * <p>
+ * <br/>
+ * See also: <br/>
+ * <a href="http://getbootstrap.com/javascript/#tooltips";>Bootstrap 
Documentation</a>
+ * <br/>
+ * See also: <br/>
+ * {@link Tooltip}
+ * </p>
+ * <p>
+ * ** Must call reconfigure() after altering any/all Tooltips!
+ * </p>
+ *
+ * @author Joshua Godi
+ * @author Pontus Enmark
+ * @author Greg Sheremeta
+ */
+public class ElementTooltip implements HasId, HasHover {
+    private static final String TOGGLE = "toggle"; //$NON-NLS-1$
+    private static final String SHOW = "show"; //$NON-NLS-1$
+    private static final String HIDE = "hide"; //$NON-NLS-1$
+    private static final String DESTROY = "destroy"; //$NON-NLS-1$
+
+    // Defaults from http://getbootstrap.com/javascript/#tooltips
+    private boolean isAnimated = true;
+    private boolean isHTML = true;
+    private Placement placement = Placement.BOTTOM;
+    private Trigger trigger = Trigger.HOVER;
+    private SafeHtml content = null;
+    private String container = null;
+    private final String selector = null;
+
+    private int hideDelayMs = 1000;
+    private int showDelayMs = 500;
+    private int reaperInterval = 7000;
+    private Timer reaperTimer = null;
+
+    private Element element;
+    private String id;
+
+    private static final Logger logger = 
Logger.getLogger(ElementTooltip.class.getName());
+
+    /**
+     * Creates an empty ElementTooltip
+     */
+    public ElementTooltip() {
+    }
+
+    /**
+     * Creates the tooltip around this element
+     *
+     * @param e Element for the tooltip
+     */
+    public ElementTooltip(final Element e) {
+        setElement(e);
+    }
+
+    /**
+     * Sets the Element that this tooltip hovers over.
+     * @param e Element for the tooltip
+     */
+    public void setElement(final Element e) {
+        element = e;
+        bindJavaScriptEvents(element);
+    }
+
+    /**
+     * Return the Element that this tooltip hovers over.
+     */
+    public Element getElement() {
+        return element;
+    }
+
+    /**
+     * Return the reaper interval.
+     */
+    public int getReaperInterval() {
+        return reaperInterval;
+    }
+
+    /**
+     * Sets the reaper interval.
+     */
+    public void setReaperInterval(int reaperInterval) {
+        this.reaperInterval = reaperInterval;
+    }
+
+    /**
+     * Starts a timer that checks for this tooltip to be hanging open.
+     * This can happen when the Element or one of its ancestors is detached 
from the document.
+     * Such detaching happens frequently when Grids are refreshed -- GWT 
replaces an entire
+     * grid row, but doesn't actually delete the row. The row just gets 
removed from its parent
+     * table. To detect this, we search up the ancestor tree for a null 
ancestor.
+     */
+    public void startHangingTooltipReaper() {
+        if (reaperTimer != null && reaperTimer.isRunning()) {
+            return;
+        }
+
+        reaperTimer = new Timer() {
+
+            @Override
+            public void run() {
+                if (hasNullAncestor()) {
+                    logger.info("tooltip for cell " + element.getId() + " 
dying"); //$NON-NLS-1$ //$NON-NLS-2$
+                    ElementTooltip.this.destroy();
+                    // cancel this timer since this tooltip is dead
+                    cancel();
+                }
+                else {
+                    logger.info("tooltip for cell " + element.getId() + " 
lives on"); //$NON-NLS-1$ //$NON-NLS-2$
+                }
+            }
+        };
+
+        reaperTimer.scheduleRepeating(getReaperInterval());
+    }
+
+    /**
+     * Check up this Elements ancestor tree, and return true if we find a null 
parent.
+     * @return true if a null parent is found, false if this Element has body 
as an ancestor
+     * (meaning it's still attached).
+     */
+    public boolean hasNullAncestor() {
+        Element parent = element.getParentElement();
+        while (parent != null) {
+            if (parent.getTagName().equalsIgnoreCase("body")) { //$NON-NLS-1$
+                return false;
+            }
+            parent = parent.getParentElement();
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setId(final String id) {
+        this.id = id;
+        if (element != null) {
+            element.setId(id);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getId() {
+        return (element == null) ? id : element.getId();
+    }
+
+    @Override
+    public void setIsAnimated(final boolean isAnimated) {
+        this.isAnimated = isAnimated;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAnimated() {
+        return isAnimated;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setIsHtml(final boolean isHTML) {
+        this.isHTML = isHTML;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isHtml() {
+        return isHTML;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setPlacement(final Placement placement) {
+        this.placement = placement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Placement getPlacement() {
+        return placement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setTrigger(final Trigger trigger) {
+        this.trigger = trigger;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Trigger getTrigger() {
+        return trigger;
+    }
+
+    @Override
+    public void setShowDelayMs(final int showDelayMs) {
+        this.showDelayMs = showDelayMs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getShowDelayMs() {
+        return showDelayMs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setHideDelayMs(final int hideDelayMs) {
+        this.hideDelayMs = hideDelayMs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getHideDelayMs() {
+        return hideDelayMs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setContainer(final String container) {
+        this.container = container;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getContainer() {
+        return container;
+    }
+
+    /**
+     * Sets the tooltip's HTML content
+     */
+    public void setContent(final SafeHtml content) {
+        this.isHTML = true;
+        this.content = content;
+    }
+
+    /**
+     * Reconfigures the tooltip, must be called when altering any tooltip 
after it has already been shown
+     */
+    public void reconfigure() {
+        // First destroy the old tooltip
+        destroy();
+
+        // Setup the new tooltip
+        if (container != null && selector != null) {
+            logger.info("tooltip mode 1 on element id " + element.getId()); 
//$NON-NLS-1$
+            tooltip(element, isAnimated, isHTML, placement.getCssName(), 
selector, content.asString(),
+                    trigger.getCssName(), showDelayMs, hideDelayMs, container);
+        } else if (container != null) {
+            logger.info("tooltip mode 2 on element id " + element.getId()); 
//$NON-NLS-1$
+            tooltip(element, isAnimated, isHTML, placement.getCssName(), 
content.asString(),
+                    trigger.getCssName(), showDelayMs, hideDelayMs, container);
+        } else if (selector != null) {
+            logger.info("tooltip mode 3 on element id " + element.getId()); 
//$NON-NLS-1$
+            tooltip(element, isAnimated, isHTML, placement.getCssName(), 
selector, content.asString(),
+                    trigger.getCssName(), showDelayMs, hideDelayMs);
+        } else {
+            logger.info("tooltip mode 4 on element id " + element.getId()); 
//$NON-NLS-1$
+            tooltip(element, isAnimated, isHTML, placement.getCssName(), 
content.asString(),
+                    trigger.getCssName(), showDelayMs, hideDelayMs);
+        }
+    }
+
+    /**
+     * Toggle the Tooltip to either show/hide
+     */
+    public void toggle() {
+        call(element, TOGGLE);
+    }
+
+    /**
+     * Force show the Tooltip. If you must, be sure to wrap this call in a 
Timer delay
+     * of getShowDelayMs() ms.
+     */
+    public void show() {
+        logger.info("tooltip show on element id " + element.getId()); 
//$NON-NLS-1$
+        call(element, SHOW);
+    }
+
+    /**
+     * Force hide the Tooltip
+     */
+    public void hide() {
+        call(element, HIDE);
+    }
+
+    /**
+     * Force the Tooltip to be destroyed
+     */
+    public void destroy() {
+        call(element, DESTROY);
+    }
+
+    /**
+     * Called when the tooltip is shown.
+     * Starts the hanging tooltip reaper timer.
+     *
+     * @param evt Event
+     */
+    protected void onShow(final Event evt) {
+        if (hasNullAncestor()) {
+            logger.info("dirty tooltip -- destroying"); //$NON-NLS-1$
+            hide();
+            destroy();
+        }
+        else {
+            logger.info("showing"); //$NON-NLS-1$
+        }
+        logger.info("starting reaper"); //$NON-NLS-1$
+        this.startHangingTooltipReaper();
+    }
+
+    private native void call(final Element e, final String arg) /*-{
+        $wnd.jQuery(e).tooltip(arg);
+    }-*/;
+
+    // @formatter:off
+    private native void bindJavaScriptEvents(final Element e) /*-{
+        var target = this;
+        var $tooltip = $wnd.jQuery(e);
+
+        $tooltip.on('show.bs.tooltip', function (evt) {
+            
targ...@org.ovirt.engine.ui.common.widget.tooltip.ElementTooltip::onShow(Lcom/google/gwt/user/client/Event;)(evt);
+        });
+    }-*/;
+
+    private native void tooltip(Element e, boolean animation, boolean html, 
String placement, String selector,
+                                String content, String trigger, int showDelay, 
int hideDelay, String container) /*-{
+        $wnd.jQuery(e).tooltip({
+            animation: animation,
+            html: html,
+            placement: placement,
+            selector: selector,
+            title: content,
+            trigger: trigger,
+            delay: {
+                show: showDelay,
+                hide: hideDelay
+            },
+            container: container
+        });
+    }-*/;
+
+    private native void tooltip(Element e, boolean animation, boolean html, 
String placement,
+                                String content, String trigger, int showDelay, 
int hideDelay, String container) /*-{
+        $wnd.jQuery(e).tooltip({
+            animation: animation,
+            html: html,
+            placement: placement,
+            title: content,
+            trigger: trigger,
+            delay: {
+                show: showDelay,
+                hide: hideDelay
+            },
+            container: container
+        });
+    }-*/;
+
+    private native void tooltip(Element e, boolean animation, boolean html, 
String placement, String selector,
+                                String content, String trigger, int showDelay, 
int hideDelay) /*-{
+        $wnd.jQuery(e).tooltip({
+            animation: animation,
+            html: html,
+            placement: placement,
+            selector: selector,
+            title: content,
+            trigger: trigger,
+            delay: {
+                show: showDelay,
+                hide: hideDelay
+            }
+        });
+    }-*/;
+
+    private native void tooltip(Element e, boolean animation, boolean html, 
String placement,
+                                String content, String trigger, int showDelay, 
int hideDelay) /*-{
+        console.log($wnd.jQuery(e).tooltip({
+            animation: animation,
+            html: html,
+            placement: placement,
+            title: content,
+            trigger: trigger,
+            delay: {
+                show: showDelay,
+                hide: hideDelay
+            }
+        }));
+    }-*/;
+}
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabClusterView.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabClusterView.java
index f4823cc..3ed044b 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabClusterView.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabClusterView.java
@@ -24,7 +24,7 @@
 import org.ovirt.engine.ui.webadmin.widget.action.WebAdminButtonDefinition;
 import 
org.ovirt.engine.ui.webadmin.widget.action.WebAdminImageButtonDefinition;
 import 
org.ovirt.engine.ui.webadmin.widget.action.WebAdminMenuBarButtonDefinition;
-import org.ovirt.engine.ui.webadmin.widget.table.column.CommentColumn;
+import org.ovirt.engine.ui.webadmin.widget.table.column.CommentColumn2;
 
 import com.google.gwt.core.client.GWT;
 import com.google.inject.Inject;
@@ -57,8 +57,11 @@
         nameColumn.makeSortable(ClusterConditionFieldAutoCompleter.NAME);
         getTable().addColumn(nameColumn, constants.nameCluster(), "150px"); 
//$NON-NLS-1$
 
-        CommentColumn<VDSGroup> commentColumn = new CommentColumn<VDSGroup>();
-        getTable().addColumnWithHtmlHeader(commentColumn, 
commentColumn.getHeaderHtml(), "30px"); //$NON-NLS-1$
+        CommentColumn2<VDSGroup> commentColumn = new 
CommentColumn2<VDSGroup>();
+        // TODO: add support for tooltips on headers
+        // TODO: don't hardcode "Comment" -- use image
+        // getTable().addColumnWithHtmlHeader(commentColumn, 
commentColumn.getHeaderHtml(), "30px"); //$NON-NLS-1$
+        getTable().addColumn(commentColumn, "Comment", "50px"); //$NON-NLS-1$ 
//$NON-NLS-2$
 
         if (ApplicationModeHelper.getUiMode() != ApplicationMode.GlusterOnly) {
             TextColumnWithTooltip<VDSGroup> dataCenterColumn = new 
TextColumnWithTooltip<VDSGroup>() {
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/widget/table/column/CommentColumn2.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/widget/table/column/CommentColumn2.java
new file mode 100644
index 0000000..3bf83d5
--- /dev/null
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/widget/table/column/CommentColumn2.java
@@ -0,0 +1,55 @@
+package org.ovirt.engine.ui.webadmin.widget.table.column;
+
+import org.ovirt.engine.core.common.businessentities.Commented;
+import org.ovirt.engine.ui.common.CommonApplicationResources;
+import org.ovirt.engine.ui.common.widget.table.column.AbstractColumn;
+import org.ovirt.engine.ui.common.widget.table.column.ImageResourceCell;
+import org.ovirt.engine.ui.common.widget.table.column.TooltipCell;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+
+/**
+ * Column that render a comment image (yellow paper icon) that, when hovered, 
shows the
+ * actual comment in a tooltip.
+ *
+ * @param <T> row type
+ */
+public class CommentColumn2<T extends Commented> extends AbstractColumn<T, 
ImageResource> {
+
+    private static final CommonApplicationResources RESOURCES = 
GWT.create(CommonApplicationResources.class);
+
+    public CommentColumn2() {
+        super(new ImageResourceCell());
+    }
+
+    public CommentColumn2(TooltipCell<ImageResource> cell) {
+        super(cell);
+    }
+
+    /**
+     * Using some row value of type T, build an ImageResource to render in 
this column.
+     *
+     * @see 
com.google.gwt.user.cellview.client.Column#getValue(java.lang.Object)
+     */
+    @Override
+    public ImageResource getValue(T value) {
+        if (value.getComment() != null && !value.getComment().isEmpty()) {
+            return RESOURCES.commentImage();
+        }
+        return null;
+    }
+
+    /**
+     * Using some row value of type T, build a SafeHtml tooltip to render when 
this column is moused over.
+     *
+     * @see 
org.ovirt.engine.ui.common.widget.table.column.AbstractColumn#getTooltip(java.lang.Object)
+     */
+    @Override
+    public SafeHtml getTooltip(T value) {
+        return SafeHtmlUtils.fromString(value.getComment());
+    }
+
+}


-- 
To view, visit http://gerrit.ovirt.org/36597
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie60ebe16e2d8830498fd819ffdb39aa043daadd2
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Greg Sheremeta <gsher...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to