package ij.gui;

import java.awt.*;
import java.util.Properties;
import java.awt.image.*;
import java.awt.event.*;
import ij.process.ImageProcessor;
import ij.measure.*;
import ij.plugin.frame.Recorder;
import ij.macro.Interpreter;
import ij.*;
import ij.util.Java2;

/** This is a Canvas used to display images in a Window. */
public class ImageCanvas extends Canvas implements MouseListener, MouseMotionListener, Cloneable {

    protected static Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
    protected static Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
    protected static Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
    protected static Cursor crosshairCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);

    public static boolean usePointer = Prefs.usePointerCursor;
    
    protected ImagePlus imp;
    protected boolean imageUpdated;
    protected Rectangle srcRect;
    protected int imageWidth, imageHeight;
    protected int xMouse; // current cursor offscreen x location 
    protected int yMouse; // current cursor offscreen y location
        
    private ImageJ ij;
    private double magnification;
    private int dstWidth, dstHeight;

    private int xMouseStart;
    private int yMouseStart;
    private int xSrcStart;
    private int ySrcStart;
    private int flags;
    
    public ImageCanvas(ImagePlus imp) {
        this.imp = imp;
        ij = IJ.getInstance();
        int width = imp.getWidth();
        int height = imp.getHeight();
        imageWidth = width;
        imageHeight = height;
        srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
        setDrawingSize(imageWidth, (int)(imageHeight));
        magnification = 1.0;
        addMouseListener(this);
        addMouseMotionListener(this);
        addKeyListener(ij);  // ImageJ handles keyboard shortcuts
    }
        
    void updateImage(ImagePlus imp) {
        this.imp = imp;
        int width = imp.getWidth();
        int height = imp.getHeight();
        imageWidth = width;
        imageHeight = height;
        srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
        setDrawingSize(imageWidth, (int)imageHeight);
        magnification = 1.0;
    }

    public void setDrawingSize(int width, int height) {
        dstWidth = width;
        dstHeight = height;
        setSize(dstWidth, dstHeight);
    }
        
    /** ImagePlus.updateAndDraw calls this method to get paint 
        to update the image from the ImageProcessor. */
    public void setImageUpdated() {
        imageUpdated = true;
    }

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {
        Roi roi = imp.getRoi();
        if (roi != null) roi.updatePaste();
        try {
            if (imageUpdated) {
                imageUpdated = false;
                imp.updateImage();
            }
            if (IJ.isJava2())
                Java2.setBilinearInterpolation(g, Prefs.interpolateScaledImages);
            Image img = imp.getImage();
            if (img!=null)
                g.drawImage(img, 0, 0, (int)(srcRect.width*magnification), (int)(srcRect.height*magnification),
                srcRect.x, srcRect.y, srcRect.x+srcRect.width, srcRect.y+srcRect.height, null);
            if (roi != null) roi.draw(g);
            if (IJ.debugMode) showFrameRate(g);
        }
        catch(OutOfMemoryError e) {IJ.outOfMemory("Paint");}
    }
    
    long firstFrame;
    int frames, fps;
        
    void showFrameRate(Graphics g) {
        frames++;
        if (System.currentTimeMillis()>firstFrame+1000) {
            firstFrame=System.currentTimeMillis();
            fps = frames;
            frames=0;
        }
        g.setColor(Color.white);
        g.fillRect(10, 12, 50, 15);
        g.setColor(Color.black);
        g.drawString((int)(fps+0.5) + " fps", 10, 25);
    }

    public Dimension getPreferredSize() {
        return new Dimension(dstWidth, dstHeight);
    }

    int count;
    
    /*
    public Graphics getGraphics() {
        Graphics g = super.getGraphics();
        IJ.write("getGraphics: "+count++);
        if (IJ.altKeyDown())
            throw new IllegalArgumentException("");
        return g;
    }
    */

    /** Returns the current cursor location. */
    public Point getCursorLoc() {
        return new Point(xMouse, yMouse);
    }

    /** Returns the mouse event modifiers. */
    public int getModifiers() {
        return flags;
    }

    /** Sets the cursor based on the current tool and cursor location. */
    public void setCursor(int sx, int sy, int ox, int oy) {
        xMouse = ox;
        yMouse = oy;
        Roi roi = imp.getRoi();
        ImageWindow win = imp.getWindow();
        if (win==null)
            return;
        if (IJ.spaceBarDown()) {
            setCursor(handCursor);
            return;
        }
        switch (Toolbar.getToolId()) {
            case Toolbar.MAGNIFIER:
                if (IJ.isMacintosh())
                    setCursor(defaultCursor);
                else 
                    setCursor(moveCursor);
                break;
            case Toolbar.HAND:
                setCursor(handCursor);
                break;
            default:  //selection tool
                if (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.isHandle(sx, sy)>=0)
                    setCursor(handCursor);
                else if (Prefs.usePointerCursor || (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.contains(ox, oy)))
                    setCursor(defaultCursor);
                else
                    setCursor(crosshairCursor);
        }
    }
        
    /**Converts a screen x-coordinate to an offscreen x-coordinate.*/
    public int offScreenX(int sx) {
        return srcRect.x + (int)(sx/magnification);
    }
        
    /**Converts a screen y-coordinate to an offscreen y-coordinate.*/
    public int offScreenY(int sy) {
        return srcRect.y + (int)(sy/magnification);
    }
    
    /**Converts a screen x-coordinate to a floating-point offscreen x-coordinate.*/
    public double offScreenXD(int sx) {
        return srcRect.x + sx/magnification;
    }
        
    /**Converts a screen y-coordinate to a floating-point offscreen y-coordinate.*/
    public double offScreenYD(int sy) {
        return srcRect.y + sy/magnification;

    }
    
    /**Converts an offscreen x-coordinate to a screen x-coordinate.*/
    public int screenX(int ox) {
        return  (int)((ox-srcRect.x)*magnification);
    }
    
    /**Converts an offscreen y-coordinate to a screen y-coordinate.*/
    public int screenY(int oy) {
        return  (int)((oy-srcRect.y)*magnification);
    }

    /**Converts a floating-point offscreen x-coordinate to a screen x-coordinate.*/
    public int screenXD(double ox) {
        return  (int)((ox-srcRect.x)*magnification);
    }
    
    /**Converts a floating-point offscreen x-coordinate to a screen x-coordinate.*/
    public int screenYD(double oy) {
        return  (int)((oy-srcRect.y)*magnification);
    }

    public double getMagnification() {
        return magnification;
    }
        
    public void setMagnification(double magnification) {
        this.magnification = magnification;
        imp.setTitle(imp.getTitle());
    }
        
    public Rectangle getSrcRect() {
        return srcRect;
    }
    
    /** Enlarge the canvas if the user enlarges the window. */
    void resizeCanvas(int width, int height) {
        if (srcRect.width<imageWidth || srcRect.height<imageHeight) {
            if (width>imageWidth*magnification)
                width = (int)(imageWidth*magnification);
            if (height>imageHeight*magnification)
                height = (int)(imageHeight*magnification);
            setDrawingSize(width, height);
            srcRect.width = (int)(dstWidth/magnification);
            srcRect.height = (int)(dstHeight/magnification);
            if ((srcRect.x+srcRect.width)>imageWidth)
                srcRect.x = imageWidth-srcRect.width;
            if ((srcRect.y+srcRect.height)>imageHeight)
                srcRect.y = imageHeight-srcRect.height;
            repaint();
            //IJ.write("resize: " + magnification + " " + srcRect.x+" "+srcRect.y+" "+srcRect.width+" "+srcRect.height+" "+dstWidth + " " + dstHeight);
        }
    }

    private static final double[] zoomLevels = {
        1/72.0, 1/48.0, 1/32.0, 1/24.0, 1/16.0, 1/12.0, 
        1/8.0, 1/6.0, 1/4.0, 1/3.0, 1/2.0, 0.75, 1.0,
        2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0, 32.0 };
    
    public static double getLowerZoomLevel(double currentMag) {
        double newMag = zoomLevels[0];
        for (int i=0; i<zoomLevels.length; i++) {
        if (zoomLevels[i] < currentMag)
            newMag = zoomLevels[i];
        else
            break;
        }
        return newMag;
    }

    public static double getHigherZoomLevel(double currentMag) {
        double newMag = 32.0;
        for (int i=zoomLevels.length-1; i>=0; i--) {
            if (zoomLevels[i]>currentMag)
                newMag = zoomLevels[i];
            else
                break;
        }
        return newMag;
    }

    /** Zooms in by making the window bigger. If we can't
        make it bigger, then make the srcRect smaller.*/
    public void zoomIn(int x, int y) {
        if (magnification>=32)
            return;
        double newMag = getHigherZoomLevel(magnification);
        int newWidth = (int)(imageWidth*newMag);
        int newHeight = (int)(imageHeight*newMag);
        if (canEnlarge(newWidth, newHeight)) {
            setDrawingSize(newWidth, newHeight);
            imp.getWindow().pack();
        } else {
            int w = (int)Math.round(dstWidth/newMag);
            if (w*newMag<dstWidth) w++;
            int h = (int)Math.round(dstHeight/newMag);
            if (h*newMag<dstHeight) h++;
            x = offScreenX(x);
            y = offScreenY(y);
            Rectangle r = new Rectangle(x-w/2, y-h/2, w, h);
            if (r.x<0) r.x = 0;
            if (r.y<0) r.y = 0;
            if (r.x+w>imageWidth) r.x = imageWidth-w;
            if (r.y+h>imageHeight) r.y = imageHeight-h;
            srcRect = r;
        }
        setMagnification(newMag);
        repaint();
    }
    
    boolean canEnlarge(int newWidth, int newHeight) {
        if ((flags&Event.SHIFT_MASK)!=0 || IJ.shiftKeyDown())
            return false;
        Rectangle r1 = imp.getWindow().getBounds();
        r1.width = newWidth + 20;
        r1.height = newHeight + 50;
        if (imp.getStackSize()>1)
            r1.height += 20;
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        boolean fitsOnScreen = r1.x+r1.width<screen.width && r1.y+r1.height+30<screen.height;
        return fitsOnScreen;
    }
        
    /**Zooms out by making srcRect bigger. If we can't make
    it bigger, then make the window smaller.*/
    public void zoomOut(int x, int y) {
        if (magnification<=0.03125)
            return;
        double newMag = getLowerZoomLevel(magnification);
        //if (newMag==imp.getWindow().getInitialMagnification()) {
        //  unzoom();
        //  return;
        //}
        if (imageWidth*newMag>dstWidth) {
            int w = (int)Math.round(dstWidth/newMag);
            if (w*newMag<dstWidth) w++;
            int h = (int)Math.round(dstHeight/newMag);
            if (h*newMag<dstHeight) h++;
            x = offScreenX(x);
            y = offScreenY(y);
            Rectangle r = new Rectangle(x-w/2, y-h/2, w, h);
            if (r.x<0) r.x = 0;
            if (r.y<0) r.y = 0;
            if (r.x+w>imageWidth) r.x = imageWidth-w;
            if (r.y+h>imageHeight) r.y = imageHeight-h;
            srcRect = r;
        }
        else {
            srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
            setDrawingSize((int)(imageWidth*newMag), (int)(imageHeight*newMag));
            //setDrawingSize(dstWidth/2, dstHeight/2);
            imp.getWindow().pack();
        }
        //IJ.write(newMag + " " + srcRect.x+" "+srcRect.y+" "+srcRect.width+" "+srcRect.height+" "+dstWidth + " " + dstHeight);
        setMagnification(newMag);
        //IJ.write(srcRect.x + " " + srcRect.width + " " + dstWidth);
        repaint();
    }

    public void unzoom() {
        double imag = imp.getWindow().getInitialMagnification();
        if (magnification==imag)
            return;
        setMagnification(imag);
        srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
        ImageWindow win = imp.getWindow();
        setDrawingSize((int)(imageWidth*imag), (int)(imageHeight*imag));
        win.pack();
        repaint();
    }
        
    protected void scroll(int sx, int sy) {
        int ox = xSrcStart + (int)(sx/magnification);  //convert to offscreen coordinates
        int oy = ySrcStart + (int)(sy/magnification);
        //IJ.log("scroll: "+ox+" "+oy+" "+xMouseStart+" "+yMouseStart);
        int newx = xSrcStart + (xMouseStart-ox);
        int newy = ySrcStart + (yMouseStart-oy);
        if (newx<0) newx = 0;
        if (newy<0) newy = 0;
        if ((newx+srcRect.width)>imageWidth) newx = imageWidth-srcRect.width;
        if ((newy+srcRect.height)>imageHeight) newy = imageHeight-srcRect.height;
        srcRect.x = newx;
        srcRect.y = newy;
        //IJ.log(sx+"  "+sy+"  "+newx+"  "+newy+"  "+srcRect);
        imp.draw();
        Thread.yield();
    }   
    
    Color getColor(int index){
        IndexColorModel cm = (IndexColorModel)imp.getProcessor().getColorModel();
        //IJ.write(""+index+" "+(new Color(cm.getRGB(index))));
        return new Color(cm.getRGB(index));
    }
    
    protected void setDrawingColor(int ox, int oy, boolean setBackground) {
        //IJ.write("setDrawingColor: "+setBackground+this);
        int type = imp.getType();
        int[] v = imp.getPixel(ox, oy);
        switch (type) {
            case ImagePlus.GRAY8: {
                if (setBackground)
                    setBackgroundColor(getColor(v[0]));
                else
                    setForegroundColor(getColor(v[0]));
                break;
            }
            case ImagePlus.GRAY16: case ImagePlus.GRAY32: {
                double min = imp.getProcessor().getMin();
                double max = imp.getProcessor().getMax();
                double value = (type==ImagePlus.GRAY32)?Float.intBitsToFloat(v[0]):v[0];
                int index = (int)(255.0*((value-min)/(max-min)));
                if (index<0) index = 0;
                if (index>255) index = 255;
                if (setBackground)
                    setBackgroundColor(getColor(index));
                else
                    setForegroundColor(getColor(index));
                break;
            }
            case ImagePlus.COLOR_RGB: case ImagePlus.COLOR_256: {
                Color c = new Color(v[0], v[1], v[2]);
                if (setBackground)
                    setBackgroundColor(c);
                else
                    setForegroundColor(c);
                break;
            }
        }
        Color c;
        if (setBackground)
            c = Toolbar.getBackgroundColor();
        else {
            c = Toolbar.getForegroundColor();
            imp.setColor(c);
        }
        IJ.showStatus("("+c.getRed()+", "+c.getGreen()+", "+c.getBlue()+")");
    }
    
    private void setForegroundColor(Color c) {
        Toolbar.setForegroundColor(c);
        if (Recorder.record)
            Recorder.record("setForegroundColor", c.getRed(), c.getGreen(), c.getBlue());
    }

    private void setBackgroundColor(Color c) {
        Toolbar.setBackgroundColor(c);
        if (Recorder.record)
            Recorder.record("setBackgroundColor", c.getRed(), c.getGreen(), c.getBlue());
    }

    public void mousePressed(MouseEvent e) {
        if (ij==null) return;
        int toolID = Toolbar.getToolId();
        ImageWindow win = imp.getWindow();
        if (win!=null && win.running2 && toolID!=Toolbar.MAGNIFIER) {
            win.running2 = false;
            return;
        }
        
        int x = e.getX();
        int y = e.getY();
        flags = e.getModifiers();
        //IJ.log("Mouse pressed: " + e.isPopupTrigger() + "  " + ij.modifiers(flags));      
        //if (toolID!=Toolbar.MAGNIFIER && e.isPopupTrigger()) {
        if (toolID!=Toolbar.MAGNIFIER && (e.isPopupTrigger() || (flags & Event.META_MASK)!=0)) {
            handlePopupMenu(e);
            return;
        }

        int ox = offScreenX(x);
        int oy = offScreenY(y);
        xMouse = ox; yMouse = oy;
        if (IJ.spaceBarDown()) {
            // temporarily switch to "hand" tool of space bar down
            setupScroll(ox, oy);
            return;
        }
        //if ((flags&Event.ALT_MASK)!=0 && toolID!=Toolbar.MAGNIFIER && toolID!=Toolbar.DROPPER) {
            // temporarily switch to color tool alt/option key down
            //setDrawingColor(ox, oy, false);
            //return;
        //}
        switch (toolID) {
            case Toolbar.MAGNIFIER:
                if ((flags & (Event.ALT_MASK|Event.META_MASK|Event.CTRL_MASK))!=0)
                    zoomOut(x, y);
                else
                    zoomIn(x, y);
                break;
            case Toolbar.HAND:
                setupScroll(ox, oy);
                break;
            case Toolbar.DROPPER:
                setDrawingColor(ox, oy, IJ.altKeyDown());
                break;
            case Toolbar.WAND:
                Roi roi = imp.getRoi();
                if (roi!=null && roi.contains(ox, oy)) {
                    Rectangle r = roi.getBounds();
                    if (r.width==imageWidth && r.height==imageHeight)
                        imp.killRoi();
                    else if (!e.isAltDown()) {
                        handleRoiMouseDown(e);
                        return;
                    }
                }
                if (roi!=null) {
                    int handle = roi.isHandle(x, y);
                    if (handle>=0) {
                        roi.mouseDownInHandle(handle, x, y);
                        return;
                    }
                }
                setRoiModState(e, roi, -1);
                int npoints = IJ.doWand(ox, oy);
                if (Recorder.record && npoints>0)
                    Recorder.record("doWand", ox, oy);
                break;
            case Toolbar.SPARE1: case Toolbar.SPARE2: case Toolbar.SPARE3: 
            case Toolbar.SPARE4: case Toolbar.SPARE5: case Toolbar.SPARE6:
            case Toolbar.SPARE7:
                Toolbar.getInstance().runMacroTool(toolID);
                break;
            default:  //selection tool
                handleRoiMouseDown(e);
        }
    }

    protected void setupScroll(int ox, int oy) {
        xMouseStart = ox;
        yMouseStart = oy;
        xSrcStart = srcRect.x;
        ySrcStart = srcRect.y;
    }

    protected void handlePopupMenu(MouseEvent e) {
        if (IJ.debugMode) IJ.log("show popup: " + (e.isPopupTrigger()?"true":"false"));
        int x = e.getX();
        int y = e.getY();
        Roi roi = imp.getRoi();
        if (roi!=null && (roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE)
        && roi.getState()==roi.CONSTRUCTING) {
            roi.handleMouseUp(x, y); // simulate double-click to finalize
            roi.handleMouseUp(x, y); // polygon or polyline selection
            return;
        }
        PopupMenu popup = Menus.getPopupMenu();
        if (popup!=null) {
            add(popup);
            if (IJ.isMacOSX()) IJ.wait(10);
            popup.show(this, x, y);
        }
    }
    
    public void mouseExited(MouseEvent e) {
        //autoScroll(e);
        ImageWindow win = imp.getWindow();
        if (win!=null)
            setCursor(defaultCursor);
        IJ.showStatus("");
    }

    /*
    public void autoScroll(MouseEvent e) {
        Roi roi = imp.getRoi();
        if (roi==null || roi.getState()!=roi.CONSTRUCTING || srcRect.width>=imageWidth || srcRect.height>=imageHeight
        || !(roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE))
            return;
        int sx = e.getX();
        int sy = e.getY();
        xMouseStart = srcRect.x+srcRect.width/2;
        yMouseStart = srcRect.y+srcRect.height/2;
        Rectangle r = roi.getBounds();
        Dimension size = getSize();
        int deltax=0, deltay=0;
        if (sx<0)
            deltax = srcRect.width/4;
        else if (sx>size.width)
            deltax = -srcRect.width/4;
        if (sy<0)
            deltay = srcRect.height/4;
        else if (sy>size.height)
            deltay = -srcRect.height/4;
        //IJ.log("autoscroll: "+sx+" "+sy+" "+deltax+" "+deltay+" "+r);
        scroll(screenX(xMouseStart+deltax), screenY(yMouseStart+deltay));
    }
    */

    public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        xMouse = offScreenX(x);
        yMouse = offScreenY(y);
        flags = e.getModifiers();
        //IJ.log("mouseDragged: "+flags);
        if (flags==0)  // workaround for Mac OS 9 bug
            flags = InputEvent.BUTTON1_MASK;
        if (Toolbar.getToolId()==Toolbar.HAND || IJ.spaceBarDown())
            scroll(x, y);
        else {
            IJ.setInputEvent(e);
            Roi roi = imp.getRoi();
            if (roi != null)
                roi.handleMouseDrag(x, y, flags);
        }
    }

    void handleRoiMouseDown(MouseEvent e) {
        int sx = e.getX();
        int sy = e.getY();
        int ox = offScreenX(sx);
        int oy = offScreenY(sy);
        Roi roi = imp.getRoi();
        int handle = roi!=null?roi.isHandle(sx, sy):-1;     
        setRoiModState(e, roi, handle);
        if (roi!=null) {
            Rectangle r = roi.getBounds();
            int type = roi.getType();
            if (type==Roi.RECTANGLE && r.width==imp.getWidth() && r.height==imp.getHeight()
            && roi.getPasteMode()==Roi.NOT_PASTING) {
                imp.killRoi();
                return;
            }
            if (handle>=0) {
                roi.mouseDownInHandle(handle, sx, sy);
                return;
            }
            if (roi.contains(ox, oy)) {
                if (roi.modState==Roi.NO_MODS)
                    roi.handleMouseDown(sx, sy);
                else {
                    imp.killRoi();
                    imp.createNewRoi(sx,sy);
                }
                return;
            }
            if ((type==Roi.POLYGON || type==Roi.POLYLINE || type==Roi.ANGLE)
            && roi.getState()==roi.CONSTRUCTING)
                return;
            if (Toolbar.getToolId()==Toolbar.POLYGON && !(IJ.shiftKeyDown()||IJ.altKeyDown())) {
                imp.killRoi();
                return;
            }
        }
        imp.createNewRoi(sx,sy);
    }
    
    void setRoiModState(MouseEvent e, Roi roi, int handle) {
        if (roi==null || (handle>=0 && roi.modState==Roi.NO_MODS))
            return;
        if (roi.state==Roi.CONSTRUCTING)
            return;
        int tool = Toolbar.getToolId();
        if (tool>Toolbar.FREEROI && tool!=Toolbar.WAND && tool!=Toolbar.POINT)
            {roi.modState = Roi.NO_MODS; return;}
        if (e.isShiftDown())
            roi.modState = Roi.ADD_TO_ROI;
        else if (e.isAltDown())
            roi.modState = Roi.SUBTRACT_FROM_ROI;
        else
            roi.modState = Roi.NO_MODS;
        //IJ.log("setRoiModState: "+roi.modState+" "+ roi.state);
    }

    public void mouseReleased(MouseEvent e) {
        flags = e.getModifiers();
        flags &= ~InputEvent.BUTTON1_MASK; // make sure button 1 bit is not set
        flags &= ~InputEvent.BUTTON2_MASK; // make sure button 2 bit is not set
        flags &= ~InputEvent.BUTTON3_MASK; // make sure button 3 bit is not set
        Roi roi = imp.getRoi();
        if (roi != null) {
            Rectangle r = roi.getBounds();
            int type = roi.getType();
            if ((r.width==0 || r.height==0)
            && !(type==Roi.POLYGON || type==Roi.POLYLINE || type==Roi.ANGLE)
            && !(roi instanceof TextRoi)
            && roi.getState()==roi.CONSTRUCTING
            && type!=roi.POINT)
                imp.killRoi();
            else
                roi.handleMouseUp(e.getX(), e.getY());
        }
    }

    public void mouseMoved(MouseEvent e) {
        if (ij==null) return;
        int sx = e.getX();
        int sy = e.getY();
        int ox = offScreenX(sx);
        int oy = offScreenY(sy);
        flags = e.getModifiers();
        setCursor(sx, sy, ox, oy);
        IJ.setInputEvent(e);
        Roi roi = imp.getRoi();
        if (roi!=null && (roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE) 
        && roi.getState()==roi.CONSTRUCTING) {
            PolygonRoi pRoi = (PolygonRoi)roi;
            pRoi.handleMouseMove(ox, oy);
        } else {
            if (ox<imageWidth && oy<imageHeight) {
                ImageWindow win = imp.getWindow();
                if (win!=null) win.mouseMoved(ox, oy);
            } else
                IJ.showStatus("");

        }
    }
    
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}

}