<<<<<<< HEAD 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(); } } ======= 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(); } } >>>>>>> origin/abdalla