/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.hepia.logisim.chronogui; import com.hepia.logisim.chronodata.ChronoData; import com.hepia.logisim.chronodata.SignalData; import com.hepia.logisim.chronodata.SignalDataBus; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import javax.imageio.ImageIO; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; /** * Export chronogram into a png image. * @author kgs */ public class ImageExporter extends javax.swing.JFrame implements ActionListener { private static final long serialVersionUID = 1L; private File fileToSave; private ChronoData chronoData; private final int tickWidth = 20; //Dimensions private final int lowPos = 32; private final int highPos = 6; private final int height = 38; int middleHeight = 38 / 2; private final Color lightGray = new Color(180, 180, 180, 100); private SignalData mSignalData; //for right panel JPanel rightBox; //for leftPanel private JTable table; private Object[][] tableData; private HashMap<SignalData, Integer> signalDataPositionInTable; // to have private SignalData[] reverseSignalDataPositionInTable; // and the reverse private JPanel leftPanel; private final int ROWHEIGHT = 38; //for timeline private JPanel timePanel; private TimelineDraw td; private ChronoFrame chronoFrame; //GUI, select image export options private JFrame frame; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JRadioButton jRadioBtn_single; private javax.swing.JRadioButton jRadioBtn_multiple; private javax.swing.JButton jBtnDone; private JLabel picture; Image img_single; Image img_multiple; final int PAGE_MAX_WIDTH = 1000; /** * * @param lp * @param td * @param sdraw */ public ImageExporter(ChronoFrame cfp,/*, TimelineDraw td, ArrayList sdraw, ,*/ ChronoData chrdata, int heightp) { this.chronoData = chrdata; this.chronoFrame = cfp; } public void createImage(File file) { if (chronoData.size() > 0) { this.fileToSave = file; createRightPanel(); createLeftPanel(); cropImage(); } else { //error message JOptionPane.showMessageDialog(null, "The chronogram is empty. Can't save it as an image."); } } private void createAndShowGUI() { frame = new JFrame("Configure image"); //Set up the content pane. addComponentsToPane(frame.getContentPane()); setAlwaysOnTop(true); frame.setLocationRelativeTo(null); //create the frame at screen center //Display the window. frame.pack(); frame.setVisible(true); } // Actually not in use. For a further development with more parameters to export the image public void addComponentsToPane(Container pane) { pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); JPanel morphPanel = new JPanel(); morphPanel.setLayout(new BoxLayout(morphPanel, BoxLayout.X_AXIS)); JPanel radioBtnMorphPanel = new JPanel(); radioBtnMorphPanel.setLayout(new BoxLayout(radioBtnMorphPanel, BoxLayout.Y_AXIS)); jRadioBtn_single = new javax.swing.JRadioButton(); jRadioBtn_multiple = new javax.swing.JRadioButton(); jRadioBtn_single.setText("Single line"); jRadioBtn_single.setToolTipText("Export chronogram as a single line"); jRadioBtn_multiple.setSelected(true); jRadioBtn_multiple.setText("Multiple lines"); jRadioBtn_multiple.setToolTipText("Export chronogram into several lines if the image is too large, more convenient for documents"); buttonGroup1 = new javax.swing.ButtonGroup(); buttonGroup1.add(jRadioBtn_single); buttonGroup1.add(jRadioBtn_multiple); radioBtnMorphPanel.add(jRadioBtn_single); radioBtnMorphPanel.add(jRadioBtn_multiple); jRadioBtn_single.addActionListener(this); jRadioBtn_multiple.addActionListener(this); jBtnDone = new javax.swing.JButton(); jBtnDone.setText("Done"); jBtnDone.setToolTipText("Save the image and close the dialog box"); jBtnDone.addActionListener(this); jBtnDone.setAlignmentX(Component.CENTER_ALIGNMENT); img_single = Toolkit.getDefaultToolkit().getImage("doc"+File.separator+"imgExport_single.gif"); img_multiple = Toolkit.getDefaultToolkit().getImage("doc"+File.separator+"imgExport_multiple.gif"); picture = new JLabel(new ImageIcon(img_multiple)); morphPanel.add(radioBtnMorphPanel); morphPanel.add(picture); pane.add(morphPanel); pane.add(jBtnDone); } // Actually not in use. For a further development with more parameters to export the image private void doNotCropImage() { JPanel pan = new JPanel(); pan.setLayout(new BorderLayout()); pan.add(leftPanel, BorderLayout.WEST); pan.add(rightBox, BorderLayout.EAST); SaveImage(pan); } // Cut the image if larger than PAGE_MAX_WIDTH. private void cropImage() { //create another image with multiple lines JPanel pan = new JPanel(); pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); pan.setBackground(Color.white); BufferedImage img = null; BufferedImage img_names = null; //convert jpanel signals to buffered image JFrame fright = new JFrame("Show remain invisible"); fright.setContentPane(rightBox); fright.pack(); BufferedImage imgright = new BufferedImage(rightBox.getWidth(), rightBox.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D gright = (Graphics2D) imgright.createGraphics(); rightBox.paint(gright); gright.dispose(); //convert jpanel names to buffered image JFrame fleft = new JFrame("Show remain invisible"); fleft.setContentPane(leftPanel); fleft.pack(); BufferedImage imgleft = new BufferedImage(leftPanel.getWidth(), leftPanel.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D gleft = (Graphics2D) imgleft.createGraphics(); leftPanel.paint(gleft); gleft.dispose(); for (int i = 0; i <= imgright.getWidth(); i = i + PAGE_MAX_WIDTH) { BufferedImage img_signals; if ((i + PAGE_MAX_WIDTH) > imgright.getWidth()) { //rest img_signals = imgright.getSubimage(i, 0, imgright.getWidth() - i, imgright.getHeight()); } else { img_signals = imgright.getSubimage(i, 0, PAGE_MAX_WIDTH, imgright.getHeight()); } JPanel panelX = new JPanel(); panelX.setLayout(new BoxLayout(panelX, BoxLayout.X_AXIS)); panelX.setAlignmentX(Component.LEFT_ALIGNMENT); JLabel jlabNames = new JLabel(new ImageIcon(imgleft)); JLabel jlabSignals = new JLabel(new ImageIcon(img_signals)); panelX.add(jlabNames); panelX.add(jlabSignals); pan.add(panelX); } SaveImage(pan); } // Create the signals panel private void createRightPanel() { rightBox = new JPanel(); rightBox.setLayout(new BoxLayout(rightBox, BoxLayout.Y_AXIS)); BufferedImage bi = null; int nbElem = 0; for (String name : chronoData.getSignalOrder()) { if (!name.equals("sysclk")) { if (nbElem == 0) { bi = CreateUpperBlankLine(chronoData.get(name).getSignalValues()); JLabel jlabl = new JLabel(new ImageIcon(bi)); rightBox.add(jlabl); } bi = CreateSignalImage(chronoData.get(name).getSignalValues(), name); JLabel jlab = new JLabel(new ImageIcon(bi)); rightBox.add(jlab); nbElem++; } } rightBox.setSize(bi.getWidth(), bi.getHeight() * nbElem); } //Create the names panel private void createLeftPanel() { leftPanel = new JPanel(); leftPanel.setLayout(new BorderLayout()); leftPanel.setBackground(Color.white); String[] names = {Strings.get("SignalNameName")}; tableData = new Object[chronoData.size() - 1][1]; int pos = 0; for (String signalName : chronoData.getSignalOrder()) { if (!signalName.equals("sysclk")) { Object[] currentData = {signalName}; tableData[pos++] = currentData; } } // creates the JTable DefaultTableModel model = new DefaultTableModel(tableData, names); table = new JTable(model) { private static final long serialVersionUID = 1L; @SuppressWarnings({"unchecked", "rawtypes"}) public Class getColumnClass(int column) { return getValueAt(0, column).getClass(); } @Override public boolean isCellEditable(int rowIndex, int colIndex) { if (colIndex == 1) { return true; // Disallow the editing of any cell } return false; } }; table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "none"); table.setRowHeight(ROWHEIGHT); JTableHeader header = table.getTableHeader(); Dimension d = header.getPreferredSize(); d.height = 20; d.width = 200; header.setPreferredSize(d); leftPanel.add(header, BorderLayout.NORTH); leftPanel.add(table, BorderLayout.CENTER); } /** * Not implemented yet. */ private void CreateTimeLine() { } /** * Create a 20 pixel high strip. */ private BufferedImage CreateUpperBlankLine(ArrayList<String> valList) { JPanel mRightPanel = new JPanel(); mRightPanel.setSize(tickWidth * valList.size(), /*lowPos + 6*/ 20); mRightPanel.setBackground(Color.white); //mRightPanel.setBackground(Color.white); BufferedImage bi = new BufferedImage(mRightPanel.getWidth(), mRightPanel.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g = bi.createGraphics(); g.setBackground(Color.white); g.clearRect(0, 0, tickWidth * valList.size(), height); g.dispose(); return bi; } /** * Create the image with the signal for the right panel. * * @param mRightPanel */ private BufferedImage CreateSignalImage(/*ChronoData chrdata, String name, SignalData sigData,*/ArrayList<String> valList, String name) { String prec, suiv; int busCrossingPosition = tickWidth - 5 < 1 ? 0 : 5; JPanel mRightPanel = new JPanel(); int posX = 0; int i = 0; mRightPanel.setSize(tickWidth * valList.size(), lowPos + 6); mRightPanel.setBackground(Color.white); BufferedImage bi = new BufferedImage(mRightPanel.getWidth(), mRightPanel.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g = bi.createGraphics(); prec = valList.get(i); g.setBackground(Color.white); g.clearRect(0, 0, tickWidth * valList.size(), height); g.setColor(Color.black); //for each value in the signal for (int index = 0; index < valList.size(); index++) { suiv = valList.get(i++); String transi = prec + suiv; if (suiv.contains("E")) { g.setColor(Color.red); g.drawLine(posX, highPos, posX + tickWidth, middleHeight); g.drawLine(posX, middleHeight, posX + tickWidth, highPos); g.drawLine(posX, middleHeight, posX + tickWidth, lowPos); g.drawLine(posX, lowPos, posX + tickWidth, middleHeight); g.setColor(Color.black); } else if (suiv.contains("x")) { g.setColor(Color.blue); g.drawLine(posX, highPos, posX + tickWidth, middleHeight); g.drawLine(posX, middleHeight, posX + tickWidth, highPos); g.drawLine(posX, middleHeight, posX + tickWidth, lowPos); g.drawLine(posX, lowPos, posX + tickWidth, middleHeight); g.setColor(Color.black); } else if (suiv.equals("0")) { g.drawLine(posX, lowPos, posX + tickWidth, lowPos); } else if (suiv.equals("1")) { g.setColor(lightGray); g.fillRect(posX + 1, highPos, tickWidth, lowPos - highPos); g.setColor(Color.black); g.drawLine(posX, highPos, posX + tickWidth, highPos); } else { mSignalData = this.chronoData.get(name); if (mSignalData instanceof SignalDataBus) { SignalDataBus sdb = (SignalDataBus) mSignalData; // first value if (i == 2) { g.drawString(/*suiv*/sdb.getValueInFormat(suiv), posX + 2, middleHeight); } // bus transition if (!suiv.contains("x") && !suiv.contains("E") && !suiv.equals(prec)) { g.drawLine(posX, lowPos, posX + busCrossingPosition, highPos); g.drawLine(posX, highPos, posX + busCrossingPosition, lowPos); g.drawLine(posX + busCrossingPosition, highPos, posX + tickWidth, highPos); g.drawLine(posX + busCrossingPosition, lowPos, posX + tickWidth, lowPos); g.drawString(/*suiv*/sdb.getValueInFormat(suiv), posX + tickWidth, middleHeight); } else { g.drawLine(posX, lowPos, posX + tickWidth, lowPos); g.drawLine(posX, highPos, posX + tickWidth, highPos); } } } // transition if (transi.equals("10")) { g.drawLine(posX, highPos, posX, lowPos); } else if (transi.equals("01")) { g.drawLine(posX, lowPos, posX, highPos); } prec = suiv; posX += tickWidth; } g.dispose(); return bi; } private void SaveImage(JPanel jp) { JFrame f = new JFrame("Show remain invisible"); f.setContentPane(jp); f.pack(); BufferedImage img = new BufferedImage(jp.getWidth(), jp.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) img.createGraphics(); jp.paint(g); g.dispose(); try { ImageIO.write(img, "png", fileToSave); //ImageIO.write(img, "png", new File("savedcall.png")); } catch (IOException ex) { ex.printStackTrace(); } } // Actually not in use. For a further development with more parameters to export the image @Override public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand() == "Single line") { picture.setIcon(new ImageIcon(img_single)); } else if (ae.getActionCommand() == "Multiple lines") { picture.setIcon(new ImageIcon(img_multiple)); } else if (ae.getActionCommand() == "Done") { if (jRadioBtn_multiple.isSelected()) { cropImage(); } else { doNotCropImage(); } frame.dispose(); } } }