/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ThreeDTab.java
*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
*
* 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.dialogs.options;
import com.sun.electric.database.text.Setting;
import com.sun.electric.technology.Layer;
import com.sun.electric.tool.user.Resources;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.dialogs.PreferencesFrame;
import com.sun.electric.tool.user.ui.LayerVisibility;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.GenMath;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* Class to handle the "3D" tab of the Preferences dialog.
*/
public class ThreeDTab extends PreferencePanel
{
/** Creates new form ThreeDTab depending on if 3Dplugin is on or not */
public static ThreeDTab create3DTab(PreferencesFrame parent, boolean modal)
{
ThreeDTab tab = null;
Class<?> plugin = Resources.get3DClass("ui.JThreeDTab");
if (plugin != null)
{
try
{
Constructor instance = plugin.getDeclaredConstructor(new Class[]{PreferencesFrame.class, Boolean.TYPE});
Object panel = instance.newInstance(new Object[] {parent, new Boolean(modal)});
tab = (ThreeDTab)panel;
}
catch (Exception e)
{
System.out.println("Cannot create instance of 3D plugin JThreeDTab: " + e.getMessage());
}
}
else
tab = new ThreeDTab(parent, modal);
return tab;
}
/** Creates new form ThreeDTab */
public ThreeDTab(PreferencesFrame parent, boolean modal)
{
super(parent, modal);
initComponents();
}
/** return the panel to use for user preferences. */
@Override
public JPanel getUserPreferencesPanel() { return threeD; }
/** return the name of this preferences tab. */
@Override
public String getName() { return "3D"; }
private boolean initial3DTextChanging = false;
private JList threeDLayerList;
private DefaultListModel threeDLayerModel;
protected Map<Layer,GenMath.MutableDouble> threeDThicknessMap, threeDDistanceMap;
private JPanel threeDSideView;
/**
* Method called at the start of the dialog.
* Caches current values and displays them in the 3D tab.
*/
@Override
public void init()
{
threeDTechnology.setText("Layer cross section for technology '" + curTech.getTechName() + "'");
threeDLayerModel = new DefaultListModel();
threeDLayerList = new JList(threeDLayerModel);
threeDLayerList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
threeDLayerPane.setViewportView(threeDLayerList);
threeDLayerList.clearSelection();
threeDLayerList.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt) { threeDValuesChanged(false); }
});
threeDThicknessMap = new HashMap<Layer,GenMath.MutableDouble>();
threeDDistanceMap = new HashMap<Layer,GenMath.MutableDouble>();
// Sorted by Height to be consistent with LayersTab
for(Layer layer : curTech.getLayersSortedByName())
{
if (layer.isPseudoLayer()) continue;
threeDLayerModel.addElement(layer.getName());
double thickness = getDouble(layer.getThicknessSetting());
double distance = getDouble(layer.getDistanceSetting());
threeDThicknessMap.put(layer, new GenMath.MutableDouble(thickness));
threeDDistanceMap.put(layer, new GenMath.MutableDouble(distance));
}
threeDLayerList.setSelectedIndex(0);
threeDHeight.getDocument().addDocumentListener(new ThreeDInfoDocumentListener(this));
threeDThickness.getDocument().addDocumentListener(new ThreeDInfoDocumentListener(this));
threeDSideView = new ThreeDSideView(this);
threeDSideView.setMinimumSize(new java.awt.Dimension(200, 450));
threeDSideView.setPreferredSize(new java.awt.Dimension(200, 450));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 1;
gbc.gridwidth = 2; gbc.gridheight = 4;
gbc.weightx = 0.5; gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(threeDSideView, gbc);
threeDValuesChanged(false);
}
private class ThreeDSideView extends JPanel
implements MouseMotionListener, MouseListener
{
ThreeDTab dialog;
double lowHeight = Double.MAX_VALUE, highHeight = Double.MIN_VALUE;
ThreeDSideView(ThreeDTab dialog)
{
this.dialog = dialog;
addMouseListener(this);
addMouseMotionListener(this);
LayerVisibility lv = LayerVisibility.getLayerVisibility();
for(Iterator<Layer> it = dialog.curTech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
if (layer.isPseudoLayer()) continue;
if (!lv.isVisible(layer)) continue;
GenMath.MutableDouble thickness = dialog.threeDThicknessMap.get(layer);
GenMath.MutableDouble distance = dialog.threeDDistanceMap.get(layer);
double dis = distance.doubleValue();
double thick = thickness.doubleValue() / 2;
double valLow = dis - thick;
double valHig = dis + thick;
if (valLow < lowHeight)
lowHeight = valLow;
if (valHig > highHeight)
highHeight = valHig;
}
lowHeight -= 4;
highHeight += 4;
}
/**
* Method to repaint this ThreeDSideView.
*/
@Override
public void paint(Graphics g)
{
Dimension dim = getSize();
g.setColor(Color.WHITE);
g.fillRect(0, 0, dim.width, dim.height);
g.setColor(Color.BLACK);
g.drawLine(0, 0, 0, dim.height-1);
g.drawLine(0, dim.height-1, dim.width-1, dim.height-1);
g.drawLine(dim.width-1, dim.height-1, dim.width-1, 0);
g.drawLine(dim.width-1, 0, 0, 0);
String layerName = (String)dialog.threeDLayerList.getSelectedValue();
Layer selectedLayer = dialog.curTech.findLayer(layerName);
for(Iterator<Layer> it = dialog.curTech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
if (layer.isPseudoLayer()) continue;
//if (!layer.isVisible()) continue;
if (layer == selectedLayer) g.setColor(Color.RED); else
g.setColor(Color.BLACK);
GenMath.MutableDouble thickness = dialog.threeDThicknessMap.get(layer);
GenMath.MutableDouble distance = dialog.threeDDistanceMap.get(layer);
double dis = distance.doubleValue() + thickness.doubleValue()/2;
int yValue = dim.height - (int)((dis - lowHeight) / (highHeight - lowHeight) * dim.height + 0.5);
int yHeight = (int)(thickness.doubleValue() / (highHeight - lowHeight) * dim.height + 0.5);
if (yHeight == 0)
{
g.drawLine(0, yValue, dim.width/3, yValue);
} else
{
//yHeight -= 4;
int firstPart = dim.width / 6;
int pointPos = dim.width / 4;
g.drawLine(0, yValue-yHeight/2, firstPart, yValue-yHeight/2);
g.drawLine(0, yValue+yHeight/2, firstPart, yValue+yHeight/2);
g.drawLine(firstPart, yValue-yHeight/2, pointPos, yValue);
g.drawLine(firstPart, yValue+yHeight/2, pointPos, yValue);
g.drawLine(pointPos, yValue, dim.width/3, yValue);
}
String string = layer.getName();
Font font = new Font(User.getDefaultFont(), Font.PLAIN, 9);
g.setFont(font);
FontRenderContext frc = new FontRenderContext(null, true, true);
GlyphVector gv = font.createGlyphVector(frc, string);
LineMetrics lm = font.getLineMetrics(string, frc);
double txtHeight = lm.getHeight();
Graphics2D g2 = (Graphics2D)g;
g2.drawGlyphVector(gv, dim.width/3 + 1, (float)(yValue + txtHeight/2) - lm.getDescent());
}
}
// the MouseListener events
public void mousePressed(MouseEvent evt)
{
Dimension dim = getSize();
String layerName = (String)dialog.threeDLayerList.getSelectedValue();
Layer selectedLayer = dialog.curTech.findLayer(layerName);
GenMath.MutableDouble height = dialog.threeDDistanceMap.get(selectedLayer);
int yValue = dim.height - (int)((height.doubleValue() - lowHeight) / (highHeight - lowHeight) * dim.height + 0.5);
if (Math.abs(yValue - evt.getY()) > 5)
{
int bestDist = dim.height;
for(Iterator<Layer> it = dialog.curTech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
if (layer.isPseudoLayer()) continue;
height = dialog.threeDDistanceMap.get(layer);
yValue = dim.height - (int)((height.doubleValue() - lowHeight) / (highHeight - lowHeight) * dim.height + 0.5);
int dist = Math.abs(yValue - evt.getY());
if (dist < bestDist)
{
bestDist = dist;
selectedLayer = layer;
}
}
dialog.threeDLayerList.setSelectedValue(selectedLayer.getName(), true);
dialog.threeDValuesChanged(false);
}
}
public void mouseReleased(MouseEvent evt) {}
public void mouseClicked(MouseEvent evt) {}
public void mouseEntered(MouseEvent evt) {}
public void mouseExited(MouseEvent evt) {}
// the MouseMotionListener events
public void mouseMoved(MouseEvent evt) {}
public void mouseDragged(MouseEvent evt)
{
Dimension dim = getSize();
String layerName = (String)dialog.threeDLayerList.getSelectedValue();
Layer layer = dialog.curTech.findLayer(layerName);
GenMath.MutableDouble height = threeDDistanceMap.get(layer);
double newHeight = (double)(dim.height - evt.getY()) / dim.height * (highHeight - lowHeight) + lowHeight;
if (height.doubleValue() != newHeight)
{
height.setValue(newHeight);
dialog.threeDHeight.setText(TextUtils.formatDistance(newHeight));
repaint();
}
}
}
/**
* Class to handle changes to the thickness or height.
*/
private static class ThreeDInfoDocumentListener implements DocumentListener
{
ThreeDTab dialog;
ThreeDInfoDocumentListener(ThreeDTab dialog) { this.dialog = dialog; }
public void changedUpdate(DocumentEvent e) { dialog.threeDValuesChanged(true); }
public void insertUpdate(DocumentEvent e) { dialog.threeDValuesChanged(true); }
public void removeUpdate(DocumentEvent e) { dialog.threeDValuesChanged(true); }
}
private void threeDValuesChanged(boolean set)
{
if (!set)
initial3DTextChanging = true;
else if (initial3DTextChanging) return;
String layerName = (String)threeDLayerList.getSelectedValue();
Layer layer = curTech.findLayer(layerName);
if (layer == null) return;
GenMath.MutableDouble thickness = threeDThicknessMap.get(layer);
GenMath.MutableDouble height = threeDDistanceMap.get(layer);
if (set)
{
thickness.setValue(TextUtils.atofDistance(threeDThickness.getText()));
height.setValue(TextUtils.atofDistance(threeDHeight.getText()));
}
else
{
threeDHeight.setText(TextUtils.formatDistance(height.doubleValue()));
threeDThickness.setText(TextUtils.formatDistance(thickness.doubleValue()));
}
if (!set) initial3DTextChanging = false;
threeDSideView.repaint();
}
/**
* Method called when the "OK" panel is hit.
* Updates any changed fields in the 3D tab.
*/
@Override
public void term()
{
for(Iterator<Layer> it = curTech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
assert !layer.isPseudoLayer();
GenMath.MutableDouble thickness = threeDThicknessMap.get(layer);
GenMath.MutableDouble height = threeDDistanceMap.get(layer);
setDouble(layer.getThicknessSetting(), thickness.doubleValue());
setDouble(layer.getDistanceSetting(), height.doubleValue());
}
}
/**
* Method called when the factory reset is requested.
*/
@Override
public void reset()
{
for(Iterator<Layer> it = curTech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
assert !layer.isPseudoLayer();
Setting thicknessSetting = layer.getThicknessSetting();
Setting distanceSetting = layer.getDistanceSetting();
setDouble(thicknessSetting, thicknessSetting.getDoubleFactoryValue());
setDouble(distanceSetting, distanceSetting.getDoubleFactoryValue());
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
private void initComponents() {//GEN-BEGIN:initComponents
java.awt.GridBagConstraints gridBagConstraints;
threeD = new javax.swing.JPanel();
threeDTechnology = new javax.swing.JLabel();
threeDLayerPane = new javax.swing.JScrollPane();
jLabel45 = new javax.swing.JLabel();
jLabel47 = new javax.swing.JLabel();
threeDThickness = new javax.swing.JTextField();
threeDHeight = new javax.swing.JTextField();
getContentPane().setLayout(new java.awt.GridBagLayout());
setTitle("Edit Options");
setName("");
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog(evt);
}
});
threeD.setLayout(new java.awt.GridBagLayout());
threeDTechnology.setText("Layer cross section for technology:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(threeDTechnology, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 0.5;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(threeDLayerPane, gridBagConstraints);
jLabel45.setText("Thickness:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(jLabel45, gridBagConstraints);
jLabel47.setText("Distance:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(jLabel47, gridBagConstraints);
threeDThickness.setColumns(6);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(threeDThickness, gridBagConstraints);
threeDHeight.setColumns(6);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
threeD.add(threeDHeight, gridBagConstraints);
getContentPane().add(threeD, new java.awt.GridBagConstraints());
pack();
}//GEN-END:initComponents
/** Closes the dialog */
private void closeDialog(java.awt.event.WindowEvent evt)//GEN-FIRST:event_closeDialog
{
setVisible(false);
dispose();
}//GEN-LAST:event_closeDialog
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel45;
private javax.swing.JLabel jLabel47;
private javax.swing.JPanel threeD;
private javax.swing.JTextField threeDHeight;
private javax.swing.JScrollPane threeDLayerPane;
private javax.swing.JLabel threeDTechnology;
private javax.swing.JTextField threeDThickness;
// End of variables declaration//GEN-END:variables
}