package uk.ac.leeds.ccg.geotools;
import java.awt.*;
import java.awt.event.*;
/**
* Mouse Status is a new class designed to take some of load (and code bloat) out of the
* main Viewer class.
* Although MouseStatus objects should only be setup and configured by Viewer objects
* MouseStatus objects will be usefull for objects that are to act as tools in conjunction with a
* Viewer as the MouseStatus should carry all of the information any tool should need to know about
* what and where a user has been interacting with the mouse on the viewer.
* The MouseStatus class tracks mouse movements (particularly drags) in three co-ordinate systems.
* Screen space
* 'GeoGraphic' space.
* projected space.
*/
public class MouseStatus
{
/**
* debug switch. used to control debug output. Output will be identified by prefix 'MoS->'
*/
private final static boolean DEBUG=false;
/**
* The last mouse event to be generated
*/
private MouseEvent lastEvent;
/**
* The viewer to which this MouseStatus is attached.
*/
private Viewer context;
/**
* Is the user draging the mouse at the moment.
*/
boolean draging = false;
/**
* Is the pointer inside the viewer at the moment.
**/
protected boolean isPointerInside = true;
/**
* A GeoRectangle that represents the bounds of the current
* drag region in the same units as the origional geography.
*/
protected GeoRectangle dragBox;
/**
* A GeoRectangle that represents the bounds of the current
* drag region projected to the current projection system.
*/
protected GeoRectangle projectedDragBox;
/**
* The geographic coordinates of the start position of the most recently started drag
*/
protected double dragStart[];
/**
* The projected coordinates of the start position of the most recently started drag
*/
protected double projectedDragStart[];
/**
* An AWT Rectangle that represents the bounds of the current
* drag region in on screen pixel coordinates.
*/
protected Rectangle screenDragBox;
protected Rectangle oldScreenDragBox;
/**
* The on screen coordinates of the start position of the most recently started drag
*/
protected int screenDragStart[];
protected boolean isDragStart = false;
protected int dragCount = 0;
/**
* A special timer that tracks how long the mouse has been stationary for.
* Its main role is to inform the viewer when the mouse has rested long enough for it
* to consider displaying a ToolTip over the feature under the pointer.
*/
private MouseIdle mouseIdle;
/**
* Notes whether or not the mouse has been idle for at least a set period of time.
*
timeout.
*/
private boolean mouseStill = true;
/**
* The period of time for which the mouse has to be idle before it is considered Still.
*/
protected int timeout = 250;
/**
* current 'on_screen' coordinates
*/
protected int screen_xy[] = {0,0};
/**
* location of the on_screen coordinates in the real world
*/
protected double map_xy[] = {0,0};
/**
* location of the on_screen coordinates in the real world, stored as a GeoPoint
*/
protected GeoPoint map_point = new GeoPoint(0,0);
/**
* location of the on_screen coordinates in projected space
*/
protected double proj_xy[] = {0,0};
/**
* contruct a MouseStatus object. This must only be called by the viewer that
* is passed in as the argument.
* @param v The viewer which is constructing this MouseStatus object, and to
* which the status values will relate.
*/
public MouseStatus(Viewer v){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.MouseStatus constructed. Will be identified as MoS->");
dragBox = new GeoRectangle();
projectedDragBox = new GeoRectangle();
screenDragBox = new Rectangle();
oldScreenDragBox = new Rectangle();
mouseIdle = new MouseIdle();
Thread mith = new Thread(mouseIdle);
mith.start();
//mouseIdle.start();
context = v;
context.addMouseMotionListener(new StatusMouseMotion());
context.addMouseListener(new StatusMouse());
}
/**
* Find out which viewer this object is attached to
* @return Viewer The viewer that this MouseStatus object is attached to
*/
public Viewer getContext(){
return context;
}
public GeoPoint getMapPoint(){
return map_point;
}
public GeoRectangle getMapDragBox(){
return dragBox;
}
/**
* The bounds of the most recently started drag box.
* @return Rectangle an AWT rectangle containing the current drag region coordinates
*/
public Rectangle getScreenDragBox(){
return screenDragBox;
}
public Rectangle getOldScreenDragBox(){
return oldScreenDragBox;
}
protected void clickTo(int x,int y){
map_xy = context.scale.toMap(x,y); //need to change scale to allow returning a point
map_point.setLocation(map_xy[0],map_xy[1]);
proj_xy = context.scale.toProj(x,y);
if(DEBUG)System.out.println("Mos>Psudo micro drag");
draging=false;
//dragTo(x,y);
//dragTo(x,y);
}
/**
* The pointer has left the viewer.
**/
protected void exitAt(int x,int y){
if(mouseIdle!=null){
mouseIdle.noGo();
}
if(mouseStill){
//The mouse is no longer still, so clear any toolTips or similer.
mouseStill=false;
context.repaint();
}
}
protected void moveTo(int x,int y){
screen_xy[0]=x;
screen_xy[1]=y;
map_xy = context.scale.toMap(x,y); //need to change scale to allow returning a point
map_point.setLocation(map_xy[0],map_xy[1]);
proj_xy = context.scale.toProj(x,y);
if(mouseIdle!=null){
mouseIdle.noGo();
}
if(mouseStill){
//The mouse is no longer still, so clear any toolTips or similer.
mouseStill=false;
context.repaint();
}
}
protected void pressed(int x,int y){
mouseIdle.noGo();
draging = false;
dragTo(x,y);
}
protected void release(int x,int y){
draging = false;
mouseIdle.noGo();
}
/**
* On some ocasions the user may click and hold the mouse button and move the pointer a very small distance
* by accident. In most cases this should not be interprited as a valid drag.
*
This method checks to see if the last drag distance was grater than a small threshold.
* @return boolean true only if the last drag was more than a small threshold value.
*/
public boolean isValidDrag(){
System.out.println("Mos>valid drag? "+!(screenDragBox.width < 5 || screenDragBox.height <5));
return !(screenDragBox.width < 5 || screenDragBox.height <5);
}
protected void dragTo(int x,int y){
if(!draging){
draging = true;
isDragStart = true;
//dragCount = 0;
screen_xy[0] = x;
screen_xy[1] = y;
map_xy = context.scale.toMap(x,y);
map_point.setLocation(map_xy[0],map_xy[1]);
proj_xy = context.scale.toProj(x,y);
dragStart = context.scale.toMap(x,y);
projectedDragStart = context.scale.toProj(x,y);
screenDragStart = new int[2];
screenDragStart[0] = x;
screenDragStart[1] = y;
screenDragBox.x = x;
screenDragBox.y = y;
screenDragBox.width = 0;
screenDragBox.height = 0;
projectedDragBox.x = proj_xy[0];
projectedDragBox.y = proj_xy[1];
projectedDragBox.width = 0;
projectedDragBox.height = 0;
dragBox.x = map_xy[0];
dragBox.y = map_xy[1];
dragBox.width = 0;
dragBox.height = 0;
}
else
{
isDragStart = false;
screen_xy[0] = x;
screen_xy[1] = y;
map_xy = context.scale.toMap(x,y);
map_point.setLocation(map_xy[0],map_xy[1]);
proj_xy = context.scale.toProj(x,y);
//dragBox.add(world_xy[0],world_xy[1]);
dragBox.x = Math.min(dragStart[0],map_xy[0]);
dragBox.y = Math.min(dragStart[1],map_xy[1]);
dragBox.width = Math.abs(map_xy[0]-dragStart[0]);
dragBox.height = Math.abs(map_xy[1]-dragStart[1]);
projectedDragBox.x = Math.min(dragStart[0],proj_xy[0]);
projectedDragBox.y = Math.min(dragStart[1],proj_xy[1]);
projectedDragBox.width = Math.abs(proj_xy[0]-dragStart[0]);
projectedDragBox.height = Math.abs(proj_xy[1]-dragStart[1]);
oldScreenDragBox.x = screenDragBox.x;
oldScreenDragBox.y = screenDragBox.y;
oldScreenDragBox.width = screenDragBox.width;
oldScreenDragBox.height = screenDragBox.height;
screenDragBox.x = Math.min(screenDragStart[0],x);
screenDragBox.y = Math.min(screenDragStart[1],y);
screenDragBox.width = Math.abs(x-screenDragStart[0]);
screenDragBox.height = Math.abs(y-screenDragStart[1]);
if(DEBUG){System.out.println("MoS updated dragboxes");}
}
}
/**
* Test to see if the mouse has not moved for a while.
*
Useful for deciding if ToolTips or similer should be displayed.
* @return boolean true if the mouse has not moved for timeout milliseconds.
*/
public boolean isMouseStill(){
return mouseStill;
}
/**
* Test to see if the user is currently performing a drag operation
* @return boolean true if the user has started but not finished a drag.
*/
public boolean isDraging(){
return draging;
}
public boolean isPointerInside(){
return isPointerInside;
}
public boolean isDragStart(){
return isDragStart;
}
public void finalize(){
System.out.println("Mos>Cleaning up MouseStatus");
mouseIdle.kill();
}
public MouseEvent getMouseEvent(){
return lastEvent;
}
public boolean isShiftDown(){
return lastEvent.isShiftDown();
}
protected class MouseIdle implements Runnable{
private final static boolean DEBUG=false;
private boolean skip = false;
private boolean finished = false;
public void kill(){
finished = true;
}
public void noGo(){
skip = true;
}
public void run(){
while(!finished){
try{
Thread.sleep(context.timeout);
if(!skip && !draging && !mouseStill){
mouseStill=true;
context.repaint();
}
else{
skip=false;
//System.out.println("Mos>Skip by logic");
}
}
catch(InterruptedException ie){
//System.out.println("Mos>skip by interupt");
}
catch(Exception e){
//System.out.println("Mos>skip boo by "+e);
}
}
if(DEBUG)System.out.println("Mos>MouseIdle stoped");
}
}
class StatusMouseMotion extends java.awt.event.MouseMotionAdapter
{
public void mouseMoved(java.awt.event.MouseEvent event)
{
lastEvent = event;
moveTo(event.getX(),event.getY());
}
public void mouseDragged(java.awt.event.MouseEvent event)
{
lastEvent = event;
dragTo(event.getX(),event.getY());
}
}
class StatusMouse extends java.awt.event.MouseAdapter
{
public void mousePressed(java.awt.event.MouseEvent event){
lastEvent = event;
pressed(event.getX(),event.getY());
}
public void mouseClicked(java.awt.event.MouseEvent event)
{
lastEvent = event;
clickTo(event.getX(),event.getY());
}
public void mouseExited(java.awt.event.MouseEvent event)
{
lastEvent = event;
isPointerInside = false;
exitAt(event.getX(),event.getY());
}
public void mouseEntered(java.awt.event.MouseEvent event)
{
lastEvent = event;
isPointerInside = true;
exitAt(event.getX(),event.getY());
}
public void mouseReleased(java.awt.event.MouseEvent event)
{
lastEvent = event;
if(DEBUG)System.out.println("Mos>Mouse status release");
dragTo(event.getX(),event.getY());
release(event.getX(),event.getY());
}
}
}