/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Highlight.java
*
* Copyright (c) 2006 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;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.GenMath;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.network.NetworkTool;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
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.variable.DisplayedText;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.Util;
import com.sun.electric.tool.user.ui.ToolBar;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.*;
/**
* A Highlight (or subclass thereof) includes a reference to something
* to which the user's attention is being called (an ElectricObject,
* some text, a region, etc) and enough information to render the
* highlighting (boldness, etc) on any given window. It is not
* specific to any given EditWindow.
*/
public abstract class Highlight implements Cloneable{
/** for drawing solid lines */ public static final BasicStroke solidLine = new BasicStroke(0);
/** for drawing dotted lines */ public static final BasicStroke dottedLine = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] {1}, 0);
/** for drawing dashed lines */ public static final BasicStroke dashedLine = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, new float[] {10}, 0);
/** for drawing dashed lines */ public static final BasicStroke boldLine = new BasicStroke(3);
/** The Cell containing the selection. */ protected Cell cell;
/** The highlight is an error */ public boolean isError;
private static final int CROSSSIZE = 3;
Highlight(Cell c)
{
this.cell = c;
}
public Cell getCell() { return cell; }
public boolean isValid()
{
if (cell != null)
if (!cell.isLinked()) return false;
return true;
}
// creating so HighlightEOBJ is not a public class
public boolean isHighlightEOBJ() { return false; }
// creating so HighlightText is not a public class
public boolean isHighlightText() { return false; }
public Object getObject() { return null; }
public Variable.Key getVarKey() { return null; }
// point variable, only useful for HighlightEOBJ?
public void setPoint(int p) {;}
public int getPoint() { return -1; }
public Object clone()
{
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
/**
* Method to tell whether two Highlights are the same.
* @param obj the Highlight to compare to this one.
* @return true if the two refer to the same thing.
*/
boolean sameThing(Highlight obj)
{
return false;
}
/**
* Method to tell whether this Highlight is text that stays with its node.
* The two possibilities are (1) text on invisible pins
* (2) export names, when the option to move exports with their labels is requested.
* @return true if this Highlight is text that should move with its node.
*/
public boolean nodeMovesWithText()
{
return false;
}
/** the highlight pattern will repeat itself rotationally every PULSATE_ROTATE_PERIOD milliseconds */
private final int PULSATE_ROTATE_PERIOD = 1000;
/** the overall intensity of the highlight pattern will repeat itself rotationally every PULSATE_INTENSITY_PERIOD milliseconds */
private final int PULSATE_INTENSITY_PERIOD = 0 /* 1000 */;
/** the hue of the highlight pattern will repeat itself rotationally every PULSATE_HUE_PERIOD milliseconds */
private final int PULSATE_HUE_PERIOD = 0 /* 30000 */;
/** the length of the rotating pattern */
private final int PULSATE_STRIPE_LENGTH = 30;
/** the number of "segments" in each stripe; increasing this number slows down rendering*/
private final int PULSATE_STRIPE_SEGMENTS = 10;
/**
* Method to display this Highlight in a window.
* @param wnd the window in which to draw this highlight.
* @param g_ the Graphics associated with the window.
*/
public void showHighlight(EditWindow wnd, Graphics g_, int highOffX, int highOffY, boolean onlyHighlight,
Color mainColor, Stroke primaryStroke, boolean setConnected)
{
if (!isValid()) return;
g_.setColor(mainColor);
Graphics2D g2 = (Graphics2D)g_;
g2.setStroke(primaryStroke);
if (User.isErrorHighlightingPulsate() && isError) {
long now = System.currentTimeMillis();
for(int i=0; i<PULSATE_STRIPE_SEGMENTS; i++) {
float h = Util.hueFromColor(mainColor);
h = PULSATE_HUE_PERIOD==0
? h
: ((now % PULSATE_HUE_PERIOD) / ((float)PULSATE_HUE_PERIOD));
float s = 1;
float v =
(i / ((float)PULSATE_STRIPE_SEGMENTS))
*
(PULSATE_INTENSITY_PERIOD == 0
? 1.0f
: (float)Math.sin((
((now % PULSATE_INTENSITY_PERIOD))
/
((float)PULSATE_INTENSITY_PERIOD)
)
* Math.PI));
g2.setColor(Util.colorFromHSV(h, s, v));
float segment_length = PULSATE_STRIPE_LENGTH / ((float)PULSATE_STRIPE_SEGMENTS);
g2.setStroke(new BasicStroke(1,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND,
20,
new float[] { segment_length,
PULSATE_STRIPE_LENGTH-segment_length },
(((now % PULSATE_ROTATE_PERIOD) * PULSATE_STRIPE_LENGTH)
/
((float)PULSATE_ROTATE_PERIOD))
+ i
));
showInternalHighlight(wnd, g2, highOffX, highOffY, onlyHighlight, setConnected);
}
} else {
showInternalHighlight(wnd, g_, highOffX, highOffY, onlyHighlight, setConnected);
}
}
abstract void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected);
/**
* Method to populate a List of all highlighted Geometrics.
* @param list the list to populate
* @param wantNodes true if NodeInsts should be included in the list.
* @param wantArcs true if ArcInsts should be included in the list.
*/
void getHighlightedEObjs(Highlighter highlighter, List<Geometric> list, boolean wantNodes, boolean wantArcs) {;}
static void getHighlightedEObjsInternal(Geometric geom, List<Geometric> list, boolean wantNodes, boolean wantArcs)
{
if (geom == null) return;
if (!wantNodes && geom instanceof NodeInst) return;
if (!wantArcs && geom instanceof ArcInst) return;
if (list.contains(geom)) return;
list.add(geom);
}
/**
* Method to return the Geometric object that is in this Highlight.
* If the highlight is a PortInst, an Export, or annotation text, its base NodeInst is returned.
* @return the Geometric object that is in this Highlight.
* Returns null if this Highlight is not on a Geometric.
*/
public Geometric getGeometric() { return null; }
/**
* Method to return a List of all highlighted NodeInsts.
* Return a list with the highlighted NodeInsts.
*/
void getHighlightedNodes(Highlighter highlighter, List<NodeInst> list) {;}
static void getHighlightedNodesInternal(Geometric geom, List<NodeInst> list)
{
if (geom == null || !(geom instanceof NodeInst)) return;
NodeInst ni = (NodeInst)geom;
if (list.contains(ni)) return;
list.add(ni);
}
/**
* Method to return a List of all highlighted ArcInsts.
* Return a list with the highlighted ArcInsts.
*/
void getHighlightedArcs(Highlighter highlighter, List<ArcInst> list) {;}
static void getHighlightedArcsInternal(Geometric geom, List<ArcInst> list)
{
if (geom == null || !(geom instanceof ArcInst)) return;
ArcInst ai = (ArcInst)geom;
if (list.contains(ai)) return;
list.add(ai);
}
/**
* Method to return a set of the currently selected networks.
* Return a set of the currently selected networks.
* If there are no selected networks, the list is empty.
*/
void getHighlightedNetworks(Set<Network> nets, Netlist netlist) {;}
/**
* Method to return a List of all highlighted text.
* @param list list to populate.
* @param unique true to request that the text objects be unique,
* and not attached to another object that is highlighted.
* For example, if a node and an export on that node are selected,
* the export text will not be included if "unique" is true.
* Return a list with the Highlight objects that point to text.
*/
void getHighlightedText(List<DisplayedText> list, boolean unique, List<Highlight> getHighlights) {;}
/**
* Method to return the bounds of the highlighted objects.
* @param wnd the window in which to get bounds.
* @return the bounds of the highlighted objects (null if nothing is highlighted).
*/
Rectangle2D getHighlightedArea(EditWindow wnd) { return null; }
/**
* Method to return the ElectricObject associated with this Highlight object.
* @return the ElectricObject associated with this Highlight object.
*/
public ElectricObject getElectricObject() { return null; }
/**
* Method to tell whether a point is over this Highlight.
* @param wnd the window being examined.
* @param x the X screen coordinate of the point.
* @param y the Y screen coordinate of the point.
* @return true if the point is over this Highlight.
*/
boolean overHighlighted(EditWindow wnd, int x, int y, Highlighter highlighter) { return false; }
public String getInfo() { return null;}
/**
* Method to load an array of counts with the number of highlighted objects in a list.
* arc = 0, node = 1, export = 2, text = 3, graphics = 4
* @param list the list of highlighted objects.
* @param counts the array of counts to set.
* @return a NodeInst, if it is in the list.
*/
public static NodeInst getInfoCommand(List<Highlight> list, int[] counts)
{
// information about the selected items
NodeInst theNode = null;
for(Highlight h : list)
{
ElectricObject eobj = h.getElectricObject();
if (h.isHighlightEOBJ())
{
if (eobj instanceof NodeInst || eobj instanceof PortInst)
{
counts[1]++;
if (eobj instanceof NodeInst) theNode = (NodeInst)eobj; else
theNode = ((PortInst)eobj).getNodeInst();
} else if (eobj instanceof ArcInst)
{
counts[0]++;
}
} else if (h.isHighlightText())
{
if (h.getVarKey() == Export.EXPORT_NAME) counts[2]++; else
{
if (h.getElectricObject() instanceof NodeInst)
theNode = (NodeInst)h.getElectricObject();
counts[3]++;
}
} else if (h instanceof HighlightArea)
{
counts[4]++;
} else if (h instanceof HighlightLine)
{
counts[4]++;
}
}
return theNode;
}
/**
* Method to draw an array of points as highlighting.
* @param wnd the window in which drawing is happening.
* @param g the Graphics for the window.
* @param points the array of points being drawn.
* @param offX the X offset of the drawing.
* @param offY the Y offset of the drawing.
* @param opened true if the points are drawn "opened".
* @param thickLine
*/
public static void drawOutlineFromPoints(EditWindow wnd, Graphics g, Point2D[] points, int offX, int offY,
boolean opened, boolean thickLine)
{
boolean onePoint = true;
if (points.length <= 0)
return;
Point firstP = wnd.databaseToScreen(points[0].getX(), points[0].getY());
for(int i=1; i<points.length; i++)
{
Point p = wnd.databaseToScreen(points[i].getX(), points[i].getY());
if (DBMath.doublesEqual(p.getX(), firstP.getX()) &&
DBMath.doublesEqual(p.getY(), firstP.getY())) continue;
onePoint = false;
break;
}
if (onePoint)
{
drawLine(g, wnd, firstP.x + offX-CROSSSIZE, firstP.y + offY, firstP.x + offX+CROSSSIZE, firstP.y + offY);
drawLine(g, wnd, firstP.x + offX, firstP.y + offY-CROSSSIZE, firstP.x + offX, firstP.y + offY+CROSSSIZE);
return;
}
// find the center
int cX = 0, cY = 0;
// if (thickCenter != null)
// {
// Point lp = wnd.databaseToScreen(thickCenter.getX(), thickCenter.getY());
// cX = lp.x;
// cY = lp.y;
// }
Point p = new Point(0, 0);
Point2D ptXF = new Point2D.Double(0, 0);
for(int i=0; i<points.length; i++)
{
int lastI = i-1;
if (lastI < 0)
{
if (opened) continue;
lastI = points.length - 1;
}
Point2D pt = points[lastI];
if (wnd.isInPlaceEdit())
{
wnd.getInPlaceTransformOut().transform(pt, ptXF);
pt = ptXF;
}
// Point p = wnd.databaseToScreen(pt.getX(), pt.getY());
wnd.gridToScreen((int)DBMath.lambdaToGrid(pt.getX()), (int)DBMath.lambdaToGrid(pt.getY()), p);
int fX = p.x + offX; int fY = p.y + offY;
pt = points[i];
if (wnd.isInPlaceEdit())
{
wnd.getInPlaceTransformOut().transform(pt, ptXF);
pt = ptXF;
}
// p = wnd.databaseToScreen(pt.getX(), pt.getY());
wnd.gridToScreen((int)DBMath.lambdaToGrid(pt.getX()), (int)DBMath.lambdaToGrid(pt.getY()), p);
int tX = p.x + offX; int tY = p.y + offY;
drawLine(g, wnd, fX, fY, tX, tY);
if (thickLine)
{
if (fX < cX) fX--; else fX++;
if (fY < cY) fY--; else fY++;
if (tX < cX) tX--; else tX++;
if (tY < cY) tY--; else tY++;
drawLine(g, wnd, fX, fY, tX, tY);
}
}
}
void internalDescribe(StringBuffer desc) {;}
/**
* Describe the Highlight
* @return a string describing the highlight
*/
public String describe() {
StringBuffer desc = new StringBuffer();
desc.append(this.getClass().getName());
if (cell != null)
{
desc.append(" in ");
desc.append(cell);
}
desc.append(": ");
internalDescribe(desc);
return desc.toString();
}
/**
* Gets a poly that describes the Highlight for the NodeInst.
* @param ni the nodeinst to get a poly that will be used to highlight it
* @return a poly outlining the nodeInst.
*/
public static Poly getNodeInstOutline(NodeInst ni)
{
AffineTransform trans = ni.rotateOutAboutTrueCenter();
Poly poly = null;
if (!ni.isCellInstance())
{
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
// special case for outline nodes
if (pn.isHoldsOutline())
{
Point2D [] outline = ni.getTrace();
if (outline != null)
{
int numPoints = outline.length;
boolean whole = true;
for(int i=1; i<numPoints; i++)
{
if (outline[i] == null)
{
whole = false;
break;
}
}
if (whole)
{
Point2D [] pointList = new Point2D.Double[numPoints];
for(int i=0; i<numPoints; i++)
{
pointList[i] = new Point2D.Double(ni.getAnchorCenterX() + outline[i].getX(),
ni.getAnchorCenterY() + outline[i].getY());
}
trans.transform(pointList, 0, pointList, 0, numPoints);
poly = new Poly(pointList);
if (ni.getFunction() == PrimitiveNode.Function.NODE)
{
poly.setStyle(Poly.Type.FILLED);
} else
{
poly.setStyle(Poly.Type.OPENED);
}
return poly;
}
}
}
// special case for circular nodes
if (pn == Artwork.tech().circleNode || pn == Artwork.tech().thickCircleNode)
{
// see if this circle is only a partial one
double [] angles = ni.getArcDegrees();
if (angles[0] != 0.0 || angles[1] != 0.0)
{
Point2D [] pointList = Artwork.fillEllipse(ni.getAnchorCenter(), ni.getXSize(), ni.getYSize(), angles[0], angles[1]);
poly = new Poly(pointList);
poly.setStyle(Poly.Type.OPENED);
poly.transform(ni.rotateOut());
}
}
}
// setup outline of node with standard offset
if (poly == null)
poly = ni.getBaseShape();
return poly;
}
/**
* Implementing clipping here speeds things up a lot if there are
* many large highlights off-screen
*/
public static void drawLine(Graphics g, EditWindow wnd, int x1, int y1, int x2, int y2)
{
Dimension size = wnd.getScreenSize();
// first clip the line
Point pt1 = new Point(x1, y1);
Point pt2 = new Point(x2, y2);
if (GenMath.clipLine(pt1, pt2, 0, size.width-1, 0, size.height-1)) return;
g.drawLine(pt1.x, pt1.y, pt2.x, pt2.y);
// if (((x1 >= 0) && (x1 <= size.getLambdaFullWidth())) || ((x2 >= 0) && (x2 <= size.getLambdaFullWidth())) ||
// ((y1 >= 0) && (y1 <= size.getHeight())) || ((y2 >= 0) && (y2 <= size.getHeight()))) {
// g.drawLine(x1, y1, x2, y2);
// }
}
/**
* General purpose function to sort Highlight objects based on their getInfo output
*/
public static class HighlightSorting implements Comparator<Highlight>
{
public int compare(Highlight h1, Highlight h2)
{
return h1.getInfo().compareTo(h2.getInfo());
}
}
}
/**
* A Highlight which calls the user's attention to a Poly.
*/
class HighlightPoly extends Highlight
{
/** The highlighted polygon */ private Poly polygon;
/** The color used when drawing polygons */ private Color color;
HighlightPoly(Cell c, Poly p, Color col)
{
super(c);
this.polygon = p;
this.color = col;
}
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
// switch colors if specified
Color oldColor = null;
if (color != null)
{
oldColor = g.getColor();
g.setColor(color);
}
// draw poly
Point2D[] points = polygon.getPoints();
if (polygon.getStyle() == Poly.Type.FILLED)
{
int [] xPoints = new int[points.length];
int [] yPoints = new int[points.length];
for(int i=0; i<points.length; i++)
{
Point p = wnd.databaseToScreen(points[i].getX(), points[i].getY());
xPoints[i] = p.x;
yPoints[i] = p.y;
}
g.fillPolygon(xPoints, yPoints, points.length);
} else
{
boolean opened = (polygon.getStyle() == Poly.Type.OPENED);
drawOutlineFromPoints(wnd, g, points, highOffX, highOffY, opened, false);
}
// switch back to old color if switched
if (oldColor != null)
g.setColor(oldColor);
}
}
/**
* A Highlight which calls the user's attention to a Line.
*/
class HighlightLine extends Highlight
{
/** The highlighted line. */ protected Point2D start, end, center;
/** The highlighted line is thick. */ protected boolean thickLine;
HighlightLine(Cell c, Point2D s, Point2D e, Point2D cen, boolean thick, boolean isError)
{
super(c);
this.start = s;
this.end = e;
this.center = cen;
this.thickLine = thick;
this.isError = isError;
}
public void showInternalHighlight(EditWindow wnd, Graphics g_, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
Point2D [] points = new Point2D.Double[2];
points[0] = new Point2D.Double(start.getX(), start.getY());
points[1] = new Point2D.Double(end.getX(), end.getY());
drawOutlineFromPoints(wnd, g_, points, highOffX, highOffY, false, thickLine);
}
Rectangle2D getHighlightedArea(EditWindow wnd)
{
double cX = Math.min(start.getX(), end.getX());
double cY = Math.min(start.getY(), end.getY());
double sX = Math.abs(start.getX() - end.getX());
double sY = Math.abs(start.getY() - end.getY());
return new Rectangle2D.Double(cX, cY, sX, sY);
}
public String getInfo()
{
String description = "Line from (" + start.getX() + "," + start.getY() + ") to (" +
end.getX() + "," + end.getY() + ")";
return description;
}
}
class HighlightObject extends Highlight
{
/** The highlighted generic object */ private Object object;
HighlightObject(Cell c, Object obj)
{
super(c);
this.object = obj;
}
public Object getObject() { return object; }
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
System.out.println("Should call this one?");
}
}
/**
* A Highlight which calls the user's attention to a Rectangle2D.
*/
class HighlightArea extends Highlight
{
/** The highlighted area. */ protected Rectangle2D bounds;
HighlightArea(Cell c, Rectangle2D area)
{
super(c);
bounds = new Rectangle2D.Double();
bounds.setRect(area);
}
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
Point2D [] points = new Point2D.Double[5];
points[0] = new Point2D.Double(bounds.getMinX(), bounds.getMinY());
points[1] = new Point2D.Double(bounds.getMinX(), bounds.getMaxY());
points[2] = new Point2D.Double(bounds.getMaxX(), bounds.getMaxY());
points[3] = new Point2D.Double(bounds.getMaxX(), bounds.getMinY());
points[4] = new Point2D.Double(bounds.getMinX(), bounds.getMinY());
drawOutlineFromPoints(wnd, g, points, highOffX, highOffY, false, false);
}
void getHighlightedEObjs(Highlighter highlighter, List<Geometric> list, boolean wantNodes, boolean wantArcs)
{
List<Highlight> inArea = Highlighter.findAllInArea(highlighter, cell, false, false, false, false, false, bounds, null);
for(Highlight ah : inArea)
{
if (!(ah instanceof HighlightEOBJ)) continue;
ElectricObject eobj = ((HighlightEOBJ)ah).eobj;
if (eobj instanceof ArcInst) {
if (wantArcs)
list.add((ArcInst)eobj);
} else if (eobj instanceof NodeInst) {
if (wantNodes)
list.add((NodeInst)eobj);
} else if (eobj instanceof PortInst) {
if (wantNodes)
list.add(((PortInst)eobj).getNodeInst());
}
// if (!wantNodes)
// {
// if (eobj instanceof NodeInst || eobj instanceof PortInst) continue;
// }
// if (!wantArcs && eobj instanceof ArcInst) continue;
// if (eobj instanceof PortInst) eobj = ((PortInst)eobj).getNodeInst();
// highlightedGeoms.add(eobj);
}
}
void getHighlightedNodes(Highlighter highlighter, List<NodeInst> list)
{
List<Highlight> inArea = Highlighter.findAllInArea(highlighter, cell, false, false, false, false, false,
bounds, null);
for(Highlight ah : inArea)
{
if (!(ah instanceof HighlightEOBJ)) continue;
ElectricObject eobj = ((HighlightEOBJ)ah).eobj;
if (eobj instanceof NodeInst)
list.add((NodeInst)eobj);
else if (eobj instanceof PortInst)
list.add(((PortInst)eobj).getNodeInst());
}
}
void getHighlightedArcs(Highlighter highlighter, List<ArcInst> list)
{
List<Highlight> inArea = Highlighter.findAllInArea(highlighter, cell, false, false, false, false, false,
bounds, null);
for(Highlight ah : inArea)
{
if (!(ah instanceof HighlightEOBJ)) continue;
ElectricObject eobj = ((HighlightEOBJ)ah).eobj;
if (eobj instanceof ArcInst)
list.add((ArcInst)eobj);
}
}
Rectangle2D getHighlightedArea(EditWindow wnd)
{
return bounds;
}
public String getInfo()
{
String description = "Area from " + bounds.getMinX() + "<=X<=" + bounds.getMaxX() +
" and " + bounds.getMinY() + "<=Y<=" + bounds.getMaxY();
return description;
}
}
/**
* A Highlight which calls the user's attention to a Point2D and includes a text message.
*/
class HighlightMessage extends Highlight
{
/** The highlighted message. */ protected String msg;
/** Location of the message highlight */ protected Point2D loc;
/** Corner of text: 0=lowerLeft, 1=upperLeft, 2=upperRight, 3=lowerRight */ protected int corner;
HighlightMessage(Cell c, String m, Point2D p, int co)
{
super(c);
this.msg = m;
this.loc = p;
this.corner = co;
}
void internalDescribe(StringBuffer desc)
{
desc.append(", ");
desc.append(msg);
}
public String getInfo() {return msg;}
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
Point location = wnd.databaseToScreen(loc.getX(), loc.getY());
if (corner != 0)
{
// determine the size of the text
Font font = g.getFont();
FontRenderContext frc = new FontRenderContext(null, true, true);
GlyphVector gv = font.createGlyphVector(frc, msg);
LineMetrics lm = font.getLineMetrics(msg, frc);
Rectangle2D rasRect = gv.getLogicalBounds();
int width = (int)rasRect.getWidth();
int height = (int)(lm.getHeight()+0.5);
switch (corner)
{
case 1: // put upper-left corner of text at drawing coordinate
location.y += height;
break;
case 2: // put upper-right corner of text at drawing coordinate
location.y += height;
location.x -= width;
break;
case 3: // put lower-right corner of text at drawing coordinate
location.y += height;
location.x -= width;
break;
}
}
Color oldColor = g.getColor();
g.setColor(new Color(255-oldColor.getRed(), 255-oldColor.getGreen(), 255-oldColor.getBlue()));
g.drawString(msg, location.x+1, location.y+1);
g.setColor(oldColor);
g.drawString(msg, location.x, location.y);
}
Rectangle2D getHighlightedArea(EditWindow wnd)
{
return new Rectangle2D.Double(loc.getX(), loc.getY(), 0, 0);
}
}
/**
* A Highlight which calls the user's attention to an ElectricObject.
*/
class HighlightEOBJ extends Highlight
{
/** The highlighted object. */ protected ElectricObject eobj;
/** For Highlighted networks, this prevents excess highlights */ private boolean highlightConnected;
/** The highlighted outline point (only for NodeInst). */ protected int point;
/** The color used when drawing polygons */ private Color color;
public HighlightEOBJ(ElectricObject e, Cell c, boolean connected, int p)
{
super(c);
this.eobj = e;
this.highlightConnected = connected;
this.point = p;
this.color = null;
}
public HighlightEOBJ(ElectricObject e, Cell c, boolean connected, int p, boolean isError)
{
super(c);
this.eobj = e;
this.highlightConnected = connected;
this.point = p;
this.color = null;
this.isError = isError;
}
public HighlightEOBJ(ElectricObject e, Cell c, boolean connected, int p, Color col)
{
super(c);
this.eobj = e;
this.highlightConnected = connected;
this.point = p;
this.color = col;
}
void internalDescribe(StringBuffer desc)
{
desc.append(", ");
if (eobj instanceof PortInst) {
desc.append(((PortInst)eobj).describe(true));
}
if (eobj instanceof NodeInst) {
desc.append(((NodeInst)eobj).describe(true));
}
if (eobj instanceof ArcInst) {
desc.append(((ArcInst)eobj).describe(true));
}
}
public ElectricObject getElectricObject() { return eobj; }
public boolean isHighlightEOBJ() { return true; }
public void setPoint(int p) { point = p;}
public int getPoint() { return point; }
public boolean isValid()
{
if (!super.isValid()) return false;
// if (eobj instanceof PortInst)
// return ((PortInst)eobj).getNodeInst().isLinked();
return eobj.isLinked();
}
boolean sameThing(Highlight obj)
{
if (this == obj) return (true);
// Consider already obj==null
if (obj == null || getClass() != obj.getClass())
return (false);
ElectricObject realEObj = eobj;
if (realEObj instanceof PortInst) realEObj = ((PortInst)realEObj).getNodeInst();
HighlightEOBJ other = (HighlightEOBJ)obj;
ElectricObject realOtherEObj = other.eobj;
if (realOtherEObj instanceof PortInst) realOtherEObj = ((PortInst)realOtherEObj).getNodeInst();
if (realEObj != realOtherEObj) return false;
return true;
}
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
Graphics2D g2 = (Graphics2D)g;
highlightConnected = setConnected;
if (eobj == null || !eobj.isLinked()) return;
// switch colors if specified
Color oldColor = null;
if (color != null)
{
oldColor = g.getColor();
g.setColor(color);
}
// highlight ArcInst
if (eobj instanceof ArcInst)
{
ArcInst ai = (ArcInst)eobj;
// if (!Job.acquireExamineLock(false)) return;
try {
// construct the polygons that describe the basic arc
Poly poly = ai.makeLambdaPoly(ai.getGridBaseWidth(), Poly.Type.CLOSED);
if (poly == null) return;
drawOutlineFromPoints(wnd, g, poly.getPoints(), highOffX, highOffY, false, false);
if (onlyHighlight)
{
// this is the only thing highlighted: give more information about constraints
String constraints = "X";
if (ai.isRigid()) constraints = "R"; else
{
if (ai.isFixedAngle())
{
if (ai.isSlidable()) constraints = "FS"; else
constraints = "F";
} else if (ai.isSlidable()) constraints = "S";
}
Point p = wnd.databaseToScreen(ai.getTrueCenterX(), ai.getTrueCenterY());
Font font = wnd.getFont(null);
if (font != null)
{
GlyphVector gv = wnd.getGlyphs(constraints, font);
Rectangle2D glyphBounds = gv.getVisualBounds();
g.drawString(constraints, (int)(p.x - glyphBounds.getWidth()/2 + highOffX),
p.y + font.getSize()/2 + highOffY);
}
}
// Job.releaseExamineLock();
} catch (Error e) {
// Job.releaseExamineLock();
throw e;
}
return;
}
// highlight NodeInst
PortProto pp = null;
ElectricObject realEObj = eobj;
PortInst originalPi = null;
if (realEObj instanceof PortInst)
{
originalPi = ((PortInst)realEObj);
pp = originalPi.getPortProto();
realEObj = ((PortInst)realEObj).getNodeInst();
}
if (realEObj instanceof NodeInst)
{
NodeInst ni = (NodeInst)realEObj;
AffineTransform trans = ni.rotateOutAboutTrueCenter();
int offX = highOffX;
int offY = highOffY;
/*
boolean drewOutline = false;
if (!ni.isCellInstance())
{
// special case for outline nodes
if (np.isHoldsOutline())
{
Point2D [] outline = ni.getTrace();
if (outline != null)
{
int numPoints = outline.length;
Point2D [] pointList = new Point2D.Double[numPoints];
for(int i=0; i<numPoints; i++)
{
pointList[i] = new Point2D.Double(ni.getTrueCenterX() + outline[i].getX(),
ni.getTrueCenterY() + outline[i].getY());
}
trans.transform(pointList, 0, pointList, 0, numPoints);
drawOutlineFromPoints(wnd, g, pointList, 0, 0, true, null);
drewOutline = true;
}
}
}
// setup outline of node with standard offset
if (!drewOutline)
{
SizeOffset so = ni.getSizeOffset();
double nodeLowX = ni.getTrueCenterX() - ni.getXSize()/2 + so.getLowXOffset();
double nodeHighX = ni.getTrueCenterX() + ni.getXSize()/2 - so.getHighXOffset();
double nodeLowY = ni.getTrueCenterY() - ni.getYSize()/2 + so.getLowYOffset();
double nodeHighY = ni.getTrueCenterY() + ni.getYSize()/2 - so.getHighYOffset();
if (nodeLowX == nodeHighX && nodeLowY == nodeHighY)
{
float x = (float)nodeLowX;
float y = (float)nodeLowY;
float size = 3 / (float)wnd.getScale();
Point c1 = wnd.databaseToScreen(x+size, y);
Point c2 = wnd.databaseToScreen(x-size, y);
Point c3 = wnd.databaseToScreen(x, y+size);
Point c4 = wnd.databaseToScreen(x, y-size);
drawLine(g, wnd, c1.x + offX, c1.y + offY, c2.x + offX, c2.y + offY);
drawLine(g, wnd, c3.x + offX, c3.y + offY, c4.x + offX, c4.y + offY);
} else
{
double nodeX = (nodeLowX + nodeHighX) / 2;
double nodeY = (nodeLowY + nodeHighY) / 2;
Poly poly = new Poly(nodeX, nodeY, nodeHighX-nodeLowX, nodeHighY-nodeLowY);
poly.transform(trans);
drawOutlineFromPoints(wnd, g, poly.getPoints(), offX, offY, false, null);
}
}
*/
// draw the selected point
if (point >= 0)
{
Point2D [] points = ni.getTrace();
if (points != null)
{
// if this is a spline, highlight the true shape
if (ni.getProto() == Artwork.tech().splineNode)
{
Point2D [] changedPoints = new Point2D[points.length];
for(int i=0; i<points.length; i++)
{
changedPoints[i] = points[i];
if (i == point)
{
double x = ni.getAnchorCenterX() + points[point].getX();
double y = ni.getAnchorCenterY() + points[point].getY();
Point2D thisPt = new Point2D.Double(x, y);
trans.transform(thisPt, thisPt);
Point cThis = wnd.databaseToScreen(thisPt);
Point2D db = wnd.screenToDatabase(cThis.x+offX, cThis.y+offY);
changedPoints[i] = new Point2D.Double(db.getX() - ni.getAnchorCenterX(), db.getY() - ni.getAnchorCenterY());
}
}
Point2D [] spPoints = Artwork.tech().fillSpline(ni.getAnchorCenterX(), ni.getAnchorCenterY(), changedPoints);
Point cLast = wnd.databaseToScreen(spPoints[0]);
for(int i=1; i<spPoints.length; i++)
{
Point cThis = wnd.databaseToScreen(spPoints[i]);
drawLine(g, wnd, cLast.x, cLast.y, cThis.x, cThis.y);
cLast = cThis;
}
}
// draw an "x" through the selected point
if (points[point] != null)
{
double x = ni.getAnchorCenterX() + points[point].getX();
double y = ni.getAnchorCenterY() + points[point].getY();
Point2D thisPt = new Point2D.Double(x, y);
trans.transform(thisPt, thisPt);
Point cThis = wnd.databaseToScreen(thisPt);
int size = 3;
drawLine(g, wnd, cThis.x + size + offX, cThis.y + size + offY, cThis.x - size + offX, cThis.y - size + offY);
drawLine(g, wnd, cThis.x + size + offX, cThis.y - size + offY, cThis.x - size + offX, cThis.y + size + offY);
// find previous and next point, and draw lines to them
boolean showWrap = ni.traceWraps();
Point2D prevPt = null, nextPt = null;
int prevPoint = point - 1;
if (prevPoint < 0 && showWrap) prevPoint = points.length - 1;
if (prevPoint >= 0 && points[prevPoint] != null)
{
prevPt = new Point2D.Double(ni.getAnchorCenterX() + points[prevPoint].getX(),
ni.getAnchorCenterY() + points[prevPoint].getY());
trans.transform(prevPt, prevPt);
if (prevPt.getX() == thisPt.getX() && prevPt.getY() == thisPt.getY()) prevPoint = -1; else
{
Point cPrev = wnd.databaseToScreen(prevPt);
drawLine(g, wnd, cThis.x + offX, cThis.y + offY, cPrev.x, cPrev.y);
}
}
int nextPoint = point + 1;
if (nextPoint >= points.length)
{
if (showWrap) nextPoint = 0; else
nextPoint = -1;
}
if (nextPoint >= 0 && points[nextPoint] != null)
{
nextPt = new Point2D.Double(ni.getAnchorCenterX() + points[nextPoint].getX(),
ni.getAnchorCenterY() + points[nextPoint].getY());
trans.transform(nextPt, nextPt);
if (nextPt.getX() == thisPt.getX() && nextPt.getY() == thisPt.getY()) nextPoint = -1; else
{
Point cNext = wnd.databaseToScreen(nextPt);
drawLine(g, wnd, cThis.x + offX, cThis.y + offY, cNext.x, cNext.y);
}
}
// draw arrows on the lines
if (offX == 0 && offY == 0 && points.length > 2 && prevPt != null && nextPt != null)
{
double arrowLen = Double.MAX_VALUE;
if (prevPoint >= 0) arrowLen = Math.min(thisPt.distance(prevPt), arrowLen);
if (nextPoint >= 0) arrowLen = Math.min(thisPt.distance(nextPt), arrowLen);
arrowLen /= 10;
double angleOfArrow = Math.PI * 0.8;
if (prevPoint >= 0)
{
Point2D prevCtr = new Point2D.Double((prevPt.getX()+thisPt.getX()) / 2,
(prevPt.getY()+thisPt.getY()) / 2);
double prevAngle = DBMath.figureAngleRadians(prevPt, thisPt);
Point2D prevArrow1 = new Point2D.Double(prevCtr.getX() + Math.cos(prevAngle+angleOfArrow) * arrowLen,
prevCtr.getY() + Math.sin(prevAngle+angleOfArrow) * arrowLen);
Point2D prevArrow2 = new Point2D.Double(prevCtr.getX() + Math.cos(prevAngle-angleOfArrow) * arrowLen,
prevCtr.getY() + Math.sin(prevAngle-angleOfArrow) * arrowLen);
Point cPrevCtr = wnd.databaseToScreen(prevCtr);
Point cPrevArrow1 = wnd.databaseToScreen(prevArrow1);
Point cPrevArrow2 = wnd.databaseToScreen(prevArrow2);
drawLine(g, wnd, cPrevCtr.x, cPrevCtr.y, cPrevArrow1.x, cPrevArrow1.y);
drawLine(g, wnd, cPrevCtr.x, cPrevCtr.y, cPrevArrow2.x, cPrevArrow2.y);
}
if (nextPoint >= 0)
{
Point2D nextCtr = new Point2D.Double((nextPt.getX()+thisPt.getX()) / 2,
(nextPt.getY()+thisPt.getY()) / 2);
double nextAngle = DBMath.figureAngleRadians(thisPt, nextPt);
Point2D nextArrow1 = new Point2D.Double(nextCtr.getX() + Math.cos(nextAngle+angleOfArrow) * arrowLen,
nextCtr.getY() + Math.sin(nextAngle+angleOfArrow) * arrowLen);
Point2D nextArrow2 = new Point2D.Double(nextCtr.getX() + Math.cos(nextAngle-angleOfArrow) * arrowLen,
nextCtr.getY() + Math.sin(nextAngle-angleOfArrow) * arrowLen);
Point cNextCtr = wnd.databaseToScreen(nextCtr);
Point cNextArrow1 = wnd.databaseToScreen(nextArrow1);
Point cNextArrow2 = wnd.databaseToScreen(nextArrow2);
drawLine(g, wnd, cNextCtr.x, cNextCtr.y, cNextArrow1.x, cNextArrow1.y);
drawLine(g, wnd, cNextCtr.x, cNextCtr.y, cNextArrow2.x, cNextArrow2.y);
}
}
}
// do not offset the node, just this point
offX = offY = 0;
}
}
// draw nodeInst outline
if ((offX == 0 && offY == 0) || point < 0)
{
Poly niPoly = getNodeInstOutline(ni);
boolean niOpened = (niPoly.getStyle() == Poly.Type.OPENED);
Point2D [] points = niPoly.getPoints();
drawOutlineFromPoints(wnd, g, points, offX, offY, niOpened, false);
}
// draw the selected port
if (pp != null)
{
//@TODO MAYBE I need the main color
// g.setColor(mainColor);
Poly poly = ni.getShapeOfPort(pp);
boolean opened = true;
Point2D [] points = poly.getPoints();
if (poly.getStyle() == Poly.Type.FILLED || poly.getStyle() == Poly.Type.CLOSED) opened = false;
if (poly.getStyle() == Poly.Type.CIRCLE || poly.getStyle() == Poly.Type.THICKCIRCLE ||
poly.getStyle() == Poly.Type.DISC)
{
double sX = points[0].distance(points[1]) * 2;
Point2D [] pts = Artwork.fillEllipse(points[0], sX, sX, 0, 360);
poly = new Poly(pts);
poly.transform(ni.rotateOut());
points = poly.getPoints();
} else if (poly.getStyle() == Poly.Type.CIRCLEARC)
{
double [] angles = ni.getArcDegrees();
double sX = points[0].distance(points[1]) * 2;
Point2D [] pts = Artwork.fillEllipse(points[0], sX, sX, angles[0], angles[1]);
poly = new Poly(pts);
poly.transform(ni.rotateOut());
points = poly.getPoints();
}
drawOutlineFromPoints(wnd, g, points, offX, offY, opened, false);
// g.setColor(mainColor);
// show name of port
if (ni.isCellInstance() && (g instanceof Graphics2D))
{
// only show name if port is wired (because all other situations already show the port)
boolean wired = false;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
if (con.getPortInst().getPortProto() == pp) { wired = true; break; }
}
if (wired)
{
Font font = new Font(User.getDefaultFont(), Font.PLAIN, (int)(1.5*EditWindow.getDefaultFontSize()));
GlyphVector v = wnd.getGlyphs(pp.getName(), font);
Point2D point = wnd.databaseToScreen(poly.getCenterX(), poly.getCenterY());
((Graphics2D)g).drawGlyphVector(v, (float)point.getX()+offX, (float)point.getY()+offY);
}
}
// if this is a port on an "example icon", show the equivalent port in the cell
// if (ni.isIconOfParent())
// {
// // find export in parent
// Export equiv = (Export)cell.findPortProto(pp.getName());
// if (equiv != null)
// {
// PortInst ePi = equiv.getOriginalPort();
// Poly ePoly = ePi.getPoly();
// Point eP = wnd.databaseToScreen(ePoly.getCenterX(), ePoly.getCenterY());
// Point p = wnd.databaseToScreen(poly.getCenterX(), poly.getCenterY());
// drawLine(g, wnd, eP.x, eP.y, p.x + offX, p.y + offY);
// }
// }
// highlight objects that are electrically connected to this object
// unless specified not to. HighlightConnected is set to false by addNetwork when
// it figures out what's connected and adds them manually. Because they are added
// in addNetwork, we shouldn't try and add connected objects here.
if (highlightConnected && onlyHighlight) {
Netlist netlist = cell.getNetlist();
if (netlist == null) return;
NodeInst originalNI = ni;
if (ni.isIconOfParent())
{
// find export in parent
Export equiv = (Export)cell.findPortProto(pp.getName());
if (equiv != null)
{
originalPi = equiv.getOriginalPort();
ni = originalPi.getNodeInst();
pp = originalPi.getPortProto();
}
}
Set<Network> networks = new HashSet<Network>();
networks = NetworkTool.getNetworksOnPort(originalPi, netlist, networks);
Set<Geometric> markObj = new HashSet<Geometric>();
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Name arcName = ai.getNameKey();
for (int i=0; i<arcName.busWidth(); i++) {
if (networks.contains(netlist.getNetwork(ai, i))) {
markObj.add(ai);
markObj.add(ai.getHeadPortInst().getNodeInst());
markObj.add(ai.getTailPortInst().getNodeInst());
break;
}
}
}
for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext(); ) {
Nodable no = it.next();
NodeInst oNi = no.getNodeInst();
if (oNi == originalNI) continue;
if (markObj.contains(ni)) continue;
boolean highlightNo = false;
for(Iterator<PortProto> eIt = no.getProto().getPorts(); eIt.hasNext(); )
{
PortProto oPp = eIt.next();
Name opName = oPp.getNameKey();
for (int j=0; j<opName.busWidth(); j++) {
if (networks.contains(netlist.getNetwork(no, oPp, j))) {
highlightNo = true;
break;
}
}
if (highlightNo) break;
}
if (highlightNo)
markObj.add(oNi);
}
//System.out.println("Search took "+com.sun.electric.database.text.TextUtils.getElapsedTime(System.currentTimeMillis()-start));
// draw lines along all of the arcs on the network
Stroke origStroke = g2.getStroke();
g2.setStroke(dashedLine);
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (!markObj.contains(ai)) continue;
Point c1 = wnd.databaseToScreen(ai.getHeadLocation());
Point c2 = wnd.databaseToScreen(ai.getTailLocation());
drawLine(g, wnd, c1.x, c1.y, c2.x, c2.y);
}
// draw dots in all connected nodes
g2.setStroke(solidLine);
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst oNi = it.next();
if (!markObj.contains(oNi)) continue;
Point c = wnd.databaseToScreen(oNi.getTrueCenter());
g.fillOval(c.x-4, c.y-4, 8, 8);
// connect the center dots to the input arcs
Point2D nodeCenter = oNi.getTrueCenter();
for(Iterator<Connection> pIt = oNi.getConnections(); pIt.hasNext(); )
{
Connection con = pIt.next();
ArcInst ai = con.getArc();
if (!markObj.contains(ai)) continue;
Point2D arcEnd = con.getLocation();
if (arcEnd.getX() != nodeCenter.getX() || arcEnd.getY() != nodeCenter.getY())
{
Point c1 = wnd.databaseToScreen(arcEnd);
Point c2 = wnd.databaseToScreen(nodeCenter);
//g2.setStroke(dottedLine);
//if (c1.distance(c2) < 15) g2.setStroke(solidLine);
drawLine(g, wnd, c1.x, c1.y, c2.x, c2.y);
}
}
}
g2.setStroke(origStroke);
}
}
}
// switch back to old color if switched
if (oldColor != null)
g.setColor(oldColor);
}
void getHighlightedEObjs(Highlighter highlighter, List<Geometric> list, boolean wantNodes, boolean wantArcs)
{
getHighlightedEObjsInternal(getGeometric(), list, wantNodes, wantArcs);
}
void getHighlightedNodes(Highlighter highlighter, List<NodeInst> list)
{
getHighlightedNodesInternal(getGeometric(), list);
}
void getHighlightedArcs(Highlighter highlighter, List<ArcInst> list)
{
getHighlightedArcsInternal(getGeometric(), list);
}
void getHighlightedNetworks(Set<Network> nets, Netlist netlist)
{
ElectricObject eObj = eobj;
if (eObj instanceof NodeInst)
{
NodeInst ni = (NodeInst)eObj;
if (ni.getNumPortInsts() == 1)
{
PortInst pi = ni.getOnlyPortInst();
if (pi != null) eObj = pi;
}
}
if (eObj instanceof PortInst)
{
PortInst pi = (PortInst)eObj;
nets = NetworkTool.getNetworksOnPort(pi, netlist, nets);
// boolean added = false;
// for(Iterator<Connection> aIt = pi.getNodeInst().getConnections(); aIt.hasNext(); )
// {
// Connection con = aIt.next();
// ArcInst ai = con.getArc();
// int wid = netlist.getBusWidth(ai);
// for(int i=0; i<wid; i++)
// {
// Network net = netlist.getNetwork(ai, i);
// if (net != null)
// {
// added = true;
// nets.add(net);
// }
// }
// }
// if (!added)
// {
// Network net = netlist.getNetwork(pi);
// if (net != null) nets.add(net);
// }
} else if (eObj instanceof ArcInst)
{
ArcInst ai = (ArcInst)eObj;
int width = netlist.getBusWidth(ai);
for(int i=0; i<width; i++)
{
Network net = netlist.getNetwork((ArcInst)eObj, i);
if (net != null) nets.add(net);
}
}
}
Rectangle2D getHighlightedArea(EditWindow wnd)
{
ElectricObject eObj = eobj;
if (eObj instanceof PortInst) eObj = ((PortInst)eObj).getNodeInst();
if (eObj instanceof Geometric)
{
Geometric geom = (Geometric)eObj;
return geom.getBounds();
}
return null;
}
public Geometric getGeometric()
{
Geometric retVal = null;
if (eobj instanceof PortInst) retVal = ((PortInst)eobj).getNodeInst(); else
if (eobj instanceof Geometric) retVal = (Geometric)eobj;
return retVal;
}
boolean overHighlighted(EditWindow wnd, int x, int y, Highlighter highlighter)
{
Point2D slop = wnd.deltaScreenToDatabase(Highlighter.EXACTSELECTDISTANCE*2, Highlighter.EXACTSELECTDISTANCE*2);
double directHitDist = slop.getX();
Point2D start = wnd.screenToDatabase(x, y);
Rectangle2D searchArea = new Rectangle2D.Double(start.getX(), start.getY(), 0, 0);
ElectricObject eobj = this.eobj;
if (eobj instanceof PortInst) eobj = ((PortInst)eobj).getNodeInst();
if (eobj instanceof Geometric)
{
boolean specialSelect = ToolBar.isSelectSpecial();
List<Highlight> gotAll = Highlighter.checkOutObject((Geometric)eobj, true, false, specialSelect, searchArea, wnd, directHitDist, false);
if (gotAll.size() == 0) return false;
boolean found = false;
for(Highlight got : gotAll)
{
if (!(got instanceof HighlightEOBJ))
System.out.println("Error?");
ElectricObject hObj = got.getElectricObject();
ElectricObject hReal = hObj;
if (hReal instanceof PortInst) hReal = ((PortInst)hReal).getNodeInst();
for(Highlight alreadyDone : highlighter.getHighlights())
{
if (!(alreadyDone instanceof HighlightEOBJ)) continue;
HighlightEOBJ alreadyHighlighted = (HighlightEOBJ)alreadyDone;
ElectricObject aHObj = alreadyHighlighted.getElectricObject();
ElectricObject aHReal = aHObj;
if (aHReal instanceof PortInst) aHReal = ((PortInst)aHReal).getNodeInst();
if (hReal == aHReal)
{
// found it: adjust the port/point
found = true;
if (hObj != aHObj || alreadyHighlighted.point != ((HighlightEOBJ)got).point)
{
alreadyHighlighted.eobj = got.getElectricObject();
alreadyHighlighted.point = ((HighlightEOBJ)got).point;
synchronized(highlighter) {
highlighter.setChanged(true);
}
}
break;
}
}
if (found) break;
}
return true;
}
return false;
}
public String getInfo()
{
String description = "";
ElectricObject realObj = eobj;
if (realObj instanceof PortInst)
realObj = ((PortInst)realObj).getNodeInst();
if (realObj instanceof NodeInst)
{
NodeInst ni = (NodeInst)realObj;
description = "Node " + ni.describe(true);
} else if (realObj instanceof ArcInst)
{
ArcInst ai = (ArcInst)eobj;
description = "Arc " + ai.describe(true);
}
return description;
}
}
/**
* A Highlight which calls the user's attention to an ElectricObject which happens to be a piece of text.
*/
class HighlightText extends Highlight
{
/** The highlighted object. */ protected final ElectricObject eobj;
/** The highlighted variable. */ protected final Variable.Key varKey;
public HighlightText(ElectricObject e, Cell c, Variable.Key key)
{
super(c);
this.eobj = e;
this.varKey = key;
Class cls = null;
if (key == NodeInst.NODE_NAME || key == NodeInst.NODE_PROTO)
cls = NodeInst.class;
else if (key == ArcInst.ARC_NAME)
cls = ArcInst.class;
else if (key == Export.EXPORT_NAME)
cls = Export.class;
if (cls != null && !cls.isInstance(e))
throw new IllegalArgumentException(key + " in " + e);
}
void internalDescribe(StringBuffer desc)
{
if (varKey != null)
{
if (varKey == NodeInst.NODE_NAME)
{
desc.append(", name: ");
desc.append(((NodeInst)eobj).getName());
} else if (varKey == NodeInst.NODE_PROTO)
{
desc.append(", instance: ");
desc.append(((NodeInst)eobj).getProto().getName());
} else if (varKey == ArcInst.ARC_NAME)
{
desc.append(", name: ");
desc.append(((ArcInst)eobj).getName());
} else if (varKey == Export.EXPORT_NAME)
{
desc.append(", export: ");
desc.append(((Export)eobj).getName());
} else
{
desc.append(", var: ");
desc.append(eobj.getParameterOrVariable(varKey).describe(-1));
}
}
}
public ElectricObject getElectricObject() { return eobj; }
// creating so HighlightText is not a public class
public boolean isHighlightText() { return true; }
public Variable.Key getVarKey() { return varKey; }
public boolean isValid()
{
if (!super.isValid()) return false;
if (eobj == null || varKey == null) return false;
if (!eobj.isLinked()) return false;
if (varKey == NodeInst.NODE_NAME ||
varKey == ArcInst.ARC_NAME ||
varKey == NodeInst.NODE_PROTO ||
varKey == Export.EXPORT_NAME) return true;
return eobj.getParameterOrVariable(varKey) != null;
}
boolean sameThing(Highlight obj)
{
if (this == obj) return (true);
// Consider already obj==null
if (obj == null || getClass() != obj.getClass())
return (false);
HighlightText other = (HighlightText)obj;
if (eobj != other.eobj) return false;
if (cell != other.cell) return false;
if (varKey != other.varKey) return false;
return true;
}
public void showInternalHighlight(EditWindow wnd, Graphics g, int highOffX, int highOffY,
boolean onlyHighlight, boolean setConnected)
{
Graphics2D g2 = (Graphics2D)g;
Point2D [] points = Highlighter.describeHighlightText(wnd, eobj, varKey);
if (points == null) return;
Point2D [] linePoints = new Point2D[2];
for(int i=0; i<points.length; i += 2)
{
linePoints[0] = points[i];
linePoints[1] = points[i+1];
drawOutlineFromPoints(wnd, g, linePoints, highOffX, highOffY, false, false);
}
if (onlyHighlight)
{
// this is the only thing highlighted: show the attached object
ElectricObject eObj = eobj;
if (eObj != null && eObj instanceof Geometric)
{
Geometric geom = (Geometric)eObj;
if (geom instanceof ArcInst || !((NodeInst)geom).isInvisiblePinWithText())
{
Point2D objCtr = geom.getTrueCenter();
Point c = wnd.databaseToScreen(objCtr);
TextDescriptor td = eobj.getTextDescriptor(varKey);
double locX = objCtr.getX() + td.getXOff();
double locY = objCtr.getY() + td.getYOff();
Point2D txtAnchor = new Point2D.Double(locX, locY);
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
AffineTransform trans = ni.rotateOut();
trans.transform(txtAnchor, txtAnchor);
}
Point a = wnd.databaseToScreen(txtAnchor);
int cX = a.x, cY = a.y;
// int lowX = Integer.MAX_VALUE, highX = Integer.MIN_VALUE;
// int lowY = Integer.MAX_VALUE, highY = Integer.MIN_VALUE;
// for(int i=0; i<points.length; i++)
// {
// Point a = wnd.databaseToScreen(points[i]);
// if (a.x < lowX) lowX = a.x;
// if (a.x > highX) highX = a.x;
// if (a.y < lowY) lowY = a.y;
// if (a.y > highY) highY = a.y;
// }
// int cX = (lowX+highX)/2;
// int cY = (lowY+highY)/2;
if (Math.abs(cX - c.x) > 4 || Math.abs(cY - c.y) > 4)
{
g.fillOval(c.x-4, c.y-4, 8, 8);
g2.setStroke(dottedLine);
drawLine(g, wnd, c.x, c.y, cX, cY);
g2.setStroke(solidLine);
}
}
}
}
}
// /**
// * Method to tell whether this Highlight is text that stays with its node.
// * The two possibilities are (1) text on invisible pins
// * (2) export names, when the option to move exports with their labels is requested.
// * @return true if this Highlight is text that should move with its node.
// */
// public boolean nodeMovesWithText()
// {
// if (varKey != null)
// {
// // moving variable text
// if (!(eobj instanceof NodeInst)) return false;
// NodeInst ni = (NodeInst)eobj;
// if (ni.isInvisiblePinWithText()) return true;
// } else
// {
// // moving export text
// if (!(eobj instanceof Export)) return false;
// Export pp = (Export)eobj;
// if (pp.getOriginalPort().getNodeInst().getProto() == Generic.tech.invisiblePinNode) return true;
// if (User.isMoveNodeWithExport()) return true;
// }
// return false;
// }
public Geometric getGeometric()
{
if (DisplayedText.objectMovesWithText(eobj, varKey))
{
if (eobj instanceof Export) return ((Export)eobj).getOriginalPort().getNodeInst();
if (eobj instanceof Geometric) return (Geometric)eobj;
}
return null;
}
void getHighlightedEObjs(Highlighter highlighter, List<Geometric> list, boolean wantNodes, boolean wantArcs)
{
getHighlightedEObjsInternal(getGeometric(), list, wantNodes, wantArcs);
}
void getHighlightedNodes(Highlighter highlighter, List<NodeInst> list)
{
getHighlightedNodesInternal(getGeometric(), list);
}
void getHighlightedArcs(Highlighter highlighter, List<ArcInst> list)
{
getHighlightedArcsInternal(getGeometric(), list);
}
void getHighlightedNetworks(Set<Network> nets, Netlist netlist)
{
if (/*varKey == null &&*/ eobj instanceof Export)
{
Export pp = (Export)eobj;
int width = netlist.getBusWidth(pp);
for(int i=0; i<width; i++)
{
Network net = netlist.getNetwork(pp, i);
if (net != null) nets.add(net);
}
}
}
DisplayedText makeDisplayedText()
{
if (varKey != null)
return new DisplayedText(eobj, varKey);
return null;
}
void getHighlightedText(List<DisplayedText> list, boolean unique, List<Highlight> getHighlights)
{
DisplayedText dt = makeDisplayedText();
if (dt == null) return;
if (list.contains(dt)) return;
// if this text is on a selected object, don't include the text
if (unique)
{
ElectricObject onObj = null;
if (varKey != null)
{
if (eobj instanceof Export)
{
onObj = ((Export)eobj).getOriginalPort().getNodeInst();
} else if (eobj instanceof PortInst)
{
onObj = ((PortInst)eobj).getNodeInst();
} else if (eobj instanceof Geometric)
{
onObj = eobj;
}
}
// now see if the object is in the list
if (eobj != null)
{
boolean found = false;
for(Highlight oH : getHighlights)
{
if (!(oH instanceof HighlightEOBJ)) continue;
ElectricObject fobj = ((HighlightEOBJ)oH).eobj;
if (fobj instanceof PortInst) fobj = ((PortInst)fobj).getNodeInst();
if (fobj == onObj) { found = true; break; }
}
if (found) return;
}
}
// add this text
list.add(dt);
}
Rectangle2D getHighlightedArea(EditWindow wnd)
{
if (wnd != null)
{
Poly poly = eobj.computeTextPoly(wnd, varKey);
if (poly != null) return poly.getBounds2D();
}
return null;
}
boolean overHighlighted(EditWindow wnd, int x, int y, Highlighter highlighter)
{
Point2D start = wnd.screenToDatabase(x, y);
Poly poly = eobj.computeTextPoly(wnd, varKey);
if (poly != null)
if (poly.isInside(start)) return true;
return false;
}
public String describe()
{
String description = "Unknown";
if (varKey != null && eobj != null)
{
if (varKey == NodeInst.NODE_NAME)
{
description = "Node name for " + ((NodeInst)eobj).describe(true);
} else if (varKey == ArcInst.ARC_NAME)
{
description = "Arc name for " + ((ArcInst)eobj).describe(true);
} else if (varKey == Export.EXPORT_NAME)
{
description = "Export '" + ((Export)eobj).getName() + "'";
} else if (varKey == NodeInst.NODE_PROTO)
{
description = "Cell instance name " + ((NodeInst)eobj).describe(true);
} else
{
Variable var = eobj.getParameterOrVariable(varKey);
if (var != null) description = var.getFullDescription(eobj);
}
}
return description;
}
public String getInfo()
{
return "Text: " + describe();
}
}