/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: ClickDragZoomListener.java * * Copyright (c) 2003 Sun Microsystems and Static Free Software * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.user.ui; import com.sun.electric.database.EditingPreferences; import com.sun.electric.database.geometry.Dimension2D; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.geometry.DBMath; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.network.Netlist; import com.sun.electric.database.network.Network; import com.sun.electric.database.prototype.PortProto; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Connection; import com.sun.electric.database.topology.Geometric; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.topology.RTBounds; import com.sun.electric.database.variable.ElectricObject; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.DRCTemplate; import com.sun.electric.technology.Layer; import com.sun.electric.technology.Technology; import com.sun.electric.tool.Client; import com.sun.electric.tool.drc.DRC; import com.sun.electric.tool.routing.InteractiveRouter; import com.sun.electric.tool.routing.SimpleWirer; import com.sun.electric.tool.user.CircuitChanges; import com.sun.electric.tool.user.Highlight; import com.sun.electric.tool.user.Highlighter; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.UserInterfaceMain; import com.sun.electric.tool.user.menus.EditMenu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; 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.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.EventListener; import java.util.Iterator; import java.util.List; import java.util.prefs.Preferences; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; /** * Handles Selection, Zooming, and Wiring. * <p>The Left Mouse Button handles Selection and Moving * <p>The Right Mouse Button handles Zooming and Wiring * <p>The Mouse Wheel handles panning * * User: gainsley * Date: Feb 19, 2004 * Time: 2:53:33 PM */ public class ClickZoomWireListener implements MouseMotionListener, MouseListener, MouseWheelListener, KeyListener, ActionListener { private static Preferences prefs = Preferences.userNodeForPackage(ClickZoomWireListener.class); private long cancelMoveDelayMillis; /* cancel move delay in milliseconds */ private long zoomInDelayMillis; /* zoom in delay in milliseconds */ // private static boolean useFatWiringMode; private boolean interactiveDRCDrag; private static final boolean debug = false; /* for debugging */ public static ClickZoomWireListener theOne = new ClickZoomWireListener(); private int clickX, clickY; /* current mouse pressed coords in screen space */ private int lastX, lastY; /* last mouse pressed coords in screen space (for panning) */ private Cell startCell; private double dbMoveStartX, dbMoveStartY; /* left mouse pressed coords for move in database space */ private double lastdbMouseX, lastdbMouseY; /* last location of mouse */ private Mode modeLeft = Mode.none; /* left mouse button context mode */ private Mode modeRight = Mode.none; /* right mouse button context mode */ private boolean specialSelect = false; /* if hard-to-select objects are to be selected */ private boolean invertSelection = false; /* if invert selection */ private boolean another; //private List underCursor = null; /* objects in popupmenu */ //private String lastPopupMenu = null; /* last used popupmenu */ private long leftMousePressedTimeStamp; /* log of last left mouse pressed time */ private long rightMousePressedTimeStamp; /* log of last left mouse pressed time */ private ElectricObject wiringTarget; /* last highlight user switched to possibly wire to */ // wiring stuff private InteractiveRouter router; /* router used to connect objects */ private ElectricObject startObj; /* object routing from */ private ElectricObject endObj; /* object routing to */ private ArcProto currentArcWhenWiringPressed; /* current arc proto when mouse pressed to draw wire: later actions of this tool may change current arc */ private int mouseX, mouseY; /* last known location of mouse */ private Highlight moveDelta; /* highlight to display move delta */ private Highlight moveDRC; /* highlight to display design rules during a move */ private EventListener oldListener; /* used when swtiching back to old listener */ // mac stuff private static final boolean isMac = Client.isOSMac(); /** Class Mode lets us set a common mode over several types of events, * letting initial events (like a right mouse click) set the context for * later events (such as pressing the CTRL button). */ private static class Mode { private final String name; public Mode(String name) { this.name = name; } public String toString() { return name; } public static final Mode none = new Mode("none"); // no context public static final Mode move = new Mode("move"); // moving objects public static final Mode stickyMove = new Mode("stickyMove"); // second move mode public static final Mode drawBox = new Mode("drawBox"); // drawing a box public static final Mode zoomBox = new Mode("zoomBox"); // drawing a box to zoom to public static final Mode zoomBoxSingleShot = new Mode("zoomBoxSingleShot"); // drawing a box to zoom to public static final Mode zoomIn = new Mode("zoomIn"); // zoom in mode public static final Mode zoomOut = new Mode("zoomOut"); // zoom in mode public static final Mode selectBox = new Mode("selectBox"); // box for selection public static final Mode wiringConnect = new Mode("wiring"); // drawing a wire between two objects public static final Mode wiringFind = new Mode("wiringFind"); // drawing wire with unknown end point public static final Mode wiringToSpace = new Mode("wiringToSpace"); // only draw wire to space, not objects public static final Mode stickyWiring = new Mode("stickyWiring"); // sticky mode wiring } /** Constructor is private */ private ClickZoomWireListener() { readPrefs(); } /** Set ClickZoomWireListener to include hard to select objects */ public void setSpecialSelect() { specialSelect = true; } /** Set ClickZoomWireListener to exclude hard to select objects */ public void clearSpecialSelect() { specialSelect = false; } /** * Returns state of 'stickyMove'. * If sticky move is true, after the user clicks and drags to * move an object, the user can release the mouse button and the * object will continue to move with the mouse. Clicking the * select mouse key again will place the object. * If sticky move is false, the user must hold and drag to move * the object. Letting go of the select mouse key will place the * object. This is the C-Electric style. * @return state of preference 'stickyMove' */ public boolean getStickyMove() { // for now just return true. // TODO: make it a preference return false; } public void setRouter(InteractiveRouter router) { this.router = router; } /** * Returns state of 'stickyWiring'. * If sticky wiring is true, after the user clicks and drags to * draw a wire, the user can release the mouse button and the UI * will remain in wire-draw mode. Click the mouse button again * will draw the wire. * If sticky wiring is false, the user must hold and drag to * draw the tentative wire, and the wire gets drawn when the * user releases the mouse button. This is C-Electric style. * @return state of preference 'stickyWiring' */ public boolean getStickyWiring() { // for now just return true // TODO: make it a preference return true; } /** * Return the last known location of the mouse. Note that these are * screen coords, and are in the coordinate system of the current container(?). * @return a Point2D containing the last mouse coords. */ public Point2D getLastMouse() { return new Point2D.Double(mouseX, mouseY); } /** * Sets the mode to zoom box for the next right click only. */ public void zoomBoxSingleShot(EventListener oldListener) { modeRight = Mode.zoomBoxSingleShot; modeLeft = Mode.zoomBoxSingleShot; this.oldListener = oldListener; } /** * See if event is a left mouse click. Platform independent. */ private boolean isLeftMouse(MouseEvent evt) { if (isMac) { if (!evt.isMetaDown()) { if ((evt.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) return true; } } else { if ((evt.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) return true; } return false; } /** * See if event is a right mouse click. Platform independent. * One-button macs: Command + click == right mouse click. */ public static boolean isRightMouse(InputEvent evt) { if (isMac) { if (evt.isMetaDown()) { if ((evt.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) return true; } if ((evt.getModifiers() & MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK) return true; } else { if ((evt.getModifiers() & MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK) return true; } return false; } /** * See if event is a middle mouse click. Platform independent. */ public static boolean isMiddleMouse(InputEvent evt) { if ((evt.getModifiers() & MouseEvent.BUTTON2_MASK) == MouseEvent.BUTTON2_MASK) return true; return false; } /** Handle mouse press events. * <p>Left Mouse Click: Select * <p>Left Mouse Drag: Move Objects (or select area if not on object) * <p>Left Mouse Double-Click: Get Info * <p>CTRL + Left Mouse Click: Cycle through select * <p>SHIFT + Left Mouse Click: invert selection * <p>Right Mouse Click/Drag: Connect wire * <p>SHIFT + Right Mouse Click: zoom out * <p>SHIFT + Right Mouse Drag: zoom in * <p>CTRL + SHIFT + Right Mouse Click: draw box * @param evt the MouseEvent * */ public void mousePressed(MouseEvent evt) { if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); long currentTime = System.currentTimeMillis(); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Highlighter highlighter = wnd.getHighlighter(); startCell = wnd.getCell(); if (startCell == null) return; clickX = evt.getX(); clickY = evt.getY(); Point2D dbClick = wnd.screenToDatabase(clickX, clickY); lastdbMouseX = dbClick.getX(); lastdbMouseY = dbClick.getY(); boolean ctrlPressed = (evt.getModifiersEx()&MouseEvent.CTRL_DOWN_MASK) != 0; invertSelection = (evt.getModifiersEx()&MouseEvent.SHIFT_DOWN_MASK) != 0; specialSelect = ToolBar.isSelectSpecial(); // ===== right mouse clicks ===== if (isRightMouse(evt)) { rightMousePressedTimeStamp = currentTime; if (modeRight == Mode.zoomBoxSingleShot) { // We will zoom to box wnd.setStartDrag(clickX, clickY); wnd.setEndDrag(clickX, clickY); wnd.setDoingAreaDrag(); return; } // draw possible wire connection if (!invertSelection) { // ignore anything that can't have a wire drawn to it // (everything except nodes, ports, and arcs) List<Highlight> highlights = new ArrayList<Highlight>(); for (Highlight h : highlighter.getHighlights()) { if (h.isHighlightEOBJ()) { ElectricObject eobj = h.getElectricObject(); if (eobj instanceof PortInst || eobj instanceof NodeInst || eobj instanceof ArcInst) highlights.add(h); } } Iterator<Highlight> hIt = highlights.iterator(); // if already 2 objects, wire them up if (highlights.size() == 2) { Highlight h1 = hIt.next(); Highlight h2 = hIt.next(); ElectricObject eobj1 = h1.getElectricObject(); ElectricObject eobj2 = h2.getElectricObject(); if (eobj1 != null && eobj2 != null) { modeRight = Mode.wiringConnect; wiringTarget = null; startObj = h1.getElectricObject(); endObj = h2.getElectricObject(); currentArcWhenWiringPressed = User.getUserTool().getCurrentArcProto(); EditWindow.gridAlign(dbClick); router.highlightRoute(wnd, startCell, h1.getElectricObject(), h2.getElectricObject(), dbClick); return; } } // if one object, put into wire find mode // which will draw possible wire route. if (highlights.size() == 1) { Highlight h1 = hIt.next(); ElectricObject eobj1 = h1.getElectricObject(); if (eobj1 != null) { modeRight = Mode.wiringFind; endObj = null; wiringTarget = null; startObj = h1.getElectricObject(); router.startInteractiveRoute(wnd); // look for stuff under the mouse Highlight h2 = null; if (!ctrlPressed) h2 = highlighter.findObject(dbClick, wnd, false, false, false, true, false, specialSelect, false); if (h2 == null) { // not over anything, nothing to connect to endObj = null; wiringTarget = null; } else { // Highlight h2 = highlighter.getHighlights().iterator().next(); endObj = h2.getElectricObject(); } currentArcWhenWiringPressed = User.getUserTool().getCurrentArcProto(); EditWindow.gridAlign(dbClick); router.highlightRoute(wnd, startCell, h1.getElectricObject(), endObj, dbClick); return; } } System.out.println("Must start new arc from one node or arc; or wire two node/arcs together"); modeRight = Mode.none; return; } // drawing some sort of box wnd.setStartDrag(clickX, clickY); wnd.setEndDrag(clickX, clickY); wnd.setDoingAreaDrag(); // zoom out and zoom to box mode if (invertSelection && !ctrlPressed) { // A single click zooms out, but a drag box zooms to box // The distinction is if the user starts dragging a box, // which we check for in mouseDragged after a set time delay modeRight = Mode.zoomOut; } // draw box if (ctrlPressed && invertSelection) { // the box we are going to draw is a highlight highlighter.clear(); modeRight = Mode.drawBox; } return; } // ===== left mouse clicks ====== if (isLeftMouse(evt)) { if (modeLeft == Mode.zoomBoxSingleShot) { // We will zoom to box wnd.setStartDrag(clickX, clickY); wnd.setEndDrag(clickX, clickY); wnd.setDoingAreaDrag(); return; } // if doing sticky move place objects now if (modeLeft == Mode.stickyMove) { // moving objects if (ctrlPressed) dbClick = convertToOrthogonal(new Point2D.Double(dbMoveStartX, dbMoveStartY), dbClick, highlighter); Point2D dbDelta = new Point2D.Double(dbClick.getX() - dbMoveStartX, dbClick.getY() - dbMoveStartY); EditWindow.gridAlign(dbDelta); if (dbDelta.getX() != 0 || dbDelta.getY() != 0) { highlighter.setHighlightOffset(0, 0); CircuitChanges.manyMove(dbDelta.getX(), dbDelta.getY()); wnd.fullRepaint(); } modeLeft = Mode.none; return; } // new time stamp must occur after checking for sticky move leftMousePressedTimeStamp = evt.getWhen(); // ----- double-click responses ----- if (evt.getClickCount() == 2) { /* if CTRL is being held, user wants to cycle through what's under the cursor--pop up menu to let them select */ /* if (another) { Rectangle2D bounds = new Rectangle2D.Double(clickX, clickY, 0, 0); underCursor = Highlight.findAllInArea(cell, false, another, true, specialSelect, true, bounds, wnd); JPopupMenu popup = selectPopupMenu(underCursor); popup.show(wnd, clickX, clickY); return; } */ /* if no modifiers, do "get info" */ if (!ctrlPressed && !invertSelection) { if (highlighter.getNumHighlights() >= 1) { EditMenu.getInfoCommand(true); return; } } } // ----- single click responses ----- // if toolbar is in select mode, draw box if (ToolBar.getSelectMode() == ToolBar.SelectMode.AREA) { // select area // area selection: just drag out a rectangle wnd.setStartDrag(clickX, clickY); wnd.setEndDrag(clickX, clickY); wnd.setDoingAreaDrag(); highlighter.clear(); modeLeft = Mode.drawBox; return; } // if already over highlighted object, move it if (!ctrlPressed && !invertSelection && highlighter.overHighlighted(wnd, clickX, clickY) != null) { highlighter.finished(); // over something, user may want to move objects dbMoveStartX = dbClick.getX(); dbMoveStartY = dbClick.getY(); moveDelta = moveDRC = null; modeLeft = Mode.move; } else { // findObject handles cycling through objects (another) // and inverting selection (invertSelection) // and selection special objects (specialSelection) Highlight h = highlighter.findObject(dbClick, wnd, false, ctrlPressed, invertSelection, true, false, specialSelect, true); if (h == null) { // not over anything: drag out a selection rectangle wnd.setStartDrag(clickX, clickY); wnd.setEndDrag(clickX, clickY); wnd.setDoingAreaDrag(); modeLeft = Mode.selectBox; } else { // over something, user may want to move objects dbMoveStartX = dbClick.getX(); dbMoveStartY = dbClick.getY(); moveDelta = moveDRC = null; modeLeft = Mode.move; } mouseOver(dbClick, wnd); } return; } if (isMiddleMouse(evt)) { // We will pan the screen lastX = evt.getX(); lastY = evt.getY(); return; } } } /** Handle mouse dragged event. * <p>Left Mouse Drag: Move Objects (or select area if not on object) * <p>Right Mouse Click/Drag: Connect wire * <p>Right Mouse Drag + (later) CTRL: Connect wire in space (ignore objects) * <p>SHIFT + Right Mouse Drag: zoom box * <p>CTRL + Right Mouse Drag: draw box * @param evt the MouseEvent */ public void mouseDragged(MouseEvent evt) { if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); long currentTime = System.currentTimeMillis(); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Highlighter highlighter = wnd.getHighlighter(); Cell cell = wnd.getCell(); if (cell == null) return; int mouseX = evt.getX(); int mouseY = evt.getY(); Point2D dbMouse = wnd.screenToDatabase(mouseX, mouseY); lastdbMouseX = (int)dbMouse.getX(); lastdbMouseY = (int)dbMouse.getY(); boolean ctrlPressed = (evt.getModifiersEx()&MouseEvent.CTRL_DOWN_MASK) != 0; specialSelect = ToolBar.isSelectSpecial(); // ===== Right mouse drags ===== if (isRightMouse(evt)) { if (modeRight == Mode.zoomBoxSingleShot) { // We will zoom to box if (!wnd.isDoingAreaDrag()) { wnd.setStartDrag(mouseX, mouseY); wnd.setEndDrag(mouseX, mouseY); wnd.setDoingAreaDrag(); } wnd.setEndDrag(mouseX, mouseY); } if (modeRight == Mode.zoomOut) { // switch to zoomBox mode if the user is really dragging a box // otherwise, we zoom out after specified delay if ((currentTime - rightMousePressedTimeStamp) > zoomInDelayMillis) modeRight = Mode.zoomBox; } if (modeRight == Mode.drawBox || modeRight == Mode.zoomBox) { // draw a box wnd.setEndDrag(mouseX, mouseY); } if (modeRight == Mode.wiringFind || modeRight == Mode.stickyWiring) { // see if anything under the pointer Highlight h3 = null; if (!ctrlPressed) h3 = highlighter.findObject(dbMouse, wnd, false, false, false, true, false, specialSelect, false); if (h3 == null) { // not over anything, nothing to connect to EditWindow.gridAlign(dbMouse); endObj = null; wiringTarget = null; } else { // The user can switch between wiring targets under the cursor using a key stroke // if wiring target non-null, and still under cursor, use that endObj = null; if (wiringTarget != null) { // check if still valid target EditWindow.gridAlign(dbMouse); List<Highlight> underCursor = Highlighter.findAllInArea(highlighter, cell, false, true, false, specialSelect, false, new Rectangle2D.Double(dbMouse.getX(), dbMouse.getY(), 0, 0), wnd); for (Highlight h : underCursor) { ElectricObject eobj = h.getElectricObject(); if (eobj == wiringTarget) { endObj = wiringTarget; break; } } // wiringTarget is no longer valid, reset it if (endObj == null) wiringTarget = null; } // if target is null, find new target if (endObj == null) { Iterator<Highlight> hIt = highlighter.getHighlights().iterator(); if (hIt.hasNext()){ Highlight h2 = hIt.next(); endObj = h2.getElectricObject(); } } EditWindow.gridAlign(dbMouse); } User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.highlightRoute(wnd, cell, startObj, endObj, dbMouse); // clear any previous popup cloud /* wnd.clearShowPopupCloud(); // popup list of stuff under mouse to connect to if more than one List underCursor = Highlight.findAllInArea(cell, false, true, true, false, specialSelect, false, new Rectangle2D.Double(dbMouse.getX(), dbMouse.getY(), 0, 0), wnd); if (underCursor.size() > 1) { ArrayList text = new ArrayList(); ArrayList portList = new ArrayList(); text.add("Connect to:"); int num = 1; for (int i=0; i<underCursor.size(); i++) { Highlight h = underCursor.get(i); ElectricObject obj = h.getElectricObject(); if (num == 10) { text.add("...too many to display"); break; } if (obj instanceof PortInst) { // only give option to connect to ports PortInst pi = (PortInst)obj; String str = "("+num+"): Port "+pi.getPortProto().getName()+" on "+pi.getNodeInst().getName(); text.add(str); portList.add(pi); num++; } } if (num > 2) { // only show if more than one port to connect to under mouse wnd.setShowPopupCloud(text, new Point2D.Double(mouseX, mouseY)); wiringPopupCloudUp = true; wiringPopupCloudList = portList; wiringLastDBMouse = dbMouse; } } */ } if (modeRight == Mode.wiringConnect) { EditWindow.gridAlign(dbMouse); User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.highlightRoute(wnd, cell, startObj, endObj, dbMouse); } if (modeRight == Mode.wiringToSpace) { // wire only to point in space EditWindow.gridAlign(dbMouse); User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.highlightRoute(wnd, cell, startObj, null, dbMouse); } } // ===== Left mouse drags ===== if (isLeftMouse(evt)) { if (modeLeft == Mode.selectBox || modeLeft == Mode.drawBox || modeLeft == Mode.zoomBoxSingleShot) { // select objects in box wnd.setEndDrag(mouseX, mouseY); wnd.repaint(); } if (modeLeft == Mode.move || modeLeft == Mode.stickyMove) { // moving objects // if CTRL held, can only move orthogonally if (ctrlPressed) dbMouse = convertToOrthogonal(new Point2D.Double(dbMoveStartX, dbMoveStartY), dbMouse, highlighter); // relocate highlight to under mouse Point2D dbDelta = new Point2D.Double(dbMouse.getX() - dbMoveStartX, dbMouse.getY() - dbMoveStartY); EditWindow.gridAlign(dbDelta); // align to grid Point2D screenDelta = wnd.deltaDatabaseToScreen(dbDelta.getX(), dbDelta.getY()); highlighter.setHighlightOffset((int)screenDelta.getX(), (int)screenDelta.getY()); // detect worst design-rule violation if moving just one object WorstSpacing ws = new WorstSpacing(); List<Geometric> selected = highlighter.getHighlightedEObjs(true, true); if (interactiveDRCDrag && selected.size() == 1) { Geometric g = selected.get(0); Netlist nl = g.getParent().getNetlist(); if (g instanceof ArcInst) { ArcInst ai = (ArcInst)g; Network net = nl.getNetwork(ai, 0); if (net != null) { Poly [] polys = ai.getProto().getTechnology().getShapeOfArc(ai); for(int i=0; i<polys.length; i++) ws.findWorstSpacing(g, polys[i], net, dbDelta, nl); } } else { NodeInst ni = (NodeInst)g; if (!ni.isCellInstance()) { AffineTransform trans = ni.rotateOut(); Poly [] polys = ni.getProto().getTechnology().getShapeOfNode(ni, true, true, null); for(int i=0; i<polys.length; i++) { PortProto pp = polys[i].getPort(); if (pp == null) continue; PortInst pi = ni.findPortInstFromProto(pp); if (pi == null) continue; Network net = nl.getNetwork(pi); if (net == null) continue; polys[i].transform(trans); ws.findWorstSpacing(g, polys[i], net, dbDelta, nl); } } } } // display DRC if known, otherwise distance moved if (moveDelta != null) highlighter.remove(moveDelta); if (moveDRC != null) highlighter.remove(moveDRC); Technology tech = wnd.getCell().getTechnology(); String deltaMessage = "Moved (" + TextUtils.formatDistance(dbDelta.getX(), tech) + "," + TextUtils.formatDistance(dbDelta.getY(), tech) + ")"; WindowFrame wf = WindowFrame.getCurrentWindowFrame(); StatusBar.setCoordinates(deltaMessage, wf); if (ws.validSpacing()) { // display worst design rule violation boolean tooClose = ws.getSeparation() < ws.getMinSpacing(); String message = ws.getOneLayer().getName(); if (ws.getOneLayer() != ws.getOtherLayer()) message += " to " + ws.getOtherLayer().getName(); message += " spacing is " + TextUtils.formatDistance(ws.getSeparation(), tech); if (tooClose) message = "ERROR! " + message + " MINIMUM IS " + TextUtils.formatDistance(ws.getMinSpacing(), tech); // compute upper-left corner of moved object and place text there Geometric g = selected.get(0); Poly hPoly; if (g instanceof NodeInst) { hPoly = Highlight.getNodeInstOutline((NodeInst)g); } else { ArcInst ai = (ArcInst)g; hPoly = ai.makeLambdaPoly(ai.getGridBaseWidth(), Poly.Type.CLOSED); } double minX = 0, maxY = 0; Point2D[] points = hPoly.getPoints(); for(int i=0; i<points.length; i++) { if (i == 0 || points[i].getX() < minX) minX = points[i].getX(); if (i == 0 || points[i].getY() > maxY) maxY = points[i].getY(); } moveDelta = highlighter.addMessage(cell, message, new Point2D.Double(minX + dbDelta.getX(), maxY + dbDelta.getY())); Point2D end1 = new Point2D.Double(ws.getOnePoint().getX() - dbDelta.getX(), ws.getOnePoint().getY() - dbDelta.getY()); Point2D end2 = new Point2D.Double(ws.getOtherPoint().getX() - dbDelta.getX(), ws.getOtherPoint().getY() - dbDelta.getY()); moveDRC = highlighter.addLine(end1, end2, cell, tooClose, true); } wnd.repaint(); } } if (isMiddleMouse(evt)) { // Continue panning the screen int newX = evt.getX(); int newY = evt.getY(); Point2D pt = wnd.getScheduledOffset(); double scale = wnd.getScale(); wnd.setOffset(new Point2D.Double(pt.getX() - (newX - lastX) / scale, pt.getY() + (newY - lastY) / scale)); wnd.getSavedFocusBrowser().updateCurrentFocus(); wnd.fullRepaint(); lastX = newX; lastY = newY; return; } wnd.repaint(); } } /** * Class to detect the worst design-rule violation while moving a node or arc. */ private static class WorstSpacing { private boolean valid; private double separation; private double worstViolation; private double minSpacing; private Point2D worstFrom, worstTo; private Layer layerFrom, layerTo; public WorstSpacing() { valid = false; worstFrom = new Point2D.Double(0, 0); worstTo = new Point2D.Double(0, 0); } public boolean validSpacing() { return valid; } public double getSeparation() { return separation; } public double getMinSpacing() { return minSpacing; } public Layer getOneLayer() { return layerFrom; } public Layer getOtherLayer() { return layerTo; } public Point2D getOnePoint() { return worstFrom; } public Point2D getOtherPoint() { return worstTo; } /** * Method to update this WorstSpacing object according to a polygon on a Geometric. * @param self the Geometric being examined. * @param poly the Poly on the Geometric. * @param net the Network connected to the polygon. * @param delta the offset that is being applied to the polygon (how much it has been dragged). * @param nl the Netlist for the cell. */ public void findWorstSpacing(Geometric self, Poly poly, Network net, Point2D delta, Netlist nl) { // ignore null layers Layer lay = poly.getLayer(); if (lay == null) return; if (lay.isPseudoLayer()) return; if (lay.getFunction().isSubstrate()) return; // move the polygon by the dragged amount Point2D [] points = poly.getPoints(); for(int j=0; j<points.length; j++) poly.setPoint(j, points[j].getX() + delta.getX(), points[j].getY() + delta.getY()); // find simplest rule for this layer to itself Rectangle2D bounds = poly.getBounds2D(); double xS = bounds.getWidth(); double yS = bounds.getHeight(); double widRule = Math.min(xS, yS); double lenRule = Math.max(xS, yS); int multiCut = -1; double surround = DRC.getMaxSurround(lay, Double.MAX_VALUE); // search up to 5 times the design-rule distance away double worstInteractionDistance = surround * 5; Rectangle2D searchBounds = new Rectangle2D.Double( bounds.getMinX()-worstInteractionDistance, bounds.getMinY()-worstInteractionDistance, bounds.getWidth() + worstInteractionDistance*2, bounds.getHeight() + worstInteractionDistance*2); for(Iterator<RTBounds> it = self.getParent().searchIterator(searchBounds); it.hasNext(); ) { Geometric neighbor = (Geometric)it.next(); if (neighbor == self) continue; if (neighbor instanceof NodeInst) { NodeInst otherNi = (NodeInst)neighbor; if (otherNi.isCellInstance()) continue; AffineTransform trans = otherNi.rotateOut(); Poly [] otherPolys = otherNi.getProto().getTechnology().getShapeOfNode(otherNi, true, true, null); for(int i=0; i<otherPolys.length; i++) { Poly otherPoly = otherPolys[i]; Layer otherLay = otherPoly.getLayer(); if (otherLay == null) continue; if (otherLay.isPseudoLayer()) continue; if (otherLay.getFunction().isSubstrate()) continue; if (lay.getTechnology() != otherLay.getTechnology()) continue; PortProto pp = otherPoly.getPort(); if (pp == null) continue; PortInst pi = otherNi.findPortInstFromProto(pp); if (pi == null) continue; Network otherNet = nl.getNetwork(pi); if (otherNet == null) continue; if (net == otherNet) continue; DRCTemplate rule = DRC.getSpacingRule(lay, null, otherLay, null, false, multiCut, widRule, lenRule); if (rule == null) continue; double dist = rule.getValue(0); otherPoly.transform(trans); double sep = poly.separation(otherPoly); if (valid && sep-dist >= worstViolation) continue; // this is a better spacing valid = true; worstViolation = sep-dist; separation = sep; minSpacing = dist; layerFrom = lay; layerTo = otherLay; findClosestPoints(poly, otherPoly); } } else { ArcInst otherAi = (ArcInst)neighbor; Network otherNet = nl.getNetwork(otherAi, 0); if (otherNet == null) continue; if (net == otherNet) continue; Poly [] otherPolys = otherAi.getProto().getTechnology().getShapeOfArc(otherAi); for(int i=0; i<otherPolys.length; i++) { Poly otherPoly = otherPolys[i]; Layer otherLay = otherPoly.getLayer(); if (otherLay == null) continue; if (otherLay.isPseudoLayer()) continue; if (otherLay.getFunction().isSubstrate()) continue; DRCTemplate rule = DRC.getSpacingRule(lay, null, otherLay, null, true, multiCut, widRule, lenRule); if (rule == null) continue; double dist = rule.getValue(0); double sep = poly.separation(otherPoly); if (valid && sep-dist >= worstViolation) continue; // this is a better spacing valid = true; worstViolation = sep-dist; separation = sep; minSpacing = dist; layerFrom = lay; layerTo = otherLay; findClosestPoints(poly, otherPoly); } } } } /** * Method to examine load the "worstFrom" and "worstTo" field variables with the * closest two points on two polygons. * @param from one of the Polys to examine. * @param to the other Poly to examine. */ private void findClosestPoints(Poly from, Poly to) { // if both are manhattan, use special cases Rectangle2D fromBox = from.getBox(); Rectangle2D toBox = to.getBox(); if (fromBox != null && toBox != null) { if (fromBox.getMinX() < toBox.getMaxX() && fromBox.getMaxX() > toBox.getMinX()) { // one above the other: find Y distance double xPos = (Math.max(fromBox.getMinX(), toBox.getMinX()) + Math.min(fromBox.getMaxX(), toBox.getMaxX())) / 2; if (fromBox.getMinY() > toBox.getMaxY()) { // from is above to worstFrom.setLocation(xPos, fromBox.getMinY()); worstTo.setLocation(xPos, toBox.getMaxY()); return; } if (toBox.getMinY() > fromBox.getMaxY()) { // to is above from worstFrom.setLocation(xPos, fromBox.getMaxY()); worstTo.setLocation(xPos, toBox.getMinY()); return; } return; } if (fromBox.getMinY() < toBox.getMaxY() && fromBox.getMaxY() > toBox.getMinY()) { // one next to the other: find X distance double yPos = (Math.max(fromBox.getMinY(), toBox.getMinY()) + Math.min(fromBox.getMaxY(), toBox.getMaxY())) / 2; if (fromBox.getMinX() > toBox.getMaxX()) { // from is above to worstFrom.setLocation(fromBox.getMinX(), yPos); worstTo.setLocation(toBox.getMaxX(), yPos); return; } if (toBox.getMinX() > fromBox.getMaxX()) { // to is above from worstFrom.setLocation(fromBox.getMaxX(), yPos); worstTo.setLocation(toBox.getMinX(), yPos); return; } return; } } // use generalized algorithm double minPD = 0; Point2D [] fromPoints = from.getPoints(); for(int f=0; f<fromPoints.length; f++) { Point2D c = to.closestPoint(fromPoints[f]); double pd = c.distance(fromPoints[f]); if (f != 0 && pd >= minPD) continue; minPD = pd; worstFrom.setLocation(fromPoints[f]); } minPD = 0; Point2D [] toPoints = to.getPoints(); for(int t=0; t<toPoints.length; t++) { double pd = worstFrom.distance(toPoints[t]); if (t != 0 && pd >= minPD) continue; minPD = pd; worstTo.setLocation(toPoints[t]); } } } /** Handle mouse released event * * @param evt the MouseEvent */ public void mouseReleased(MouseEvent evt) { if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Highlighter highlighter = wnd.getHighlighter(); Cell cell = wnd.getCell(); if (cell == null) return; if (cell != startCell) { escapePressed(wnd); return; } // add back in offset int releaseX = evt.getX(); int releaseY = evt.getY(); Point2D dbMouse = wnd.screenToDatabase(releaseX, releaseY); boolean ctrlPressed = (evt.getModifiersEx()&MouseEvent.CTRL_DOWN_MASK) != 0; specialSelect = ToolBar.isSelectSpecial(); // ===== Right Mouse Release ===== if (isRightMouse(evt)) { if (modeRight == Mode.zoomIn) { // zoom in by a factor of two double scale = wnd.getScale(); wnd.setScale(scale * 2); wnd.clearDoingAreaDrag(); wnd.getSavedFocusBrowser().saveCurrentFocus(); wnd.fullRepaint(); } if (modeRight == Mode.zoomOut) { // zoom out by a factor of two double scale = wnd.getScale(); wnd.setScale(scale / 2); if (wnd.isInPlaceEdit()) wnd.getInPlaceTransformOut().transform(dbMouse, dbMouse); wnd.setOffset(dbMouse); wnd.clearDoingAreaDrag(); wnd.getSavedFocusBrowser().saveCurrentFocus(); wnd.fullRepaint(); } if (modeRight == Mode.drawBox || modeRight == Mode.zoomBox || modeRight == Mode.zoomBoxSingleShot) { // drawing boxes Point2D start = wnd.screenToDatabase((int)wnd.getStartDrag().getX(), (int)wnd.getStartDrag().getY()); Point2D end = wnd.screenToDatabase((int)wnd.getEndDrag().getX(), (int)wnd.getEndDrag().getY()); double minSelX = Math.min(start.getX(), end.getX()); double maxSelX = Math.max(start.getX(), end.getX()); double minSelY = Math.min(start.getY(), end.getY()); double maxSelY = Math.max(start.getY(), end.getY()); // determine if the user clicked on a single point to prevent unintended zoom-in // a single point is 4 lambda or less AND 10 screen pixels or less boolean onePoint = true; Rectangle2D bounds = new Rectangle2D.Double(minSelX, minSelY, maxSelX-minSelX, maxSelY-minSelY); if (bounds.getHeight() > 4 && bounds.getWidth() > 4) onePoint = false; if (Math.abs(wnd.getStartDrag().getX()-wnd.getEndDrag().getX()) > 10 || Math.abs(wnd.getStartDrag().getY()-wnd.getEndDrag().getY()) > 10) onePoint = false; if (modeRight == Mode.drawBox) { // just draw a highlight box highlighter.addArea(new Rectangle2D.Double(minSelX, minSelY, maxSelX-minSelX, maxSelY-minSelY), cell); } if (modeRight == Mode.zoomBoxSingleShot) { // zoom to box: focus on box if (!onePoint) wnd.focusScreen(bounds); WindowFrame.setListener(oldListener); if (modeLeft == Mode.zoomBoxSingleShot) modeLeft = Mode.none; } if (modeRight == Mode.zoomBox) { // zoom to box: focus on box if (onePoint) { // modeRight == Mode.zoomOut // if not zoomBox, then user meant to zoomOut double scale = wnd.getScale(); wnd.setScale(scale / 2); wnd.clearDoingAreaDrag(); wnd.getSavedFocusBrowser().saveCurrentFocus(); wnd.fullRepaint(); } else { wnd.focusScreen(bounds); } } highlighter.finished(); wnd.clearDoingAreaDrag(); wnd.repaint(); } if (modeRight == Mode.wiringFind || modeRight == Mode.stickyWiring) { // see if anything under the pointer /* int numFound = Highlight.findObject(dbMouse, wnd, false, false, false, true, false, specialSelect, true); if (numFound == 0) { // not over anything, nothing to connect to EditWindow.gridAlign(dbMouse); endObj = null; } else { // connect objects Iterator hIt = Highlight.getHighlights().iterator(); Highlight h2 = (Highlight)hIt.next(); EditWindow.gridAlign(dbMouse); endObj = h2.getElectricObject(); }*/ EditWindow.gridAlign(dbMouse); User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.makeRoute(wnd, cell, startObj, endObj, dbMouse); // clear any popup cloud we had //wnd.clearShowPopupCloud(); // clear last switched to highlight wiringTarget = null; } if (modeRight == Mode.wiringConnect) { EditWindow.gridAlign(dbMouse); User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.makeRoute(wnd, cell, startObj, endObj, dbMouse); wiringTarget = null; } if (modeRight == Mode.wiringToSpace) { EditWindow.gridAlign(dbMouse); User.getUserTool().setCurrentArcProto(currentArcWhenWiringPressed); router.makeRoute(wnd, cell, startObj, null, dbMouse); wiringTarget = null; } modeRight = Mode.none; } // ===== Left Mouse Release ===== if (isLeftMouse(evt)) { // ignore move if done within cancelMoveDelayMillis long curTime = evt.getWhen(); if (debug) System.out.println("Time diff between click->release is: "+(curTime - leftMousePressedTimeStamp)); if (modeLeft == Mode.move || modeLeft == Mode.stickyMove) { if ((curTime - leftMousePressedTimeStamp) < cancelMoveDelayMillis) { highlighter.setHighlightOffset(0, 0); modeLeft = Mode.none; if (moveDelta != null) highlighter.remove(moveDelta); if (moveDRC != null) highlighter.remove(moveDRC); wnd.repaint(); return; } } // if 'stickyMove' is true and we are moving stuff, ignore mouse release if (getStickyMove() && (modeLeft == Mode.move)) { // only do so if after cancel move delay /* if ((System.currentTimeMillis() - leftMousePressedTimeStamp) < dragDelayMillis) modeLeft = Mode.none; // user left mouse button single click else */ modeLeft = Mode.stickyMove; // user moving stuff in sticky mode } else { if (modeLeft == Mode.selectBox || modeLeft == Mode.drawBox || modeLeft == Mode.zoomBoxSingleShot) { // select all in box Point2D start = wnd.screenToDatabase((int)wnd.getStartDrag().getX(), (int)wnd.getStartDrag().getY()); Point2D end = wnd.screenToDatabase((int)wnd.getEndDrag().getX(), (int)wnd.getEndDrag().getY()); double minSelX = Math.min(start.getX(), end.getX()); double maxSelX = Math.max(start.getX(), end.getX()); double minSelY = Math.min(start.getY(), end.getY()); double maxSelY = Math.max(start.getY(), end.getY()); // determine if the user clicked on a single point to prevent unintended zoom-in // a single point is 4 lambda or less AND 10 screen pixels or less boolean onePoint = true; Rectangle2D bounds = new Rectangle2D.Double(minSelX, minSelY, maxSelX-minSelX, maxSelY-minSelY); if (bounds.getHeight() > 4 && bounds.getWidth() > 4) onePoint = false; if (Math.abs(wnd.getStartDrag().getX()-wnd.getEndDrag().getX()) > 10 || Math.abs(wnd.getStartDrag().getY()-wnd.getEndDrag().getY()) > 10) onePoint = false; if (modeLeft == Mode.selectBox) { if (!invertSelection) highlighter.clear(); highlighter.selectArea(wnd, minSelX, maxSelX, minSelY, maxSelY, invertSelection, specialSelect); } if (modeLeft == Mode.drawBox) { // just draw a highlight box highlighter.addArea(new Rectangle2D.Double(minSelX, minSelY, maxSelX-minSelX, maxSelY-minSelY), cell); } if (modeLeft == Mode.zoomBoxSingleShot) { // zoom to box: focus on box if (!onePoint) wnd.focusScreen(bounds); WindowFrame.setListener(oldListener); if (modeRight == Mode.zoomBoxSingleShot) modeRight = Mode.none; } highlighter.finished(); wnd.clearDoingAreaDrag(); wnd.repaint(); } if (modeLeft == Mode.move || modeLeft == Mode.stickyMove) { // moving objects if (ctrlPressed) dbMouse = convertToOrthogonal(new Point2D.Double(dbMoveStartX, dbMoveStartY), dbMouse, highlighter); Point2D dbDelta = new Point2D.Double(dbMouse.getX() - dbMoveStartX, dbMouse.getY() - dbMoveStartY); EditWindow.gridAlign(dbDelta); if (moveDelta != null) highlighter.remove(moveDelta); if (moveDRC != null) highlighter.remove(moveDRC); if (dbDelta.getX() != 0 || dbDelta.getY() != 0) { highlighter.setHighlightOffset(0, 0); CircuitChanges.manyMove(dbDelta.getX(), dbDelta.getY()); wnd.fullRepaint(); } } modeLeft = Mode.none; } } } } /** * Use to track sticky move of objects * @param evt the MouseEvent */ public void mouseMoved(MouseEvent evt) { mouseX = evt.getX(); mouseY = evt.getY(); //if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Highlighter highlighter = wnd.getHighlighter(); Cell cell = wnd.getCell(); if (cell == null) return; specialSelect = ToolBar.isSelectSpecial(); Point2D dbMouse = wnd.screenToDatabase(mouseX, mouseY); if (modeLeft == Mode.stickyMove) { if (another) dbMouse = convertToOrthogonal(new Point2D.Double(dbMoveStartX, dbMoveStartY), dbMouse, highlighter); Point2D dbDelta = new Point2D.Double(dbMouse.getX() - dbMoveStartX, dbMouse.getY() - dbMoveStartY); EditWindow.gridAlign(dbDelta); Point2D screenDelta = wnd.deltaDatabaseToScreen((int)dbDelta.getX(), (int)dbDelta.getY()); highlighter.setHighlightOffset((int)screenDelta.getX(), (int)screenDelta.getY()); wnd.repaint(); } mouseOver(dbMouse, wnd); } } /** * Draw a mouse over highlight * @param dbMouse * @param wnd */ private void mouseOver(Point2D dbMouse, EditWindow wnd) { if (!User.isMouseOverHighlightingEnabled()) return; if (ToolBar.getSelectMode() == ToolBar.SelectMode.AREA) return; Highlighter highlighter = wnd.getHighlighter(); Highlighter mouseOverHighlighter = wnd.getMouseOverHighlighter(); // create temporary highlighter Highlighter tempHighlighter = new Highlighter(Highlighter.MOUSEOVER_HIGHLIGHTER, null); tempHighlighter.copyState(highlighter); Point2D screenMouse = wnd.databaseToScreen(dbMouse); Highlight found = null; if (!another && !invertSelection) // maintain current selection found = tempHighlighter.overHighlighted(wnd, (int)screenMouse.getX(), (int)screenMouse.getY()); if (found == null) { // find something that would get selected found = tempHighlighter.findObject(dbMouse, wnd, false, another, invertSelection, true, false, specialSelect, true); } // Checking if found highlight is not found in existing list and then force the update. tempHighlighter.clear(); if (found != null) tempHighlighter.addHighlight(found); // check if mouse-over highlight needs to change boolean changed = false; List<Highlight> mouseOld = mouseOverHighlighter.getHighlights(); // assert(mouseOld.size() <= 1); List<Highlight> mouseNew = tempHighlighter.getHighlights(); // assert(mouseNew.size() <= 1); if (mouseOld.size() == mouseNew.size()) { for (int i=0; i<mouseOld.size(); i++) { Highlight h1 = mouseOld.get(i); Highlight h2 = mouseNew.get(i); if (!h1.equals(h2)) { changed = true; break; } } } else changed = true; if (changed) { // copy state into mouse highlighter, signal change (using finished) mouseOverHighlighter.copyState(tempHighlighter); mouseOverHighlighter.finished(); wnd.repaint(); } // JFluid results. tempHighlighter.delete(); // remove from database tempHighlighter = null; } public void mouseClicked(MouseEvent e) { //To change body of implemented methods use File | Settings | File Templates // to detect connection with other WindowContents. if (e.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)e.getSource(); Highlighter highlighter = wnd.getHighlighter(); //wnd.getWindowFrame().getFrame().getStatusBar().highlightChanged(highlighter); TODO WindowFrame.show3DHighlight(); } } public void mouseEntered(MouseEvent e) { //To change body of implemented methods use File | Settings | File Templates. } public void mouseExited(MouseEvent e) { //To change body of implemented methods use File | Settings | File Templates. } /** Mouse Wheel Events are used for panning * Wheel Forward: scroll up * Wheel Back: scroll down * SHIFT + Wheel Forward: scroll right * SHIFT + Wheel Back: scroll left * @param evt the MouseWheelEvent */ public void mouseWheelMoved(MouseWheelEvent evt) { if (debug) System.out.println(" "+evt.paramString()); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Cell cell = wnd.getCell(); if (cell == null) return; boolean sideways = (evt.getModifiersEx()&MouseEvent.SHIFT_DOWN_MASK) != 0; boolean zoom = (evt.getModifiersEx()&MouseEvent.CTRL_DOWN_MASK) != 0; int rotation = evt.getWheelRotation(); if (zoom) { // Control held: zoom in (roll back) or out (roll forward) double scale = wnd.getScale(); double dY = rotation / 10.0; if (dY < 0) scale = scale - scale * dY; else scale = scale * Math.exp(-dY); wnd.setScale(scale); wnd.getSavedFocusBrowser().updateCurrentFocus(); wnd.fullRepaint(); } else if (sideways) { // Shift held: scroll left (roll back) or right (roll forward) ZoomAndPanListener.panXOrY(0, wnd.getWindowFrame(), rotation > 0 ? 1 : -1); } else { // no shift held: scroll up (roll forward) or down (roll back) ZoomAndPanListener.panXOrY(1, wnd.getWindowFrame(), rotation > 0 ? 1 : -1); } } } /** Key pressed event * Delete or Move selected objects * @param evt the KeyEvent */ public void keyPressed(KeyEvent evt) { if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); int chr = evt.getKeyCode(); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Cell cell = wnd.getCell(); if (cell == null) return; boolean redrawMouseOver = false; // move stuff around // if (chr == KeyEvent.VK_LEFT) { // moveSelected(-1, 0, evt.isShiftDown(), evt.isControlDown()); // } else if (chr == KeyEvent.VK_RIGHT) { // moveSelected(1, 0, evt.isShiftDown(), evt.isControlDown()); // } else if (chr == KeyEvent.VK_UP) { // moveSelected(0, 1, evt.isShiftDown(), evt.isControlDown()); // } else if (chr == KeyEvent.VK_DOWN) { // moveSelected(0, -1, evt.isShiftDown(), evt.isControlDown()); // } else // cancel current mode if (chr == KeyEvent.VK_ESCAPE) { escapePressed(wnd); } else if (chr == KeyEvent.VK_CONTROL) { if (!another) redrawMouseOver = true; another = true; } else if (chr == KeyEvent.VK_SHIFT) { if (!invertSelection) redrawMouseOver = true; invertSelection = true; } if (redrawMouseOver) { mouseOver(wnd.screenToDatabase(mouseX, mouseY), wnd); } // wiring popup cloud selection /* if (wiringPopupCloudUp && (modeRight == Mode.stickyWiring || modeRight == Mode.wiringFind)) { for (int i=0; i<wiringPopupCloudList.size(); i++) { if (chr == (KeyEvent.VK_1 + i)) { PortInst pi = wiringPopupCloudList.get(i); EditWindow.gridAlign(wiringLastDBMouse); router.makeRoute(wnd, startObj, pi, wiringLastDBMouse); wnd.clearShowPopupCloud(); // clear popup cloud wiringPopupCloudUp = false; modeRight = Mode.none; return; } } } */ } } private void escapePressed(EditWindow wnd) { Highlighter highlighter = wnd.getHighlighter(); if (modeRight == Mode.wiringConnect || modeRight == Mode.wiringFind || modeRight == Mode.stickyWiring) router.cancelInteractiveRoute(); if (modeRight == Mode.zoomBox || modeRight == Mode.zoomBoxSingleShot || modeRight == Mode.zoomOut || modeLeft == Mode.drawBox || modeLeft == Mode.selectBox) { wnd.clearDoingAreaDrag(); } modeLeft = Mode.none; modeRight = Mode.none; highlighter.setHighlightOffset(0, 0); wnd.repaint(); } public void keyReleased(KeyEvent evt) { int chr = evt.getKeyCode(); if (evt.getSource() instanceof EditWindow) { EditWindow wnd = (EditWindow)evt.getSource(); Cell cell = wnd.getCell(); if (cell == null) return; boolean redrawMouseOver = false; if (chr == KeyEvent.VK_CONTROL) { if (another) redrawMouseOver = true; another = false; } else if (chr == KeyEvent.VK_SHIFT) { if (invertSelection) redrawMouseOver = true; invertSelection = false; } if (redrawMouseOver) { mouseOver(wnd.screenToDatabase(mouseX, mouseY), wnd); } } } public void keyTyped(KeyEvent evt) { if (debug) System.out.println(" ["+modeLeft+","+modeRight+"] "+evt.paramString()); } // ********************************* Moving Stuff ******************************** /** Move selected object(s) via keystroke. If either scaleMove or scaleMove2 * is true, the move is multiplied by the grid Bold frequency. If both are * true the move gets multiplied twice. * @param dX amount to move in X in lambda * @param dY amount to move in Y in lambda * @param scaleMove scales move up if true * @param scaleMove2 scales move up if true (stacks with scaleMove) */ public static void moveSelected(double dX, double dY, boolean scaleMove, boolean scaleMove2) { // scale distance according to arrow motion EditWindow wnd = EditWindow.getCurrent(); if (wnd == null) return; Dimension2D arrowDistance = User.getAlignmentToGrid(); dX *= arrowDistance.getWidth(); dY *= arrowDistance.getHeight(); double scaleX = User.getDefGridXBoldFrequency(); double scaleY = User.getDefGridYBoldFrequency(); if (scaleMove) { dX *= scaleX; dY *= scaleY; } if (scaleMove2) { dX *= scaleX; dY *= scaleY; } // make sure the movement amount is grid-aligned Point2D del = new Point2D.Double(dX, dY); EditWindow.gridAlign(del); dX = del.getX(); dY = del.getY(); // for edit windows doing outline editing, move the selected point if (WindowFrame.getListener() == OutlineListener.theOne) { OutlineListener.theOne.moveSelectedPoint(dX, dY); return; } Highlighter highlighter = wnd.getHighlighter(); highlighter.setHighlightOffset(0, 0); if (wnd.isInPlaceEdit()) { Point2D delta = new Point2D.Double(dX, dY); AffineTransform trans = wnd.getInPlaceTransformIn(); double m00 = trans.getScaleX(); double m01 = trans.getShearX(); double m10 = trans.getShearY(); double m11 = trans.getScaleY(); AffineTransform justRot = new AffineTransform(m00, m10, m01, m11, 0, 0); justRot.transform(delta, delta); dX = delta.getX(); dY = delta.getY(); } CircuitChanges.manyMove(dX, dY); wnd.fullRepaint(); } /** * Convert the mousePoint to be orthogonal to the startPoint. * Chooses direction which is orthogonally farther from startPoint * @param startPoint the reference point. * @param mousePoint the mouse point. * @param highlighter to find out what is selected. * @return a new point orthogonal to startPoint. */ private static Point2D convertToOrthogonal(Point2D startPoint, Point2D mousePoint, Highlighter highlighter) { int orthoAngle = 90; List<Geometric> highObjs = highlighter.getHighlightedEObjs(true, true); for(Geometric geom : highObjs) { if (geom instanceof NodeInst) { NodeInst ni = (NodeInst)geom; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); int thisAngle = getAngleIncrement(con.getArc()); if (thisAngle < orthoAngle) orthoAngle = thisAngle; } } else { int thisAngle = getAngleIncrement((ArcInst)geom); if (thisAngle < orthoAngle) orthoAngle = thisAngle; } } if (orthoAngle == 0) return mousePoint; return InteractiveRouter.getClosestAngledPoint(startPoint, mousePoint, orthoAngle); // // move in direction that is farther // double xdist, ydist; // xdist = Math.abs(mousePoint.getX() - startPoint.getX()); // ydist = Math.abs(mousePoint.getY() - startPoint.getY()); // if (ydist > xdist) // return new Point2D.Double(startPoint.getX(), mousePoint.getY()); // return new Point2D.Double(mousePoint.getX(), startPoint.getY()); } private static int getAngleIncrement(ArcInst ai) { int angle = ai.getAngle(); if ((angle%900) == 0) return 90; if ((angle%450) == 0) return 45; if ((angle%300) == 0) return 30; return 90; } // ********************************* Wiring Stuff ******************************** public void switchWiringTarget() { EditWindow wnd = EditWindow.getCurrent(); if (wnd == null) return; Highlighter highlighter = wnd.getHighlighter(); Cell cell = wnd.getCell(); // if in mode wiringToSpace, switch out of it, and drop to next // block to find new wiring target if (modeRight == Mode.wiringToSpace) { modeRight = Mode.wiringFind; } // this command only valid if in wiring mode if (modeRight == Mode.wiringFind || modeRight == Mode.stickyWiring) { // can only switch if something under the mouse to wire to //if (endObj != null) { Point2D dbMouse = new Point2D.Double(DBMath.round(lastdbMouseX), DBMath.round(lastdbMouseY)); Rectangle2D bounds = new Rectangle2D.Double(lastdbMouseX, lastdbMouseY, 0, 0); List<Highlight> targets = Highlighter.findAllInArea(highlighter, wnd.getCell(), false, true, false, specialSelect, false, bounds, wnd); Iterator<Highlight> it = targets.iterator(); // find wiringTarget in list, if it exists boolean found = false; if (wiringTarget == null) wiringTarget = endObj; while (it.hasNext()) { if ((it.next()).getElectricObject() == wiringTarget) { found = true; // get next object if (!it.hasNext()) { // this is the last target in list, switch to wiringToSpace mode modeRight = Mode.wiringToSpace; wiringTarget = null; break; } wiringTarget = (it.next()).getElectricObject(); break; } } // if not found in list, use head of list if (!found) { it = targets.iterator(); if (it.hasNext()) wiringTarget = (it.next()).getElectricObject(); else wiringTarget = null; } // special case: switching modes to wire to space if (modeRight == Mode.wiringToSpace) { endObj = null; System.out.println("Switching to 'ignore all wiring targets'"); router.highlightRoute(wnd, cell, startObj, null, dbMouse); return; } // if same target, do nothing if (endObj == wiringTarget) return; // draw new route to target endObj = wiringTarget; if (wiringTarget == null) { System.out.println("Switching to wiring target 'none'"); } else { System.out.println("Switching to wiring target '"+wiringTarget+"'"); } router.highlightRoute(wnd, cell, startObj, wiringTarget, dbMouse); //} // nothing under mouse to route to/switch between, return } else { // toggle fat wiring mode EditingPreferences ep = UserInterfaceMain.getEditingPreferences(); ep = ep.withFatWires(!ep.fatWires); UserInterfaceMain.setEditingPreferences(ep); if (ep.fatWires) { System.out.println("Enabling fat wiring mode"); } else { System.out.println("Disabling fat wiring mode"); } } } /** * Wire to a layer. * @param layerNumber */ public void wireTo(int layerNumber) { EditWindow wnd = EditWindow.getCurrent(); if (wnd == null) return; Highlighter highlighter = wnd.getHighlighter(); Cell cell = wnd.getCell(); if (cell == null) return; ArcProto ap = null; Technology tech = Technology.getCurrent(); boolean found = false; for (Iterator<ArcProto> it = tech.getArcs(); it.hasNext(); ) { ap = it.next(); if (ap.isNotUsed()) continue; // ignore arcs that aren't used switch(layerNumber) { case 0: { if (ap.getFunction() == ArcProto.Function.POLY1) { found = true; } break; } case 1: { if (ap.getFunction() == ArcProto.Function.METAL1) { found = true; } break; } case 2: { if (ap.getFunction() == ArcProto.Function.METAL2 || ap.getFunction() == ArcProto.Function.BUS) { found = true; } break; } case 3: { if (ap.getFunction() == ArcProto.Function.METAL3) { found = true; } break; } case 4: { if (ap.getFunction() == ArcProto.Function.METAL4) { found = true; } break; } case 5: { if (ap.getFunction() == ArcProto.Function.METAL5) { found = true; } break; } case 6: { if (ap.getFunction() == ArcProto.Function.METAL6) { found = true; } break; } case 7: { if (ap.getFunction() == ArcProto.Function.METAL7) { found = true; } break; } case 8: { if (ap.getFunction() == ArcProto.Function.METAL8) { found = true; } break; } case 9: { if (ap.getFunction() == ArcProto.Function.METAL9) { found = true; } break; } } if (found) break; } if (!found) return; //if (ap == User.getUserTool().getCurrentArcProto()) return; // if a single portinst highlighted, route from that to node that can connect to arc if (highlighter.getNumHighlights() == 1 && cell != null) { ElectricObject obj = highlighter.getOneHighlight().getElectricObject(); if (obj instanceof PortInst) { PortInst pi = (PortInst)obj; router.makeVerticalRoute(wnd, pi, ap); } } // switch palette to arc User.getUserTool().setCurrentArcProto(ap); } // ********************************* Popup Menus ********************************* /** * Popup menu when user is cycling through objects under pointer * @param objects list of objects to put in menu * @return the popup menu */ public JPopupMenu selectPopupMenu(List<Highlight> objects) { JPopupMenu popup = new JPopupMenu("Choose One"); JMenuItem m; for (Highlight obj : objects) { m = new JMenuItem(obj.toString()); m.addActionListener(this); popup.add(m); } //lastPopupMenu = "Select"; return popup; } /** Select object or Wire to object, depending upon popup menu used */ public void actionPerformed(ActionEvent e) { } // ------------------------------------ Preferences ----------------------------------- private static final String cancelMoveDelayMillisPref = "cancelMoveDelayMillis"; private static final String zoomInDelayMillisPref = "zoomInDelayMillis"; // private static final String useFatWiringModePref = "UseFatWiringMode"; /** * Recached Preferences after change */ public void readPrefs() { router = new SimpleWirer(EditingPreferences.getThreadEditingPreferences().fatWires); router.setTool(User.getUserTool()); cancelMoveDelayMillis = prefs.getLong(cancelMoveDelayMillisPref, getFactoryCancelMoveDelayMillis()); zoomInDelayMillis = prefs.getLong(zoomInDelayMillisPref, 120); // useFatWiringMode = prefs.getBoolean(useFatWiringModePref, true); interactiveDRCDrag = new DRC.DRCPreferences(false).interactiveDRCDrag; } public long getCancelMoveDelayMillis() { return cancelMoveDelayMillis; } public static long getFactoryCancelMoveDelayMillis() { return 200; } public void setCancelMoveDelayMillis(long delay) { cancelMoveDelayMillis = delay; prefs.putLong(cancelMoveDelayMillisPref, delay); } public long getZoomInDelayMillis() { return zoomInDelayMillis; } public void setZoomInDelayMillis(long delay) { zoomInDelayMillis = delay; prefs.putLong(zoomInDelayMillisPref, delay); } // // public boolean getUseFatWiringMode() { return useFatWiringMode; } // // public void setUseFatWiringMode(boolean b) { // useFatWiringMode = b; // prefs.putBoolean(useFatWiringModePref, b); // } }