Author: snjeza
Date: 2012-02-16 15:03:16 -0500 (Thu, 16 Feb 2012)
New Revision: 38843
Added:
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/ToolTip.java
Modified:
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/DescriptionToolTip.java
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/GettingStartedPage.java
Log:
JBIDE-10938 tooltips fail in wizard examples
Modified:
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/DescriptionToolTip.java
===================================================================
---
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/DescriptionToolTip.java 2012-02-16
19:56:21 UTC (rev 38842)
+++
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/DescriptionToolTip.java 2012-02-16
20:03:16 UTC (rev 38843)
@@ -11,7 +11,6 @@
package org.jboss.tools.central.editors;
import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
Modified:
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/GettingStartedPage.java
===================================================================
---
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/GettingStartedPage.java 2012-02-16
19:56:21 UTC (rev 38842)
+++
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/GettingStartedPage.java 2012-02-16
20:03:16 UTC (rev 38843)
@@ -929,7 +929,7 @@
categoryComposite.setClient(composite);
String description = category.getDescription();
if (description != null && !description.isEmpty() &&
categoryComposite.getControl() != null) {
- final ToolTip toolTip = new DescriptionToolTip(categoryComposite.getControl(),
description);
+ final DescriptionToolTip toolTip = new
DescriptionToolTip(categoryComposite.getControl(), description);
toolTip.activate();
}
}
@@ -957,7 +957,7 @@
private void hookTooltip(FormText tutorialText, ProjectExample tutorial) {
final String description =
JBossCentralActivator.getDefault().getDescription(tutorial);
if (description != null && !description.isEmpty()) {
- ToolTip toolTip = new DescriptionToolTip(tutorialText, description);
+ DescriptionToolTip toolTip = new DescriptionToolTip(tutorialText, description);
toolTip.activate();
}
}
Added:
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/ToolTip.java
===================================================================
---
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/ToolTip.java
(rev 0)
+++
trunk/central/plugins/org.jboss.tools.central/src/org/jboss/tools/central/editors/ToolTip.java 2012-02-16
20:03:16 UTC (rev 38843)
@@ -0,0 +1,681 @@
+package org.jboss.tools.central.editors;
+
+import java.util.HashMap;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * This class gives implementors to provide customized tooltips for any control.
+ *
+ * @since 3.3
+ */
+public abstract class ToolTip {
+ private Control control;
+
+ private int xShift = 3;
+
+ private int yShift = 0;
+
+ private int popupDelay = 0;
+
+ private int hideDelay = 0;
+
+ private ToolTipOwnerControlListener listener;
+
+ private HashMap data;
+
+ // Ensure that only one tooltip is active in time
+ private static Shell CURRENT_TOOLTIP;
+
+ /**
+ * Recreate the tooltip on every mouse move
+ */
+ public static final int RECREATE = 1;
+
+ /**
+ * Don't recreate the tooltip as long the mouse doesn't leave the area
+ * triggering the tooltip creation
+ */
+ public static final int NO_RECREATE = 1 << 1;
+
+ private TooltipHideListener hideListener = new TooltipHideListener();
+
+ private Listener shellListener;
+
+ private boolean hideOnMouseDown = true;
+
+ private boolean respectDisplayBounds = true;
+
+ private boolean respectMonitorBounds = true;
+
+ private int style;
+
+ private Object currentArea;
+
+ /**
+ * Create new instance which add TooltipSupport to the widget
+ *
+ * @param control
+ * the control on whose action the tooltip is shown
+ */
+ public ToolTip(Control control) {
+ this(control, RECREATE, false);
+ }
+
+ /**
+ * @param control
+ * the control to which the tooltip is bound
+ * @param style
+ * style passed to control tooltip behavior
+ *
+ * @param manualActivation
+ * <code>true</code> if the activation is done manually using
+ * {@link #show(Point)}
+ * @see #RECREATE
+ * @see #NO_RECREATE
+ */
+ public ToolTip(Control control, int style, boolean manualActivation) {
+ this.control = control;
+ this.style = style;
+ this.control.addDisposeListener(new DisposeListener() {
+
+ public void widgetDisposed(DisposeEvent e) {
+ data = null;
+ deactivate();
+ }
+
+ });
+
+ this.listener = new ToolTipOwnerControlListener();
+ this.shellListener = new Listener() {
+ public void handleEvent(final Event event) {
+ if (ToolTip.this.control != null
+ && !ToolTip.this.control.isDisposed()) {
+ ToolTip.this.control.getDisplay().asyncExec(new Runnable() {
+
+ public void run() {
+ // Check if the new active shell is the tooltip
+ // itself
+ if (ToolTip.this.control.getDisplay()
+ .getActiveShell() != CURRENT_TOOLTIP) {
+ toolTipHide(CURRENT_TOOLTIP, event);
+ }
+ }
+
+ });
+ }
+ }
+ };
+
+ if (!manualActivation) {
+ activate();
+ }
+ }
+
+ /**
+ * Restore arbitrary data under the given key
+ *
+ * @param key
+ * the key
+ * @param value
+ * the value
+ */
+ public void setData(String key, Object value) {
+ if (data == null) {
+ data = new HashMap();
+ }
+ data.put(key, value);
+ }
+
+ /**
+ * Get the data restored under the key
+ *
+ * @param key
+ * the key
+ * @return data or <code>null</code> if no entry is restored under the key
+ */
+ public Object getData(String key) {
+ if (data != null) {
+ return data.get(key);
+ }
+ return null;
+ }
+
+ /**
+ * Set the shift (from the mouse position triggered the event) used to
+ * display the tooltip.
+ * <p>
+ * By default the tooltip is shifted 3 pixels to the right.
+ * </p>
+ *
+ * @param p
+ * the new shift
+ */
+ public void setShift(Point p) {
+ xShift = p.x;
+ yShift = p.y;
+ }
+
+ /**
+ * Activate tooltip support for this control
+ */
+ public void activate() {
+ deactivate();
+ control.addListener(SWT.Dispose, listener);
+ control.addListener(SWT.MouseHover, listener);
+ control.addListener(SWT.MouseMove, listener);
+ control.addListener(SWT.MouseExit, listener);
+ control.addListener(SWT.MouseDown, listener);
+ control.addListener(SWT.MouseWheel, listener);
+ }
+
+ /**
+ * Deactivate tooltip support for the underlying control
+ */
+ public void deactivate() {
+ control.removeListener(SWT.Dispose, listener);
+ control.removeListener(SWT.MouseHover, listener);
+ control.removeListener(SWT.MouseMove, listener);
+ control.removeListener(SWT.MouseExit, listener);
+ control.removeListener(SWT.MouseDown, listener);
+ control.removeListener(SWT.MouseWheel, listener);
+ }
+
+ /**
+ * Return whether the tooltip respects bounds of the display.
+ *
+ * @return <code>true</code> if the tooltip respects bounds of the display
+ */
+ public boolean isRespectDisplayBounds() {
+ return respectDisplayBounds;
+ }
+
+ /**
+ * Set to <code>false</code> if display bounds should not be respected or
+ * to <code>true</code> if the tooltip is should repositioned to not
+ * overlap the display bounds.
+ * <p>
+ * Default is <code>true</code>
+ * </p>
+ *
+ * @param respectDisplayBounds
+ */
+ public void setRespectDisplayBounds(boolean respectDisplayBounds) {
+ this.respectDisplayBounds = respectDisplayBounds;
+ }
+
+ /**
+ * Return whether the tooltip respects bounds of the monitor.
+ *
+ * @return <code>true</code> if tooltip respects the bounds of the monitor
+ */
+ public boolean isRespectMonitorBounds() {
+ return respectMonitorBounds;
+ }
+
+ /**
+ * Set to <code>false</code> if monitor bounds should not be respected or
+ * to <code>true</code> if the tooltip is should repositioned to not
+ * overlap the monitors bounds. The monitor the tooltip belongs to is the
+ * same is control's monitor the tooltip is shown for.
+ * <p>
+ * Default is <code>true</code>
+ * </p>
+ *
+ * @param respectMonitorBounds
+ */
+ public void setRespectMonitorBounds(boolean respectMonitorBounds) {
+ this.respectMonitorBounds = respectMonitorBounds;
+ }
+
+ /**
+ * Should the tooltip displayed because of the given event.
+ * <p>
+ * <b>Subclasses may overwrite this to get custom behavior</b>
+ * </p>
+ *
+ * @param event
+ * the event
+ * @return <code>true</code> if tooltip should be displayed
+ */
+ protected boolean shouldCreateToolTip(Event event) {
+ if ((style & NO_RECREATE) != 0) {
+ Object tmp = getToolTipArea(event);
+
+ // No new area close the current tooltip
+ if (tmp == null) {
+ hide();
+ return false;
+ }
+
+ boolean rv = !tmp.equals(currentArea);
+ return rv;
+ }
+
+ return true;
+ }
+
+ /**
+ * This method is called before the tooltip is hidden
+ *
+ * @param event
+ * the event trying to hide the tooltip
+ * @return <code>true</code> if the tooltip should be hidden
+ */
+ private boolean shouldHideToolTip(Event event) {
+ if (event != null && event.type == SWT.MouseMove
+ && (style & NO_RECREATE) != 0) {
+ Object tmp = getToolTipArea(event);
+
+ // No new area close the current tooltip
+ if (tmp == null) {
+ hide();
+ return false;
+ }
+
+ boolean rv = !tmp.equals(currentArea);
+ return rv;
+ }
+
+ return true;
+ }
+
+ /**
+ * This method is called to check for which area the tooltip is
+ * created/hidden for. In case of {@link #NO_RECREATE} this is used to
+ * decide if the tooltip is hidden recreated.
+ *
+ * <code>By the default it is the widget the tooltip is created for but could be
any object. To decide if
+ * the area changed the {@link Object#equals(Object)} method is used.</code>
+ *
+ * @param event
+ * the event
+ * @return the area responsible for the tooltip creation or
+ * <code>null</code> this could be any object describing the area
+ * (e.g. the {@link Control} onto which the tooltip is bound to, a
+ * part of this area e.g. for {@link ColumnViewer} this could be a
+ * {@link ViewerCell})
+ */
+ protected Object getToolTipArea(Event event) {
+ return control;
+ }
+
+ /**
+ * Start up the tooltip programmatically
+ *
+ * @param location
+ * the location relative to the control the tooltip is shown
+ */
+ public void show(Point location) {
+ Event event = new Event();
+ event.x = location.x;
+ event.y = location.y;
+ event.widget = control;
+ toolTipCreate(event);
+ }
+
+ private Shell toolTipCreate(final Event event) {
+ if (shouldCreateToolTip(event)) {
+ Shell shell = new Shell(control.getShell(), SWT.ON_TOP | SWT.TOOL
+ | SWT.NO_FOCUS);
+ shell.setLayout(new FillLayout());
+
+ toolTipOpen(shell, event);
+
+ return shell;
+ }
+
+ return null;
+ }
+
+ private void toolTipShow(Shell tip, Event event) {
+ if (!tip.isDisposed()) {
+ currentArea = getToolTipArea(event);
+ createToolTipContentArea(event, tip);
+ if (isHideOnMouseDown()) {
+ toolTipHookBothRecursively(tip);
+ } else {
+ toolTipHookByTypeRecursively(tip, true, SWT.MouseExit);
+ }
+
+ tip.pack();
+ Point size = tip.getSize();
+ Point location = fixupDisplayBounds(size, getLocation(size, event));
+
+ // Need to adjust a bit more if the mouse cursor.y == tip.y and
+ // the cursor.x is inside the tip
+ Point cursorLocation = tip.getDisplay().getCursorLocation();
+
+ if (cursorLocation.y == location.y && location.x < cursorLocation.x
+ && location.x + size.x > cursorLocation.x) {
+ location.y -= 2;
+ }
+
+ tip.setLocation(location);
+ tip.setVisible(true);
+ }
+ }
+
+ private Point fixupDisplayBounds(Point tipSize, Point location) {
+ if (respectDisplayBounds || respectMonitorBounds) {
+ Rectangle bounds;
+ Point rightBounds = new Point(tipSize.x + location.x, tipSize.y
+ + location.y);
+
+ Monitor[] ms = control.getDisplay().getMonitors();
+
+ if (respectMonitorBounds && ms.length > 1) {
+ // By default present in the monitor of the control
+ bounds = control.getMonitor().getBounds();
+ Point p = new Point(location.x, location.y);
+
+ // Search on which monitor the event occurred
+ Rectangle tmp;
+ for (int i = 0; i < ms.length; i++) {
+ tmp = ms[i].getBounds();
+ if (tmp.contains(p)) {
+ bounds = tmp;
+ break;
+ }
+ }
+
+ } else {
+ bounds = control.getDisplay().getBounds();
+ }
+
+ if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
+ if (rightBounds.x > bounds.x + bounds.width) {
+ location.x -= rightBounds.x - (bounds.x + bounds.width);
+ }
+
+ if (rightBounds.y > bounds.y + bounds.height) {
+ location.y -= rightBounds.y - (bounds.y + bounds.height);
+ }
+
+ if (location.x < bounds.x) {
+ location.x = bounds.x;
+ }
+
+ if (location.y < bounds.y) {
+ location.y = bounds.y;
+ }
+ }
+ }
+
+ return location;
+ }
+
+ /**
+ * Get the display relative location where the tooltip is displayed.
+ * Subclasses may overwrite to implement custom positioning.
+ *
+ * @param tipSize
+ * the size of the tooltip to be shown
+ * @param event
+ * the event triggered showing the tooltip
+ * @return the absolute position on the display
+ */
+ public Point getLocation(Point tipSize, Event event) {
+ return control.toDisplay(event.x + xShift, event.y + yShift);
+ }
+
+ private void toolTipHide(Shell tip, Event event) {
+ if (tip != null && !tip.isDisposed() && shouldHideToolTip(event)) {
+ if (!control.getShell().isDisposed()) {
+ control.getShell().removeListener(SWT.Deactivate, shellListener);
+ }
+ currentArea = null;
+ passOnEvent(tip, event);
+ tip.dispose();
+ CURRENT_TOOLTIP = null;
+ afterHideToolTip(event);
+ }
+ }
+
+ private void passOnEvent(Shell tip, Event event) {
+ if (control != null && !control.isDisposed() && event != null
+ && event.widget != control && event.type == SWT.MouseDown) {
+ // the following was left in order to fix bug 298770 with minimal change. In 3.7, the
complete method should be removed.
+ tip.close();
+ }
+ }
+
+ private void toolTipOpen(final Shell shell, final Event event) {
+ // Ensure that only one Tooltip is shown in time
+ if (CURRENT_TOOLTIP != null) {
+ toolTipHide(CURRENT_TOOLTIP, null);
+ }
+
+ CURRENT_TOOLTIP = shell;
+
+ control.getShell().addListener(SWT.Deactivate, shellListener);
+ shell.addDisposeListener(new DisposeListener() {
+
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ control.getShell().removeListener(SWT.Deactivate, shellListener);
+ control.getShell().removeDisposeListener(this);
+ }
+ });
+ if (popupDelay > 0) {
+ control.getDisplay().timerExec(popupDelay, new Runnable() {
+ public void run() {
+ toolTipShow(shell, event);
+ }
+ });
+ } else {
+ toolTipShow(CURRENT_TOOLTIP, event);
+ }
+
+ if (hideDelay > 0) {
+ control.getDisplay().timerExec(popupDelay + hideDelay,
+ new Runnable() {
+
+ public void run() {
+ toolTipHide(shell, null);
+ }
+ });
+ }
+ }
+
+ private void toolTipHookByTypeRecursively(Control c, boolean add, int type) {
+ if (add) {
+ c.addListener(type, hideListener);
+ } else {
+ c.removeListener(type, hideListener);
+ }
+
+ if (c instanceof Composite) {
+ Control[] children = ((Composite) c).getChildren();
+ for (int i = 0; i < children.length; i++) {
+ toolTipHookByTypeRecursively(children[i], add, type);
+ }
+ }
+ }
+
+ private void toolTipHookBothRecursively(Control c) {
+ c.addListener(SWT.MouseDown, hideListener);
+ c.addListener(SWT.MouseExit, hideListener);
+
+ if (c instanceof Composite) {
+ Control[] children = ((Composite) c).getChildren();
+ for (int i = 0; i < children.length; i++) {
+ toolTipHookBothRecursively(children[i]);
+ }
+ }
+ }
+
+ /**
+ * Creates the content area of the the tooltip.
+ *
+ * @param event
+ * the event that triggered the activation of the tooltip
+ * @param parent
+ * the parent of the content area
+ * @return the content area created
+ */
+ protected abstract Composite createToolTipContentArea(Event event,
+ Composite parent);
+
+ /**
+ * This method is called after a tooltip is hidden.
+ * <p>
+ * <b>Subclasses may override to clean up requested system resources</b>
+ * </p>
+ *
+ * @param event
+ * event triggered the hiding action (may be <code>null</code>
+ * if event wasn't triggered by user actions directly)
+ */
+ protected void afterHideToolTip(Event event) {
+
+ }
+
+ /**
+ * Set the hide delay.
+ *
+ * @param hideDelay
+ * the delay before the tooltip is hidden. If <code>0</code>
+ * the tooltip is shown until user moves to other item
+ */
+ public void setHideDelay(int hideDelay) {
+ this.hideDelay = hideDelay;
+ }
+
+ /**
+ * Set the popup delay.
+ *
+ * @param popupDelay
+ * the delay before the tooltip is shown to the user. If
+ * <code>0</code> the tooltip is shown immediately
+ */
+ public void setPopupDelay(int popupDelay) {
+ this.popupDelay = popupDelay;
+ }
+
+ /**
+ * Return if hiding on mouse down is set.
+ *
+ * @return <code>true</code> if hiding on mouse down in the tool tip is on
+ */
+ public boolean isHideOnMouseDown() {
+ return hideOnMouseDown;
+ }
+
+ /**
+ * If you don't want the tool tip to be hidden when the user clicks inside
+ * the tool tip set this to <code>false</code>. You maybe also need to
+ * hide the tool tip yourself depending on what you do after clicking in the
+ * tooltip (e.g. if you open a new {@link Shell})
+ *
+ * @param hideOnMouseDown
+ * flag to indicate of tooltip is hidden automatically on mouse
+ * down inside the tool tip
+ */
+ public void setHideOnMouseDown(final boolean hideOnMouseDown) {
+ // Only needed if there's currently a tooltip active
+ if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
+ // Only change if value really changed
+ if (hideOnMouseDown != this.hideOnMouseDown) {
+ control.getDisplay().syncExec(new Runnable() {
+
+ public void run() {
+ if (CURRENT_TOOLTIP != null
+ && CURRENT_TOOLTIP.isDisposed()) {
+ toolTipHookByTypeRecursively(CURRENT_TOOLTIP,
+ hideOnMouseDown, SWT.MouseDown);
+ }
+ }
+
+ });
+ }
+ }
+
+ this.hideOnMouseDown = hideOnMouseDown;
+ }
+
+ /**
+ * Hide the currently active tool tip
+ */
+ public void hide() {
+ toolTipHide(CURRENT_TOOLTIP, null);
+ }
+
+ private class ToolTipOwnerControlListener implements Listener {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose:
+ case SWT.KeyDown:
+ case SWT.MouseDown:
+ case SWT.MouseMove:
+ case SWT.MouseWheel:
+ toolTipHide(CURRENT_TOOLTIP, event);
+ break;
+ case SWT.MouseHover:
+ toolTipCreate(event);
+ break;
+ case SWT.MouseExit:
+ /*
+ * Check if the mouse exit happened because we move over the
+ * tooltip
+ */
+ if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
+ if (CURRENT_TOOLTIP.getBounds().contains(
+ control.toDisplay(event.x, event.y))) {
+ break;
+ }
+ }
+
+ toolTipHide(CURRENT_TOOLTIP, event);
+ break;
+ }
+ }
+ }
+
+ private class TooltipHideListener implements Listener {
+ public void handleEvent(Event event) {
+ if (event.widget instanceof Control) {
+
+ Control c = (Control) event.widget;
+ Shell shell = c.getShell();
+
+ switch (event.type) {
+ case SWT.MouseDown:
+ if (isHideOnMouseDown()) {
+ toolTipHide(shell, event);
+ }
+ break;
+ case SWT.MouseExit:
+ /*
+ * Give some insets to ensure we get exit informations from
+ * a wider area ;-)
+ */
+ Rectangle rect = shell.getBounds();
+ rect.x += 5;
+ rect.y += 5;
+ rect.width -= 10;
+ rect.height -= 10;
+
+ if (!rect.contains(c.getDisplay().getCursorLocation())) {
+ toolTipHide(shell, event);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}