/* * Copyright (c) 2016 Fraunhofer IGD * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Fraunhofer IGD <http://www.igd.fraunhofer.de/> */ package de.fhg.igd.swingrcp; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.MouseTrackListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; /** * HTMLTooltipProvider * * @author Simon Templer */ public class HTMLToolTipProvider { /** * ToolTipManager * * @author <a href="mailto:simon.templer@igd.fhg.de">Simon Templer</a> */ public class ToolTipManager implements MouseMoveListener, MouseTrackListener { private static final int HOVER_DELAY = 400; private final ToolBar toolBar; private Timer hoverTimer = null; private Shell toolShell; private ToolItem currentToolItem = null; /** * Creates a tool tip manager for a tool bar * * @param toolBar the tool bar */ public ToolTipManager(final ToolBar toolBar) { this.toolBar = toolBar; toolBar.addMouseMoveListener(this); toolBar.addMouseTrackListener(this); } /** * @see MouseMoveListener#mouseMove(MouseEvent) */ @Override public void mouseMove(final MouseEvent e) { final ToolItem item = toolBar.getItem(new Point(e.x, e.y)); // cancel old task if (hoverTimer != null) { hoverTimer.cancel(); hoverTimer.purge(); } if (currentToolItem != item) { hideToolTip(); } if (currentToolItem == null && item != null && tooltips.containsKey(item)) { // start new one hoverTimer = new Timer(true); hoverTimer.schedule(new TimerTask() { /** * @see TimerTask#run() */ @Override public void run() { if (!toolBar.isDisposed()) { toolBar.getDisplay().asyncExec(new Runnable() { @Override public void run() { // test if mouse was moved Point evt = new Point(e.x, e.y); Point pos = toolBar.getDisplay().getCursorLocation(); Point pt = toolBar.toControl(pos); // Rectangle bounds = toolBar.getBounds(); if (evt.equals(pt)) { showToolTip(e, tooltips.get(item), item); } } }); } } }, HOVER_DELAY); } } /** * @see MouseTrackListener#mouseEnter(MouseEvent) */ @Override public void mouseEnter(MouseEvent e) { mouseMove(e); } /** * @see MouseTrackListener#mouseExit(MouseEvent) */ @Override public void mouseExit(MouseEvent e) { if (hoverTimer != null) { hoverTimer.cancel(); hoverTimer.purge(); // XXX on Windows this occurs when showing the tooltip - // hideToolTip(); } } /** * @see MouseTrackListener#mouseHover(MouseEvent) */ @Override public void mouseHover(MouseEvent e) { // seems to occur never // log.warn("Mouse hover occurred!"); } /** * Show the tool tip * * @param e the mouse event * @param toolTip the tool tip string * @param item the tool item */ protected void showToolTip(MouseEvent e, String toolTip, final ToolItem item) { // set as current item currentToolItem = item; toolShell = new Shell(toolBar.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL); FillLayout layout = new FillLayout(); toolShell.setLayout(layout); try { Browser browser = new Browser(toolShell, SWT.NONE); browser.setFont(toolBar.getDisplay().getSystemFont()); browser.setForeground( toolBar.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND)); browser.setBackground( toolBar.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); browser.setText(toolTip); // Point size = toolShell.computeSize( SWT.DEFAULT, // SWT.DEFAULT); int tbh = toolBar.getBounds().height - 1; // use toolbar height // to show tooltip // below the bar int tix = item.getBounds().x + 1; Point pt = toolBar.toDisplay(tix, tbh); // e.x, e.y); final int topBuffer = tbh - e.y + 1; Rectangle bounds = toolBar.getDisplay().getBounds(); int x = (pt.x + toolTipWidth > bounds.x + bounds.width) ? (bounds.x + bounds.width - toolTipWidth) : (pt.x); toolShell.setBounds(x, pt.y, toolTipWidth, toolTipHeight); // toolShell.setBounds(pt.x, pt.y, size.x, size.y); toolShell.addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { hideToolTip(); } }); final Timer closeTimer = new Timer(true); closeTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if (toolShell != null && !toolShell.isDisposed()) { toolShell.getDisplay().asyncExec(new Runnable() { @Override public void run() { if (item == currentToolItem && toolShell != null && !toolShell.isDisposed()) { // check if cursor is over tooltip Point cursor = toolShell.getDisplay().getCursorLocation(); Rectangle bounds = toolShell.getBounds(); // create top buffer bounds = new Rectangle(bounds.x, bounds.y - topBuffer, bounds.width, bounds.height); if (!bounds.contains(cursor)) { hideToolTip(); closeTimer.cancel(); } } } }); } else { // disposed -> cancel timer closeTimer.cancel(); } } }, 2 * HOVER_DELAY, 1000); toolShell.setVisible(true); } catch (SWTError err) { log.error(err.getMessage(), err); } } /** * Hide the tool tip */ protected void hideToolTip() { currentToolItem = null; if (toolShell != null) { toolShell.close(); toolShell.dispose(); toolShell = null; } } } /** * Custom action item */ public class CustomActionItem extends ContributionItem { private final IAction action; private CustomActionItem(final IAction action) { this.action = action; } /** * @see ContributionItem#fill(ToolBar, int) */ @Override public void fill(ToolBar parent, int index) { // register toolbar addToolBar(parent); // determine ToolItem style int style; switch (action.getStyle()) { case IAction.AS_RADIO_BUTTON: style = SWT.RADIO; break; case IAction.AS_CHECK_BOX: style = SWT.CHECK; break; case IAction.AS_DROP_DOWN_MENU: style = SWT.DROP_DOWN; break; case IAction.AS_PUSH_BUTTON: default: style = SWT.PUSH; } // create ToolItem final ToolItem item = new ToolItem(parent, style, index); // determine ToolItem properties Image image = null; if (action.getImageDescriptor() != null) image = action.getImageDescriptor().createImage(); if (image != null) item.setImage(image); else item.setText(action.getText()); item.setSelection(action.isChecked()); item.setEnabled(action.isEnabled()); item.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { action.run(); } @Override public void widgetSelected(SelectionEvent e) { action.run(); } }); // add property change listeners action.addPropertyChangeListener(new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pce) { // text if (pce.getProperty().equals(IAction.TEXT)) item.setText((String) pce.getNewValue()); // enabled else if (pce.getProperty().equals(IAction.ENABLED)) item.setEnabled((Boolean) pce.getNewValue()); // image else if (pce.getProperty().equals(IAction.IMAGE)) { if (pce.getNewValue() != null) { Image image = ((ImageDescriptor) pce.getNewValue()).createImage(); item.setImage(image); } else item.setImage(null); } } }); // add to factories map tooltips.put(item, action.getToolTipText()); } } private static final Log log = LogFactory.getLog(HTMLToolTipProvider.class); private final Map<ToolItem, String> tooltips = new HashMap<ToolItem, String>(); private final Set<ToolBar> toolBars = new HashSet<ToolBar>(); private int toolTipWidth = 240; private int toolTipHeight = 100; /** * Create a custom action item for the given action * * @param action the action * * @return the custom action item */ public CustomActionItem createItem(final IAction action) { return new CustomActionItem(action); } private void addToolBar(ToolBar bar) { if (!toolBars.contains(bar)) { toolBars.add(bar); new ToolTipManager(bar); } } /** * @return the toolTipWidth */ public int getToolTipWidth() { return toolTipWidth; } /** * @param toolTipWidth the toolTipWidth to set */ public void setToolTipWidth(int toolTipWidth) { this.toolTipWidth = toolTipWidth; } /** * @return the toolTipHeight */ public int getToolTipHeight() { return toolTipHeight; } /** * @param toolTipHeight the toolTipHeight to set */ public void setToolTipHeight(int toolTipHeight) { this.toolTipHeight = toolTipHeight; } }