/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LayoutText.java
*
* Copyright (c) 2003, 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;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.util.TextUtils;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.util.Iterator;
/**
* Class to handle the "Layout Text" dialog.
*/
public class LayoutText extends EDialog
{
private static int lastSize = 12;
private static double lastScale = 1;
private static double lastSeparation = 0;
private static boolean lastItalic = false;
private static boolean lastBold = false;
private static boolean lastUnderline = false;
private static boolean lastInvertDots = false;
private static String lastFont = User.getDefaultFont();
private static String lastLayer = null;
private static String lastMessage = null;
/** Creates new form Layout Text */
public LayoutText(Frame parent)
{
super(parent, true);
initComponents();
getRootPane().setDefaultButton(ok);
// make all text fields select-all when entered
EDialog.makeTextFieldSelectAllOnTab(textSize);
EDialog.makeTextFieldSelectAllOnTab(textScale);
EDialog.makeTextFieldSelectAllOnTab(dotSeparation);
Technology tech = Technology.getCurrent();
textSize.setText(Integer.toString(lastSize));
textScale.setText(TextUtils.formatDouble(lastScale));
dotSeparation.setText(TextUtils.formatDistance(lastSeparation, tech));
textItalic.setSelected(lastItalic);
textBold.setSelected(lastBold);
textUnderline.setSelected(lastUnderline);
invertDots.setSelected(lastInvertDots);
for(Iterator<PrimitiveNode> it = tech.getNodes(); it.hasNext(); )
{
PrimitiveNode np = it.next();
if (np.getFunction() == PrimitiveNode.Function.NODE)
textLayer.addItem(np.getName());
}
if (lastLayer != null)
textLayer.setSelectedItem(lastLayer);
if (lastMessage != null)
textMessage.setText(lastMessage);
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
for(int i=0; i<fonts.length; i++)
textFont.addItem(fonts[i].getFontName());
if (lastFont != null)
textFont.setSelectedItem(lastFont);
// have fields update the message display
textFont.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { updateMessageField(); }
});
textItalic.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { updateMessageField(); }
});
textBold.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { updateMessageField(); }
});
textSize.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { updateMessageField(); }
});
finishInitialization();
}
protected void escapePressed() { cancel(null); }
/**
* Method called when a dialog field has changed, and the message area must be redisplayed.
*/
private void updateMessageField()
{
String fontName = (String)textFont.getSelectedItem();
int fontStyle = Font.PLAIN;
if (textItalic.isSelected()) fontStyle |= Font.ITALIC;
if (textBold.isSelected()) fontStyle |= Font.BOLD;
int size = TextUtils.atoi(textSize.getText());
Font theFont = new Font(fontName, fontStyle, size);
if (theFont != null)
textMessage.setFont(theFont);
}
/** 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.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
cancel = new javax.swing.JButton();
ok = new javax.swing.JButton();
jLabel1 = new javax.swing.JLabel();
textSize = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
textScale = new javax.swing.JTextField();
jLabel3 = new javax.swing.JLabel();
dotSeparation = new javax.swing.JTextField();
jLabel4 = new javax.swing.JLabel();
textFont = new javax.swing.JComboBox();
textItalic = new javax.swing.JCheckBox();
textBold = new javax.swing.JCheckBox();
jLabel5 = new javax.swing.JLabel();
textLayer = new javax.swing.JComboBox();
jLabel6 = new javax.swing.JLabel();
textUnderline = new javax.swing.JCheckBox();
textMessage = new javax.swing.JTextArea();
invertDots = new javax.swing.JCheckBox();
getContentPane().setLayout(new java.awt.GridBagLayout());
setTitle("Make Layout Text");
setName("");
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog(evt);
}
});
cancel.setText("Cancel");
cancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancel(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 8;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.weightx = 0.5;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(cancel, gridBagConstraints);
ok.setText("OK");
ok.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ok(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 8;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.weightx = 0.5;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(ok, gridBagConstraints);
jLabel1.setText("Size (max 63):");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(jLabel1, gridBagConstraints);
textSize.setColumns(8);
textSize.setText(" ");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textSize, gridBagConstraints);
jLabel2.setText("Scale factor:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(jLabel2, gridBagConstraints);
textScale.setColumns(8);
textScale.setText(" ");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textScale, gridBagConstraints);
jLabel3.setText("Dot separation (units):");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(jLabel3, gridBagConstraints);
dotSeparation.setColumns(8);
dotSeparation.setText(" ");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(dotSeparation, gridBagConstraints);
jLabel4.setText("Font:");
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);
getContentPane().add(jLabel4, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textFont, gridBagConstraints);
textItalic.setText("Italic");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textItalic, gridBagConstraints);
textBold.setText("Bold");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textBold, gridBagConstraints);
jLabel5.setText("Layer:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(jLabel5, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 5;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textLayer, gridBagConstraints);
jLabel6.setText("Message:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(jLabel6, gridBagConstraints);
textUnderline.setText("Underline");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 4;
getContentPane().add(textUnderline, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(textMessage, gridBagConstraints);
invertDots.setText("Reverse-video");
invertDots.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
invertDots.setMargin(new java.awt.Insets(0, 0, 0, 0));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
getContentPane().add(invertDots, gridBagConstraints);
pack();
}// </editor-fold>//GEN-END:initComponents
private void cancel(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cancel
{//GEN-HEADEREND:event_cancel
closeDialog(null);
}//GEN-LAST:event_cancel
private void ok(java.awt.event.ActionEvent evt)//GEN-FIRST:event_ok
{//GEN-HEADEREND:event_ok
grabDialogValues();
Cell curCell = WindowFrame.needCurCell();
if (curCell == null) return;
// determine the primitive to use for the layout
NodeProto primNode = Technology.getCurrent().findNodeProto(lastLayer);
if (primNode == null)
{
System.out.println("Cannot find " + lastLayer + " primitive");
return;
}
// get the raster
int yOffset = 0;
String [] strings = lastMessage.split("\n");
for(int i=0; i<strings.length; i++)
{
String str = strings[i];
Raster ras = renderText(str, lastFont, lastSize, lastItalic, lastBold, lastUnderline);
if (ras == null) return;
DataBufferByte dbb = (DataBufferByte)ras.getDataBuffer();
byte [] samples = dbb.getData();
// create the layout text
new CreateLayoutText(curCell, primNode, lastScale, lastSeparation, ras.getWidth(), ras.getHeight(),
yOffset, lastInvertDots, samples);
yOffset += ras.getHeight();
}
closeDialog(null);
}//GEN-LAST:event_ok
/**
* Method to convert text to an array of pixels.
* This is used for text rendering, as well as for creating "layout text" which is placed as geometry in the circuit.
* @param msg the string of text to be converted.
* @param fontName the name of the font to use.
* @param tSize the size of the font to use.
* @param italic true to make the text italic.
* @param bold true to make the text bold.
* @param underline true to underline the text.
* @param invertDots true to invert dot placement.
* @return a Raster with the text bits.
*/
private static Raster renderText(String msg, String fontName, int tSize, boolean italic, boolean bold, boolean underline)
{
int fontStyle = Font.PLAIN;
if (italic) fontStyle |= Font.ITALIC;
if (bold) fontStyle |= Font.BOLD;
Font font = new Font(fontName, fontStyle, tSize);
if (font == null) {
System.out.println("Could not find font "+font+" to render text: "+msg);
return null;
}
// convert the text to a GlyphVector
FontRenderContext frc = new FontRenderContext(null, true, true);
GlyphVector gv = font.createGlyphVector(frc, msg);
LineMetrics lm = font.getLineMetrics(msg, frc);
// figure bounding box of text
Rectangle2D rasRect = gv.getLogicalBounds();
int width = (int)rasRect.getWidth();
int height = (int)(lm.getHeight()+0.5);
if (width <= 0 || height <= 0) return null;
fontStyle = font.getStyle();
if (underline) height++;
Rectangle2D rasBounds = new Rectangle2D.Double(0, lm.getAscent()-lm.getLeading(), width, height);
// if the new image is larger than what is saved, must rebuild
// create a new text buffer
BufferedImage textImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2 = textImage.createGraphics();
// now render it
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setColor(new Color(255,255,255));
g2.drawGlyphVector(gv, (float)-rasBounds.getX(), lm.getAscent()-lm.getLeading());
if (underline)
g2.drawLine(0, height-1, width-1, height-1);
// return the bits
return textImage.getData();
}
/** Closes the dialog */
private void closeDialog(java.awt.event.WindowEvent evt)//GEN-FIRST:event_closeDialog
{
grabDialogValues();
setVisible(false);
dispose();
}//GEN-LAST:event_closeDialog
private void grabDialogValues()
{
lastSize = TextUtils.atoi(textSize.getText());
lastScale = TextUtils.atof(textScale.getText());
Technology tech = Technology.getCurrent();
lastSeparation = TextUtils.atofDistance(dotSeparation.getText(), tech);
lastItalic = textItalic.isSelected();
lastBold = textBold.isSelected();
lastUnderline = textUnderline.isSelected();
lastInvertDots = invertDots.isSelected();
lastLayer = (String)textLayer.getSelectedItem();
lastFont = (String)textFont.getSelectedItem();
lastMessage = textMessage.getText();
}
/**
* Class to create a cell in a new thread.
*/
private static class CreateLayoutText extends Job
{
private Cell curCell;
private NodeProto primNode;
private double lastScale;
private double lastSeparation;
private int wid, hei, yOffset;
private boolean invertDots;
private byte [] samples;
protected CreateLayoutText(
Cell curCell,
NodeProto primNode,
double lastScale,
double lastSeparation,
int wid, int hei,
int yOffset,
boolean invertDots,
byte [] samples)
{
super("Create Layout Text", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.curCell = curCell;
this.primNode = primNode;
this.lastScale = lastScale;
this.lastSeparation = lastSeparation;
this.wid = wid;
this.hei = hei;
this.yOffset = yOffset;
this.invertDots = invertDots;
this.samples = samples;
startJob();
}
public boolean doIt() throws JobException
{
double width = lastScale - lastSeparation;
double height = lastScale - lastSeparation;
int samp = 0;
for(int y=0; y<hei; y++)
{
for(int x=0; x<wid; x++)
{
if (samples[samp++] == 0 != invertDots) continue;
Point2D center = new Point2D.Double(x*lastScale, -(y+yOffset)*lastScale);
NodeInst.newInstance(primNode, center, width, height, curCell);
}
}
return true;
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancel;
private javax.swing.JTextField dotSeparation;
private javax.swing.JCheckBox invertDots;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JButton ok;
private javax.swing.JCheckBox textBold;
private javax.swing.JComboBox textFont;
private javax.swing.JCheckBox textItalic;
private javax.swing.JComboBox textLayer;
private javax.swing.JTextArea textMessage;
private javax.swing.JTextField textScale;
private javax.swing.JTextField textSize;
private javax.swing.JCheckBox textUnderline;
// End of variables declaration//GEN-END:variables
}