Package ui

Source Code of ui.Display

package ui;

import classes.Layer;
import classes.Project;
import classes.ProjectElement;
import enums.EditingMode;
import enums.SelectionType;
import enums.ToolType;
import helpers.Broadcaster;
import helpers.CursorProvider;
import interfaces.CurrentLayerChangedListener;
import interfaces.ProjectChangedListener;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.RectangularShape;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import roadnetwork.Edge;
import roadnetwork.RoadNetwork;
import roadnetwork.RoadNetworkElement;
import trafficdefinition.Accident;
import trafficdefinition.AreaFlow;
import trafficdefinition.Flow;
import trafficdefinition.Handle;
import trafficdefinition.HotSpot;
import trafficdefinition.School;
import trafficdefinition.TrafficArea;
import trafficdefinition.TrafficDefinitionElement;
import trafficdefinition.TrafficDefinitionLayer;

/**
* @author  PapaleonLe01
*/
public class Display extends JComponent implements CurrentLayerChangedListener, ProjectChangedListener, MouseListener, MouseMotionListener, MouseWheelListener, AdjustmentListener, KeyListener {

  private static final long serialVersionUID = 7162061244639610972L;

  private JScrollBar scrlVertical = null;

  private JScrollBar scrlHorizontal = null;

  /**
   * The currently selected traffic layer
   */
  private TrafficDefinitionLayer currentTrafficLayer;

  /**
   * The current project's road network
   */
  private RoadNetwork roadNetwork;

  /**
   * Flag that states whether a project is open
   */
  private boolean projectLoaded = false;

  /**
   * The horizontal map translation
   */
  private double translateX;

  /**
   * The vertical map translation
   */
  private double translateY;

  /**
   * The minimum and maximum allowe transalation values
   */
  private double mintranslateX;
  /**
   * The minimum and maximum allowe transalation values
   */
  private double maxtranslateX;
  /**
   * The minimum and maximum allowe transalation values
   */
  private double mintranslateY;
  /**
   * The minimum and maximum allowe transalation values
   */
  private double maxtranslateY;

  /**
   * Scaling factor (zoom in - out)
   */
  private double scale;

  private Point previousMouseLocation;

  private boolean areaZoom;

  /**
   * The current tool
   */
  private ToolType tool = ToolType.None;

  /**
   * The current selection type
   */
  private SelectionType selectionType = SelectionType.New;

  /**
   * Helper variable to store a new element during its creation process
   */
  private TrafficDefinitionElement newElement;

  /**
   * Flag to know whether to draw all traffic layers or just the currently
   * selected one
   */
  private boolean drawAllTrafficLayers = false;

  private GeneralPath areaPath;

  private boolean creatingNewElement = false;

  private Handle currentHandle = null;

  private Rectangle marquee;

  private Point marqueeStart;

  private Point marqueeEnd;

  /**
   * The transformation used while drawing the map and traffic layers
   */
  private AffineTransform tx = new AffineTransform();

  private EditingMode editingMode;

  private boolean manipulatingHandle = false;

  /**
   * Default constructor.
   *
   * @param scrlvertical
   * @param scrlhorizontal
   */
  public Display(JScrollBar scrlvertical, JScrollBar scrlhorizontal) {

    scrlVertical = scrlvertical;
    scrlHorizontal = scrlhorizontal;

    // Initialize transformation variables
    translateX = 0;
    translateY = 0;
    scale = 1;

    setOpaque(true);
    setDoubleBuffered(true);

    // Register event handlers
    addMouseListener(this);
    addMouseMotionListener(this);
    addMouseWheelListener(this);
    addKeyListener(this);

    scrlVertical.addAdjustmentListener(this);
    scrlHorizontal.addAdjustmentListener(this);

    // Be notified when the user opens or closes the project
    Broadcaster.addProjectChangedListener(this);
  }

  /**
   * Event handler for scroll bar events. Sets the translation variables so
   * that the map moves when the user scrolls
   */
  public void adjustmentValueChanged(AdjustmentEvent e) {
    // Horizontal scrolling
    if (e.getSource() == scrlHorizontal) {
      translateX = Math.max(maxtranslateX, mintranslateX) - e.getValue() + Math.min(maxtranslateX, mintranslateX);
      repaint();
    }
    // Vertical scrolling
    else if (e.getSource() == scrlVertical) {
      translateY = Math.max(maxtranslateY, mintranslateY) - e.getValue() + Math.min(maxtranslateY, mintranslateY);
      repaint();
    }

  }

  /**
   * Handler for the event of the current traffic layer changed.
   */
  public void CurrentLayerChanged(Layer<? extends ProjectElement> newCurrentLayer) {
    // Deselect all elements before changing the layer
    if (currentTrafficLayer != null) {
      currentTrafficLayer.SelectNoneElements();
    }

    if (roadNetwork != null) {
      roadNetwork.SelectNoneElements();
    }

    // If the user has selected the map layer then hide any previously
    // selected traffic layer
    if (newCurrentLayer instanceof RoadNetwork) {
      setEditingMode(EditingMode.Map);

      currentTrafficLayer = null;
    } else if (newCurrentLayer instanceof TrafficDefinitionLayer) {
      currentTrafficLayer = (TrafficDefinitionLayer) newCurrentLayer;

      setEditingMode(EditingMode.GenericTraffic);
    }

    repaint();
  }

  /**
   * Deletes the selected elements of the currently editing map or traffic
   * layer
   *
   */
  public void DeleteSelectedObjects() {
    switch (editingMode) {
    case Map:
      roadNetwork.DeleteSelectedElements();
      break;
    case GenericTraffic:
      currentTrafficLayer.DeleteSelectedElements();
    }

    repaint();
  }

  /**
   * Updates the mouse cursor when a key is pressed, or deletes selected
   * elements when the delete key is pressed,or cancels the creation of a new
   * object if the Esc key was pressed
   */
  public void keyPressed(KeyEvent e) {
    if (!projectLoaded) {
      return;
    }

    // Delete the selected elements if the Delete key was pressed
    if (e.getKeyCode() == KeyEvent.VK_DELETE) {
      DeleteSelectedObjects();

      return;
    }
    // Cancel the creation of a new element if the Escape key was pressed
    else if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) && (creatingNewElement)) {
      creatingNewElement = false;
      newElement = null;
      repaint();

      return;
    }

    if ((tool == ToolType.Select) && (e.getKeyCode() == KeyEvent.VK_CONTROL)) {
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.SelectAdd));

      return;
    }

    if ((tool == ToolType.Select) && (e.getKeyCode() == KeyEvent.VK_SHIFT)) {
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.SelectRemove));

      return;
    }

    if ((tool == ToolType.Zoom) && (e.getKeyCode() == KeyEvent.VK_CONTROL)) {
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.ZoomOut));

      return;
    }
  }

  /**
   * Updates the mouse cursor when a key is released
   */
  public void keyReleased(KeyEvent e) {
    if (!projectLoaded) {
      return;
    }

    if ((tool == ToolType.Select) && ((e.getKeyCode() == KeyEvent.VK_CONTROL) || (e.getKeyCode() == KeyEvent.VK_SHIFT))) {
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Select));

      return;
    }

    if ((tool == ToolType.Zoom) && (e.getKeyCode() == KeyEvent.VK_CONTROL)) {
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.ZoomIn));

      return;
    }
  }

  public void keyTyped(KeyEvent e) {
  }

  public void mouseClicked(MouseEvent e) {
    if (!projectLoaded) {
      return;
    }

    // Transform the point to map coordinates
    Point2D.Double transformedPoint = TransformPoint(e.getPoint());

    if (transformedPoint == null) {
      return;
    }

    switch (tool) {
    // Create school
    case School:
      if (e.getButton() == MouseEvent.BUTTON1) {
        currentTrafficLayer.AddTrafficElement(new School(transformedPoint));
      }

      repaint();
      break;
    case Hotspot:
      // If we are in the middle of creating a new hotspot and the user
      // right clicks then cancel the creation
      if ((creatingNewElement) && (e.getButton() == MouseEvent.BUTTON3)) {
        creatingNewElement = false;
        newElement = null;
        repaint();

        return;
      }

      // If this is the second click (we were creating a hot spot)
      if (creatingNewElement) {
        // Finalize
        ((HotSpot) newElement).createHandles();

        // Add the hotspot to the layer
        currentTrafficLayer.AddTrafficElement(newElement);

        // Reset helper variables
        newElement = null;
        creatingNewElement = false;

        repaint();
      }
      // If this is the first click
      else {
        // Create a temporary hotspot and set its center
        newElement = new HotSpot(transformedPoint);

        creatingNewElement = true;
      }

      return;
    case AreaFlow:
      // If we are in the middle of creating a new area flow and the user
      // right clicks then cancel the creation
      if ((creatingNewElement) && (e.getButton() == MouseEvent.BUTTON3)) {
        creatingNewElement = false;
        newElement = null;
        repaint();

        return;
      }

      // If we are in the middle of creating an area flow
      if (creatingNewElement) {
        AreaFlow newFlow = (AreaFlow) newElement;

        newFlow.constructionPhase++;

        // If the construction has finished (4 clicks needed)
        if (newFlow.constructionPhase == 3) {
          newFlow.createHandles();

          currentTrafficLayer.AddTrafficElement(newElement);

          // Reset helper variables
          creatingNewElement = false;
          newElement = null;

          repaint();
        }
      }
      // First click - construction beginning
      else {
        // Create temporary area flow and set its starting location
        newElement = new AreaFlow();
        ((AreaFlow) newElement).setStartAreaCenter(transformedPoint);

        ((AreaFlow) newElement).constructionPhase = 0;

        creatingNewElement = true;
      }

      return;
    case Flow:
      // Get the map element below the mouse
      RoadNetworkElement elementBelowMouse = roadNetwork.PointHitTest(transformedPoint);

      // If we are in the middle of creating a new flow and the user right
      // clicks or clicks somewhere that is not an edge cancel the
      // operation
      if ((creatingNewElement) && ((e.getButton() == MouseEvent.BUTTON3) || (elementBelowMouse == null) || !(elementBelowMouse instanceof Edge))) {
        creatingNewElement = false;
        newElement = null;
        repaint();

        return;
      }

      if ((elementBelowMouse != null) && (elementBelowMouse instanceof Edge)) {
        // Set the flow's ending point and add it to the traffic layer
        if (creatingNewElement) {
          ((Flow) newElement).setTo(new Point2D.Double(elementBelowMouse.getBounds().getCenterX(), elementBelowMouse.getBounds().getCenterY()));

          ((Flow) newElement).setEnd((Edge) elementBelowMouse);

          ((Flow) newElement).SetDependencies();

          currentTrafficLayer.AddTrafficElement(newElement);

          creatingNewElement = false;
          newElement = null;

          repaint();
        }
        // Start creating the flow
        else {
          newElement = new Flow();
          ((Flow) newElement).setStart((Edge) elementBelowMouse);
          ((Flow) newElement).setFrom(new Point2D.Float((float) elementBelowMouse.getBounds().getCenterX(), (float) elementBelowMouse.getBounds().getCenterY()));

          creatingNewElement = true;
        }
      }

      return;
    case Accident:
      // Get the map element below the mouse
      RoadNetworkElement elementBelowMouse2 = roadNetwork.PointHitTest(transformedPoint);

      if ((elementBelowMouse2 != null) && (elementBelowMouse2 instanceof Edge) && (e.getButton() == MouseEvent.BUTTON1)) {
        currentTrafficLayer.AddTrafficElement(new Accident((Edge) elementBelowMouse2, transformedPoint));
      }

      repaint();

      break;
    case Select:
      switch (editingMode) {
      case Map:
        if (e.isControlDown()) {
          roadNetwork.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), SelectionType.Add);
        } else if (e.isShiftDown()) {
          roadNetwork.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), SelectionType.Remove);
        } else {
          roadNetwork.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), selectionType);
        }

        repaint();

        break;
      case GenericTraffic:
        if (e.isControlDown()) {
          currentTrafficLayer.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), SelectionType.Add);
        } else if (e.isShiftDown()) {
          currentTrafficLayer.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), SelectionType.Remove);
        } else {
          currentTrafficLayer.AlterSelection(new Rectangle((int) Math.round(transformedPoint.x), (int) Math.round(transformedPoint.y), 1, 1), selectionType);
        }

        repaint();

        break;
      }
      break;
    case Zoom:
      // Zoom out
      if (e.isControlDown()) {
        // If the map fits in the display then disable further zoom out
        if ((getWidth() > roadNetwork.getBounds().width * scale) && (getHeight() > roadNetwork.getBounds().height * scale)) {
          return;
        } else {
          scale -= 0.5;
        }
      }
      // Zoom in
      else {
        scale += 0.5;
      }

      // Move the previous center of the map to the center of the display
      MoveMapPointToDisplayCenter(transformedPoint);

      UpdateScrollBars();

      repaint();

      break;
    }
  }

  public void mouseDragged(MouseEvent e) {
    if (!projectLoaded) {
      return;
    }

    // Transform from device coordinates to map coordinates
    Point2D.Double transformedPoint = TransformPoint(e.getPoint());

    if (transformedPoint == null) {
      return;
    }

    switch (tool) {
    case Pan:
      // Update the translation variables
      translateX += e.getX() - previousMouseLocation.getX();
      translateY += e.getY() - previousMouseLocation.getY();

      // Save the new mouse position
      previousMouseLocation = e.getPoint();

      UpdateScrollBars();

      repaint();

      break;
    case Zoom:
      // Marquee zooming
      areaZoom = true;

      // Update the marquee
      marqueeEnd = e.getPoint();

      marquee.setFrameFromDiagonal(marqueeStart, marqueeEnd);

      repaint();

      break;
    case Select:
      // If we are moving a handle then change the handle's location
      if (manipulatingHandle) {
        currentHandle.setLocationAndNotify(transformedPoint);
      }
      // If we are creating a marquee
      else {
        areaZoom = true;

        // Update the marquee
        marqueeEnd = e.getPoint();

        marquee.setFrameFromDiagonal(marqueeStart, marqueeEnd);
      }
      repaint();

      break;
    case Area:
      // If we are creating a traffic area then update its path
      areaPath.lineTo((float) transformedPoint.x, (float) transformedPoint.y);

      repaint();

      break;
    }

  }

  /**
   * Set the focus on the display so that we can catch key events
   */
  public void mouseEntered(MouseEvent e) {
    setFocusable(true);
    requestFocus(true);
  }

  public void mouseExited(MouseEvent e) {
    manipulatingHandle = false;
  }

  public void mouseMoved(MouseEvent e) {
    if (!projectLoaded) {
      return;
    }

    // Transform the mouse's location to map coordinates
    Point2D.Double transformedPoint = TransformPoint(e.getPoint());

    if (transformedPoint == null) {
      return;
    }

    // Find if an element is below the mouse location to show its tooltip
    TrafficDefinitionElement trafficElementBelowMouse = null;
    RoadNetworkElement roadElementBelowMouse = roadNetwork.PointHitTest(transformedPoint);

    if (currentTrafficLayer != null) {
      trafficElementBelowMouse = currentTrafficLayer.PointHitTest(transformedPoint);
    }

    // Set the tooltip of the element below the mouse
    if (trafficElementBelowMouse != null) {
      setToolTipText(trafficElementBelowMouse.getToolTip());
    } else if (roadElementBelowMouse != null) {
      setToolTipText(roadElementBelowMouse.getToolTip());
    } else {
      setToolTipText(null);
    }

    switch (tool) {
    case Select:
      // Check to see if the mouse is over a selected element's handle
      currentHandle = null;

      boolean overHandle = false;

      if (currentTrafficLayer != null) {
        List<TrafficDefinitionElement> selectedTrafficElements = currentTrafficLayer.getSelectedElements();

        for (TrafficDefinitionElement t : selectedTrafficElements) {
          currentHandle = t.handleHit(e.getPoint(), tx);

          if (currentHandle != null) {
            overHandle = true;
            break;
          }
        }
      }

      // If it is, update its cursor
      if (overHandle) {
        this.setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Hand));
      } else {
        this.setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Select));
      }
      break;
    case Hotspot:
      // Set the hotspot's radius
      if (creatingNewElement) {
        HotSpot newHotSpot = (HotSpot) newElement;

        newHotSpot.setRadius(transformedPoint);

        repaint();
      }

      break;
    case AreaFlow:
      // Set the flow's properties based on the construction phase
      if (creatingNewElement) {
        AreaFlow newFlow = (AreaFlow) newElement;

        switch (newFlow.constructionPhase) {
        case 0:
          newFlow.setStartAreaRadius(transformedPoint);
          break;
        case 1:
          newFlow.setEndAreaCenter(transformedPoint);
          break;
        case 2:
          newFlow.setEndAreaRadius(transformedPoint);
          break;
        }

        repaint();
      }

      break;
    case Flow:
      // Update the cursor if the mouse is over an edge
      if ((roadElementBelowMouse != null) && (roadElementBelowMouse instanceof Edge)) {
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
      } else {
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }

      if (!creatingNewElement) {
        return;
      }

      // Show flow while creating it
      ((Flow) newElement).setTo(transformedPoint);

      repaint();

      break;
    case Accident:
      // Update the cursor if the mouse is over an edge
      if ((roadElementBelowMouse != null) && (roadElementBelowMouse instanceof Edge)) {
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
      } else {
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }

      break;
    }
  }

  public void mousePressed(MouseEvent e) {
    if (!projectLoaded) {
      return;
    }

    // Capture starting point
    previousMouseLocation = e.getPoint();

    // Transform the point to map coordinates
    Point2D.Double transformedPoint = TransformPoint(e.getPoint());

    if (transformedPoint == null) {
      return;
    }

    switch (tool) {
    case Select:

      // If the mouse was over a handle then start manipulating it
      if (currentHandle != null) {
        this.setCursor(CursorProvider.getCursor(CursorProvider.Cursors.HandClosed));

        manipulatingHandle = true;
      } else {
        // If the user is making a new selection first deselect all
        // previous elements
        if ((selectionType == SelectionType.New) && (!e.isShiftDown()) && (!e.isControlDown())) {
          switch (editingMode) {
          case Map:
            roadNetwork.SelectNoneElements();
            break;
          case GenericTraffic:
            currentTrafficLayer.SelectNoneElements();
            break;
          }
        }

        areaZoom = false;

        // Create the marquee rectangle
        marquee = new Rectangle();
        marqueeStart = e.getPoint();

        manipulatingHandle = false;
      }
      break;
    case Zoom:
      areaZoom = false;

      // Create the marquee rectangle
      marquee = new Rectangle();
      marqueeStart = e.getPoint();

      break;
    case Pan:
      // Update cursor
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.HandClosed));

      break;
    case Area:
      areaPath = new GeneralPath();

      areaPath.moveTo((float) transformedPoint.x, (float) transformedPoint.y);

      break;
    }
  }

  public void mouseReleased(MouseEvent e) {
    if (!projectLoaded) {
      return;
    }

    switch (tool) {
    case Pan:
      // Restore cursor
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Hand));
      break;
    case Zoom:
      if ((!areaZoom) || (marquee.height * marquee.width < 10)) {
        return;
      }

      // Transform the marquee to map coordinates
      Point2D.Double transformedMarqueeStart = TransformPoint(marqueeStart);
      Point2D.Double transformedMarqueeEnd = TransformPoint(marqueeEnd);

      if ((transformedMarqueeStart == null) || (transformedMarqueeEnd == null)) {
        return;
      }

      Rectangle transformedRectangle = new Rectangle();
      transformedRectangle.setFrameFromDiagonal(transformedMarqueeStart, transformedMarqueeEnd);

      // Zoom the map to the rectangle specified by the marquee
      ZoomToRectangleAndCenter(transformedRectangle);

      UpdateScrollBars();

      marquee = null;

      repaint();

      break;
    case Select:
      // Release the handle if we were manipulating it
      if (manipulatingHandle) {
        manipulatingHandle = false;
      } else {
        if ((!areaZoom) || (marquee.height * marquee.width < 10)) {
          return;
        }

        // Transform the marquee to map coordinates
        Point2D.Double transformedMarqueeStart1 = TransformPoint(marqueeStart);
        Point2D.Double transformedMarqueeEnd1 = TransformPoint(marqueeEnd);

        if ((transformedMarqueeStart1 == null) || (transformedMarqueeEnd1 == null)) {
          return;
        }

        Rectangle transformedRectangle1 = new Rectangle();
        transformedRectangle1.setFrameFromDiagonal(transformedMarqueeStart1, transformedMarqueeEnd1);

        // Select the current layer's elements based on the selection
        // type and the marquee
        switch (editingMode) {
        case Map:
          if (e.isControlDown()) {
            roadNetwork.AlterSelection(transformedRectangle1, SelectionType.Add);
          } else if (e.isShiftDown()) {
            roadNetwork.AlterSelection(transformedRectangle1, SelectionType.Remove);
          } else {
            roadNetwork.AlterSelection(transformedRectangle1, selectionType);
          }

          repaint();

          break;
        case GenericTraffic:
          if (e.isControlDown()) {
            currentTrafficLayer.AlterSelection(transformedRectangle1, SelectionType.Add);
          } else if (e.isShiftDown()) {
            currentTrafficLayer.AlterSelection(transformedRectangle1, SelectionType.Remove);
          } else {
            currentTrafficLayer.AlterSelection(transformedRectangle1, selectionType);
          }

          repaint();

          break;
        }

        marquee = null;

        repaint();
      }

      break;
    case Area:
      // Finalize the area's shape
      areaPath.closePath();

      currentTrafficLayer.AddTrafficElement(new TrafficArea(areaPath));

      areaPath = null;

      repaint();
      break;
    }

  }

  /**
   * Zooms in or out when the user scrolls the mouse wheel
   */
  public void mouseWheelMoved(MouseWheelEvent e) {
    if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {

      // If we are zooming out
      if (e.getWheelRotation() < 0) {
        // If the map fits in the display then disable further zoom out
        if ((getWidth() > roadNetwork.getBounds().width * scale) && (getHeight() > roadNetwork.getBounds().height * scale)) {
          return;
        }
      }

      Point2D.Double transformedPoint = TransformPoint(e.getPoint());

      if (transformedPoint == null) {
        return;
      }

      // Make it a reasonable amount of zoom
      // 0.01 gives a nice slow transition
      scale += 0.01 * e.getWheelRotation();

      MoveMapPointToDisplayPoint(transformedPoint, e.getPoint());

      UpdateScrollBars();

      repaint();
    }
  }

  /**
   * Moves the map point (expressed in map coordinates) to the specified
   * display point (expressed in device coordinates)
   *
   * @param mapPoint
   * @param displayPoint
   */
  private void MoveMapPointToDisplayPoint(Point2D.Double mapPoint, Point displayPoint) {
    translateX = displayPoint.x - (mapPoint.x * scale);
    translateY = displayPoint.y + (mapPoint.y * scale);
  }

  /**
   * Translates the map so that the specified point is at the center of the
   * display
   *
   * @param mapPoint
   */
  public void MoveMapPointToDisplayCenter(Point2D.Double mapPoint) {
    MoveMapPointToDisplayPoint(mapPoint, new Point(getWidth() / 2, getHeight() / 2));
  }

  /**
   * Responsible for the drawing of the component. Draws the map and traffic
   * layers.
   */
  @Override
  public void paintComponent(Graphics g) {
    Graphics2D graphics2D = (Graphics2D) g;

    // Get the clip bounds
    Rectangle clip = graphics2D.getClipBounds();

    // If only part of the component needs repainting then repaint the whole
    // display anyway.
    // No mechanism yet for painting only part of the component.
    if ((clip.x != 0) || (clip.y != 0)) {
      repaint();
      return;
    }

    // Draw gray background
    graphics2D.setColor(Color.GRAY);
    graphics2D.fillRect(0, 0, getWidth(), getHeight());

    // If no project is loaded don't continue
    if (!projectLoaded) {
      return;
    }

    // Set transformation matrix (for pan and zoom)
    tx.setToIdentity();
    tx.translate(translateX, translateY);

    // Minus is for y-axis reversal
    tx.scale(scale, -scale);

    graphics2D.setTransform(tx);

    // Set rendering hints
    graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    // Draw white page
    graphics2D.setColor(Color.WHITE);
    graphics2D.fill(roadNetwork.getBounds());

    // Draw page border
    graphics2D.setColor(Color.BLACK);
    graphics2D.draw(roadNetwork.getBounds());

    // Draw the road network
    roadNetwork.Draw(graphics2D, scale);

    if(drawAllTrafficLayers){
      // Draw all the project traffic definition layers
      for (TrafficDefinitionLayer tdl : Project.getCurrentlyLoadedProject().getTrafficLayers()) {
        tdl.Draw(graphics2D, scale);
      }
    }
    else{
       //Draw the current traffic definition layer
       if (currentTrafficLayer != null) {
         currentTrafficLayer.Draw(graphics2D, scale);
       }
    }
   
    // Draw the currently creating new element
    if (newElement != null) {
      graphics2D.setColor(Color.RED);
      newElement.Draw(graphics2D, scale, false, false);
    }

    // Draw area
    if (areaPath != null) {
      graphics2D.setColor(new Color(120, 65, 87, 125));
      graphics2D.fill(areaPath);
    }

    // Draw marquee
    if (marquee != null) {
      graphics2D.setTransform(new AffineTransform());
      graphics2D.setColor(Color.RED);
      graphics2D.draw(marquee);
    }
  }

  /**
   * Handler for the event of the project change.
   *
   * @param project
   *            the newly loaded project or null if the project was closed
   */
  public void ProjectChanged(Project project) {
    // If the new project is null it means that the project was closed.
    if (project == null) {
      projectLoaded = false;

      roadNetwork = null;
      currentTrafficLayer = null;

      repaint();
    }
    // Else it means the project was opened or created
    else {
      projectLoaded = true;

      roadNetwork = project.getRoadNetwork();

      ZoomToFitAndCenter();

      UpdateScrollBars();

      setEditingMode(EditingMode.Map);
    }
  }

  /**
   * Repaints the display
   *
   */

  // Refreshes the display
  public void Refresh() {
    repaint();
  }

  /**
   * Selects all elements on the currently editing map or traffic layer
   *
   */
  public void SelectAll() {
    switch (editingMode) {
    case Map:
      roadNetwork.SelectAllElements();
      break;
    case GenericTraffic:
      currentTrafficLayer.SelectAllElements();
    }

    repaint();
  }

  /**
   * Inverts the selection on the currently editing map or traffic layer
   *
   */
  public void SelectInverse() {
    switch (editingMode) {
    case Map:
      roadNetwork.SelectInverseElements();
      break;
    case GenericTraffic:
      currentTrafficLayer.SelectInverseElements();
      break;
    }

    repaint();
  }

  /**
   * Deselects all elements on the currently editing map or traffic layer
   *
   */
  public void SelectNone() {
    switch (editingMode) {
    case Map:
      roadNetwork.SelectNoneElements();
      break;
    case GenericTraffic:
      currentTrafficLayer.SelectNoneElements();
      break;
    }

    repaint();

  }

  /**
   * Sets the editing mode (Map or Traffic)
   * @param  newMode
   * @uml.property  name="editingMode"
   */
  public void setEditingMode(EditingMode newMode) {
    editingMode = newMode;
  }

  /**
   * Sets the type of selection (New, Add or Remove)
   * @param  selectionType
   * @uml.property  name="selectionType"
   */
  public void setSelectionType(SelectionType selectionType) {
    this.selectionType = selectionType;
  }

  /**
   * Shows or hides the map's deleted elements
   *
   * @param b
   */
  public void setShowDeleted(boolean b) {
    if (!projectLoaded) {
      return;
    }

    roadNetwork.setShowDeleted(b);

    repaint();
  }

  /**
   * Sets the drawAllTrafficLayers flag
   * @param  b
   * @uml.property  name="drawAllTrafficLayers"
   */
  public void setDrawAllTrafficLayers(boolean b){
    drawAllTrafficLayers = b;
  }
 
  /**
   * Sets the current tool
   * @param  tool
   * @uml.property  name="tool"
   */
  public void setTool(ToolType tool) {
    this.tool = tool;

    // Set the cursor based on the tool
    switch (tool) {
    case Pan:
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Hand));
      break;
    case Zoom:
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.ZoomIn));
      break;
    case Select:
      setCursor(CursorProvider.getCursor(CursorProvider.Cursors.Select));
      break;
    default:
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      break;
    }
  }

  /**
   * Undeletes the selected deleted elements of the map
   *
   */
  public void UndeleteSelectedObjects() {
    roadNetwork.UndeleteSelectedMapElements();

    repaint();
  }

  /**
   * Centers the map on the display and zooms so that the whole map fits in
   * the display.
   *
   */
  public void ZoomToFitAndCenter() {
    if (!projectLoaded) {
      return;
    }

    ZoomToRectangleAndCenter(roadNetwork.getBounds());

    UpdateScrollBars();

    repaint();
  }

  /**
   * Transforms the specified point from device coordinates to map coordinates
   *
   * @param p
   *            the point to transform
   * @return the transformed point
   */
  private Point2D.Double TransformPoint(Point p) {
    try {
      return (Point2D.Double) tx.inverseTransform(new Point2D.Double(p.getX(), p.getY()), null);
    } catch (NoninvertibleTransformException e) {
      return null;
    }
  }

  /**
   * Updates the scrollbars based on the scaling and translation variables
   *
   */
  private void UpdateScrollBars() {
    mintranslateX = (getWidth() / 2) - (roadNetwork.getBounds().getMinX() * scale);
    maxtranslateX = (getWidth() / 2) - (roadNetwork.getBounds().getMaxX() * scale);

    mintranslateY = (getHeight() / 2) + (roadNetwork.getBounds().getMinY() * scale);
    maxtranslateY = (getHeight() / 2) + (roadNetwork.getBounds().getMaxY() * scale);

    scrlVertical.removeAdjustmentListener(this);

    scrlVertical.setMinimum((int) Math.min(mintranslateY, maxtranslateY));
    scrlVertical.setMaximum((int) Math.max(mintranslateY, maxtranslateY));

    scrlVertical.setValue((int) -translateY + (int) Math.max(maxtranslateY, mintranslateY) + (int) Math.min(maxtranslateY, mintranslateY));

    scrlVertical.addAdjustmentListener(this);

    scrlHorizontal.removeAdjustmentListener(this);

    scrlHorizontal.setMinimum((int) Math.min(mintranslateX, maxtranslateX));
    scrlHorizontal.setMaximum((int) Math.max(mintranslateX, maxtranslateX));

    scrlHorizontal.setValue((int) -translateX + (int) Math.max(maxtranslateX, mintranslateX) + (int) Math.min(maxtranslateX, mintranslateX));

    scrlHorizontal.addAdjustmentListener(this);
  }

  /**
   * Zooms the map so that it fits in the specified rectangle and centers it
   * on the display
   *
   * @param rectangle
   */
  private void ZoomToRectangleAndCenter(RectangularShape rectangle) {
    // Fit in height
    if (rectangle.getHeight() > rectangle.getWidth()) {
      scale = (getHeight()) / rectangle.getHeight();
    }
    // Fit in width
    else if (rectangle.getHeight() < rectangle.getWidth()) {
      scale = (getWidth()) / rectangle.getWidth();
    }
    // If the map is square then fit it based on the display's minimum
    // dimension
    else {
      int minDimension = Math.min(getWidth(), getHeight());

      scale = (minDimension) / rectangle.getWidth();
    }

    // Move map to the center of the display
    MoveMapPointToDisplayCenter(new Point2D.Double(rectangle.getCenterX(), rectangle.getCenterY()));
  }

  /**
   * Zooms and centers on the specified element. This method is called when
   * the user selects an element from the property panel combo box
   *
   * @param element
   */
  public void CenterAndZoomOnElement(ProjectElement element) {
    // Move map to the center of the display
    MoveMapPointToDisplayCenter(new Point2D.Double(element.getBounds().getCenterX(), element.getBounds().getCenterY()));

    // TODO: Zooming correctly
    // ZoomToRectangleAndCenter(element.getBounds());

    UpdateScrollBars();

    repaint();
  }
}
TOP

Related Classes of ui.Display

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.