/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: OutlineListener.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.geometry.EPoint;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.CircuitChangeJobs;
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.menus.EditMenu;
import java.awt.Point;
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;
/**
* Class to make changes to the outline information on a node.
*/
public class OutlineListener
implements MouseMotionListener, MouseListener, MouseWheelListener, KeyListener
{
public static OutlineListener theOne = new OutlineListener();
private double oldX, oldY;
private boolean doingMotionDrag;
private int point;
private NodeInst outlineNode;
private Highlight high;
private OutlineListener() {}
public void setNode(NodeInst ni)
{
EditWindow wnd = EditWindow.getCurrent();
Highlighter highlighter = wnd.getHighlighter();
high = highlighter.getOneHighlight();
point = 0;
outlineNode = ni;
Point2D [] origPoints = outlineNode.getTrace();
if (origPoints == null)
{
// node has no points: fake some
if (ni.getFunction() == PrimitiveNode.Function.NODE)
{
new InitializePoints(this, ni);
return;
}
}
high.setPoint(0);
if (wnd != null) wnd.fullRepaint();
}
public void deletePoint()
{
// delete a point
Point2D [] origPoints = outlineNode.getTrace();
if (origPoints.length <= 2)
{
System.out.println("Cannot delete the last point on an outline");
return;
}
AffineTransform trans = outlineNode.rotateOutAboutTrueCenter();
Point2D [] newPoints = new Point2D[origPoints.length-1];
int pt = point;
int j = 0;
for(int i=0; i<origPoints.length; i++)
{
if (i == pt) continue;
if (origPoints[j] != null)
{
newPoints[j] = new Point2D.Double(outlineNode.getAnchorCenterX() + origPoints[i].getX(),
outlineNode.getAnchorCenterY() + origPoints[i].getY());
trans.transform(newPoints[j], newPoints[j]);
}
j++;
}
if (pt > 0) pt--;
setNewPoints(newPoints, pt);
}
/**
* Class to initialize the points on an outline node that has no points.
*/
private static class InitializePoints extends Job
{
private transient OutlineListener listener;
private NodeInst ni;
protected InitializePoints(OutlineListener listener, NodeInst ni)
{
super("Initialize Outline Points", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.listener = listener;
this.ni = ni;
startJob();
}
public boolean doIt() throws JobException
{
EPoint [] points = new EPoint[4];
double halfWid = ni.getXSize() / 2;
double halfHei = ni.getYSize() / 2;
double cX = ni.getAnchorCenterX();
double cY = ni.getAnchorCenterY();
points[0] = new EPoint(cX-halfWid, cY-halfHei);
points[1] = new EPoint(cX-halfWid, cY+halfHei);
points[2] = new EPoint(cX+halfWid, cY+halfHei);
points[3] = new EPoint(cX+halfWid, cY-halfHei);
ni.setTrace(points);
return true;
}
public void terminateOK()
{
listener.high.setPoint(0);
EditWindow wnd = EditWindow.getCurrent();
if (wnd != null) wnd.fullRepaint();
}
}
public void mousePressed(MouseEvent evt)
{
int x = evt.getX();
int y = evt.getY();
EditWindow wnd = (EditWindow)evt.getSource();
Highlighter highlighter = wnd.getHighlighter();
// show "get info" on double-click
if (evt.getClickCount() == 2)
{
if (highlighter.getNumHighlights() >= 1)
{
EditMenu.getInfoCommand(true);
return;
}
}
// right click: add a point
if (ClickZoomWireListener.isRightMouse(evt))
{
// add a point
AffineTransform trans = outlineNode.rotateOutAboutTrueCenter();
Point2D [] origPoints = outlineNode.getTrace();
if (origPoints == null)
{
Point2D [] newPoints = new Point2D[1];
newPoints[0] = new Point2D.Double(outlineNode.getAnchorCenterX(), outlineNode.getAnchorCenterY());
EditWindow.gridAlign(newPoints[0]);
setNewPoints(newPoints, 0);
point = 0;
oldX = newPoints[point].getX();
oldY = newPoints[point].getY();
} else if (origPoints.length == 1)
{
Point2D [] newPoints = new Point2D[2];
newPoints[0] = new Point2D.Double(outlineNode.getAnchorCenterX() + origPoints[0].getX(),
outlineNode.getAnchorCenterY() + origPoints[0].getY());
trans.transform(newPoints[0], newPoints[0]);
EditWindow.gridAlign(newPoints[0]);
newPoints[1] = new Point2D.Double(newPoints[0].getX() + 2, newPoints[0].getY() + 2);
EditWindow.gridAlign(newPoints[1]);
setNewPoints(newPoints, 1);
point = 1;
oldX = newPoints[point].getX();
oldY = newPoints[point].getY();
} else
{
Point2D [] newPoints = new Point2D[origPoints.length+1];
int j = 0;
for(int i=0; i<origPoints.length; i++)
{
// copy the original point
if (origPoints[i] != null)
{
newPoints[j] = new Point2D.Double(outlineNode.getAnchorCenterX() + origPoints[i].getX(),
outlineNode.getAnchorCenterY() + origPoints[i].getY());
}
j++;
if (i == point)
{
// found the selected point, make the insertion
newPoints[j] = wnd.screenToDatabase(x, y);
EditWindow.gridAlign(newPoints[j]);
j++;
}
}
trans.transform(newPoints, 0, newPoints, 0, newPoints.length);
setNewPoints(newPoints, point+1);
oldX = newPoints[point+1].getX();
oldY = newPoints[point+1].getY();
}
doingMotionDrag = true;
wnd.repaint();
return;
}
// standard click-and-drag: see if cursor is over anything
Point2D pt = wnd.screenToDatabase(x, y);
Highlight found = highlighter.findObject(pt, wnd, true, false, false, false, true, false, false);
doingMotionDrag = false;
if (found != null)
{
high = highlighter.getOneHighlight();
assert (high == found);
outlineNode = (NodeInst)highlighter.getOneElectricObject(NodeInst.class);
if (high != null && outlineNode != null)
{
point = high.getPoint();
Point2D [] origPoints = outlineNode.getTrace();
if (origPoints != null)
{
doingMotionDrag = true;
EditWindow.gridAlign(pt);
oldX = pt.getX();
oldY = pt.getY();
}
}
}
wnd.repaint();
}
public void mouseReleased(MouseEvent evt)
{
EditWindow wnd = (EditWindow)evt.getSource();
if (wnd == null) return;
Highlighter highlighter = wnd.getHighlighter();
// handle moving the selected point
if (doingMotionDrag)
{
doingMotionDrag = false;
int newX = evt.getX();
int newY = evt.getY();
Point2D curPt = wnd.screenToDatabase(newX, newY);
EditWindow.gridAlign(curPt);
highlighter.setHighlightOffset(0, 0);
moveSelectedPoint(curPt.getX() - oldX, curPt.getY() - oldY);
wnd.fullRepaint();
}
}
public void mouseClicked(MouseEvent evt) {}
public void mouseEntered(MouseEvent evt) {}
public void mouseExited(MouseEvent evt) {}
public void mouseMoved(MouseEvent evt) {}
public void mouseDragged(MouseEvent evt)
{
EditWindow wnd = (EditWindow)evt.getSource();
if (wnd == null) return;
Highlighter highlighter = wnd.getHighlighter();
int newX = evt.getX();
int newY = evt.getY();
Point2D curPt = wnd.screenToDatabase(newX, newY);
EditWindow.gridAlign(curPt);
Point pt = wnd.databaseToScreen(curPt.getX(), curPt.getY());
Point gridPt = wnd.databaseToScreen(oldX, oldY);
// show moving of the selected point
if (doingMotionDrag)
{
highlighter.setHighlightOffset(pt.x - gridPt.x, pt.y - gridPt.y);
wnd.repaint();
return;
}
wnd.repaint();
}
public void mouseWheelMoved(MouseWheelEvent evt)
{
}
public void keyPressed(KeyEvent evt)
{
int chr = evt.getKeyCode();
EditWindow wnd = (EditWindow)evt.getSource();
Cell cell = wnd.getCell();
if (cell == null) return;
if (chr == KeyEvent.VK_PERIOD)
{
// advance to next point
Point2D [] origPoints = outlineNode.getTrace();
int nextPoint = point + 1;
if (nextPoint >= origPoints.length) nextPoint = 0;
high.setPoint(point = nextPoint);
wnd.repaint();
} else if (chr == KeyEvent.VK_COMMA)
{
// backup to previous point
Point2D [] origPoints = outlineNode.getTrace();
int prevPoint = point - 1;
if (prevPoint < 0) prevPoint = origPoints.length - 1;
high.setPoint(point = prevPoint);
wnd.repaint();
}
}
public void keyReleased(KeyEvent evt) {}
public void keyTyped(KeyEvent evt) {}
public void moveSelectedPoint(double dx, double dy)
{
Point2D [] origPoints = outlineNode.getTrace();
if (origPoints == null) return;
Point2D [] newPoints = new Point2D[origPoints.length];
for(int i=0; i<origPoints.length; i++)
{
if (origPoints[i] != null)
newPoints[i] = new Point2D.Double(outlineNode.getAnchorCenterX() + origPoints[i].getX(),
outlineNode.getAnchorCenterY() + origPoints[i].getY());
}
AffineTransform trans = outlineNode.rotateOutAboutTrueCenter();
for(int i=0; i<newPoints.length; i++)
{
if (newPoints[i] != null)
trans.transform(newPoints[i], newPoints[i]);
}
if (newPoints[point] != null)
newPoints[point].setLocation(newPoints[point].getX()+dx, newPoints[point].getY()+dy);
setNewPoints(newPoints, point);
}
private void setNewPoints(Point2D [] newPoints, int newPoint)
{
EPoint [] pts = new EPoint[newPoints.length];
for(int i=0; i<newPoints.length; i++)
{
if (newPoints[i] != null)
pts[i] = new EPoint(newPoints[i].getX(), newPoints[i].getY());
}
new SetPoints(this, outlineNode, pts, newPoint);
}
/**
* Class to change the points on an outline node.
*/
private static class SetPoints extends Job
{
private transient OutlineListener listener;
private NodeInst ni;
private EPoint [] pts;
private int newPoint;
protected SetPoints(OutlineListener listener, NodeInst ni, EPoint [] pts, int newPoint)
{
super("Change Outline Points", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.listener = listener;
this.ni = ni;
this.pts = pts;
this.newPoint = newPoint;
startJob();
}
public boolean doIt() throws JobException
{
// make sure outline adjustment is allowed
if (CircuitChangeJobs.cantEdit(ni.getParent(), ni, true, false, true) != 0) return false;
// get the extent of the data
ni.setTrace(pts);
return true;
}
public void terminateOK()
{
listener.high.setPoint(listener.point = newPoint);
}
}
}