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()); } } }