/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.display.axes;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import org.opensourcephysics.display.DrawingPanel;
import org.opensourcephysics.display.Interactive;
import org.opensourcephysics.display.PlottingPanel;
import org.opensourcephysics.display.Selectable;
import org.opensourcephysics.display.dialogs.DialogsRes;
import org.opensourcephysics.media.core.ScientificField;
import org.opensourcephysics.tools.FontSizer;
import org.opensourcephysics.tools.ResourceLoader;

/**
 * A cartesian axis class that provides interactive scale adjustment with a mouse.
 */
public class CartesianInteractive extends CartesianType1 implements Selectable {
  // static constant plot regions
  public static final int INSIDE = 0, HORZ_MIN = 1, HORZ_MAX = 2, VERT_MIN = 3, 
  	VERT_MAX = 4, HORZ_AXIS = 5, HORZ_AXIS_MIN = 6, HORZ_AXIS_MAX = 7, 
  	VERT_AXIS = 8, VERT_AXIS_MIN = 9, VERT_AXIS_MAX = 10, HORZ_VAR = 11, VERT_VAR = 12;
  // instance fields
  Rectangle hitRect = new Rectangle();
  boolean drawHitRect;
  ScaleSetter scaleSetter;
  JPanel scaleSetterPanel;
  AxisMouseListener axisListener;
  int mouseRegion;
  Point mouseLoc;
  double mouseX, mouseY;
  PlottingPanel plot;
  boolean enabled = true;
  boolean altDown;
  Cursor horzCenter, horzRight, horzLeft, vertCenter, vertUp, vertDown, move;

  /**
   * Constructs a set of interactive axes for a plotting panel.
   *
   * @param panel the PlottingPanel
   */
  public CartesianInteractive(PlottingPanel panel) {
    super(panel);
    plot = panel;
    axisListener = new AxisMouseListener();
    panel.addMouseListener(axisListener);
    panel.addMouseMotionListener(axisListener);
    panel.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyPressed(java.awt.event.KeyEvent e) {
        if((mouseRegion==INSIDE)&&!drawingPanel.isFixedScale()&&(e.getKeyCode()==java.awt.event.KeyEvent.VK_ALT)) {
          altDown = true;
          plot.setMouseCursor(getPreferredCursor());
        }
      }
      public void keyReleased(java.awt.event.KeyEvent e) {
        if(e.getKeyCode()==java.awt.event.KeyEvent.VK_ALT) {
          altDown = false;
          plot.setMouseCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        }
      }

    });
    scaleSetter = new ScaleSetter();
    // create transparent scaleSetterPanel with no LayoutManager
    scaleSetterPanel = new javax.swing.JPanel(null);
    scaleSetterPanel.setOpaque(false);
    scaleSetterPanel.add(scaleSetter);
    plot.getGlassPanel().add(scaleSetterPanel, BorderLayout.CENTER);
  }

  /**
   * Gets the current plot region containing the mouse.
   *
   * @return one of the static plot regions defined by CartesianInteractive, or -1
   */
  public int getMouseRegion() {
    return mouseRegion;
  }

  /**
   * Draws the axes.
   *
   * @param panel the drawing panel
   * @param g the graphics context
   */
  public void draw(DrawingPanel panel, Graphics g) {
    super.draw(panel, g);
    if(drawHitRect) {
      g.drawRect(hitRect.x, hitRect.y, hitRect.width, hitRect.height);
    }
    if(!panel.isFixedScale()&&scaleSetter.isVisible()&&(scaleSetter.scaleField.getBackground()!=Color.yellow)) {
      switch(scaleSetter.region) {
         case HORZ_MIN :
           scaleSetter.scaleField.setValue(drawingPanel.getXMin());
           scaleSetter.autoscaleCheckbox.setSelected(drawingPanel.isAutoscaleXMin());
           break;
         case HORZ_MAX :
           scaleSetter.scaleField.setValue(drawingPanel.getXMax());
           scaleSetter.autoscaleCheckbox.setSelected(drawingPanel.isAutoscaleXMax());
           break;
         case VERT_MIN :
           scaleSetter.scaleField.setValue(drawingPanel.getYMin());
           scaleSetter.autoscaleCheckbox.setSelected(drawingPanel.isAutoscaleYMin());
           break;
         case VERT_MAX :
           scaleSetter.scaleField.setValue(drawingPanel.getYMax());
           scaleSetter.autoscaleCheckbox.setSelected(drawingPanel.isAutoscaleYMax());
      }
    }
  }

  // overrides CartesianType1 method
  public double getX() {
    return Double.isNaN(mouseX) ? plot.pixToX(plot.getMouseIntX()) : mouseX;
  }

  // overrides CartesianType1 method
  public double getY() {
    return Double.isNaN(mouseY) ? plot.pixToY(plot.getMouseIntY()) : mouseY;
  }

  // implements Selectable
  public void setSelected(boolean selectable) {}

  // implements Selectable
  public boolean isSelected() {
    return false;
  }

  // implements Selectable
  public void toggleSelected() {}

  // implements Selectable
  public Cursor getPreferredCursor() {
    switch(mouseRegion) {
       case HORZ_AXIS_MIN :
         if(horzLeft==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/horzleft.gif";                     //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           horzLeft = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Horizontal Left"); //$NON-NLS-1$
         }
         return horzLeft;
       case HORZ_AXIS_MAX :
         if(horzRight==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/horzright.gif";                      //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           horzRight = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Horizontal Right"); //$NON-NLS-1$
         }
         return horzRight;
       case HORZ_AXIS :
         if(horzCenter==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/horzcenter.gif";                       //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           horzCenter = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Horizontal Center"); //$NON-NLS-1$
         }
         return horzCenter;
       case VERT_AXIS_MIN :
         if(vertDown==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/vertdown.gif";                   //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           vertDown = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Vertical Down"); //$NON-NLS-1$
         }
         return vertDown;
       case VERT_AXIS_MAX :
         if(vertUp==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/vertup.gif";                         //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           vertUp = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Vertical Up");         //$NON-NLS-1$
         }
         return vertUp;
       case VERT_AXIS :
         if(vertCenter==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/vertcenter.gif";                     //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           vertCenter = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Vertical Center"); //$NON-NLS-1$
         }
         return vertCenter;
       case INSIDE :
         if(move==null) {
           // create cursor
           String imageFile = "/org/opensourcephysics/resources/tools/images/movecursor.gif";             //$NON-NLS-1$
           Image im = ResourceLoader.getImage(imageFile);
           move = Toolkit.getDefaultToolkit().createCustomCursor(im, new Point(16, 16), "Move All Ways"); //$NON-NLS-1$
         }
         return move;
       case HORZ_VAR :
       case VERT_VAR :
         return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
    }
    return Cursor.getDefaultCursor();
  }

  // implements Interactive
  public boolean isEnabled() {
    return enabled;
  }

  // implements Interactive
  public void setEnabled(boolean enable) {
    enabled = enable;
  }

  // implements Interactive
  public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) {
    if(drawingPanel.isFixedScale()) {
      return null;
    }
    if(mouseRegion>=HORZ_MIN) {
      return this;
    }
    if(mouseRegion==-1) {
      return this;
    }
    if((mouseRegion==INSIDE)&&altDown) {
      return this;
    }
    return null;
  }

  // implements Interactive
  public void setXY(double x, double y) {}

  // implements Measurable
  public boolean isMeasured() {
    return true;
  }

  // implements Measurable
  public double getXMin() {
    return drawingPanel.getXMin();
  }

  // implements Measurable
  public double getXMax() {
    return drawingPanel.getXMax();
  }

  // implements Measurable
  public double getYMin() {
    return drawingPanel.getYMin();
  }

  // implements Measurable
  public double getYMax() {
    return drawingPanel.getYMax();
  }

  /**
   * Hides the scale setter.
   */
  public void hideScaleSetter() {
    if(scaleSetter!=null) {
      scaleSetter.autoscaleCheckbox.requestFocusInWindow();
      scaleSetter.setVisible(false);
    }
  }

  /**
   * Resizes fonts by the specified factor.
   *
   * @param factor the factor
   * @param panel the drawing panel on which these axes are drawn
   */
  public void resizeFonts(double factor, DrawingPanel panel) {
    super.resizeFonts(factor, panel);
    if(scaleSetter!=null) {
      scaleSetter.scaleField.setFont(FontSizer.getResizedFont(scaleSetter.scaleField.getFont(), factor));
      scaleSetter.autoscaleCheckbox.setFont(FontSizer.getResizedFont(scaleSetter.autoscaleCheckbox.getFont(), factor));
    }
  }

  /**
   * Reports whether this provides a popup menu for setting the horizontal axis variable.
   *
   * @return true if this has a popup menu with horizontal axis variables
   */
  protected boolean hasHorzVariablesPopup() {
    return false;
  }

  /**
   * Gets a popup menu with horizontal axis variables.
   * This default method returns null; subclasses should override to return
   * a popup with associated action for setting horizontal axis variable.
   *
   * @return the popup menu
   */
  protected JPopupMenu getHorzVariablesPopup() {
    return null;
  }

  /**
   * Reports whether this provides a popup menu for setting the vertical axis variable.
   *
   * @return true if this has a popup menu with vertical axis variables
   */
  protected boolean hasVertVariablesPopup() {
    return false;
  }

  /**
   * Gets a popup menu with vertical axis variables.
   * This default method returns null; subclasses should override to return
   * a popup with associated action for setting vertical axis variable.
   *
   * @return the popup menu
   */
  protected JPopupMenu getVertVariablesPopup() {
    return null;
  }

  /**
   * Finds the plot region containing the specified point.
   *
   * @param p the point
   * @return one of the static regions defined by CartesianInteractive
   */
  protected int findRegion(Point p) {
    int l = drawingPanel.getLeftGutter();
    int r = drawingPanel.getRightGutter();
    int t = drawingPanel.getTopGutter();
    int b = drawingPanel.getBottomGutter();
    Dimension plotDim = drawingPanel.getSize();
    // horizontal axis
    int axisLen = plotDim.width-r-l;
    hitRect.setSize(axisLen/4, 12);
    hitRect.setLocation(l+axisLen/2-hitRect.width/2, plotDim.height-b-hitRect.height/2);
    if(hitRect.contains(p)) {
      return HORZ_AXIS;
    }
    hitRect.setLocation(l+4, plotDim.height-b-hitRect.height/2);
    if(hitRect.contains(p)) {
      return HORZ_AXIS_MIN;
    }
    hitRect.setLocation(l+axisLen-hitRect.width-4, plotDim.height-b-hitRect.height/2);
    if(hitRect.contains(p)) {
      return HORZ_AXIS_MAX;
    }
    // vertical axis
    axisLen = plotDim.height-t-b;
    hitRect.setSize(12, axisLen/4);
    hitRect.setLocation(l-hitRect.width/2, t+axisLen/2-hitRect.height/2);
    if(hitRect.contains(p)) {
      return VERT_AXIS;
    }
    hitRect.setLocation(l-hitRect.width/2, t+4);
    if(hitRect.contains(p)) {
      return VERT_AXIS_MAX;
    }
    hitRect.setLocation(l-hitRect.width/2, t+axisLen-hitRect.height-4);
    if(hitRect.contains(p)) {
      return VERT_AXIS_MIN;
    }
    // horizontal variable
    Graphics g = drawingPanel.getGraphics();
    int w = xLine.getWidth(g)+8;
    int h = xLine.getHeight(g);
    hitRect.setSize(w, h);
    int x = (int) (xLine.getX()-w/2);
    int y = (int) (xLine.getY()-h/2-xLine.getFontSize()/3);
    hitRect.setLocation(x, y);
    if(hitRect.contains(p)&&hasHorzVariablesPopup()) {
      return HORZ_VAR;
    }
    // vertical variable: drawn sideways, so width<->height reversed
    w = yLine.getHeight(g);
    h = yLine.getWidth(g)+8;
    hitRect.setSize(w, h);
    x = (int) (yLine.getX()-w/2-yLine.getFontSize()/3);
    y = (int) (yLine.getY()-h/2-1);
    hitRect.setLocation(x, y);
    if(hitRect.contains(p)&&hasVertVariablesPopup()) {
      return VERT_VAR;
    }
    // inside
    if(!((p.x<l)||(p.y<t)||(p.x>plotDim.width-r)||(p.y>plotDim.height-b))) {
      return INSIDE;
    }
    // scale setter regions
    ScientificField field = scaleSetter.scaleField;
    Dimension fieldDim = field.getPreferredSize();
    hitRect.setSize(fieldDim);
    double xmin = drawingPanel.getXMin();
    double xmax = drawingPanel.getXMax();
    double ymin = drawingPanel.getYMin();
    double ymax = drawingPanel.getYMax();
    int offset = 8; // approx distance from axis to hitRect for scale setter 
    // horizontal min
    hitRect.setLocation(l-12, plotDim.height-b+6+offset);
    if(hitRect.contains(p)) {
      Point hitLoc = hitRect.getLocation(); // relative to plotPanel 
      scaleSetter.add(scaleSetter.autoscaleCheckbox, BorderLayout.NORTH);
      scaleSetter.validate();
      Point fieldLoc = field.getLocation(); // relative to scaleSetter
      Dimension size = scaleSetter.getPreferredSize();
      scaleSetter.setBounds(hitLoc.x-fieldLoc.x, hitLoc.y-fieldLoc.y-offset, size.width, size.height);
      return HORZ_MIN;
    }
    // horizontal max
    hitRect.setLocation(plotDim.width-r-fieldDim.width+12, plotDim.height-b+6+offset);
    if(hitRect.contains(p)) {
      field.setExpectedRange(xmin, xmax);
      Point hitLoc = hitRect.getLocation(); // relative to plotPanel  
      scaleSetter.add(scaleSetter.autoscaleCheckbox, BorderLayout.NORTH);
      scaleSetter.validate();
      Point fieldLoc = field.getLocation(); // relative to scaleSetter
      Dimension size = scaleSetter.getPreferredSize();
      scaleSetter.setBounds(hitLoc.x-fieldLoc.x, hitLoc.y-fieldLoc.y-offset, size.width, size.height);
      return HORZ_MAX;
    }
    // vertical min
    hitRect.setLocation(l-fieldDim.width-1-offset, plotDim.height-b-fieldDim.height+8);
    if(hitRect.contains(p)) {
      field.setExpectedRange(ymin, ymax);
      Point hitLoc = hitRect.getLocation(); // relative to plotPanel  
      scaleSetter.add(scaleSetter.autoscaleCheckbox, BorderLayout.EAST);
      scaleSetter.validate();
      Point fieldLoc = field.getLocation(); // relative to scaleSetter
      int minLoc = hitLoc.x-fieldLoc.x;
      Dimension size = scaleSetter.getPreferredSize();
      scaleSetter.setBounds(Math.max(minLoc, 1-fieldLoc.x), hitLoc.y-fieldLoc.y, size.width, size.height);
      return VERT_MIN;
    }
    // vertical max
    hitRect.setLocation(l-fieldDim.width-1-offset, t-8);
    if(hitRect.contains(p)) {
      field.setExpectedRange(ymin, ymax);
      Point hitLoc = hitRect.getLocation(); // relative to plotPanel  
      scaleSetter.add(scaleSetter.autoscaleCheckbox, BorderLayout.EAST);
      scaleSetter.validate();
      Point fieldLoc = field.getLocation(); // relative to scaleSetter
      int minLoc = hitLoc.x-fieldLoc.x;
      Dimension size = scaleSetter.getPreferredSize();
      scaleSetter.setBounds(Math.max(minLoc, 1-fieldLoc.x), hitLoc.y-fieldLoc.y, size.width, size.height);
      return VERT_MAX;
    }
    return -1;
  }

  /**
   * Gets the scale setter.
   *
   * @return the ScaleSetter dialog
   */
  public ScaleSetter getScaleSetter() {
    // refresh autoscale checkbox text if needed (eg, new Locale)
    String s = DialogsRes.SCALE_AUTO;
    if(!s.equals(scaleSetter.autoscaleCheckbox.getText())) {
      scaleSetter.autoscaleCheckbox.setText(s);
    }
    return scaleSetter;
  }

  /**
   * A dialog with value field and autoscale checkbox.
   */
  public class ScaleSetter extends JPanel {
    Action scaleAction;
    JCheckBox autoscaleCheckbox;
    ScientificField scaleField;
    int region;             // determines which axis and end are active
    boolean pinned = false; // prevents hiding this when true

    private ScaleSetter() {
      super(new BorderLayout());
      scaleAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          scaleField.setBackground(Color.white);
          pinned = false;
          boolean auto = autoscaleCheckbox.isSelected();
          boolean horzAxis = true;
          double min = auto ? Double.NaN : scaleField.getValue();
          double max = min;
          switch(region) {
             case HORZ_MIN :
               max = drawingPanel.isAutoscaleXMax()? Double.NaN: drawingPanel.getXMax();
               break;
             case HORZ_MAX :
               min = drawingPanel.isAutoscaleXMin()? Double.NaN: drawingPanel.getXMin();
               break;
             case VERT_MIN :
               horzAxis = false;
               max = drawingPanel.isAutoscaleYMax()? Double.NaN: drawingPanel.getYMax();
               break;
             case VERT_MAX :
               horzAxis = false;
               min = drawingPanel.isAutoscaleYMin()? Double.NaN: drawingPanel.getYMin();
          }
          if(horzAxis) {
            drawingPanel.setPreferredMinMaxX(min, max);
          } else {
            drawingPanel.setPreferredMinMaxY(min, max);
          }
          Rectangle bounds = drawingPanel.getBounds();
          bounds.setLocation(0, 0);
          drawingPanel.paintImmediately(bounds);
        }

      };
      autoscaleCheckbox = new JCheckBox();
      autoscaleCheckbox.setBorder(BorderFactory.createEmptyBorder(1, 2, 2, 1));
      autoscaleCheckbox.setBackground(drawingPanel.getBackground());
      autoscaleCheckbox.setHorizontalTextPosition(SwingConstants.RIGHT);
      autoscaleCheckbox.addActionListener(scaleAction);
      scaleField = new ScientificField(6, 3) {
        public Dimension getPreferredSize() {
          Dimension dim = super.getPreferredSize();
          dim.width -= 4;
          return dim;
        }

      };
      scaleField.addActionListener(new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          autoscaleCheckbox.setSelected(false);
          scaleAction.actionPerformed(null);
        }

      });
      scaleField.addFocusListener(new FocusAdapter() {
        public void focusLost(FocusEvent e) {
          if(scaleField.getBackground()==Color.yellow) {
            autoscaleCheckbox.setSelected(false);
            scaleAction.actionPerformed(null);
          }
        }

      });
      scaleField.addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
          pinned = true;
          if(e.getClickCount()==2) {
            scaleField.selectAll();
          }
        }

      });
      add(scaleField, BorderLayout.CENTER);
    }

    void hideIfInactive() {
      if((scaleField.getBackground()!=Color.yellow)&&(scaleField.getSelectedText()==null)&&!pinned) {
        hideScaleSetter();
      }
    }

    void setRegion(int mouseRegion) {
      if(region!=mouseRegion) {
        autoscaleCheckbox.requestFocusInWindow();
        if(scaleField.getBackground()==Color.yellow) {
          autoscaleCheckbox.setSelected(false);
          scaleAction.actionPerformed(null);
        }
        region = mouseRegion;
        pinned = false;
        scaleField.select(20, 20); // clears selection and places caret at end
        scaleField.requestFocusInWindow();
      }
    }

  }

  /**
   * A mouse listener for handling interactivity
   */
  class AxisMouseListener extends javax.swing.event.MouseInputAdapter {
    public void mouseMoved(MouseEvent e) {
    	plot.requestFocusInWindow();
      altDown = e.isAltDown();
      Point p = e.getPoint();
      mouseRegion = findRegion(p);
      if((mouseRegion>INSIDE)&&(mouseRegion<HORZ_AXIS)&&!drawingPanel.isFixedScale()) {
        getScaleSetter().setRegion(mouseRegion);
        scaleSetter.validate();
        scaleSetter.setVisible(true);
      } else {
        scaleSetter.hideIfInactive();
      }
      drawHitRect = ((mouseRegion==HORZ_VAR)||(mouseRegion==VERT_VAR));
      plot.repaint();
    }

    public void mouseDragged(MouseEvent e) {
      double dx = 0, dy = 0, min = 0, max = 0;
      switch(mouseRegion) {
         case INSIDE :
           if(!altDown||drawingPanel.isFixedScale()) {
             return;
           }
           dx = (mouseLoc.x-e.getX())/plot.getXPixPerUnit();
           min = plot.getXMin()+dx;
           max = plot.getXMax()+dx;
           dx = 0;
           plot.setPreferredMinMaxX(min, max);
           dy = (e.getY()-mouseLoc.y)/plot.getYPixPerUnit();
           min = plot.getYMin()+dy;
           max = plot.getYMax()+dy;
           break;
         case HORZ_AXIS :
           dx = (mouseLoc.x-e.getX())/plot.getXPixPerUnit();
           min = plot.getXMin()+dx;
           max = plot.getXMax()+dx;
           break;
         case HORZ_AXIS_MIN :
           dx = 2*(mouseLoc.x-e.getX())/plot.getXPixPerUnit();
           min = plot.getXMin()+dx;
           max = plot.isAutoscaleXMax() ? Double.NaN : plot.getXMax();
           break;
         case HORZ_AXIS_MAX :
           dx = 2*(mouseLoc.x-e.getX())/plot.getXPixPerUnit();
           min = plot.isAutoscaleXMin() ? Double.NaN : plot.getXMin();
           max = plot.getXMax()+dx;
           break;
         case VERT_AXIS :
           dy = (e.getY()-mouseLoc.y)/plot.getYPixPerUnit();
           min = plot.getYMin()+dy;
           max = plot.getYMax()+dy;
           break;
         case VERT_AXIS_MIN :
           dy = 2*(e.getY()-mouseLoc.y)/plot.getYPixPerUnit();
           min = plot.getYMin()+dy;
           max = plot.isAutoscaleYMax() ? Double.NaN : plot.getYMax();
           break;
         case VERT_AXIS_MAX :
           dy = 2*(e.getY()-mouseLoc.y)/plot.getYPixPerUnit();
           min = plot.isAutoscaleYMin() ? Double.NaN : plot.getYMin();
           max = plot.getYMax()+dy;
           break;
      }
      if(dx!=0) {
        plot.setPreferredMinMaxX(min, max);
      } else if(dy!=0) {
        plot.setPreferredMinMaxY(min, max);
      }
      plot.invalidateImage();
      plot.repaint();
      mouseLoc = e.getPoint();
    }

    public void mousePressed(MouseEvent e) {
      altDown = e.isAltDown();
      mouseLoc = e.getPoint();
      mouseX = plot.pixToX(plot.getMouseIntX());
      mouseY = plot.pixToY(plot.getMouseIntY());
      mouseRegion = findRegion(mouseLoc);
      if(scaleSetter==null) {
        return;
      }
      if((mouseRegion>INSIDE)&&(mouseRegion<HORZ_AXIS)&&!drawingPanel.isFixedScale()) {
        scaleSetter.setVisible(true);
        return;
      }
      hideScaleSetter();
      if(mouseRegion==HORZ_VAR) {
        drawHitRect = false;
        getHorzVariablesPopup().show(plot, mouseLoc.x-20, mouseLoc.y-12);
      } else if(mouseRegion==VERT_VAR) {
        drawHitRect = false;
        getVertVariablesPopup().show(plot, mouseLoc.x-20, mouseLoc.y-12);
      }
      plot.repaint();
    }

    public void mouseReleased(MouseEvent e) {
      mouseX = Double.NaN;
      mouseY = Double.NaN;
    }

    public void mouseExited(MouseEvent e) {
      Point p = e.getPoint();
      if(!new Rectangle(plot.getSize()).contains(p)&&(scaleSetter!=null)&&"".equals(InputEvent.getModifiersExText(e.getModifiersEx()))) { //$NON-NLS-1$
        hideScaleSetter();
      }
    }

  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
