/*
* 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.mapviewer.tip;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.jdesktop.swingx.mapviewer.JXMapViewer;
import org.jdesktop.swingx.mapviewer.PixelConverter;
/**
* Map tooltip
*
* @author Simon Templer
*/
public abstract class HoverMapTip extends AbstractMapTip {
private static final int HOVER_DELAY = 400;
private final ScheduledExecutorService scheduleService = Executors.newScheduledThreadPool(2);
private ScheduledFuture<?> hoverTimer = null;
private ScheduledFuture<?> closeTimer = null;
/**
* If the tip shall be hidden on mouse move
*/
private boolean hideOnMouseMove = true;
/**
* Initialize the map tip with the given map
*
* @param map the map
*/
@Override
public void init(JXMapViewer map) {
super.init(map);
MouseAdapter mouse = new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
mouseMoved(e);
}
@Override
public void mouseExited(MouseEvent e) {
if (hoverTimer != null) {
hoverTimer.cancel(true);
// XXX on Windows this occurs when showing the tooltip -
// hideToolTip();
}
}
@Override
public void mouseDragged(MouseEvent e) {
hideTip();
}
@Override
public void mouseMoved(final MouseEvent e) {
// cancel old task
if (hoverTimer != null) {
hoverTimer.cancel(true);
}
if (hideOnMouseMove) {
hideTip();
}
if (true) { // start hover timer
// start new one
hoverTimer = scheduleService.schedule(new Runnable() {
@Override
public void run() {
// test if mouse was moved
Point evt = new Point(e.getXOnScreen(), e.getYOnScreen());
Point pos = MouseInfo.getPointerInfo().getLocation();
if (evt.equals(pos)) {
String text = getTipText(e.getX(), e.getY(),
getMap().getTileFactory().getTileProvider().getConverter(),
getMap().getZoom());
if (text != null) {
showTip(e, text);
}
}
}
}, HOVER_DELAY, TimeUnit.MILLISECONDS);
}
}
};
map.addMouseListener(mouse);
map.addMouseMotionListener(mouse);
}
/**
* @see AbstractMapTip#wantsToPaint()
*/
@Override
public boolean wantsToPaint() {
synchronized (this) {
return super.wantsToPaint() && closeTimer != null;
}
}
/**
* Get the tip text for the given position
*
* @param x the x viewport pixel ordinate
* @param y the y viewport pixel ordinate
* @param converter the converter
* @param zoom the zoom level
* @return the tip text, <code>null</code> for no tip
*/
protected abstract String getTipText(int x, int y, PixelConverter converter, int zoom);
private void showTip(final MouseEvent e, final String text) {
synchronized (HoverMapTip.this) {
PixelConverter converter = getMap().getTileFactory().getTileProvider().getConverter();
int zoom = getMap().getZoom();
Rectangle viewPort = getMap().getViewportBounds();
GeoPosition pos = converter
.pixelToGeo(new Point(viewPort.x + e.getX(), viewPort.y + e.getY()), zoom);
setTipText(text, pos);
if (closeTimer != null) {
closeTimer.cancel(true);
}
closeTimer = scheduleService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
hideTip();
}
}, 5 * HOVER_DELAY, 1000, TimeUnit.MILLISECONDS);
}
}
private void hideTip() {
synchronized (HoverMapTip.this) {
if (closeTimer != null) {
closeTimer.cancel(true);
closeTimer = null;
}
clearTip();
}
}
/**
* @return if the tip shall be hidden on mouse move
*/
public boolean isHideOnMouseMove() {
return hideOnMouseMove;
}
/**
* Set if the tip shall be hidden on mouse move
*
* @param hideOnMouseMove if the tip shall be hidden on mouse move
*/
public void setHideOnMouseMove(boolean hideOnMouseMove) {
this.hideOnMouseMove = hideOnMouseMove;
}
}